Commit graph

24 commits

Author SHA1 Message Date
b70b480d0d feat(voyage): build A11Y-panel from DS-primitives (greenfield) 2026-05-10 17:56:49 +02:00
df0e7837af feat(voyage): implement two-opacity pattern (active/inactive/resolved) 2026-05-10 17:53:43 +02:00
224517f205 feat(voyage): implement J/K keyboard navigation + Esc + aria-live announces
Step 20 of v4.3 playground plan. Document-level keydown handler:
  - J = next annotation (next sorted-by-line draft, wraps)
  - K = prev annotation (wraps)
  - ] = toggle sidebar visibility
  - Escape = clear active anchor + sidebar list selection

Active annotation gets yellow-tint (Step 18 setActiveAnchor) and the
matching gutter-badge receives focus + scrollIntoView. Aria-live region
announces position + target: "Annotering 3 av 7: <target> — <snippet>".

Skips input/textarea/select/contenteditable so playground never steals
keystrokes from form fields. Modifier keys (Ctrl/Alt/Meta) pass through
to browser shortcuts. Wired in init() after dashboard nav.

Trace: SC2 (WCAG AA keyboard), SC6, research/04 Dim 2 + Insight 5 +
Recommendation keyboard-navigation.
2026-05-10 17:10:59 +02:00
6db7c72511 feat(voyage): implement hidden-by-default sidebar-rail with ordered list + filter + jumplist count
Step 19 of v4.3 playground plan. Sidebar now default aria-hidden=true
(translateX collapses panel, leaves 40px FAB rail). FAB toggle has
data-action=toggle-sidebar for keyboard binding (] in Step 20).

New annotation-list section in sidebar panel:
  - filter radiogroup: Alle (default), Åpne (unresolved), Resolved
  - voyage-jumplist ordered list with numbered badges matching the
    gutter-badge ordering (sorted by line ASC)
  - aria-live jumplist count: "X av N" (filtered/total)
  - click list-item -> setActiveAnchor + scrollIntoView + data-active

renderAnnotationList wires into mountRender so list refreshes on every
render. Filter state (voyageFilterState) persists across renders within
the session.

Trace: SC6, research/04 Dim 1 (hidden-by-default) + Insight 1 +
Recommendation sidebar/navigation.
2026-05-10 17:09:26 +02:00
84f41014f9 feat(voyage): replace pencil-icon with numbered-badge gutter + yellow-tint highlight
Step 18 of v4.3 playground plan. Replaces v4.2 Gesture 2 pencil-icon
hover-reveal with numbered circular badges in the left gutter (one per
anchored paragraph; ordering matches sidebar jumplist). 2-3px accent stripe
extends right from the badge into the gutter. Yellow-tint highlight
(rgba 255, 235, 59, 0.25 — Google Docs pattern) applies to the anchored
paragraph when an annotation is active. Body text never reflowed or
recolored. Gesture 1 (text-select adder) and Gesture 3 (page-level note)
remain for new annotation creation.

CSS uses --color-scope-voyage token for badge background and stripe.
JS adds injectAnchorBadges() + setActiveAnchor() and rewires mountRender.

Trace: SC1 + SC6, research/04 Insight 3 + Recommendation marker-design.
2026-05-10 17:06:59 +02:00
75130fe979 feat(voyage): implement block-boundary-fallback for code-fence/table/list anchors
Step 17 of v4.3 playground plan. Pure function relocateAnchorsToBlockBoundaries
(text, anchors) detects atomic markdown blocks (fenced code, tables, deeply
nested lists) and relocates anchor-comment insertion to the line BEFORE block
opening rather than inside the block. Pure markdown-text -> markdown-text
transform (no DOM, no markdown-it dependency).

Companion test tests/integration/annotation-block-boundary.test.mjs extracts
the function via balanced-brace scan and exercises it through Function() —
7 unit tests covering empty anchors, outside-block stays, fenced-code
relocation, table relocation, deeply-nested list relocation, mixed
inside/outside, and shape contract.

