cvpfus Codex commited on
Commit
5b9a6bf
·
1 Parent(s): d1cf993

Add visible repeat reader control

Browse files

Co-authored-by: Codex <codex@openai.com>

Files changed (7) hide show
  1. FIELD_NOTES.md +1 -1
  2. README.md +1 -1
  3. SUBMISSION.md +1 -1
  4. app.py +1 -1
  5. scripts/verify.py +3 -1
  6. static/app.js +11 -1
  7. 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`. Escape also has a visible Stop button, 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
 
 
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 Escape shortcut is mirrored by a visible Stop control 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
 
 
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` and a visible Stop command for Escape.
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" && currentIndex >= 0) narrate(currentIndex);
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