Skip to content

Commit 8a4ef65

Browse files
Add annotate wide mode (#578)
* Add annotate wide mode * Extend wide mode: add Focus variant, enable in all modes, reposition controls - Drop annotate-only gate in canUseAnnotateWideMode; available in plan review, annotate, and linked-doc flows (archive/diff still excluded) - Add Focus mode alongside Wide: both collapse sidebars and suppress annotation panel auto-open; only Wide removes the reader width cap - Move controls out of AnnotationToolstrip/StickyHeaderLane; render as absolute-positioned "Wide | Focus" text buttons floating above the plan card's top-right edge (no vertical space cost) - Add Radix-based Tooltip primitive (packages/ui/components/Tooltip.tsx) and TooltipProvider; replaces the ad-hoc CSS tooltip with portaled, accessible, animated content - Polish: tracking-wide for 11px labels, focus-visible ring, active press state, aria-pressed, weight-stable toggle (color-only), 60px right offset under tater mode For provenance purposes, this commit was AI assisted. * Update wideMode test to cover both annotate and plan review modes The annotateMode gate was removed — update the test matrix to assert wide mode is enabled in annotate AND plan review, and disabled for archive/diff across both mode values. For provenance purposes, this commit was AI assisted. * Drop unused annotateMode parameter from canUseAnnotateWideMode The parameter was ignored after the annotate-only gate was removed. Prune it from the signature, the App.tsx call site and deps, and the test matrix to match the actual behavior (disabled only for archive and diff). For provenance purposes, this commit was AI assisted. --------- Co-authored-by: Michael Ramos <mdramos8@gmail.com>
1 parent 615cc59 commit 8a4ef65

File tree

12 files changed

+460
-29
lines changed

12 files changed

+460
-29
lines changed

apps/hook/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default defineConfig({
1818
resolve: {
1919
alias: {
2020
'@': path.resolve(__dirname, '.'),
21+
'@plannotator/shared': path.resolve(__dirname, '../../packages/shared'),
2122
'@plannotator/ui': path.resolve(__dirname, '../../packages/ui'),
2223
'@plannotator/editor/styles': path.resolve(__dirname, '../../packages/editor/index.css'),
2324
'@plannotator/editor': path.resolve(__dirname, '../../packages/editor/App.tsx'),

apps/review/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default defineConfig({
1717
resolve: {
1818
alias: {
1919
'@': path.resolve(__dirname, '.'),
20+
'@plannotator/shared': path.resolve(__dirname, '../../packages/shared'),
2021
'@plannotator/ui': path.resolve(__dirname, '../../packages/ui'),
2122
'@plannotator/review-editor/styles': path.resolve(__dirname, '../../packages/review-editor/index.css'),
2223
'@plannotator/review-editor': path.resolve(__dirname, '../../packages/review-editor/App.tsx'),

bun.lock

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/adversarial_rubric.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Adversarial Rubric
2+
3+
Last Updated: 2026-04-17
4+
5+
This rubric captures the main adversarial and drift vectors for Plannotator's review and annotation surfaces. It is intended for milestone reviews, especially for UI state changes that can unintentionally cross plan, annotate, archive, and review modes.
6+
7+
## Data Boundaries
8+
9+
| Boundary | Format | Validation | Failure Mode |
10+
| --- | --- | --- | --- |
11+
| `/api/plan`, `/api/feedback`, `/api/draft`, `/api/upload` between browser and Bun server | JSON, multipart form data, markdown text | Per-endpoint parsing in `packages/server/index.ts`, `packages/server/annotate.ts`, `packages/server/shared-handlers.ts` | Invalid payloads can silently fall back to demo/empty state or reject late in the flow |
12+
| Linked-doc file resolution via `/api/doc` and Obsidian doc endpoints | Relative/absolute markdown paths | `packages/server/reference-handlers.ts`, `packages/shared/resolve-file.ts` normalize and constrain paths | Path confusion can open the wrong file or expose unintended content if guards drift |
13+
| Share/import URLs and paste payloads | URL hash, compressed JSON, encrypted blobs | `packages/ui/utils/sharing.ts` parses, decompresses, decrypts, and reconstructs annotations | Malformed share payloads can break annotation restore or produce partial state |
14+
| External annotations stream and snapshot APIs | SSE + JSON annotations | `packages/server/external-annotations.ts`, shared annotation types in `packages/shared/external-annotation.ts` | Unsanitized/invalid annotation payloads can corrupt UI state or highlight bookkeeping |
15+
| Cookie-backed UI preferences | Strings in `document.cookie` | `packages/ui/utils/storage.ts`, `packages/ui/utils/uiPreferences.ts`, `packages/ui/config/settings.ts` coerce to enums/bools | Invalid cookie values can create inconsistent mode/layout defaults across sessions |
16+
17+
## Type Coercion Vectors
18+
19+
| Coercion | Location | Risk | Test Exists? |
20+
| --- | --- | --- | --- |
21+
| Cookie string → boolean / enum | `packages/ui/utils/uiPreferences.ts`, `packages/ui/config/settings.ts` | Invalid values can silently select unsafe defaults or inconsistent layout state | Partial |
22+
| URL hash / paste payload → structured annotations | `packages/ui/utils/sharing.ts` | Malformed arrays or unexpected tuple shapes can restore incomplete/shifted annotations | Partial |
23+
| Query/path input → resolved markdown path | `packages/shared/resolve-file.ts` | Separator normalization and basename fallback can drift from intended trust boundary | Yes |
24+
| External annotation JSON → internal annotation model | `packages/shared/external-annotation.ts` | Missing/extra fields can degrade rendering or selection restoration | Partial |
25+
| Resize/cap values → persisted panel widths | `packages/ui/hooks/useResizablePanel.ts` | Invalid saved widths can distort layout or hide controls | No |
26+
27+
## Trust Assumptions
28+
29+
| Assumption | What Breaks | Severity | Test Exists? |
30+
| --- | --- | --- | --- |
31+
| Annotate-only UI changes will not leak into plan/review/archive modes | Hidden controls or layout regressions in other surfaces | HIGH | No |
32+
| Session-scoped UI modes restore the user’s prior layout exactly | Users lose sidebar/panel context or hidden state drifts | HIGH | No |
33+
| Shared workspace aliases stay aligned across app Vite configs | Local builds fail even though workspace packages compile | MEDIUM | No |
34+
| Linked-doc navigation only needs the sidebar capabilities it declares | Runtime mismatches if hook expectations drift | MEDIUM | No |
35+
| Cookie defaults are benign when malformed or missing | Surprising startup state, especially around sidebar and panel behavior | LOW | Partial |
36+
37+
## Cascade Risks
38+
39+
| Cascade Point | Blast Radius | Isolation | Test Exists? |
40+
| --- | --- | --- | --- |
41+
| Viewer/layout mode toggles in `packages/editor/App.tsx` | Can affect annotate, plan, linked-doc, archive, and sticky-header behavior at once | Manual branching by `annotateMode`, `archiveMode`, `isPlanDiffActive` | No |
42+
| Sticky header lane width calculations | Reader chrome can diverge from document width and overlay controls incorrectly | Separate `StickyHeaderLane` component with measured widths | No |
43+
| Linked-doc state swap and cached annotations | Annotation state can leak between source doc and linked doc | `useLinkedDoc` caches/restores per file | No |
44+
| External annotation highlight replay | DOM highlights can desync when switching linked docs or diff mode | `useExternalAnnotationHighlights` and explicit reset hooks | Partial |
45+
46+
## Registry Drift Risks
47+
48+
| Registry | Code Location | Drift Detection | Last Verified |
49+
| --- | --- | --- | --- |
50+
| Hook/review app workspace aliases | `apps/hook/vite.config.ts`, `apps/review/vite.config.ts` | Manual build of both apps | 2026-04-17 |
51+
| Public API endpoint docs vs runtime endpoints | `AGENTS.md`, marketing docs, `packages/server/*.ts` | Manual review + endpoint additions in PR review | 2026-04-17 |
52+
| Shared package exports vs app imports | `packages/shared/package.json` and app/package imports | Typecheck/build | 2026-04-17 |
53+
| UI preference keys vs Settings UI | `packages/ui/utils/uiPreferences.ts`, `packages/ui/components/Settings.tsx` | Manual review | 2026-04-17 |
54+
55+
## Learned Vectors
56+
57+
| Vector | Source Milestone | Category | Recurrence |
58+
| --- | --- | --- | --- |
59+
| Session-scoped layout modes can mutate hidden panel state unless every reopen path exits the mode first | `feat/annotate-wide-mode` | Cascade / Trust Assumption | Likely |
60+
| Annotate-only controls must be explicitly gated to avoid leaking into plan/review surfaces through shared components | `feat/annotate-wide-mode` | Trust Assumption | Likely |
61+
| Build-time alias drift can look like a feature regression even when the code change is correct | `feat/annotate-wide-mode` | Registry Drift | Likely |

0 commit comments

Comments
 (0)