feat(voyage): add export flow + A11Y baseline — v4.2 Step 11 [skip-docs]

Closes Wave 2 (Steps 6-11) of v4.2 implementation. Playground now
delivers the complete annotation pipeline: render -> create gestures
-> sidebar -> export.

Export flow:
  - 'Eksporter batch' button in sidebar export-bar
  - Export modal with role="dialog" aria-modal="true"
  - Generated /trekrevise command-string ready to copy
  - Two paths:
      navigator.clipboard.writeText (modern) with execCommand('copy')
      legacy fallback for cross-browser support
      Blob + URL.createObjectURL download as annotated-{brief|plan|review}.md
  - buildAnnotatedMarkdown injects voyage:anchor comments above target
    lines (mirrors lib/parsers/anchor-parser.mjs addAnchors() behaviour)

Resolve-til-arkiv (Google Docs pattern, per research-06):
  - Post-export marks pending drafts as exported (NOT delete)
  - Tab 2 ('Alle revisjoner') surfaces history with revision-stamp
  - aria-live='polite' toast announces export status

A11Y baseline (per research-06 + llm-security A11Y-RAPPORT.md):
  - aria-live='polite' toast region (Step 1)
  - Skip-to-main link (.visually-hidden + #main target)
  - role='dialog' + aria-modal='true' on form modal (Step 9)
                                    on export modal (Step 11)
  - role='tablist' / role='tab' / aria-selected / tabindex roving (Step 10)
  - aria-controls + aria-labelledby on tabpanels
  - aria-pressed on intent buttons (radiogroup-like)
  - aria-expanded + aria-controls on sidebar FAB
  - aria-hidden='true' on decorative SVG paths
  - aria-label on icon-only buttons
  - .visually-hidden labels for textarea + clipboard helper

Test coverage: tests/playground/voyage-playground.test.mjs +4 cases —
aria-live='polite', Skip to main, Blob, clipboard.writeText.

Verify: node --test tests/playground/voyage-playground.test.mjs ->
22 pass / 0 fail.
Full npm test: 596 pass / 0 fail / 2 skipped (Docker).

Refs plan.md Step 11 + research-06 + llm-security A11Y baseline.
This commit is contained in:
Kjell Tore Guttormsen 2026-05-09 15:27:01 +02:00
commit 97b6f5406e
2 changed files with 261 additions and 0 deletions

View file

@ -124,3 +124,25 @@ test('voyage-playground.html declares tabindex (Step 10 focus management)', () =
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /tabindex/i, 'sidebar tabs must use tabindex for keyboard focus management');
});
// --- Step 11 — export flow + A11Y baseline -----------------------------
test('voyage-playground.html declares aria-live="polite" toast region (Step 11 A11Y)', () => {
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /aria-live="polite"/, 'aria-live="polite" toast region required for status announcements');
});
test('voyage-playground.html includes Skip to main link (Step 11 A11Y baseline)', () => {
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /Skip to main/, 'Skip to main content link required for keyboard A11Y');
});
test('voyage-playground.html uses Blob for download flow (Step 11 export)', () => {
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /\bnew Blob\b/, 'Blob download path required for annotated.md export');
});
test('voyage-playground.html uses clipboard.writeText for copy flow (Step 11 export)', () => {
const text = readFileSync(HTML, 'utf-8');
assert.match(text, /clipboard\.writeText/, 'navigator.clipboard.writeText path required for command-copy');
});