Trace: SC6, research/04 Dim 3 (Notion block-level fallback), plan-critic
major #6 (DOM-vs-no-DOM contradiction resolved via pure-function design).
2026-05-10 17:04:27 +02:00
3973be2a90 feat(voyage): sync browser-side anchor-parser regex with Node-side allowlist
Step 16 of v4.3 playground plan. Mirror lib/parsers/anchor-parser.mjs
ANCHOR_LINE_RE + ATTR_RE + ID_RE constants verbatim into voyage-playground.html
inline-script (file COLON COLON  scheme compat — no ES-module). parseAnchor(line)
validates id matches ANN-NNNN, target non-empty, line positive integer,
snippet ≤80c, intent in {fix,change,question,block}.

Trace: SC6, research/02 Sec T4, plan-critic blocker B4 + scope-guardian DEP-3.
Cross-file regex sync verified by static-grep test.
2026-05-10 17:01:14 +02:00
946eb7ab0f feat(voyage): implement drill-down + back-nav + URL-parameter project
Step 15 (v4.3 Sesjon 3 — Wave 3) — wires the dashboard fleet-tiles
to a drill-down view with breadcrumb update + back-to-dashboard
navigation + browser back/forward restoration via popstate.

renderArtifactDetail(artifactKey) renders the chosen artifact into
the #voyage-detail slot using renderPageShell + renderArtifact:
  - brief / plan / review → markdown render
  - progress              → JSON pretty-print in <pre>
  - research              → list of all research-briefs
  - missing entry         → "Artifact mangler" placeholder

Click delegation on .fleet-tile[data-artifact] triggers detail render
+ pushDetailURL (?artifact=<key>); data-action=back-to-dashboard
returns to the dashboard view + pushDashboardURL. Topbar breadcrumb
gets a third segment for detail views.

URL-parameter deep-linking: at page-load, ?project=<basePath>
surfaces a guide-panel hint explaining that webkitdirectory requires
a user-gesture; the hint links to the same Last prosjektmappe button
that wireProjectLoader already exposes. popstate handler restores
the view-state on browser back/forward (no-op when no project loaded).

Test additions (4): renderArtifactDetail function, URLSearchParams
presence, data-action=back-to-dashboard attribute, popstate listener.
2026-05-10 16:46:13 +02:00
a479f47b4e feat(voyage): implement dashboard via fleet-grid + fleet-tile with status vocabulary
Step 14 (v4.3 Sesjon 3 — Wave 3) — adds renderDashboard pipeline that
turns a ProjectArtifacts struct (produced by loadProjectDirectory in
Step 13) into a fleet-grid of fleet-tiles, one per artifact-type
(brief / plan / review / research / progress).

Status vocabulary: complete, in-progress, blocked, missing, stale
Severity mapping: missing → critical, blocked → high, in-progress
+ stale → medium, complete → low. Severity drives DS color tokens
via [data-severity] attribute selectors.

When loadProjectDirectory completes, dashboard takes over the main
stage (paste-flow elements hidden); topbar updates with project
breadcrumb. Step 13's pipeline already calls renderDashboard via
graceful-fallback, so wiring is automatic.

