cvpfus Codex commited on
Commit ·
5b9a6bf
1
Parent(s): d1cf993
Add visible repeat reader control
Browse filesCo-authored-by: Codex <codex@openai.com>
- FIELD_NOTES.md +1 -1
- README.md +1 -1
- SUBMISSION.md +1 -1
- app.py +1 -1
- scripts/verify.py +3 -1
- static/app.js +11 -1
- static/index.html +1 -0
FIELD_NOTES.md
CHANGED
|
@@ -46,7 +46,7 @@ When screen reader mode turns on, the reader cursor starts at the focused articl
|
|
| 46 |
|
| 47 |
Reader shortcuts deliberately ignore form controls. A user can adjust the Kokoro voice, speed slider, and auto-advance checkbox without the page treating those keystrokes as navigation commands.
|
| 48 |
|
| 49 |
-
The reader bar exposes the same shortcut contract through `aria-keyshortcuts`.
|
| 50 |
|
| 51 |
Each narration is also added to a visible transcript log. This gives the demo a second accessible surface: users can review what was spoken, copy the session transcript, or clear it before exploring another part of the article.
|
| 52 |
|
|
|
|
| 46 |
|
| 47 |
Reader shortcuts deliberately ignore form controls. A user can adjust the Kokoro voice, speed slider, and auto-advance checkbox without the page treating those keystrokes as navigation commands.
|
| 48 |
|
| 49 |
+
The reader bar exposes the same shortcut contract through `aria-keyshortcuts`. Repeat and Stop also have visible buttons, which keeps the assistive command model available to pointer users and easy for judges to inspect.
|
| 50 |
|
| 51 |
Each narration is also added to a visible transcript log. This gives the demo a second accessible surface: users can review what was spoken, copy the session transcript, or clear it before exploring another part of the article.
|
| 52 |
|
README.md
CHANGED
|
@@ -107,7 +107,7 @@ When screen-reader mode turns on, it selects the focused or most visible article
|
|
| 107 |
|
| 108 |
Global reader shortcuts ignore buttons, links, selects, and inputs so the voice, speed, and auto-advance controls remain usable while reader mode is active.
|
| 109 |
|
| 110 |
-
Reader buttons expose `aria-keyshortcuts`, and the
|
| 111 |
|
| 112 |
The session panel keeps a transcript of recent narration with copy and clear controls, making the spoken path inspectable during demos and useful for the Field Notes write-up.
|
| 113 |
|
|
|
|
| 107 |
|
| 108 |
Global reader shortcuts ignore buttons, links, selects, and inputs so the voice, speed, and auto-advance controls remain usable while reader mode is active.
|
| 109 |
|
| 110 |
+
Reader buttons expose `aria-keyshortcuts`, and the Repeat and Stop shortcuts are mirrored by visible controls so pointer and keyboard users share the same command surface.
|
| 111 |
|
| 112 |
The session panel keeps a transcript of recent narration with copy and clear controls, making the spoken path inspectable during demos and useful for the Field Notes write-up.
|
| 113 |
|
SUBMISSION.md
CHANGED
|
@@ -48,7 +48,7 @@ The prototype is designed for a live hackathon demo: every model-facing path has
|
|
| 48 |
|
| 49 |
The checks in `/api/demo-script` include curl and PowerShell-friendly `curl.exe` commands generated from `PUBLIC_BASE_URL`, and the POST checks include sample JSON bodies for `/api/reader-brain` and `/api/speak`.
|
| 50 |
|
| 51 |
-
The accessibility audit also documents reader-mode details that matter during judging: the active item is exposed as a reader cursor with focus, visible outline, stable id, and `aria-current`; global shortcuts ignore form controls so voice, speed, and auto-advance settings remain usable while reader mode is active; reader controls expose `aria-keyshortcuts`
|
| 52 |
|
| 53 |
The image receipts keep the generated-asset claim inspectable: each article illustration names the planned `black-forest-labs/FLUX.2-klein-4B` path, prompt, seed, and bundled fallback asset.
|
| 54 |
|
|
|
|
| 48 |
|
| 49 |
The checks in `/api/demo-script` include curl and PowerShell-friendly `curl.exe` commands generated from `PUBLIC_BASE_URL`, and the POST checks include sample JSON bodies for `/api/reader-brain` and `/api/speak`.
|
| 50 |
|
| 51 |
+
The accessibility audit also documents reader-mode details that matter during judging: the active item is exposed as a reader cursor with focus, visible outline, stable id, and `aria-current`; global shortcuts ignore form controls so voice, speed, and auto-advance settings remain usable while reader mode is active; reader controls expose `aria-keyshortcuts` plus visible Repeat and Stop commands.
|
| 52 |
|
| 53 |
The image receipts keep the generated-asset claim inspectable: each article illustration names the planned `black-forest-labs/FLUX.2-klein-4B` path, prompt, seed, and bundled fallback asset.
|
| 54 |
|
app.py
CHANGED
|
@@ -421,7 +421,7 @@ def accessibility_audit_core() -> dict[str, Any]:
|
|
| 421 |
"id": "keyboard_navigation",
|
| 422 |
"label": "Keyboard navigation",
|
| 423 |
"status": "pass",
|
| 424 |
-
"evidence": "Reader controls expose Space, N, P, H, I, S, R, and Esc shortcuts through the manifest and aria-keyshortcuts.",
|
| 425 |
},
|
| 426 |
{
|
| 427 |
"id": "reader_cursor",
|
|
|
|
| 421 |
"id": "keyboard_navigation",
|
| 422 |
"label": "Keyboard navigation",
|
| 423 |
"status": "pass",
|
| 424 |
+
"evidence": "Reader controls expose Space, N, P, H, I, S, R, and Esc shortcuts through the manifest, visible buttons, and aria-keyshortcuts.",
|
| 425 |
},
|
| 426 |
{
|
| 427 |
"id": "reader_cursor",
|
scripts/verify.py
CHANGED
|
@@ -37,9 +37,10 @@ def verify_static_assets() -> None:
|
|
| 37 |
assert_true('data-reader-type="heading"' in index_html, "Article should mark heading reader nodes")
|
| 38 |
assert_true('data-reader-type="image"' in index_html, "Article should mark image reader nodes")
|
| 39 |
assert_true('aria-live="polite"' in index_html, "Article should expose an aria-live narration region")
|
| 40 |
-
for shortcut in ['aria-keyshortcuts="Space"', 'aria-keyshortcuts="N"', 'aria-keyshortcuts="S"']:
|
| 41 |
assert_true(shortcut in index_html, f"Reader controls should expose {shortcut}")
|
| 42 |
assert_true("transcriptLog" in index_html, "Article should expose a visible transcript log")
|
|
|
|
| 43 |
assert_true("stopButton" in index_html, "Reader controls should expose a visible stop command")
|
| 44 |
assert_true("demoScriptList" in index_html, "Article should expose the judge demo runbook")
|
| 45 |
assert_true("demoApiCheckList" in index_html, "Article should expose judge API evidence checks")
|
|
@@ -58,6 +59,7 @@ def verify_static_assets() -> None:
|
|
| 58 |
)
|
| 59 |
assert_true("aria-current" in app_js, "Reader mode should expose the active item as current")
|
| 60 |
assert_true("shouldHandleReaderShortcut" in app_js, "Reader shortcuts should not hijack form controls")
|
|
|
|
| 61 |
assert_true("controls.stop.click()" in app_js, "Escape should route through the visible stop command")
|
| 62 |
assert_true("reader-node-" in app_js, "Reader nodes should receive stable ids for control context")
|
| 63 |
assert_true("narrate(node.index)" in app_js, "Reader mode should support click-to-read article items")
|
|
|
|
| 37 |
assert_true('data-reader-type="heading"' in index_html, "Article should mark heading reader nodes")
|
| 38 |
assert_true('data-reader-type="image"' in index_html, "Article should mark image reader nodes")
|
| 39 |
assert_true('aria-live="polite"' in index_html, "Article should expose an aria-live narration region")
|
| 40 |
+
for shortcut in ['aria-keyshortcuts="Space"', 'aria-keyshortcuts="N"', 'aria-keyshortcuts="R"', 'aria-keyshortcuts="S"']:
|
| 41 |
assert_true(shortcut in index_html, f"Reader controls should expose {shortcut}")
|
| 42 |
assert_true("transcriptLog" in index_html, "Article should expose a visible transcript log")
|
| 43 |
+
assert_true("repeatButton" in index_html, "Reader controls should expose a visible repeat command")
|
| 44 |
assert_true("stopButton" in index_html, "Reader controls should expose a visible stop command")
|
| 45 |
assert_true("demoScriptList" in index_html, "Article should expose the judge demo runbook")
|
| 46 |
assert_true("demoApiCheckList" in index_html, "Article should expose judge API evidence checks")
|
|
|
|
| 59 |
)
|
| 60 |
assert_true("aria-current" in app_js, "Reader mode should expose the active item as current")
|
| 61 |
assert_true("shouldHandleReaderShortcut" in app_js, "Reader shortcuts should not hijack form controls")
|
| 62 |
+
assert_true("controls.repeat.click()" in app_js, "R should route through the visible repeat command")
|
| 63 |
assert_true("controls.stop.click()" in app_js, "Escape should route through the visible stop command")
|
| 64 |
assert_true("reader-node-" in app_js, "Reader nodes should receive stable ids for control context")
|
| 65 |
assert_true("narrate(node.index)" in app_js, "Reader mode should support click-to-read article items")
|
static/app.js
CHANGED
|
@@ -38,6 +38,7 @@ const controls = {
|
|
| 38 |
heading: document.querySelector("#headingButton"),
|
| 39 |
image: document.querySelector("#imageButton"),
|
| 40 |
summary: document.querySelector("#summaryButton"),
|
|
|
|
| 41 |
stop: document.querySelector("#stopButton"),
|
| 42 |
};
|
| 43 |
|
|
@@ -670,12 +671,21 @@ function nextByType(type) {
|
|
| 670 |
liveNarration.textContent = `No ${readerTypeLabel(type).toLowerCase()} items in this article.`;
|
| 671 |
}
|
| 672 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 673 |
toggle.addEventListener("click", () => setEnabled(!enabled));
|
| 674 |
controls.next.addEventListener("click", () => narrate(Math.min(currentIndex + 1, nodes.length - 1)));
|
| 675 |
controls.prev.addEventListener("click", () => narrate(Math.max(currentIndex - 1, 0)));
|
| 676 |
controls.heading.addEventListener("click", () => nextByType("heading"));
|
| 677 |
controls.image.addEventListener("click", () => nextByType("image"));
|
| 678 |
controls.summary.addEventListener("click", () => summarizeCurrentSection());
|
|
|
|
| 679 |
controls.stop.addEventListener("click", () => {
|
| 680 |
stopAudio();
|
| 681 |
liveNarration.textContent = "Reading stopped.";
|
|
@@ -776,6 +786,6 @@ document.addEventListener("keydown", (event) => {
|
|
| 776 |
if (key === "h") controls.heading.click();
|
| 777 |
if (key === "i") controls.image.click();
|
| 778 |
if (key === "s") controls.summary.click();
|
| 779 |
-
if (key === "r"
|
| 780 |
if (key === "escape") controls.stop.click();
|
| 781 |
});
|
|
|
|
| 38 |
heading: document.querySelector("#headingButton"),
|
| 39 |
image: document.querySelector("#imageButton"),
|
| 40 |
summary: document.querySelector("#summaryButton"),
|
| 41 |
+
repeat: document.querySelector("#repeatButton"),
|
| 42 |
stop: document.querySelector("#stopButton"),
|
| 43 |
};
|
| 44 |
|
|
|
|
| 671 |
liveNarration.textContent = `No ${readerTypeLabel(type).toLowerCase()} items in this article.`;
|
| 672 |
}
|
| 673 |
|
| 674 |
+
function repeatCurrentItem() {
|
| 675 |
+
if (currentIndex >= 0) {
|
| 676 |
+
narrate(currentIndex);
|
| 677 |
+
} else {
|
| 678 |
+
liveNarration.textContent = "No reader item selected.";
|
| 679 |
+
}
|
| 680 |
+
}
|
| 681 |
+
|
| 682 |
toggle.addEventListener("click", () => setEnabled(!enabled));
|
| 683 |
controls.next.addEventListener("click", () => narrate(Math.min(currentIndex + 1, nodes.length - 1)));
|
| 684 |
controls.prev.addEventListener("click", () => narrate(Math.max(currentIndex - 1, 0)));
|
| 685 |
controls.heading.addEventListener("click", () => nextByType("heading"));
|
| 686 |
controls.image.addEventListener("click", () => nextByType("image"));
|
| 687 |
controls.summary.addEventListener("click", () => summarizeCurrentSection());
|
| 688 |
+
controls.repeat.addEventListener("click", () => repeatCurrentItem());
|
| 689 |
controls.stop.addEventListener("click", () => {
|
| 690 |
stopAudio();
|
| 691 |
liveNarration.textContent = "Reading stopped.";
|
|
|
|
| 786 |
if (key === "h") controls.heading.click();
|
| 787 |
if (key === "i") controls.image.click();
|
| 788 |
if (key === "s") controls.summary.click();
|
| 789 |
+
if (key === "r") controls.repeat.click();
|
| 790 |
if (key === "escape") controls.stop.click();
|
| 791 |
});
|
static/index.html
CHANGED
|
@@ -163,6 +163,7 @@
|
|
| 163 |
<button id="headingButton" type="button" title="Next heading" aria-keyshortcuts="H">Heading</button>
|
| 164 |
<button id="imageButton" type="button" title="Next image" aria-keyshortcuts="I">Image</button>
|
| 165 |
<button id="summaryButton" type="button" title="Summarize current section" aria-keyshortcuts="S">Summary</button>
|
|
|
|
| 166 |
<button id="stopButton" type="button" title="Stop audio" aria-keyshortcuts="Escape">Stop</button>
|
| 167 |
<label>
|
| 168 |
Voice
|
|
|
|
| 163 |
<button id="headingButton" type="button" title="Next heading" aria-keyshortcuts="H">Heading</button>
|
| 164 |
<button id="imageButton" type="button" title="Next image" aria-keyshortcuts="I">Image</button>
|
| 165 |
<button id="summaryButton" type="button" title="Summarize current section" aria-keyshortcuts="S">Summary</button>
|
| 166 |
+
<button id="repeatButton" type="button" title="Repeat current item" aria-keyshortcuts="R">Repeat</button>
|
| 167 |
<button id="stopButton" type="button" title="Stop audio" aria-keyshortcuts="Escape">Stop</button>
|
| 168 |
<label>
|
| 169 |
Voice
|