Commit Graph

3 Commits

Author SHA1 Message Date
8632f7b04a Implement CE-WP-0004 T01-T05: citation card export (Markdown + HTML)
Per-evidence-item export: click Export → Copy as Markdown / Copy as HTML
writes a portable citation card to the clipboard. Cmd/Ctrl+Shift+C
exports the active evidence as Markdown.

- ADR-0007 locks the Markdown + HTML output formats.
- New shared types: CitationCard, openContextUrl(), resolveSourceLabel().
- Engine renderers under src/engine/rendering/: renderCitationCardMarkdown,
  renderCitationCardHtml — snapshot-tested, escape-safe, BEM classes for HTML.
- src/work/useExportEvidence.ts wires engine + renderers + clipboard.
- EvidenceSidebar gains an Export popover per row + auto-dismissing toast.
- E2E test (tests/integration/citation-card-export-e2e.dom.test.tsx)
  walks PRD scenario steps 10-11 and asserts the clipboard payload.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:43:17 +02:00
8607c252c4 Implement CE-WP-0003 T01-T08: form binding + visual guide overlay
T01 EvidenceLink/EvidenceSet types
  - src/shared/evidence-link.ts: status (§2.4), relation (§2.5), target
  - src/shared/evidence-set.ts: ordered group + activeEvidenceItemId
  - enum-conformance test parses SharedContracts.md and asserts the
    runtime lists match exactly

T02 Binding service + in-memory link repo + active-state machine
  - src/binder/repos/in-memory-links.ts: Map-backed EvidenceLinkRepository
  - src/binder/services/bindings.ts: link/unlink/list/update/setActive
    emitting §4 EvidenceLinkCreated / EvidenceLinkUpdated /
    EvidenceItemActivated
  - src/binder/state/active.ts: (target, evidence, annotation) reducer
    + ActiveStateProvider + useActiveState hook
  - extended engine/events/types.ts with EvidenceLinkCreated,
    EvidenceLinkUpdated, FormFieldActivated payloads

T03 Rect registry (SharedContracts §7 — contract FROZEN)
  - src/binder/visual-guide/rect-registry.ts: register/getRect/subscribe
    + invalidate + getVersion for useSyncExternalStore
  - events.ts: scroll/resize/focus pumps via window + ResizeObserver +
    IntersectionObserver, rAF-throttled
  - react-hooks.ts: RectRegistryProvider, useRegisterRect(kind,id,ref),
    useRectRegistryVersion

T04 Form schema + renderer
  - src/app/forms/demo-schema.ts: text/textarea/date minimal schema
  - src/binder/FormRenderer.tsx: renders schema, each field registers
    as rect kind="field"; active field gets aria-current="true"
  - placed in binder/ (not work/) because work cannot import binder per
    DependencyMap.md §2 and the renderer needs the rect-registry hook;
    workplan T04 was amended in-place to document this

T05 Side-by-side Forms layout + click-to-link
  - src/app/forms/FormsApp.tsx + src/app/App.tsx top-bar router with
    hash route #/forms/demo
  - BinderProvider mounted at app root so links survive tab switching
  - stage-evidence-then-click-field linking interaction with banner
    + per-field link-count chip

T06 Active-evidence cycling
  - src/app/forms/ActiveEvidenceChips.tsx: chips per active target,
    Tab cycles natively, first chip auto-activates on field focus,
    each chip registers as rect kind="evidence-card"
  - ScrollBridge in FormsApp wires activeAnnotationId to viewer scroll
  - EvidenceSidebar + EvidenceStrip highlight the active item via the
    new useLastActivatedEvidence hook in work/EngineContext

T07 SVG visual-guide overlay
  - src/binder/visual-guide/Overlay.tsx: single fixed-positioned SVG,
    draws field→card and card→highlight bezier curves for the active
    triple, rAF-throttled via the registry
  - src/anchor exposes getHighlightClientRects(annotationId); the
    spike viewer wraps highlights in [data-highlight-id] so the helper
    can locate them
  - src/app/forms/HighlightRectBridge.tsx: registers the active
    annotation's rect via that helper

T08 End-to-end test (PRD scenario steps 5-9)
  - tests/integration/forms-overlay-e2e.dom.test.tsx: full path from
    Review-mode capture through Forms-mode link to active triple +
    aria-current assertions + 2 SVG paths in the overlay
  - additional integration coverage: forms-link-flow + forms-active-cycling

Gates: typecheck ✓ · lint ✓ · build ✓ · 152/152 tests across 21 files.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 15:53:17 +02:00
d54daf2e61 Implement CE-WP-0002 T03-T09: ingest, anchor resolution, engine, UI, persistence, e2e
Completes the PDF review slice end-to-end. After this commit a user can
open a fixture, select text, save an evidence item with commentary, see
it in the sidebar, reload the page, click the item, and the viewer
scrolls to the passage.

- T03 src/source/pdf/{fingerprint,extract,ingest}.ts + 39 fixture tests
  - SHA-256 fingerprint over a fresh ArrayBuffer (TS BufferSource-safe)
  - PDF.js text extract; per-page normalize then join with "\n\n"
  - PageMap + OffsetMap (gap-free coverage); pageLength = end - start
  - Updated manifest's Betriebskosten quote to one PDF.js extracts cleanly
- T04 src/anchor/selectors/{create,resolve}.ts + 25 unit + 7 fixture tests
  - createSelectors emits the maximal redundant set (TextQuote +
    TextPosition + PdfRect + PdfPageText when available)
  - resolveSelectors implements the SharedContracts §7 ladder; confidence
    1.0 (pos+quote) → 0.7 (rect-only) → 0 (unresolved)
  - Cross-module integration test moved to tests/integration/ to honor
    the anchor↛source boundary lint rule
- T05 engine: sync event bus over the closed §4 vocabulary, Map-backed
  repos, services, createEngine() composition root, 12 tests
- T06 work + app: three-pane shell (CollectionList | ViewerShell |
  EvidenceSidebar) wired through EngineProvider; EngineContext lives in
  src/work/ to respect the work↛app boundary; SpikeApp deleted
- T07 AnnotationToolbar: pendingSelection in context; Save runs
  createSelectors → engine.annotations.create → engine.evidence.create
- T08 click-to-reopen + localStorage persistence
  - scrollToAnnotation state in context with a version counter so a
    second click on the same item re-fires the viewer scroll
  - captureSnapshot/restoreSnapshot/attachPersister/restoreFromStorage;
    restore bypasses services to avoid event-loops
  - active-document id persisted alongside the snapshot so reload lands
    on the same fixture; ADR-0005 written
  - 9 persistence tests
- T09 tests/integration/app-prd-scenario.dom.test.tsx
  - end-to-end happy-dom test of PRD scenario steps 1-8 through the real
    React tree; viewer + ingest mocked per ADR-0004's headless-Chromium
    limitation. Fixed memo-deps bug in EvidenceSidebar/ViewerShell where
    useEngineEventTick values were not included in the useMemo deps,
    leaving stale memoization across event-driven re-renders
- vitest.config.ts: happy-dom for *.dom.test.{ts,tsx} files
- noEmit added to tsconfig so tsc -b doesn't litter src/ with .js outputs

Gates: typecheck ✓ lint ✓ test 109/109 across 11 files ✓ build ✓

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 10:58:11 +02:00