Test additions (4): fleet-grid + fleet-tile presence, renderDashboard
function declaration, status vocabulary completeness.
2026-05-10 16:43:22 +02:00
2bf766673d feat(voyage): implement loadProjectDirectory pipeline (validate + classify + read) 2026-05-10 16:19:28 +02:00
974835537a feat(voyage): add drag-drop with webkitGetAsEntry + Firefox 150 Win guard 2026-05-10 16:18:23 +02:00
e04915882e feat(voyage): add webkitdirectory primary project-loader 2026-05-10 16:17:13 +02:00
820ebf0f02 feat(voyage): add A11Y skip-link + aria-label foundations 2026-05-10 16:16:18 +02:00
7c468097cf feat(voyage): implement page-shell pattern (eyebrow + title + lede + meta) 2026-05-10 16:15:25 +02:00
0eb56edb6d feat(voyage): implement renderTopbar with badge--scope-voyage and breadcrumb 2026-05-10 16:15:02 +02:00
109a17e044 feat(voyage): add theme toggle button + delegated handler 2026-05-10 10:29:27 +02:00
79e5609a0e feat(voyage): port theme-bootstrap IIFE from llm-security; set data-theme dark default 2026-05-10 10:28:03 +02:00
ee1f4055c9 refactor(voyage): fix CSS link-order + replace literal pixel font-sizes 2026-05-10 10:27:24 +02:00
01f31e73c3 refactor(voyage): replace non-canonical DS tokens with canonical names
Replaces --color-accent/--font-mono/--color-bg/--color-bg-hover/--color-bg-code/--color-text-muted/--color-text/--color-success/--color-border with their canonical DS-token equivalents. Drops all hex-fallbacks in var() per Step 4 spec.

