Skip to content

Commit a997022

Browse files
committed
Site: honest offline copy, privacy engines, copy a11y
1 parent bd9d6a2 commit a997022

5 files changed

Lines changed: 53 additions & 6 deletions

File tree

web/content/features/clipboard-preservation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ Many voice-to-text apps on macOS handle clipboard carelessly. VocaMac is one of
117117
| Feature | VocaMac | Typical App |
118118
|---------|---------|-------------|
119119
| **Preserves clipboard** | Yes | No |
120-
| **Works offline** | Yes | No |
120+
| **On-device transcription after model download** | Yes | No |
121121
| **Adjustable silence detection** | Yes | Rare |
122122
| **Menu bar integration** | Yes | No |
123123
| **Open source** | Yes | No |

web/layouts/features/single.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ <h1 id="page-title">{{ .Title }}</h1>
3030
</div>
3131
</div>
3232
</section>
33-
<section class="section" aria-labelledby="feature-content-title">
33+
<section class="section">
3434
<div class="shell doc-layout">
35-
<article class="prose" id="feature-content-title">
35+
<article class="prose">
3636
{{ .Content }}
3737
<div class="feature-cta">
3838
<p>Ready to try {{ .Title }}?</p>

web/layouts/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ <h2 class="section-title" id="privacy-title">Your voice has a path.<br>We show y
201201
<li class="route-step">
202202
<div class="route-node">
203203
<span class="route-icon">{{ partial "icon.html" "chip" }}</span>
204-
<span class="route-name">{{ $p.engine.name }} / {{ $p.engine.runtime }}</span>
205-
<span class="route-sub">Runs on your Mac</span>
204+
<span class="route-name">Selected on-device engine</span>
205+
<span class="route-sub">WhisperKit/CoreML, Parakeet, Apple Speech, ONNX</span>
206206
</div>
207207
<svg class="route-link" width="56" height="24" viewBox="0 0 56 24" aria-hidden="true"><path class="route-track" d="M2 12h48"/><path class="route-pulse" d="M2 12h48"/><path class="route-head" d="M46 7.5 50.5 12 46 16.5"/></svg>
208208
</li>

web/static/script.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,24 @@
6161
/* ---------- Copy buttons ---------- */
6262

6363
var canCopy = !!(navigator.clipboard && navigator.clipboard.writeText);
64+
var copyBlocks = document.querySelectorAll("[data-copy]");
65+
var copyStatus = null;
66+
67+
if (canCopy && copyBlocks.length) {
68+
copyStatus = document.createElement("div");
69+
copyStatus.className = "sr-only";
70+
copyStatus.setAttribute("aria-live", "polite");
71+
copyStatus.setAttribute("aria-atomic", "true");
72+
document.body.appendChild(copyStatus);
73+
}
74+
75+
var announceCopy = function (message) {
76+
if (!copyStatus) { return; }
77+
copyStatus.textContent = "";
78+
copyStatus.textContent = message;
79+
};
6480

65-
document.querySelectorAll("[data-copy]").forEach(function (block) {
81+
copyBlocks.forEach(function (block) {
6682
var source = block.querySelector("code");
6783
if (!source || !canCopy) { return; }
6884

@@ -84,16 +100,19 @@
84100
var restore = function () {
85101
button.textContent = "Copy";
86102
button.classList.remove("is-copied");
103+
announceCopy("");
87104
};
88105

89106
navigator.clipboard.writeText(text).then(
90107
function () {
91108
button.textContent = "Copied";
92109
button.classList.add("is-copied");
110+
announceCopy("Copied");
93111
setTimeout(restore, 2000);
94112
},
95113
function () {
96114
button.textContent = "Press ⌘C";
115+
announceCopy("Press ⌘C");
97116
setTimeout(restore, 2000);
98117
}
99118
);

web/tests/site.test.mjs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,34 @@ test("every rendered page has one heading and image alternatives", async () => {
164164
}
165165
});
166166

167+
test("keeps the site-audit copy and a11y fixes", async () => {
168+
assert.match(index, /Selected on-device engine/);
169+
assert.match(index, /WhisperKit\/CoreML, Parakeet, Apple Speech, ONNX/);
170+
assert.doesNotMatch(
171+
index,
172+
/<span class="route-name">WhisperKit \/ CoreML<\/span>/,
173+
);
174+
175+
const clipboard = await readFile(
176+
join(outputRoot, "features/clipboard-preservation/index.html"),
177+
"utf8",
178+
);
179+
assert.match(clipboard, /On-device transcription after model download/);
180+
assert.doesNotMatch(clipboard, /Works offline/);
181+
182+
for (const page of pages) {
183+
const rel = relative(outputRoot, page);
184+
if (!rel.startsWith("features/") || rel === "features/index.html") continue;
185+
const html = await readFile(page, "utf8");
186+
assert.doesNotMatch(html, /aria-labelledby="feature-content-title"/);
187+
assert.doesNotMatch(html, /id="feature-content-title"/);
188+
}
189+
190+
assert.match(script, /aria-live", "polite"/);
191+
assert.match(script, /announceCopy\("Copied"\)/);
192+
assert.match(script, /announceCopy\("Press C"\)/);
193+
});
194+
167195
test("all rendered local references resolve", async () => {
168196
const references = new Set();
169197
for (const page of pages) {

0 commit comments

Comments
 (0)