Deviation note: plan's literal Verify regex "--color-bg\b" matches --color-bg-soft (canonical) due to \b dash-boundary semantics. Manifest must_contain (--color-scope-voyage) verifies; corrected intent-regex (--color-bg[^-a-z]|--color-bg$) returns empty. Treated as PASS on implementation grounds.
2026-05-10 10:26:12 +02:00
97b6f5406e 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.
2026-05-09 15:27:01 +02:00
125bfb02b2 feat(voyage): add playground sidebar with tabs + critique-card-list — v4.2 Step 10 [skip-docs]
Right-collapsible sidebar (320px) with 40px icon-rail when collapsed
(per critical decision #4 + research-06):

- 2-state FAB toggle (aria-expanded toggles aria-hidden on aside)
- Visible draft-count badge on FAB (mitigates 'forgot to export' friction)
- Two tabs:
    'Denne planen (N drafts)' — pending annotations
    'Alle revisjoner (M historiske)' — exported (Step 11 wires this)
- role="tablist" + role="tab" + aria-selected + tabindex roving
- ArrowLeft/ArrowRight keyboard nav between tabs
- .findings list of .critique-card per annotation
- Click on critique-card scrolls to anchor + .lint-annotation-glow
  1s pulse animation
- Sort-by-location (Hypothes.is pattern; line ASC)

Card visual: intent-badge (color-coded fix=green/change=blue/question=yellow/block=red),
ANN-NNNN ID, snippet preview, comment, exported-status.

Layout: main shifts margin-right: 320px above 1024px viewport so the
sidebar doesn't overlap the rendered artifact.

saveModalAsAnnotation + mountRender hooks now refresh the sidebar so
new drafts appear immediately and re-render preserves visibility.

Test coverage: tests/playground/voyage-playground.test.mjs +2 cases —
role="tablist", tabindex.

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

Refs plan.md Step 10 + critical decision #4 + research-06.
2026-05-09 15:25:01 +02:00
a7a6a53686 feat(voyage): add three annotation creation gestures + form modal — v4.2 Step 9 [skip-docs]
Three creation gestures + shared form modal for the v4.2 annotation
playground (per critical decisions #2-#4 + research-06):

Gesture 1 — text-anchored adder-popup:
  - mouseup-debounce 200ms (settles selection)
  - 300ms grace before hide (Hypothes.is friction-mitigation)
  - floating .voyage-adder-popup positioned at selection-bound corner
  - click -> opens form modal with derived heading-path + line + snippet

Gesture 2 — paragraph-anchored hover-icon:
  - 24px pencil SVG injected per <p>/<li> after each render
  - opacity 0 default, opacity 1 on hover/focus-visible
  - click -> opens form modal with no snippet

Gesture 3 — page-level note:
  - .voyage-page-note-btn injected in viewport
  - click -> opens form modal with target=page

Form modal (shared):
  - role="dialog" aria-modal="true" + aria-labelledby
  - 400px right-anchored on backdrop (per critical decision #3)
  - 4 intent buttons (Fiks / Endre / Spørsmål / Block) as aria-pressed group
  - <textarea> required for comment
  - ESC + backdrop-click + Avbryt close
  - Lagre persists via saveModalAsAnnotation

Anchor-ID generation (per critical decision #2 + risk-assessor H7):
  - sequential ANN-NNNN per project+file scope
  - persisted in localStorage under voyage_ann_<project>__<file>.drafts

Test coverage: tests/playground/voyage-playground.test.mjs +3 cases —
aria-modal, ANN-, 300ms grace.

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

Refs plan.md Step 9 + critical decisions #2/#3/#4 + research-06.
2026-05-09 15:22:52 +02:00
249142df2f feat(voyage): vendor markdown-it/highlight.js + playground render-pipeline + scripts/render-artifact.mjs CLI — v4.2 Step 8 [skip-docs]
Vendored libs (locked headless via scripts/vendor-playground-libs.mjs;
plan-critic B3 — never use highlightjs.org website builder):
  - playground/lib/markdown-it.min.js           — markdown-it@14.1.0 UMD bundle
  - playground/lib/markdown-it-front-matter.min.js — markdown-it-front-matter@0.2.4 IIFE-wrapped
  - playground/lib/highlight.min.js             — highlight.js@11.11.1 (5-lang bundle:
                                                   yaml/json/javascript/bash/markdown/diff)
  - playground/lib/VENDOR-MANIFEST.json         — pin record + audit trail

scripts/vendor-playground-libs.mjs implements the reproducible
CommonJS-to-IIFE wrapping. Re-vendoring requires only:
  node scripts/vendor-playground-libs.mjs

Render pipeline in playground/voyage-playground.html (~330 LoC total):
  - inline <script src=lib/...> for the three vendored bundles
  - markdown-it init with html: true (preserves voyage:anchor comments)
  - front-matter plugin with pre-render-then-wrap pattern (research/03)
  - paste-import-row textarea + Render/Sample/Clear buttons
  - voyage-viewport region with role + aria-live for A11Y
  - localStorage key pattern: voyage_ann_<project>__<slug> (risk-assessor H7)
  - inline sample plan (mirrors annotation-plan.md fixture)

scripts/render-artifact.mjs CLI (~200 LoC) — brief SC1 + SC11:
  - reads input.md, runs same vendored pipeline server-side
  - inlines DS CSS + (URL-stripped) highlight.js into output
  - zero http://https:// URLs in output (verified by test)
  - deterministic: two invocations -> byte-identical sha256
  - default output: <input>.html next to input

Test coverage:
  - tests/scripts/render-artifact.test.mjs — 5 cases (SC1/SC11)
  - tests/playground/voyage-playground.test.mjs — +5 cases (Step 8 extension)

Verify: node --test tests/playground/voyage-playground.test.mjs
       tests/scripts/render-artifact.test.mjs -> 18 pass / 0 fail.
Full npm test: 587 pass / 0 fail / 2 skipped (Docker).

Refs plan.md Step 8 + plan-critic B3 + scope-guardian B1.
2026-05-09 15:20:17 +02:00
2f4330265c feat(voyage): scaffold playground/ with DS vendor sync — v4.2 Step 5
- playground/voyage-playground.html: minimal skeleton (DOCTYPE, app-header, guide-panel, aria-live region, skip-to-main link). Steps 8-11 will extend with render-pipeline + creation gestures + sidebar + export.
- playground/vendor/playground-design-system/: synced via 'node scripts/sync-design-system.mjs voyage' (27 files + MANIFEST.json with source_commit + sync_date + SHA-256 per file).
- tests/playground/voyage-playground.test.mjs: 8 tests pinning HTML existence, DOCTYPE, no-external-URLs, no-marked, A11Y skip-to-main + aria-live, MANIFEST.json structure, vendored DS files present.
- shared/PLAYGROUND-MAINTENANCE.md: consumer list updated 5 -> 6 (added voyage).
2026-05-09 12:55:02 +02:00