bigwolfeman commited on
Commit
3c7dbf6
Β·
1 Parent(s): a7c5777
CLAUDE.md CHANGED
@@ -275,6 +275,8 @@ Obtain JWT: `POST /api/tokens` after HF OAuth login.
275
  ## Active Technologies
276
  - Python 3.11+ (backend), TypeScript (frontend) + FastAPI, LlamaIndex, llama-index-llms-google-genai, llama-index-embeddings-google-genai, React 18+, Tailwind CSS, Shadcn/UI (004-gemini-vault-chat)
277
  - Filesystem vault (existing), LlamaIndex persisted vector store (new, under `data/llamaindex/`) (004-gemini-vault-chat)
 
 
278
 
279
  ## Recent Changes
280
  - 004-gemini-vault-chat: Added Python 3.11+ (backend), TypeScript (frontend) + FastAPI, LlamaIndex, llama-index-llms-google-genai, llama-index-embeddings-google-genai, React 18+, Tailwind CSS, Shadcn/UI
 
275
  ## Active Technologies
276
  - Python 3.11+ (backend), TypeScript (frontend) + FastAPI, LlamaIndex, llama-index-llms-google-genai, llama-index-embeddings-google-genai, React 18+, Tailwind CSS, Shadcn/UI (004-gemini-vault-chat)
277
  - Filesystem vault (existing), LlamaIndex persisted vector store (new, under `data/llamaindex/`) (004-gemini-vault-chat)
278
+ - TypeScript 5.x, React 18+ (006-ui-polish)
279
+ - localStorage for user preferences (font size, TOC panel state) (006-ui-polish)
280
 
281
  ## Recent Changes
282
  - 004-gemini-vault-chat: Added Python 3.11+ (backend), TypeScript (frontend) + FastAPI, LlamaIndex, llama-index-llms-google-genai, llama-index-embeddings-google-genai, React 18+, Tailwind CSS, Shadcn/UI
specs/006-ui-polish/checklists/requirements.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Specification Quality Checklist: UI Polish Pack
2
+
3
+ **Purpose**: Validate specification completeness and quality before proceeding to planning
4
+ **Created**: 2025-11-28
5
+ **Feature**: [spec.md](../spec.md)
6
+
7
+ ## Content Quality
8
+
9
+ - [x] No implementation details (languages, frameworks, APIs)
10
+ - [x] Focused on user value and business needs
11
+ - [x] Written for non-technical stakeholders
12
+ - [x] All mandatory sections completed
13
+
14
+ ## Requirement Completeness
15
+
16
+ - [x] No [NEEDS CLARIFICATION] markers remain
17
+ - [x] Requirements are testable and unambiguous
18
+ - [x] Success criteria are measurable
19
+ - [x] Success criteria are technology-agnostic (no implementation details)
20
+ - [x] All acceptance scenarios are defined
21
+ - [x] Edge cases are identified
22
+ - [x] Scope is clearly bounded
23
+ - [x] Dependencies and assumptions identified
24
+
25
+ ## Feature Readiness
26
+
27
+ - [x] All functional requirements have clear acceptance criteria
28
+ - [x] User scenarios cover primary flows
29
+ - [x] Feature meets measurable outcomes defined in Success Criteria
30
+ - [x] No implementation details leak into specification
31
+
32
+ ## Validation Results
33
+
34
+ **Status**: βœ… PASSED - All quality criteria met
35
+
36
+ **Details**:
37
+ - Specification clearly separates WHAT from HOW
38
+ - All 7 user stories include acceptance scenarios
39
+ - 18 functional requirements are testable and unambiguous
40
+ - 10 success criteria are measurable and technology-agnostic
41
+ - Edge cases address boundary conditions
42
+ - Assumptions document reasonable defaults
43
+ - No clarifications needed (all features well-understood from user input)
44
+ - Priorities clearly assigned (P1, P2, P3, STRETCH)
45
+
46
+ **Notes**:
47
+ - Specification is ready for `/speckit.plan`
48
+ - No updates required before proceeding to planning phase
specs/006-ui-polish/data-model.md ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data Model: UI Polish Pack
2
+
3
+ **Feature**: 006-ui-polish
4
+ **Date**: 2025-11-28
5
+ **Type**: Client-side only (no backend/database models)
6
+
7
+ ## Overview
8
+
9
+ All data for UI polish features is stored client-side in browser localStorage or ephemeral React component state. No backend API changes or database modifications required.
10
+
11
+ ## Entities
12
+
13
+ ### 1. FontSizePreference
14
+
15
+ **Storage**: localStorage
16
+ **Key**: `note-font-size`
17
+ **Type**: `"small" | "medium" | "large"`
18
+ **Scope**: Global (applies to all notes across sessions)
19
+
20
+ **Attributes**:
21
+ - `size`: The user's selected font size
22
+ - `"small"` = 0.875rem (14px)
23
+ - `"medium"` = 1rem (16px) - default
24
+ - `"large"` = 1.125rem (18px)
25
+
26
+ **Lifecycle**:
27
+ - Created: First time user clicks font size button
28
+ - Updated: Each font size change
29
+ - Persisted: Indefinitely (until user clears browser data)
30
+ - Default: `"medium"` if not set
31
+
32
+ **Usage**:
33
+ ```typescript
34
+ // Read
35
+ const savedSize = localStorage.getItem('note-font-size') ?? 'medium';
36
+
37
+ // Write
38
+ localStorage.setItem('note-font-size', 'large');
39
+ ```
40
+
41
+ ---
42
+
43
+ ### 2. TOCPanelState
44
+
45
+ **Storage**: localStorage
46
+ **Key**: `toc-panel-open`
47
+ **Type**: `boolean`
48
+ **Scope**: Global (persists across sessions)
49
+
50
+ **Attributes**:
51
+ - `isOpen`: Whether TOC panel is currently visible
52
+ - `true` = panel expanded
53
+ - `false` = panel collapsed
54
+
55
+ **Lifecycle**:
56
+ - Created: First time user clicks TOC button
57
+ - Updated: Each TOC toggle
58
+ - Persisted: Indefinitely
59
+ - Default: `false` (collapsed) if not set
60
+
61
+ **Usage**:
62
+ ```typescript
63
+ // Read
64
+ const isOpen = localStorage.getItem('toc-panel-open') === 'true';
65
+
66
+ // Write
67
+ localStorage.setItem('toc-panel-open', String(isOpen));
68
+ ```
69
+
70
+ ---
71
+
72
+ ### 3. WikilinkPreviewCache
73
+
74
+ **Storage**: React component state (ephemeral)
75
+ **Type**: `Map<string, string>`
76
+ **Scope**: Component lifecycle (NoteViewer or markdown renderer)
77
+
78
+ **Attributes**:
79
+ - `key`: Note path (string) - e.g., "guides/Quick Reference.md"
80
+ - `value`: Preview text (string) - first 150-200 characters of note body
81
+
82
+ **Lifecycle**:
83
+ - Created: Component mount
84
+ - Updated: On first preview fetch for each unique wikilink
85
+ - Cleared: Component unmount
86
+ - Not persisted to localStorage (ephemeral per session)
87
+
88
+ **Usage**:
89
+ ```typescript
90
+ const [previewCache, setPreviewCache] = useState<Map<string, string>>(new Map());
91
+
92
+ // Check cache
93
+ const cached = previewCache.get(notePath);
94
+ if (cached) return cached;
95
+
96
+ // Fetch and cache
97
+ const preview = await fetchPreview(notePath);
98
+ setPreviewCache(prev => new Map(prev).set(notePath, preview));
99
+ ```
100
+
101
+ **Rationale for ephemeral storage**:
102
+ - Preview content can change between sessions
103
+ - Avoids stale data in localStorage
104
+ - Cache hit rate still high within single reading session
105
+
106
+ ---
107
+
108
+ ### 4. TOCHeading
109
+
110
+ **Storage**: Computed (not persisted)
111
+ **Type**: Array of heading objects
112
+ **Scope**: Per-note (recomputed when note.body changes)
113
+
114
+ **Attributes**:
115
+ - `id`: Slugified heading text for anchor links (string)
116
+ - Generated via: `text.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '')`
117
+ - Example: "API Documentation" β†’ "api-documentation"
118
+ - `text`: Raw heading text (string) - e.g., "API Documentation"
119
+ - `level`: Heading depth (1 | 2 | 3)
120
+ - 1 = H1 (`#`)
121
+ - 2 = H2 (`##`)
122
+ - 3 = H3 (`###`)
123
+ - H4-H6 not included in TOC
124
+
125
+ **Lifecycle**:
126
+ - Computed: When markdown is rendered via react-markdown
127
+ - Updated: When note.body changes
128
+ - Cached: Via useMemo to avoid re-computation on re-renders
129
+ - Not persisted (derived data)
130
+
131
+ **Example**:
132
+ ```typescript
133
+ const headings: TOCHeading[] = [
134
+ { id: 'getting-started', text: 'Getting Started', level: 1 },
135
+ { id: 'installation', text: 'Installation', level: 2 },
136
+ { id: 'prerequisites', text: 'Prerequisites', level: 3 },
137
+ ];
138
+ ```
139
+
140
+ **Usage**:
141
+ ```typescript
142
+ const headings = useMemo(() => {
143
+ const extracted: TOCHeading[] = [];
144
+ // Extract from markdown during render
145
+ // (via custom react-markdown heading renderer)
146
+ return extracted;
147
+ }, [note.body]);
148
+ ```
149
+
150
+ ---
151
+
152
+ ## State Management
153
+
154
+ ### Component Hierarchy
155
+
156
+ ```text
157
+ MainApp (root)
158
+ β”œβ”€β”€ fontSize state (localStorage-backed)
159
+ β”œβ”€β”€ DirectoryTree
160
+ β”‚ └── expandAll/collapseAll triggers (ephemeral)
161
+ └── NoteViewer
162
+ β”œβ”€β”€ TOC state (localStorage-backed)
163
+ β”œβ”€β”€ TOC headings (computed from markdown)
164
+ └── Wikilink preview cache (ephemeral Map)
165
+ ```
166
+
167
+ ### Data Flow
168
+
169
+ **Font Size**:
170
+ ```
171
+ User clicks A+/A-/A
172
+ ↓
173
+ useFontSize hook updates state
174
+ ↓
175
+ localStorage.setItem('note-font-size', newSize)
176
+ ↓
177
+ CSS variable --content-font-size updated
178
+ ↓
179
+ .prose container text resizes
180
+ ```
181
+
182
+ **TOC**:
183
+ ```
184
+ Note body changes
185
+ ↓
186
+ useTableOfContents extracts headings (useMemo)
187
+ ↓
188
+ User clicks TOC button
189
+ ↓
190
+ toggleTOC() updates isOpen state + localStorage
191
+ ↓
192
+ TableOfContents component renders/hides
193
+ ```
194
+
195
+ **Wikilink Preview**:
196
+ ```
197
+ User hovers on [[wikilink]]
198
+ ↓
199
+ 500ms delay
200
+ ↓
201
+ Check previewCache Map
202
+ ↓ (cache miss)
203
+ Fetch note via GET /api/notes/{path}
204
+ ↓
205
+ Extract first 150 chars
206
+ ↓
207
+ Update cache + display HoverCard
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Validation Rules
213
+
214
+ ### FontSizePreference
215
+ - Must be one of: `"small" | "medium" | "large"`
216
+ - If invalid value in localStorage, default to `"medium"`
217
+
218
+ ### TOCPanelState
219
+ - Must be parseable as boolean
220
+ - If invalid value, default to `false`
221
+
222
+ ### TOCHeading.id
223
+ - Must be unique within a note (handle duplicate headings)
224
+ - Must be valid HTML ID (a-z, 0-9, -, _)
225
+ - If duplicate heading text, append `-2`, `-3`, etc.
226
+
227
+ ---
228
+
229
+ ## Performance Considerations
230
+
231
+ ### LocalStorage Access
232
+ - Read once on component mount
233
+ - Write only on user action (not on every render)
234
+ - Max size: ~5MB per domain (plenty for small preference values)
235
+
236
+ ### Wikilink Cache
237
+ - Map lookups are O(1) - efficient
238
+ - Cleared on unmount to avoid memory leaks
239
+ - Max realistic size: ~50 previews Γ— 200 chars = ~10KB
240
+
241
+ ### TOC Extraction
242
+ - useMemo prevents re-computation on unrelated re-renders
243
+ - Only recomputes when note.body changes
244
+ - Max realistic cost: 50 headings Γ— minimal parsing = <10ms
245
+
246
+ ---
247
+
248
+ ## Migration Notes
249
+
250
+ **No migrations required** - All data is client-side and newly created.
251
+
252
+ If user has existing localStorage data:
253
+ - New keys (`note-font-size`, `toc-panel-open`) won't conflict
254
+ - Existing keys (e.g., `tts-volume`) unaffected
255
+
256
+ ---
257
+
258
+ ## Relationships
259
+
260
+ No relationships between entities - each is independent:
261
+ - Font size doesn't affect TOC
262
+ - TOC state doesn't affect preview cache
263
+ - All features operate independently
264
+
265
+ ---
266
+
267
+ ## Summary
268
+
269
+ | Entity | Storage | Persisted | Scope | Size |
270
+ |--------|---------|-----------|-------|------|
271
+ | FontSizePreference | localStorage | Yes | Global | ~10 bytes |
272
+ | TOCPanelState | localStorage | Yes | Global | ~5 bytes |
273
+ | WikilinkPreviewCache | React state | No | Component | ~10 KB |
274
+ | TOCHeading | Computed | No | Per-note | Negligible |
275
+
276
+ **Total localStorage footprint**: ~15 bytes (negligible)
277
+ **Total memory footprint**: ~10 KB (preview cache)
specs/006-ui-polish/plan.md ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan: UI Polish Pack
2
+
3
+ **Branch**: `006-ui-polish` | **Date**: 2025-11-28 | **Spec**: [spec.md](./spec.md)
4
+ **Input**: Feature specification from `/specs/006-ui-polish/spec.md`
5
+
6
+ ## Summary
7
+
8
+ This feature adds seven quick-win UX enhancements to improve the document viewer's polish and usability for hackathon demo. All features are frontend-only with no backend changes required. Implementation uses existing patterns (CSS variables, tailwindcss-animate, React hooks, shadcn/ui components) to minimize complexity and maintain consistency.
9
+
10
+ **Primary Requirements**:
11
+ - P1: Font size adjuster (3 sizes: small/medium/large)
12
+ - P1: Expand/collapse all folders in directory tree
13
+ - P2: Wikilink preview tooltips on hover
14
+ - P2: Reading time estimator badge
15
+ - P2: Table of contents flyout panel
16
+ - P3: Smooth transitions/animations
17
+ - STRETCH: Particle click effects
18
+
19
+ **Technical Approach**: Leverage existing architecture (CSS custom properties for font sizing, state propagation for tree expansion, HoverCard for previews, ResizablePanel for TOC, tailwindcss-animate for transitions). Zero backend changes, minimal new dependencies.
20
+
21
+ ## Technical Context
22
+
23
+ **Language/Version**: TypeScript 5.x, React 18+
24
+ **Primary Dependencies**:
25
+ - React 18, TypeScript, Vite (existing)
26
+ - Tailwind CSS + tailwindcss-animate (existing)
27
+ - shadcn/ui components (existing)
28
+ - react-markdown + remark-gfm (existing)
29
+ - NEW: @radix-ui/react-hover-card (for wikilink previews)
30
+
31
+ **Storage**: localStorage for user preferences (font size, TOC panel state)
32
+ **Testing**: Manual verification + browser console (no automated tests for UI polish)
33
+ **Target Platform**: Modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)
34
+ **Project Type**: Web (frontend-only changes)
35
+ **Performance Goals**:
36
+ - Font size change: < 100ms
37
+ - Expand All (100 folders): < 2s
38
+ - Wikilink preview fetch: < 600ms total (500ms delay + 100ms fetch)
39
+ - TOC generation: < 500ms for 50 headings
40
+ - All animations: 60 FPS (no dropped frames)
41
+
42
+ **Constraints**:
43
+ - WCAG 2.2 Level AA compliance
44
+ - No backend API changes
45
+ - No new data models or database changes
46
+ - Respect prefers-reduced-motion media query
47
+ - Mobile-friendly (responsive design)
48
+
49
+ **Scale/Scope**:
50
+ - 7 features (6 core + 1 stretch)
51
+ - 18 functional requirements
52
+ - Est. 4-6 hours implementation
53
+
54
+ ## Constitution Check
55
+
56
+ *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
57
+
58
+ ### I. Brownfield Integration βœ… PASS
59
+
60
+ **Assessment**: All features leverage existing patterns and components. No rewrites or refactors required.
61
+
62
+ **Evidence**:
63
+ - Font sizing uses existing CSS custom properties pattern (`index.css:6-50`)
64
+ - Tree expansion extends existing DirectoryTree component (`DirectoryTree.tsx`)
65
+ - Tooltips use shadcn/ui (existing component library)
66
+ - TOC uses existing ResizablePanel pattern (`MainApp.tsx:583-778`)
67
+ - Transitions use existing tailwindcss-animate (`tailwind.config.js:57-97`)
68
+ - Reading time uses existing markdownToPlainText (`markdownToText.ts`)
69
+
70
+ ### II. Test-Backed Development ⚠️ PARTIAL
71
+
72
+ **Assessment**: Frontend UI polish features rely on manual verification. No automated tests required per constitution ("UI components rely on manual verification/E2E").
73
+
74
+ **Evidence**:
75
+ - Spec includes detailed acceptance scenarios for manual testing
76
+ - No backend changes = no pytest requirements
77
+ - Frontend E2E tests not in scope for polish features
78
+ - Regression: Existing tests unaffected (no changes to tested code paths)
79
+
80
+ ### III. Incremental Delivery βœ… PASS
81
+
82
+ **Assessment**: Features are independently testable and can be implemented/deployed incrementally.
83
+
84
+ **Evidence**:
85
+ - Spec prioritizes features (P1, P2, P3, STRETCH)
86
+ - Each feature in separate component/file
87
+ - No interdependencies between features
88
+ - Can ship P1 features first, P2/P3 later
89
+
90
+ ### IV. Specification-Driven βœ… PASS
91
+
92
+ **Assessment**: All work traces back to spec.md with clear functional requirements.
93
+
94
+ **Evidence**:
95
+ - 18 functional requirements (FR-001 through FR-018)
96
+ - Each feature has acceptance scenarios
97
+ - Success criteria defined
98
+ - No ghost features planned
99
+
100
+ **RESULT**: βœ… **APPROVED FOR IMPLEMENTATION** - All gates pass or justified
101
+
102
+ ## Project Structure
103
+
104
+ ### Documentation (this feature)
105
+
106
+ ```text
107
+ specs/006-ui-polish/
108
+ β”œβ”€β”€ spec.md # Feature specification
109
+ β”œβ”€β”€ plan.md # This file
110
+ β”œβ”€β”€ research.md # Technology decisions and best practices
111
+ β”œβ”€β”€ checklists/
112
+ β”‚ └── requirements.md # Spec quality validation
113
+ └── tasks.md # Will be generated by /speckit.tasks
114
+ ```
115
+
116
+ ### Source Code (repository root)
117
+
118
+ ```text
119
+ frontend/
120
+ β”œβ”€β”€ src/
121
+ β”‚ β”œβ”€β”€ components/
122
+ β”‚ β”‚ β”œβ”€β”€ DirectoryTree.tsx # Modified: add expand/collapse all
123
+ β”‚ β”‚ β”œβ”€β”€ NoteViewer.tsx # Modified: add font controls, reading time, TOC button
124
+ β”‚ β”‚ β”œβ”€β”€ TableOfContents.tsx # NEW: TOC panel component
125
+ β”‚ β”‚ └── ui/
126
+ β”‚ β”‚ ��── hover-card.tsx # NEW: shadcn component (via CLI)
127
+ β”‚ β”‚ └── slider.tsx # Existing: used for font size
128
+ β”‚ β”œβ”€β”€ pages/
129
+ β”‚ β”‚ └── MainApp.tsx # Modified: font size state, smooth transitions
130
+ β”‚ β”œβ”€β”€ lib/
131
+ β”‚ β”‚ β”œβ”€β”€ markdown.tsx # Modified: add heading IDs, wikilink HoverCard
132
+ β”‚ β”‚ └── markdownToText.ts # Existing: used for reading time
133
+ β”‚ β”œβ”€β”€ hooks/
134
+ β”‚ β”‚ β”œβ”€β”€ useFontSize.ts # NEW: font size preference hook
135
+ β”‚ β”‚ └── useTableOfContents.ts # NEW: TOC extraction hook
136
+ β”‚ └── index.css # Modified: add --content-font-size variable
137
+ └── tailwind.config.js # Modified: extend transition utilities
138
+
139
+ backend/
140
+ └── [NO CHANGES]
141
+
142
+ data/
143
+ └── [NO CHANGES]
144
+ ```
145
+
146
+ **Structure Decision**: Web application (Option 2) - Frontend-only changes. All modifications confined to `frontend/src/`. No backend, API, or database changes required.
147
+
148
+ ## Complexity Tracking
149
+
150
+ **No violations to justify** - All constitution gates pass.
151
+
152
+ ## Phase 0: Research (COMPLETE)
153
+
154
+ **Output**: `research.md` - See [research.md](./research.md)
155
+
156
+ **Key Decisions**:
157
+ 1. **Font Size**: CSS custom properties scoped to `.prose` (WCAG compliant)
158
+ 2. **Expand/Collapse**: State propagation via `forceExpandState` prop
159
+ 3. **Wikilink Previews**: shadcn HoverCard with 500ms delay + cache
160
+ 4. **TOC**: Custom react-markdown renderer + ResizablePanel
161
+ 5. **Transitions**: tailwindcss-animate (existing)
162
+ 6. **Reading Time**: markdownToPlainText + 200 WPM
163
+ 7. **Particles**: CSS-only or shadcn component (stretch)
164
+
165
+ **Resolved Clarifications**: None - all technical approaches clear from research.
166
+
167
+ ## Phase 1: Design & Contracts
168
+
169
+ ### Data Model
170
+
171
+ **File**: `data-model.md`
172
+
173
+ **Entities**:
174
+
175
+ 1. **FontSizePreference** (localStorage)
176
+ - Key: `note-font-size`
177
+ - Value: `"small"` | `"medium"` | `"large"`
178
+ - Scope: Global (applies to all notes)
179
+
180
+ 2. **TOCPanelState** (localStorage)
181
+ - Key: `toc-panel-open`
182
+ - Value: `boolean`
183
+ - Scope: Global (persists across sessions)
184
+
185
+ 3. **WikilinkPreviewCache** (React state)
186
+ - Type: `Map<string, string>` (path β†’ preview text)
187
+ - Lifecycle: Component mount (cleared on unmount)
188
+ - Purpose: Avoid re-fetching previews
189
+
190
+ 4. **TOCHeading** (computed)
191
+ - Fields: `{ id: string, text: string, level: 1|2|3 }`
192
+ - Source: Extracted from markdown during render
193
+ - Lifecycle: Re-computed when note.body changes
194
+
195
+ **No backend data models** - All state is client-side.
196
+
197
+ ### API Contracts
198
+
199
+ **No new API endpoints** - Features use existing endpoints:
200
+
201
+ - `GET /api/notes/{path}` - Fetch note for wikilink preview (existing)
202
+ - All other features are pure frontend (no API calls)
203
+
204
+ ### Component Contracts
205
+
206
+ **New Components**:
207
+
208
+ 1. **TableOfContents.tsx**
209
+ ```typescript
210
+ interface TableOfContentsProps {
211
+ headings: TOCHeading[];
212
+ isOpen: boolean;
213
+ onToggle: () => void;
214
+ onHeadingClick: (id: string) => void;
215
+ }
216
+ ```
217
+
218
+ 2. **useFontSize.ts**
219
+ ```typescript
220
+ interface UseFontSizeReturn {
221
+ fontSize: "small" | "medium" | "large";
222
+ setFontSize: (size: "small" | "medium" | "large") => void;
223
+ fontSizeRem: number; // 0.875 | 1 | 1.125
224
+ }
225
+ ```
226
+
227
+ 3. **useTableOfContents.ts**
228
+ ```typescript
229
+ interface UseTableOfContentsReturn {
230
+ headings: TOCHeading[];
231
+ isOpen: boolean;
232
+ toggleTOC: () => void;
233
+ scrollToHeading: (id: string) => void;
234
+ }
235
+ ```
236
+
237
+ **Modified Components**:
238
+
239
+ 1. **DirectoryTree.tsx**
240
+ ```typescript
241
+ // Add props:
242
+ interface DirectoryTreeProps {
243
+ // ... existing props
244
+ expandAll?: boolean; // Trigger expand all
245
+ collapseAll?: boolean; // Trigger collapse all
246
+ }
247
+ ```
248
+
249
+ 2. **NoteViewer.tsx**
250
+ ```typescript
251
+ // Add props:
252
+ interface NoteViewerProps {
253
+ // ... existing props
254
+ fontSize?: "small" | "medium" | "large";
255
+ onFontSizeChange?: (size: "small" | "medium" | "large") => void;
256
+ }
257
+ ```
258
+
259
+ ### Quick Start Guide
260
+
261
+ **File**: `quickstart.md`
262
+
263
+ **Development Setup**:
264
+ ```bash
265
+ # 1. Switch to feature branch
266
+ git checkout 006-ui-polish
267
+
268
+ # 2. Install new shadcn component
269
+ cd frontend
270
+ npx shadcn@latest add hover-card
271
+
272
+ # 3. Start dev server
273
+ npm run dev
274
+
275
+ # 4. Test in browser at http://localhost:5173
276
+ ```
277
+
278
+ **Testing Checklist**:
279
+ - [ ] Font size: Click A-, A, A+ buttons and verify text resizes
280
+ - [ ] Font persistence: Reload page and verify size retained
281
+ - [ ] Expand All: Click and verify all folders open
282
+ - [ ] Collapse All: Click and verify all folders close
283
+ - [ ] Wikilink preview: Hover over `[[link]]` for 500ms, see tooltip
284
+ - [ ] Reading time: Open long note, see "X min read" badge
285
+ - [ ] TOC: Open long note, click TOC button, see headings panel
286
+ - [ ] TOC scroll: Click heading in TOC, verify smooth scroll
287
+ - [ ] Transitions: Switch notes, verify fade-in animation
288
+ - [ ] Accessibility: Tab through controls, verify keyboard nav
289
+ - [ ] Mobile: Test on small screen, verify responsive
290
+
291
+ **Deployment**:
292
+ ```bash
293
+ # Build production bundle
294
+ npm run build
295
+
296
+ # Verify build output
297
+ ls -lh dist/
298
+
299
+ # Deploy (HuggingFace Space auto-deploys on git push)
300
+ git push origin 006-ui-polish
301
+ ```
302
+
303
+ ## Constitution Re-Check (Post-Design)
304
+
305
+ All gates remain **PASS** after design phase:
306
+
307
+ - βœ… Brownfield: No existing code rewritten
308
+ - βœ… Testing: Manual verification per constitution
309
+ - βœ… Incremental: Features independently deployable
310
+ - βœ… Spec-driven: All work traces to FR-001 through FR-018
311
+
312
+ **No new violations introduced.**
313
+
314
+ ## Technology Additions
315
+
316
+ **New Dependencies**:
317
+ - `@radix-ui/react-hover-card` (via shadcn CLI)
318
+
319
+ **Optional (Stretch)**:
320
+ - shadcn particles component (if particle effects implemented)
321
+
322
+ ## Next Steps
323
+
324
+ 1. Run `/speckit.tasks` to generate implementation task list
325
+ 2. Implement features in priority order (P1 β†’ P2 β†’ P3 β†’ STRETCH)
326
+ 3. Manual testing per quickstart.md checklist
327
+ 4. Merge to master when P1+P2 features complete (P3+STRETCH optional)
328
+
329
+ ## Files Generated
330
+
331
+ - βœ… `research.md` - Technology decisions and best practices
332
+ - βœ… `data-model.md` - Client-side data structures
333
+ - βœ… `quickstart.md` - Development and testing guide
334
+ - ⏳ `contracts/` - N/A (no API contracts, component contracts in plan.md)
335
+ - ⏳ `tasks.md` - To be generated by `/speckit.tasks`
336
+
337
+ ---
338
+
339
+ **Plan Status**: βœ… COMPLETE - Ready for `/speckit.tasks`
specs/006-ui-polish/quickstart.md ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Quick Start Guide: UI Polish Pack
2
+
3
+ **Feature**: 006-ui-polish
4
+ **Branch**: `006-ui-polish`
5
+ **Estimated Time**: 4-6 hours implementation
6
+
7
+ ## Prerequisites
8
+
9
+ - Node.js 18+ and npm
10
+ - Git
11
+ - Code editor (VS Code recommended)
12
+ - Modern browser (Chrome/Firefox/Safari/Edge)
13
+
14
+ ## Development Setup
15
+
16
+ ### 1. Check Out Feature Branch
17
+
18
+ ```bash
19
+ # Ensure you're in project root
20
+ cd /home/wolfe/Projects/Document-MCP
21
+
22
+ # Check out feature branch
23
+ git checkout 006-ui-polish
24
+
25
+ # Verify branch
26
+ git branch --show-current
27
+ # Should show: 006-ui-polish
28
+ ```
29
+
30
+ ### 2. Install Dependencies
31
+
32
+ ```bash
33
+ # Navigate to frontend
34
+ cd frontend
35
+
36
+ # Install new shadcn/ui component for wikilink previews
37
+ npx shadcn@latest add hover-card
38
+
39
+ # Verify installation
40
+ ls src/components/ui/hover-card.tsx
41
+ # Should exist
42
+
43
+ # Return to project root
44
+ cd ..
45
+ ```
46
+
47
+ ### 3. Start Development Server
48
+
49
+ ```bash
50
+ # Start frontend dev server
51
+ cd frontend
52
+ npm run dev
53
+
54
+ # Server starts at http://localhost:5173
55
+ # Open in browser to begin testing
56
+ ```
57
+
58
+ ### 4. Verify Existing Features
59
+
60
+ Before implementing polish features, verify baseline functionality:
61
+
62
+ - [ ] Notes load and display correctly
63
+ - [ ] Directory tree shows folders and files
64
+ - [ ] Clicking notes navigates successfully
65
+ - [ ] Wikilinks render and are clickable
66
+ - [ ] TTS controls work (if available)
67
+
68
+ ## Implementation Order
69
+
70
+ Follow priority order from spec:
71
+
72
+ ### Phase 1: P1 Features (Core)
73
+
74
+ 1. **Font Size Adjuster** (~15 min)
75
+ - Files: `frontend/src/index.css`, `frontend/src/hooks/useFontSize.ts`, `frontend/src/components/NoteViewer.tsx`
76
+ - Test: Click A-/A/A+ buttons, verify text resizes
77
+
78
+ 2. **Expand/Collapse All** (~20 min)
79
+ - Files: `frontend/src/components/DirectoryTree.tsx`, `frontend/src/pages/MainApp.tsx`
80
+ - Test: Click "Expand All" / "Collapse All" buttons
81
+
82
+ ### Phase 2: P2 Features (Important)
83
+
84
+ 3. **Reading Time Estimator** (~10 min)
85
+ - Files: `frontend/src/components/NoteViewer.tsx`
86
+ - Test: Open notes of varying lengths, verify badge appears
87
+
88
+ 4. **Wikilink Preview Tooltips** (~30 min)
89
+ - Files: `frontend/src/lib/markdown.tsx`, `frontend/src/components/ui/hover-card.tsx`
90
+ - Test: Hover over `[[wikilink]]` for 500ms, see preview
91
+
92
+ 5. **Table of Contents** (~40 min)
93
+ - Files: `frontend/src/hooks/useTableOfContents.ts`, `frontend/src/components/TableOfContents.tsx`, `frontend/src/components/NoteViewer.tsx`
94
+ - Test: Open long note, click TOC button, navigate headings
95
+
96
+ ### Phase 3: P3 Features (Polish)
97
+
98
+ 6. **Smooth Transitions** (~10 min)
99
+ - Files: `frontend/tailwind.config.js`, `frontend/src/pages/MainApp.tsx`
100
+ - Test: Switch notes, toggle views, verify animations
101
+
102
+ ### Phase 4: Stretch (Optional)
103
+
104
+ 7. **Particle Effects** (~45 min + tuning)
105
+ - Files: CSS or shadcn particles component
106
+ - Test: Click UI elements, verify no lag
107
+
108
+ ## Testing Checklist
109
+
110
+ Use this checklist during development and before deployment:
111
+
112
+ ### Font Size Adjuster
113
+
114
+ - [ ] **A+ button**: Text increases to 18px
115
+ - [ ] **A- button**: Text decreases to 14px
116
+ - [ ] **A button**: Text resets to 16px (default)
117
+ - [ ] **Persistence**: Reload page, verify size retained
118
+ - [ ] **Scope**: Only note body text resizes (not UI chrome)
119
+ - [ ] **Accessibility**: Can tab to buttons with keyboard
120
+ - [ ] **Mobile**: Buttons visible and tappable on small screen
121
+
122
+ ### Expand/Collapse All
123
+
124
+ - [ ] **Expand All**: All folders open, showing contents
125
+ - [ ] **Collapse All**: All folders close, hiding contents
126
+ - [ ] **Mixed state**: Works correctly when some folders already open
127
+ - [ ] **Performance**: Completes in < 2s for 100+ folders
128
+ - [ ] **Visual feedback**: Buttons show active state
129
+ - [ ] **Accessibility**: Keyboard navigable
130
+
131
+ ### Wikilink Preview Tooltips
132
+
133
+ - [ ] **500ms delay**: Tooltip appears after hovering 500ms
134
+ - [ ] **Preview content**: Shows first 150-200 characters
135
+ - [ ] **Mouse away**: Tooltip disappears when mouse moves away
136
+ - [ ] **Broken link**: Shows "Note not found" for invalid links
137
+ - [ ] **No flickering**: Quick mouse movement doesn't trigger tooltips
138
+ - [ ] **Cache**: Second hover on same link is instant
139
+ - [ ] **Accessibility**: Wikilinks still keyboard navigable
140
+
141
+ ### Reading Time Estimator
142
+
143
+ - [ ] **Badge appears**: Shows "X min read" for notes > 200 words
144
+ - [ ] **No badge**: Hidden for very short notes (< 200 words)
145
+ - [ ] **Accuracy**: Estimates reasonable (within Β±20% of actual)
146
+ - [ ] **Position**: Badge near note title, visually balanced
147
+ - [ ] **Mobile**: Badge visible on small screens
148
+
149
+ ### Table of Contents
150
+
151
+ - [ ] **TOC button**: Appears in note toolbar
152
+ - [ ] **Panel opens**: Clicking button shows TOC sidebar
153
+ - [ ] **Headings listed**: H1, H2, H3 shown with indentation
154
+ - [ ] **Click to scroll**: Clicking heading scrolls to section
155
+ - [ ] **Smooth scroll**: Scrolling is smooth (not instant)
156
+ - [ ] **Close panel**: Clicking button again or outside closes panel
157
+ - [ ] **Persistence**: Panel state retained after reload
158
+ - [ ] **Empty state**: Shows "No headings found" for notes without headings
159
+ - [ ] **Performance**: Generates TOC in < 500ms for 50 headings
160
+ - [ ] **Mobile**: Panel overlays content, doesn't break layout
161
+
162
+ ### Smooth Transitions
163
+
164
+ - [ ] **Note switch**: New note fades in over 300ms
165
+ - [ ] **Graph toggle**: Graph slides in over 250ms
166
+ - [ ] **Tree selection**: Selected item highlights smoothly
167
+ - [ ] **60 FPS**: No dropped frames or jank
168
+ - [ ] **Reduced motion**: Respects `prefers-reduced-motion` setting
169
+
170
+ ### Particle Effects (Stretch)
171
+
172
+ - [ ] **Click feedback**: Particles appear on click
173
+ - [ ] **Performance**: No lag or frame drops
174
+ - [ ] **Multiple clicks**: Handles rapid clicking gracefully
175
+ - [ ] **Reduced motion**: Disabled when user prefers reduced motion
176
+ - [ ] **Mobile**: Disabled on mobile or optimized
177
+
178
+ ## Browser Testing
179
+
180
+ Test in multiple browsers:
181
+
182
+ - [ ] Chrome/Chromium (latest)
183
+ - [ ] Firefox (latest)
184
+ - [ ] Safari (if on macOS)
185
+ - [ ] Edge (latest)
186
+ - [ ] Mobile Safari (iOS)
187
+ - [ ] Mobile Chrome (Android)
188
+
189
+ ## Performance Verification
190
+
191
+ Use browser DevTools to verify performance:
192
+
193
+ ### Chrome DevTools Performance
194
+
195
+ 1. Open DevTools β†’ Performance tab
196
+ 2. Click Record
197
+ 3. Test feature (e.g., click font size button)
198
+ 4. Stop recording
199
+ 5. Verify:
200
+ - [ ] No long tasks (> 50ms)
201
+ - [ ] 60 FPS maintained
202
+ - [ ] No layout thrashing
203
+
204
+ ### Memory Profiling
205
+
206
+ 1. Open DevTools β†’ Memory tab
207
+ 2. Take heap snapshot before testing
208
+ 3. Use features extensively
209
+ 4. Take heap snapshot after
210
+ 5. Verify:
211
+ - [ ] No memory leaks
212
+ - [ ] Preview cache clears on unmount
213
+ - [ ] localStorage usage minimal (< 1KB)
214
+
215
+ ## Build & Deploy
216
+
217
+ ### Local Build
218
+
219
+ ```bash
220
+ # Build production bundle
221
+ cd frontend
222
+ npm run build
223
+
224
+ # Expected output:
225
+ # - dist/ directory created
226
+ # - Assets minified and optimized
227
+ # - No TypeScript errors
228
+ # - No linting errors
229
+
230
+ # Verify build output
231
+ ls -lh dist/
232
+ # Should show:
233
+ # - index.html
234
+ # - assets/ (JS/CSS chunks)
235
+ # - Total size ~400-500KB gzipped
236
+ ```
237
+
238
+ ### Preview Production Build
239
+
240
+ ```bash
241
+ # Serve production build locally
242
+ npm run preview
243
+
244
+ # Open http://localhost:4173
245
+ # Test all features in production mode
246
+ ```
247
+
248
+ ### Deploy to HuggingFace Space
249
+
250
+ ```bash
251
+ # Ensure all changes committed
252
+ git status
253
+ # Should show clean working tree
254
+
255
+ # Push to remote
256
+ git push origin 006-ui-polish
257
+
258
+ # HuggingFace Space auto-deploys from git
259
+ # Monitor build logs in Space settings
260
+ # Deployment takes ~3-5 minutes
261
+ ```
262
+
263
+ ## Troubleshooting
264
+
265
+ ### HoverCard Not Found
266
+
267
+ **Error**: `Cannot find module '@/components/ui/hover-card'`
268
+
269
+ **Solution**:
270
+ ```bash
271
+ cd frontend
272
+ npx shadcn@latest add hover-card
273
+ ```
274
+
275
+ ### Font Size Not Applying
276
+
277
+ **Error**: Font size changes don't affect text
278
+
279
+ **Solution**:
280
+ - Check `--content-font-size` CSS variable in index.css
281
+ - Verify `.prose` class applied to note body
282
+ - Check browser console for CSS errors
283
+
284
+ ### TOC Not Extracting Headings
285
+
286
+ **Error**: TOC panel shows "No headings found" but note has headings
287
+
288
+ **Solution**:
289
+ - Verify headings use markdown syntax (#, ##, ###)
290
+ - Check custom renderer in markdown.tsx
291
+ - Check browser console for extraction errors
292
+
293
+ ### Animations Janky
294
+
295
+ **Error**: Transitions stutter or drop frames
296
+
297
+ **Solution**:
298
+ - Use transform/opacity only (not width/height)
299
+ - Check for layout thrashing in DevTools
300
+ - Reduce animation duration or complexity
301
+
302
+ ### LocalStorage Not Persisting
303
+
304
+ **Error**: Preferences lost on reload
305
+
306
+ **Solution**:
307
+ - Check browser doesn't have localStorage disabled
308
+ - Verify localStorage.setItem() calls succeed
309
+ - Check browser console for quota errors
310
+
311
+ ## Rollback Plan
312
+
313
+ If issues arise in production:
314
+
315
+ ```bash
316
+ # Revert to previous working state
317
+ git checkout master
318
+
319
+ # Or revert specific commit
320
+ git revert <commit-hash>
321
+
322
+ # Push to trigger redeployment
323
+ git push origin master
324
+ ```
325
+
326
+ ## Next Steps
327
+
328
+ After implementation and testing:
329
+
330
+ 1. Run `/speckit.tasks` to generate detailed task breakdown
331
+ 2. Implement features in priority order
332
+ 3. Manual test after each feature
333
+ 4. Commit incrementally (one feature per commit)
334
+ 5. Merge to master when P1+P2 complete
335
+ 6. Deploy to production
336
+ 7. Monitor for issues
337
+
338
+ ## Support Resources
339
+
340
+ - [React Docs](https://react.dev)
341
+ - [Tailwind CSS Docs](https://tailwindcss.com/docs)
342
+ - [shadcn/ui Components](https://ui.shadcn.com)
343
+ - [react-markdown Docs](https://github.com/remarkjs/react-markdown)
344
+ - [WCAG 2.2 Guidelines](https://www.w3.org/WAI/WCAG22/quickref/)
345
+
346
+ ---
347
+
348
+ **Ready to Start**: All setup complete, begin with P1 features! πŸš€
specs/006-ui-polish/research.md ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Research: UI Polish Pack Implementation
2
+
3
+ **Feature**: 006-ui-polish
4
+ **Date**: 2025-11-28
5
+ **Researcher**: Explore Agent (Sonnet 4.5)
6
+
7
+ ## 1. Font Size Adjuster
8
+
9
+ **Decision**: CSS Custom Properties (CSS Variables) scoped to content area
10
+
11
+ **Rationale**:
12
+ - WCAG 2.2 compliance requires text scalable to 200% without loss of functionality
13
+ - CSS custom properties provide centralized control and easy dynamic updates
14
+ - Existing codebase already uses CSS variables for theming
15
+ - Avoids inline styles and className switching complexity
16
+
17
+ **Implementation Details**:
18
+ - Add CSS variable `--content-font-size` to `:root`
19
+ - Apply to `.prose` container (already used in NoteViewer.tsx:150)
20
+ - Range: 0.875rem (14px small), 1rem (16px medium), 1.125rem (18px large)
21
+ - Persist to localStorage key `note-font-size`
22
+ - Use rem units (relative to root) for scalability
23
+
24
+ **Alternatives Considered**:
25
+ - Inline styles: Too scattered, hard to maintain
26
+ - className switching: Limited to predefined sizes
27
+ - Browser zoom: Affects entire page including UI chrome
28
+
29
+ **Code References**:
30
+ - CSS variables: `frontend/src/index.css:6-50`
31
+ - Prose container: `frontend/src/components/NoteViewer.tsx:150`
32
+ - Slider pattern: `frontend/src/components/ui/slider.tsx`
33
+ - TTS volume pattern: `frontend/src/components/NoteViewer.tsx:115-130`
34
+
35
+ ---
36
+
37
+ ## 2. Directory Tree Expand/Collapse
38
+
39
+ **Decision**: Global expand/collapse state management in DirectoryTree component
40
+
41
+ **Rationale**:
42
+ - Current implementation uses local `isOpen` state per folder
43
+ - Auto-expands first 2 levels (depth < 2)
44
+ - Pattern already established, needs propagation mechanism
45
+
46
+ **Implementation Details**:
47
+ - Add buttons above directory tree: "Expand All" / "Collapse All"
48
+ - Add `forceExpandState` prop (undefined | boolean) to TreeNodeItem
49
+ - Override local `isOpen` when `forceExpandState` is set
50
+ - Reset to undefined after 300ms transition
51
+ - Leverage existing `buildTree()` structure
52
+
53
+ **Pattern**:
54
+ ```typescript
55
+ const [expandAllState, setExpandAllState] = useState<boolean | undefined>(undefined);
56
+ const effectiveIsOpen = forceExpandState ?? isOpen;
57
+ ```
58
+
59
+ **Alternatives Considered**:
60
+ - Fully controlled component: More complex, loses local state
61
+ - Context API: Overkill for simple toggle
62
+ - Recursive traversal: Already supported by tree structure
63
+
64
+ **Code References**:
65
+ - Folder state: `frontend/src/components/DirectoryTree.tsx:97`
66
+ - Tree building: `frontend/src/components/DirectoryTree.tsx:29-86`
67
+ - Folder rendering: `frontend/src/components/DirectoryTree.tsx:134-173`
68
+
69
+ ---
70
+
71
+ ## 3. Wikilink Preview Tooltips
72
+
73
+ **Decision**: Use shadcn/ui HoverCard with 500ms delay
74
+
75
+ **Rationale**:
76
+ - HoverCard supports rich content vs Tooltip (simple text)
77
+ - 500ms delay prevents flickering during cursor movement
78
+ - Designed specifically for link previews
79
+ - Existing wikilink handling in markdown.tsx
80
+
81
+ **Implementation Details**:
82
+ - Install: `npx shadcn@latest add hover-card`
83
+ - Wrap wikilink spans with HoverCard component
84
+ - Set `openDelay={500}` and `closeDelay={100}`
85
+ - Fetch first 200 characters of target note body
86
+ - Cache previews in React.useMemo
87
+ - Show loading skeleton during fetch
88
+
89
+ **Cache Strategy**:
90
+ ```typescript
91
+ const [previewCache, setPreviewCache] = useState<Map<string, string>>(new Map());
92
+ ```
93
+
94
+ **Accessibility Note**:
95
+ - HoverCard is hover-only (not keyboard accessible)
96
+ - Ensure wikilinks remain clickable and keyboard navigable
97
+
98
+ **Alternatives Considered**:
99
+ - Tooltip: Too limited for content
100
+ - Popover: Requires click, disrupts flow
101
+ - Custom implementation: More work, less accessible
102
+
103
+ **Code References**:
104
+ - Wikilink component: `frontend/src/lib/markdown.tsx:16-44`
105
+ - Note fetching: `frontend/src/services/api.ts` (getNote)
106
+
107
+ ---
108
+
109
+ ## 4. Table of Contents
110
+
111
+ **Decision**: Extract headings via custom react-markdown renderer, render in collapsible sidebar
112
+
113
+ **Rationale**:
114
+ - react-markdown already parses headings
115
+ - Can intercept rendering to extract TOC data
116
+ - ResizablePanel pattern matches existing architecture
117
+ - Smooth scrolling with prefers-reduced-motion support
118
+
119
+ **Implementation Details**:
120
+ - Add ID generation to h1-h6 renderers
121
+ - Slugify: `text.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '')`
122
+ - Extract: `{ id, text, level }[]` using useRef
123
+ - Render in ResizablePanel (right sidebar)
124
+ - Smooth scroll:
125
+ ```typescript
126
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
127
+ element.scrollIntoView({ behavior: prefersReducedMotion ? 'auto' : 'smooth' });
128
+ ```
129
+
130
+ **Panel Pattern**:
131
+ - Button in NoteViewer header (near TTS)
132
+ - Default collapsed, opens on click
133
+ - Hierarchical indentation by heading level
134
+ - Persist state to localStorage
135
+
136
+ **Alternatives Considered**:
137
+ - Sheet component: Not in codebase, needs installation
138
+ - Dialog: Blocks content, poor UX
139
+ - Inline floating: Overlaps on small screens
140
+
141
+ **Code References**:
142
+ - Heading renderers: `frontend/src/lib/markdown.tsx:61-75`
143
+ - ResizablePanel: `frontend/src/components/NoteEditor.tsx:8`
144
+ - Three-panel layout: `frontend/src/pages/MainApp.tsx:583-778`
145
+
146
+ ---
147
+
148
+ ## 5. Smooth Transitions
149
+
150
+ **Decision**: CSS transitions with tailwindcss-animate
151
+
152
+ **Rationale**:
153
+ - Already using `tailwindcss-animate` plugin
154
+ - 60fps GPU-accelerated performance
155
+ - No JavaScript overhead
156
+ - Existing animations: fade-in, slide-in-up, etc.
157
+ - Framer Motion adds 90KB - unnecessary
158
+
159
+ **Implementation Details**:
160
+ - Extend tailwind.config.js keyframes
161
+ - Add transition utilities:
162
+ ```css
163
+ transition: {
164
+ 'smooth': 'all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)',
165
+ 'fade': 'opacity 0.2s ease-in-out',
166
+ }
167
+ ```
168
+ - Apply via className
169
+ - Animate only transform/opacity (GPU properties)
170
+
171
+ **Performance Rules**:
172
+ - AVOID: margin, padding, border, width, height (causes reflow)
173
+ - USE: transform, opacity only
174
+ - Use `will-change` sparingly
175
+
176
+ **Alternatives Considered**:
177
+ - Framer Motion: 90KB, complex API
178
+ - React Spring: Physics-based, unnecessary
179
+ - GSAP: 50KB, license issues
180
+
181
+ **Code References**:
182
+ - Existing animations: `frontend/tailwind.config.js:57-97`
183
+ - Usage: `frontend/src/components/NoteViewer.tsx:79-163`
184
+ - Package: `package.json:61` (tailwindcss-animate)
185
+
186
+ ---
187
+
188
+ ## 6. Reading Time Estimation
189
+
190
+ **Decision**: Calculate from markdown using markdownToPlainText, 200 WPM standard
191
+
192
+ **Rationale**:
193
+ - Industry standard: 200 WPM (conservative)
194
+ - Existing utility strips formatting
195
+ - Simple: `Math.ceil(wordCount / 200)`
196
+ - Display as Badge near title
197
+
198
+ **Implementation Details**:
199
+ - Use `markdownToPlainText(note.body)`
200
+ - Word count: `plainText.trim().split(/\s+/).length`
201
+ - Format: "X min read"
202
+ - Location: After note title (NoteViewer.tsx:82-83)
203
+ - Cache in useMemo
204
+
205
+ **Pattern**:
206
+ ```typescript
207
+ const readingTime = useMemo(() => {
208
+ const plainText = markdownToPlainText(note.body);
209
+ const wordCount = plainText.trim().split(/\s+/).length;
210
+ const minutes = Math.ceil(wordCount / 200);
211
+ return minutes >= 1 ? `${minutes} min read` : null;
212
+ }, [note.body]);
213
+ ```
214
+
215
+ **Alternatives Considered**:
216
+ - reading-time npm: Overkill
217
+ - 238/250 WPM: Less conservative
218
+ - Character-based: Less accurate
219
+
220
+ **Code References**:
221
+ - markdownToPlainText: `frontend/src/lib/markdownToText.ts`
222
+ - Badge: `frontend/src/components/ui/badge.tsx`
223
+ - Header: `frontend/src/components/NoteViewer.tsx:79-146`
224
+
225
+ ---
226
+
227
+ ## 7. Particle Effects (Stretch)
228
+
229
+ **Decision**: CSS-only particles OR shadcn particles component
230
+
231
+ **Rationale**:
232
+ - CSS-only: Zero bundle, 60fps
233
+ - shadcn has Particles component (installable)
234
+ - Decorative only - no core impact
235
+ - Must respect prefers-reduced-motion
236
+
237
+ **Implementation Options**:
238
+
239
+ **Option A: CSS-Only (Recommended)**
240
+ - @keyframes with transform/opacity
241
+ - Multiple elements, staggered delays
242
+ - Box-shadow for many particles
243
+ - Low z-index, pointer-events: none
244
+
245
+ **Option B: shadcn Particles**
246
+ - Install: `npx shadcn@latest add particles`
247
+ - Limit 50-100 particles max
248
+ - Disable on mobile
249
+
250
+ **Performance Mitigation**:
251
+ - Max 50 particles
252
+ - Transform-only animations
253
+ - Disable on mobile (media query)
254
+ - Respect prefers-reduced-motion
255
+ - Optional/opt-in feature
256
+
257
+ **When to Use**:
258
+ - Success celebrations
259
+ - Easter eggs
260
+ - Optional backgrounds
261
+
262
+ **When NOT to Use**:
263
+ - During reading
264
+ - On lower-end devices
265
+ - By default in production
266
+
267
+ **Alternatives Considered**:
268
+ - Lottie: Large files
269
+ - Canvas particles: Complex
270
+ - WebGL: Massive overkill
271
+
272
+ **Code References**:
273
+ - shadcn particles: Available via CLI
274
+ - Animations: `frontend/tailwind.config.js:57-97`
275
+ - Settings: `frontend/src/pages/Settings.tsx` (toggle location)
276
+
277
+ ---
278
+
279
+ ## Technology Decisions Summary
280
+
281
+ | Feature | Technology | Rationale |
282
+ |---------|-----------|-----------|
283
+ | Font Size | CSS Custom Properties | WCAG compliance, existing pattern |
284
+ | Expand/Collapse | State propagation | Matches existing architecture |
285
+ | Wikilink Preview | shadcn HoverCard | Rich content, accessibility |
286
+ | Table of Contents | Custom renderer + ResizablePanel | Existing markdown/layout patterns |
287
+ | Transitions | tailwindcss-animate | Already installed, performant |
288
+ | Reading Time | markdownToPlainText + 200 WPM | Industry standard, simple |
289
+ | Particles | CSS-only or shadcn | Zero/minimal bundle impact |
290
+
291
+ ## Dependencies to Add
292
+
293
+ - `hover-card` (shadcn/ui component) - for wikilink previews
294
+ - `particles` (optional, stretch goal) - for particle effects
295
+
296
+ ## Performance Targets
297
+
298
+ - Font size change: < 100ms
299
+ - Expand All (100 folders): < 2s
300
+ - Wikilink preview: < 600ms (500ms delay + 100ms fetch)
301
+ - TOC generation: < 500ms (50 headings)
302
+ - All animations: 60 FPS
303
+ - No layout thrashing or jank
304
+
305
+ ## Accessibility Compliance
306
+
307
+ - WCAG 2.2 Level AA compliance
308
+ - Text scalable to 200%
309
+ - Keyboard navigation support
310
+ - prefers-reduced-motion respect
311
+ - ARIA labels on controls
312
+ - Minimum touch target: 24x24px
specs/006-ui-polish/spec.md ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Feature Specification: UI Polish Pack
2
+
3
+ **Feature Branch**: `006-ui-polish`
4
+ **Created**: 2025-11-28
5
+ **Status**: Draft
6
+ **Input**: User description: "Font size adjuster, small, medium (default), and large. Smooth transitions. I am also thinking particle effects when clicking on something, lets put this as a stretch goal as it might be buggy to do right. Expand and collapse all folders button. Wikilink preview tooltip. Reading time estimator. Table of contents as a fly out pane."
7
+
8
+ ## User Scenarios & Testing
9
+
10
+ ### User Story 1 - Adjust Reading Comfort (Priority: P1)
11
+
12
+ As a user with vision preferences or accessibility needs, I want to adjust the font size of note content so that I can read documentation comfortably without straining my eyes or using browser zoom.
13
+
14
+ **Why this priority**: Accessibility is critical and affects all users reading notes. This is the simplest feature to implement and delivers immediate value independently.
15
+
16
+ **Independent Test**: Can be fully tested by opening any note, clicking the font size buttons (A-, A, A+), and verifying text resizes. Delivers immediate reading comfort value without requiring other features.
17
+
18
+ **Acceptance Scenarios**:
19
+
20
+ 1. **Given** I am viewing a note, **When** I click the "A+" button, **Then** the note body text increases to large size (18px) and my preference is saved
21
+ 2. **Given** I am viewing a note with large text, **When** I click the "A-" button, **Then** the note body text decreases to small size (14px)
22
+ 3. **Given** I have set a font size preference, **When** I navigate to a different note or refresh the page, **Then** my font size preference is retained
23
+ 4. **Given** I am viewing a note, **When** I click the "A" button, **Then** the note body text resets to default size (16px)
24
+
25
+ ---
26
+
27
+ ### User Story 2 - Navigate Large Documentation Structures (Priority: P1)
28
+
29
+ As a user working with a vault containing many nested folders, I want to quickly expand all folders or collapse them all so that I can find notes faster without manually clicking each folder.
30
+
31
+ **Why this priority**: Critical for usability in large vaults. Users with 50+ notes in nested folders waste significant time navigating. Can be tested independently by creating a test vault with nested folders.
32
+
33
+ **Independent Test**: Can be fully tested by creating a vault with 3+ levels of nested folders, clicking "Expand All" to see entire tree, then "Collapse All" to hide all folders. Delivers navigation efficiency value independently.
34
+
35
+ **Acceptance Scenarios**:
36
+
37
+ 1. **Given** I have a vault with multiple nested folders, **When** I click "Expand All", **Then** all folder nodes in the directory tree expand to show their contents
38
+ 2. **Given** all folders are expanded, **When** I click "Collapse All", **Then** all folder nodes close, hiding their contents
39
+ 3. **Given** some folders are expanded and some collapsed, **When** I click "Expand All", **Then** all folders expand regardless of their previous state
40
+ 4. **Given** I have folders expanded, **When** I click "Collapse All", **Then** the tree returns to showing only top-level items
41
+
42
+ ---
43
+
44
+ ### User Story 3 - Preview Note Content Without Navigation (Priority: P2)
45
+
46
+ As a user exploring documentation with many cross-references, I want to hover over a wikilink and see a preview of the linked note's content so that I can decide whether to navigate without losing my current reading context.
47
+
48
+ **Why this priority**: Significantly improves information browsing efficiency, but not essential for basic reading. Requires wikilink detection to be working, making it dependent on core functionality.
49
+
50
+ **Independent Test**: Can be fully tested by creating notes with wikilinks, hovering over links for 500ms, and verifying preview appears with first 150 characters. Delivers context-aware navigation value independently.
51
+
52
+ **Acceptance Scenarios**:
53
+
54
+ 1. **Given** I am viewing a note containing a wikilink, **When** I hover my mouse over the wikilink for 500 milliseconds, **Then** a tooltip appears showing the first 150 characters of the linked note
55
+ 2. **Given** a wikilink preview tooltip is displayed, **When** I move my mouse away, **Then** the tooltip disappears
56
+ 3. **Given** a wikilink points to a non-existent note, **When** I hover over it, **Then** the tooltip shows "Note not found" or similar message
57
+ 4. **Given** I quickly move my mouse across multiple wikilinks, **When** I don't pause for 500ms on any link, **Then** no tooltips appear (prevents flickering)
58
+
59
+ ---
60
+
61
+ ### User Story 4 - Estimate Reading Time (Priority: P2)
62
+
63
+ As a user planning my documentation reading sessions, I want to see an estimated reading time for each note so that I can decide whether to read now or bookmark for later.
64
+
65
+ **Why this priority**: Helpful for time management but not critical for basic functionality. Very quick to implement and adds professional polish.
66
+
67
+ **Independent Test**: Can be fully tested by opening notes of varying lengths and verifying reading time badge appears with accurate estimates. Delivers time-planning value independently.
68
+
69
+ **Acceptance Scenarios**:
70
+
71
+ 1. **Given** I am viewing a note with 600 words, **When** the note loads, **Then** I see a badge showing "~3 min read" near the note title
72
+ 2. **Given** I am viewing a note with fewer than 200 words, **When** the note loads, **Then** no reading time badge is displayed (< 1 minute threshold)
73
+ 3. **Given** I am viewing a note with 1500 words, **When** the note loads, **Then** I see "~8 min read" (1500 / 200 = 7.5, rounded up)
74
+
75
+ ---
76
+
77
+ ### User Story 5 - Navigate Long Technical Documentation (Priority: P2)
78
+
79
+ As a user reading lengthy technical documentation, I want to see a table of contents with clickable headings so that I can quickly jump to specific sections without scrolling through the entire document.
80
+
81
+ **Why this priority**: Very valuable for long documents but only applies to a subset of notes. More complex to implement than other features.
82
+
83
+ **Independent Test**: Can be fully tested by opening a long note with H1-H3 headings, toggling the TOC panel, clicking headings to scroll, and verifying persistence. Delivers section navigation value independently.
84
+
85
+ **Acceptance Scenarios**:
86
+
87
+ 1. **Given** I am viewing a note with multiple headings, **When** I click the "TOC" button in the toolbar, **Then** a flyout panel appears on the right side showing all H1, H2, and H3 headings
88
+ 2. **Given** the TOC panel is open, **When** I click on a heading in the TOC, **Then** the note scrolls smoothly to that section
89
+ 3. **Given** the TOC panel is open, **When** I click the "TOC" button again or click outside the panel, **Then** the panel closes
90
+ 4. **Given** I have opened the TOC panel, **When** I close it and reload the page, **Then** the panel state (open/closed) is remembered
91
+ 5. **Given** I am viewing a note with no headings, **When** I click "TOC", **Then** the panel shows "No headings found" message
92
+
93
+ ---
94
+
95
+ ### User Story 6 - Experience Polished Interface (Priority: P3)
96
+
97
+ As a user navigating between notes and views, I want to see smooth transitions and animations so that the interface feels polished and professional rather than abrupt.
98
+
99
+ **Why this priority**: Nice-to-have polish that improves perceived quality but doesn't add functional value. Should be implemented last.
100
+
101
+ **Independent Test**: Can be fully tested by switching between notes, toggling graph view, and selecting tree items to verify animations play. Delivers visual polish value independently.
102
+
103
+ **Acceptance Scenarios**:
104
+
105
+ 1. **Given** I am viewing one note, **When** I select a different note, **Then** the new note content fades in over 300 milliseconds
106
+ 2. **Given** I am in note view, **When** I toggle to graph view, **Then** the graph slides in over 250 milliseconds
107
+ 3. **Given** I click on a directory tree item, **When** the item becomes selected, **Then** it highlights with a smooth transition
108
+
109
+ ---
110
+
111
+ ### User Story 7 - Visual Click Feedback (Priority: STRETCH)
112
+
113
+ As a user clicking on interactive elements, I want to see playful particle effects so that the interface feels more engaging and responsive to my actions.
114
+
115
+ **Why this priority**: Stretch goal - adds delight but risks feeling gimmicky or causing performance issues. Only implement if time permits and other features are stable.
116
+
117
+ **Independent Test**: Can be fully tested by clicking various UI elements (links, buttons, tree items) and verifying particle animations appear without lag. Delivers engagement value independently but has higher risk of bugs.
118
+
119
+ **Acceptance Scenarios**:
120
+
121
+ 1. **Given** I am using the interface, **When** I click on a wikilink, **Then** a small particle burst animation appears at the click point
122
+ 2. **Given** I am using the interface, **When** I click on a tree item, **Then** particle effects appear without causing UI lag or freezing
123
+ 3. **Given** particle effects are enabled, **When** I perform rapid clicks, **Then** the system gracefully handles multiple simultaneous animations
124
+
125
+ ---
126
+
127
+ ### Edge Cases
128
+
129
+ - What happens when a user sets font size to large and then views a note with very long lines? (Text should wrap properly, not overflow)
130
+ - How does the TOC handle notes with duplicate heading text? (Should still scroll to correct position based on document order)
131
+ - What happens when a wikilink preview is requested for a very large note? (Show only first 150 characters, no performance impact)
132
+ - How does "Expand All" perform with 100+ folders? (Should complete in under 2 seconds, show loading state if needed)
133
+ - What happens when a user hovers on a wikilink but the note hasn't been indexed yet? (Show "Loading..." then content or error)
134
+
135
+ ## Requirements
136
+
137
+ ### Functional Requirements
138
+
139
+ - **FR-001**: System MUST provide three font size options: Small (14px), Medium (16px), and Large (18px) for note body text
140
+ - **FR-002**: System MUST persist user's font size preference across browser sessions using localStorage
141
+ - **FR-003**: System MUST display "Expand All" and "Collapse All" buttons above the directory tree
142
+ - **FR-004**: "Expand All" button MUST open all folder nodes in the directory tree regardless of current state
143
+ - **FR-005**: "Collapse All" button MUST close all folder nodes in the directory tree
144
+ - **FR-006**: System MUST display a preview tooltip when user hovers over a wikilink for 500 milliseconds
145
+ - **FR-007**: Wikilink preview tooltip MUST show the first 150 characters of the target note's body content
146
+ - **FR-008**: System MUST calculate reading time as word count divided by 200 words per minute
147
+ - **FR-009**: System MUST display reading time badge only for notes estimated to take 1 minute or longer
148
+ - **FR-010**: System MUST generate a table of contents from H1, H2, and H3 headings in the current note
149
+ - **FR-011**: TOC panel MUST be toggleable via a "TOC" button in the note viewer toolbar
150
+ - **FR-012**: Clicking a TOC heading MUST smoothly scroll the note to that section
151
+ - **FR-013**: TOC panel state (open/closed) MUST persist across page reloads using localStorage
152
+ - **FR-014**: Note content transitions MUST use fade-in animation (300ms duration)
153
+ - **FR-015**: Graph view transitions MUST use slide-in animation (250ms duration)
154
+ - **FR-016**: Font size changes MUST only affect note body text, not UI chrome (sidebar, toolbar, headers)
155
+ - **FR-017**: Wikilink preview MUST not appear if mouse moves away before 500ms delay
156
+ - **FR-018** (STRETCH): System MAY display particle burst effects on click events for wikilinks, buttons, and tree items
157
+
158
+ ### Key Entities
159
+
160
+ - **Font Size Preference**: User's selected text size (small/medium/large), persisted in localStorage
161
+ - **TOC Panel State**: Boolean indicating whether TOC panel is open or closed, persisted in localStorage
162
+ - **TOC Heading**: Extracted heading from markdown with text, level (H1/H2/H3), and scroll position
163
+ - **Reading Time Estimate**: Calculated value in minutes based on note word count
164
+
165
+ ## Success Criteria
166
+
167
+ ### Measurable Outcomes
168
+
169
+ - **SC-001**: Users can adjust font size and see the change applied instantly (< 100ms)
170
+ - **SC-002**: Font size preference persists correctly across 100% of browser sessions
171
+ - **SC-003**: "Expand All" completes in under 2 seconds for vaults with up to 100 folders
172
+ - **SC-004**: Wikilink preview tooltips appear within 100ms after the 500ms hover delay
173
+ - **SC-005**: Reading time estimates are accurate within Β±20% of actual reading time for 90% of notes
174
+ - **SC-006**: TOC generation completes in under 500ms for notes with up to 50 headings
175
+ - **SC-007**: All transitions and animations complete without visual jank or frame drops (60 FPS)
176
+ - **SC-008**: TOC panel state persists correctly across 100% of page reloads
177
+ - **SC-009**: Users can navigate to any section in a 20-heading document in under 3 seconds using TOC
178
+ - **SC-010**: Rapid hovering over multiple wikilinks does not cause tooltip flickering or performance degradation
179
+
180
+ ## Assumptions
181
+
182
+ - Users primarily read documentation rather than edit it, so reading comfort features are valuable
183
+ - Most vaults contain between 10-100 notes with 2-4 levels of folder nesting
184
+ - Average reading speed is 200 words per minute (industry standard)
185
+ - Users are accessing the application via modern browsers with localStorage support
186
+ - Notes use standard markdown heading syntax (#, ##, ###)
187
+ - Font size preference applies globally to all notes, not per-note
188
+ - Wikilink previews fetching note content from existing API endpoints is acceptable (no new backend changes required)
189
+ - Particle effects (stretch goal) would use CSS-based animations or lightweight canvas library
190
+ - TOC panel appears on right side of note viewer, overlaying content on smaller screens
191
+ - Smooth transitions use CSS transitions or React spring animations (existing capabilities)
specs/006-ui-polish/tasks.md ADDED
@@ -0,0 +1,392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+
3
+ description: "Implementation tasks for UI Polish Pack feature"
4
+ ---
5
+
6
+ # Tasks: UI Polish Pack
7
+
8
+ **Input**: Design documents from `/specs/006-ui-polish/`
9
+ **Prerequisites**: plan.md, spec.md, research.md, data-model.md, quickstart.md
10
+
11
+ **Tests**: No automated tests - manual verification per quickstart.md checklist
12
+
13
+ **Organization**: Tasks are grouped by user story (P1 β†’ P2 β†’ P3 β†’ STRETCH) to enable independent implementation and testing of each polish feature.
14
+
15
+ ## Format: `[ID] [P?] [Story] Description`
16
+
17
+ - **[P]**: Can run in parallel (different files, no dependencies)
18
+ - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
19
+ - Include exact file paths in descriptions
20
+
21
+ ## Path Conventions
22
+
23
+ - **Web app**: `frontend/src/` for all React/TypeScript code
24
+ - No backend changes required for this feature
25
+
26
+ ---
27
+
28
+ ## Phase 1: Setup (Shared Infrastructure)
29
+
30
+ **Purpose**: Install dependencies and prepare development environment
31
+
32
+ - [ ] T001 Install shadcn/ui HoverCard component via `npx shadcn@latest add hover-card` in frontend/
33
+ - [ ] T002 [P] Verify tailwindcss-animate is installed in frontend/package.json (existing dependency)
34
+ - [ ] T003 [P] Verify development server runs successfully via `npm run dev` in frontend/
35
+
36
+ **Checkpoint**: Dependencies ready, dev server running
37
+
38
+ ---
39
+
40
+ ## Phase 2: Foundational (No blocking prerequisites)
41
+
42
+ **Purpose**: This feature has NO foundational blocking tasks - all user stories are independent frontend polish features
43
+
44
+ **⚠️ NOTE**: User story implementation can begin immediately after Phase 1
45
+
46
+ **Checkpoint**: Foundation ready - proceed directly to user stories
47
+
48
+ ---
49
+
50
+ ## Phase 3: User Story 1 - Adjust Reading Comfort (Priority: P1) 🎯 MVP
51
+
52
+ **Goal**: Allow users to adjust font size (small/medium/large) with localStorage persistence for comfortable reading
53
+
54
+ **Independent Test**: Open any note, click A-/A/A+ buttons, verify text resizes instantly and persists after page reload
55
+
56
+ ### Implementation for User Story 1
57
+
58
+ - [ ] T004 [P] [US1] Add CSS custom property `--content-font-size: 1rem;` to `:root` in frontend/src/index.css
59
+ - [ ] T005 [P] [US1] Apply `font-size: var(--content-font-size)` to `.prose` class in frontend/src/index.css
60
+ - [ ] T006 [US1] Create useFontSize hook in frontend/src/hooks/useFontSize.ts with localStorage persistence
61
+ - [ ] T007 [US1] Add font size state management to MainApp component in frontend/src/pages/MainApp.tsx
62
+ - [ ] T008 [US1] Pass fontSize props to NoteViewer in frontend/src/pages/MainApp.tsx
63
+ - [ ] T009 [US1] Add font size buttons (A-, A, A+) to NoteViewer toolbar in frontend/src/components/NoteViewer.tsx
64
+ - [ ] T010 [US1] Update CSS variable dynamically when font size changes in frontend/src/hooks/useFontSize.ts
65
+ - [ ] T011 [US1] Verify font size only affects .prose content, not UI chrome (manual test)
66
+
67
+ **Checkpoint**: Font size adjuster complete - text resizes with buttons, preference persists, UI chrome unaffected
68
+
69
+ ---
70
+
71
+ ## Phase 4: User Story 2 - Navigate Large Documentation Structures (Priority: P1)
72
+
73
+ **Goal**: Provide "Expand All" and "Collapse All" buttons for directory tree navigation in large vaults
74
+
75
+ **Independent Test**: Create vault with 3+ levels of nested folders, click Expand All to see entire tree, click Collapse All to hide all folders
76
+
77
+ ### Implementation for User Story 2
78
+
79
+ - [ ] T012 [P] [US2] Add expandAll state to DirectoryTree component in frontend/src/components/DirectoryTree.tsx
80
+ - [ ] T013 [P] [US2] Add collapseAll state to DirectoryTree component in frontend/src/components/DirectoryTree.tsx
81
+ - [ ] T014 [US2] Add forceExpandState prop to TreeNodeItem recursive component in frontend/src/components/DirectoryTree.tsx
82
+ - [ ] T015 [US2] Implement expand/collapse state propagation logic in frontend/src/components/DirectoryTree.tsx
83
+ - [ ] T016 [US2] Add "Expand All" button above directory tree in frontend/src/components/DirectoryTree.tsx
84
+ - [ ] T017 [US2] Add "Collapse All" button above directory tree in frontend/src/components/DirectoryTree.tsx
85
+ - [ ] T018 [US2] Verify expand all completes in <2s for 100+ folders (performance test)
86
+
87
+ **Checkpoint**: Expand/collapse all buttons work, all folders respond to state changes, performance acceptable
88
+
89
+ ---
90
+
91
+ ## Phase 5: User Story 3 - Preview Note Content Without Navigation (Priority: P2)
92
+
93
+ **Goal**: Show HoverCard preview with first 150 characters of linked note when hovering over wikilink for 500ms
94
+
95
+ **Independent Test**: Create notes with wikilinks, hover over links for 500ms, verify preview appears with first 150 characters
96
+
97
+ ### Implementation for User Story 3
98
+
99
+ - [ ] T019 [P] [US3] Create wikilink preview cache state (Map<string, string>) in markdown.tsx or NoteViewer component
100
+ - [ ] T020 [P] [US3] Import HoverCard component in frontend/src/lib/markdown.tsx
101
+ - [ ] T021 [US3] Wrap wikilink spans with HoverCard in wikilink renderer in frontend/src/lib/markdown.tsx
102
+ - [ ] T022 [US3] Set HoverCard openDelay={500} and closeDelay={100} in frontend/src/lib/markdown.tsx
103
+ - [ ] T023 [US3] Implement preview fetch logic using existing GET /api/notes/{path} endpoint
104
+ - [ ] T024 [US3] Extract first 150 characters from note body for preview display
105
+ - [ ] T025 [US3] Add loading skeleton during preview fetch in HoverCard content
106
+ - [ ] T026 [US3] Handle broken wikilinks (show "Note not found" message)
107
+ - [ ] T027 [US3] Verify no tooltips appear when mouse moves away before 500ms (flicker prevention test)
108
+ - [ ] T028 [US3] Verify preview cache works (second hover on same link is instant)
109
+
110
+ **Checkpoint**: Wikilink previews appear after 500ms hover, show correct content, handle errors gracefully
111
+
112
+ ---
113
+
114
+ ## Phase 6: User Story 4 - Estimate Reading Time (Priority: P2)
115
+
116
+ **Goal**: Display reading time badge ("X min read") for notes estimated to take 1+ minutes at 200 WPM
117
+
118
+ **Independent Test**: Open notes of varying lengths, verify reading time badge appears with accurate estimates
119
+
120
+ ### Implementation for User Story 4
121
+
122
+ - [ ] T029 [P] [US4] Import markdownToPlainText utility in frontend/src/components/NoteViewer.tsx
123
+ - [ ] T030 [P] [US4] Import Badge component from shadcn/ui in frontend/src/components/NoteViewer.tsx
124
+ - [ ] T031 [US4] Create useMemo hook to calculate reading time from note.body in frontend/src/components/NoteViewer.tsx
125
+ - [ ] T032 [US4] Extract word count: plainText.trim().split(/\\s+/).length in reading time calculation
126
+ - [ ] T033 [US4] Calculate minutes: Math.ceil(wordCount / 200) in reading time calculation
127
+ - [ ] T034 [US4] Return null if <1 minute (200 words threshold) in reading time calculation
128
+ - [ ] T035 [US4] Render Badge with "X min read" near note title in frontend/src/components/NoteViewer.tsx
129
+ - [ ] T036 [US4] Verify badge appears only for notes >200 words (manual test)
130
+
131
+ **Checkpoint**: Reading time badge displays for long notes, hidden for short notes, estimates accurate within Β±20%
132
+
133
+ ---
134
+
135
+ ## Phase 7: User Story 5 - Navigate Long Technical Documentation (Priority: P2)
136
+
137
+ **Goal**: Provide table of contents flyout panel with clickable headings (H1-H3) for long notes
138
+
139
+ **Independent Test**: Open long note with H1-H3 headings, toggle TOC panel, click headings to scroll, verify persistence
140
+
141
+ ### Implementation for User Story 5
142
+
143
+ - [ ] T037 [P] [US5] Create useTableOfContents hook in frontend/src/hooks/useTableOfContents.ts
144
+ - [ ] T038 [P] [US5] Create TableOfContents component in frontend/src/components/TableOfContents.tsx
145
+ - [ ] T039 [US5] Add heading ID generation to h1/h2/h3 renderers in frontend/src/lib/markdown.tsx
146
+ - [ ] T040 [US5] Implement slugify function: text.toLowerCase().replace(/\\s+/g, '-').replace(/[^\\w-]/g, '') in markdown.tsx
147
+ - [ ] T041 [US5] Extract headings { id, text, level }[] using useRef during render in useTableOfContents hook
148
+ - [ ] T042 [US5] Add TOC panel state (isOpen) to NoteViewer in frontend/src/components/NoteViewer.tsx
149
+ - [ ] T043 [US5] Persist TOC panel state to localStorage key 'toc-panel-open' in useTableOfContents hook
150
+ - [ ] T044 [US5] Add "TOC" button to NoteViewer toolbar in frontend/src/components/NoteViewer.tsx
151
+ - [ ] T045 [US5] Render TableOfContents component as ResizablePanel (right sidebar) in frontend/src/components/NoteViewer.tsx
152
+ - [ ] T046 [US5] Implement scrollToHeading function with smooth scroll behavior in useTableOfContents hook
153
+ - [ ] T047 [US5] Respect prefers-reduced-motion media query in scroll behavior in useTableOfContents hook
154
+ - [ ] T048 [US5] Add hierarchical indentation by heading level in TableOfContents component
155
+ - [ ] T049 [US5] Show "No headings found" message when headings array is empty in TableOfContents component
156
+ - [ ] T050 [US5] Handle duplicate heading text by appending -2, -3 to IDs in slugify function
157
+ - [ ] T051 [US5] Verify TOC panel state persists after reload (manual test)
158
+ - [ ] T052 [US5] Verify TOC generation completes in <500ms for 50 headings (performance test)
159
+
160
+ **Checkpoint**: TOC panel toggles, headings are clickable, smooth scrolling works, state persists, performance acceptable
161
+
162
+ ---
163
+
164
+ ## Phase 8: User Story 6 - Experience Polished Interface (Priority: P3)
165
+
166
+ **Goal**: Add smooth CSS transitions/animations to note switching, graph toggle, and tree selection
167
+
168
+ **Independent Test**: Switch between notes, toggle graph view, select tree items to verify animations play smoothly at 60 FPS
169
+
170
+ ### Implementation for User Story 6
171
+
172
+ - [ ] T053 [P] [US6] Extend Tailwind config with custom transition utilities in frontend/tailwind.config.js
173
+ - [ ] T054 [P] [US6] Add fade-in transition keyframe (300ms) in frontend/tailwind.config.js
174
+ - [ ] T055 [P] [US6] Add slide-in transition keyframe (250ms) in frontend/tailwind.config.js
175
+ - [ ] T056 [US6] Apply fade-in transition to note content container in frontend/src/components/NoteViewer.tsx
176
+ - [ ] T057 [US6] Apply slide-in transition to graph view toggle in frontend/src/pages/MainApp.tsx
177
+ - [ ] T058 [US6] Apply smooth transition to directory tree item selection in frontend/src/components/DirectoryTree.tsx
178
+ - [ ] T059 [US6] Add prefers-reduced-motion media query support to all transitions
179
+ - [ ] T060 [US6] Verify animations maintain 60 FPS (Chrome DevTools Performance tab test)
180
+ - [ ] T061 [US6] Verify animations only use transform/opacity (no layout thrashing)
181
+
182
+ **Checkpoint**: All transitions smooth, 60 FPS maintained, reduced motion respected
183
+
184
+ ---
185
+
186
+ ## Phase 9: User Story 7 - Visual Click Feedback (Priority: STRETCH)
187
+
188
+ **Goal**: Add playful particle effects on click events for wikilinks, buttons, and tree items
189
+
190
+ **Independent Test**: Click various UI elements, verify particle animations appear without lag
191
+
192
+ **⚠️ STRETCH**: Only implement if time permits and P1/P2/P3 features are stable
193
+
194
+ ### Implementation for User Story 7
195
+
196
+ - [ ] T062 [P] [US7] Decide implementation approach: CSS-only vs shadcn particles component
197
+ - [ ] T063 [P] [US7] If CSS-only: Create particle animation keyframes in frontend/src/index.css
198
+ - [ ] T064 [P] [US7] If shadcn: Install particles component via `npx shadcn@latest add particles`
199
+ - [ ] T065 [US7] Add particle effect trigger to wikilink click handler in frontend/src/lib/markdown.tsx
200
+ - [ ] T066 [US7] Add particle effect trigger to tree item click handler in frontend/src/components/DirectoryTree.tsx
201
+ - [ ] T067 [US7] Add particle effect trigger to button click handlers in NoteViewer/MainApp
202
+ - [ ] T068 [US7] Limit particle count to 50 max for performance
203
+ - [ ] T069 [US7] Disable particles on mobile devices (media query)
204
+ - [ ] T070 [US7] Respect prefers-reduced-motion (disable particles if set)
205
+ - [ ] T071 [US7] Verify no lag or frame drops during rapid clicking (stress test)
206
+ - [ ] T072 [US7] Verify particles use transform-only animations (no layout thrashing)
207
+
208
+ **Checkpoint**: Particle effects enhance engagement without performance degradation
209
+
210
+ ---
211
+
212
+ ## Phase 10: Polish & Cross-Cutting Concerns
213
+
214
+ **Purpose**: Final verification and cross-story polish
215
+
216
+ - [ ] T073 [P] Run complete quickstart.md testing checklist for all implemented stories
217
+ - [ ] T074 [P] Test font size adjuster checklist (7 items) from quickstart.md
218
+ - [ ] T075 [P] Test expand/collapse all checklist (6 items) from quickstart.md
219
+ - [ ] T076 [P] Test wikilink preview tooltips checklist (7 items) from quickstart.md
220
+ - [ ] T077 [P] Test reading time estimator checklist (5 items) from quickstart.md
221
+ - [ ] T078 [P] Test table of contents checklist (10 items) from quickstart.md
222
+ - [ ] T079 [P] Test smooth transitions checklist (5 items) from quickstart.md
223
+ - [ ] T080 [P] Test particle effects checklist (5 items) from quickstart.md (if implemented)
224
+ - [ ] T081 [P] Browser compatibility testing: Chrome, Firefox, Safari, Edge
225
+ - [ ] T082 [P] Mobile responsiveness testing: iOS Safari, Android Chrome
226
+ - [ ] T083 [P] Accessibility verification: keyboard navigation, ARIA labels, WCAG 2.2 Level AA
227
+ - [ ] T084 [P] Performance profiling: Chrome DevTools Performance tab
228
+ - [ ] T085 [P] Memory profiling: Check for leaks in preview cache and event listeners
229
+ - [ ] T086 Build production bundle via `npm run build` in frontend/
230
+ - [ ] T087 Preview production build via `npm run preview` and re-test all features
231
+ - [ ] T088 Verify bundle size is reasonable (~400-500KB gzipped)
232
+ - [ ] T089 Update CLAUDE.md if new patterns established (already done in planning phase)
233
+ - [ ] T090 Commit final changes with message per git commit guidelines
234
+
235
+ ---
236
+
237
+ ## Dependencies & Execution Order
238
+
239
+ ### Phase Dependencies
240
+
241
+ - **Setup (Phase 1)**: No dependencies - can start immediately
242
+ - **Foundational (Phase 2)**: N/A - no blocking foundational tasks for this feature
243
+ - **User Stories (Phase 3-9)**: Each user story is independent and can start after Phase 1
244
+ - US1 (Font Size): Can start immediately after Phase 1
245
+ - US2 (Expand/Collapse): Can start immediately after Phase 1
246
+ - US3 (Wikilink Preview): Can start immediately after Phase 1
247
+ - US4 (Reading Time): Can start immediately after Phase 1
248
+ - US5 (Table of Contents): Can start immediately after Phase 1
249
+ - US6 (Transitions): Can start immediately after Phase 1
250
+ - US7 (Particles): STRETCH - only if time permits after P1/P2/P3
251
+ - **Polish (Phase 10)**: Depends on all desired user stories being complete
252
+
253
+ ### User Story Dependencies
254
+
255
+ - **User Story 1 (P1)**: INDEPENDENT - No dependencies on other stories
256
+ - **User Story 2 (P1)**: INDEPENDENT - No dependencies on other stories
257
+ - **User Story 3 (P2)**: INDEPENDENT - No dependencies on other stories
258
+ - **User Story 4 (P2)**: INDEPENDENT - No dependencies on other stories
259
+ - **User Story 5 (P2)**: INDEPENDENT - No dependencies on other stories
260
+ - **User Story 6 (P3)**: INDEPENDENT - No dependencies on other stories (applies transitions to existing UI)
261
+ - **User Story 7 (STRETCH)**: INDEPENDENT - No dependencies on other stories
262
+
263
+ ### Within Each User Story
264
+
265
+ - Tasks within a story may have sequential dependencies (e.g., create hook before using it)
266
+ - Tasks marked [P] can run in parallel (different files)
267
+ - Complete story checkpoint before moving to next priority
268
+
269
+ ### Parallel Opportunities
270
+
271
+ - **Phase 1**: T002 and T003 can run in parallel with T001
272
+ - **User Stories**: All 7 user stories can be implemented in parallel by different developers (after Phase 1)
273
+ - **Within Stories**: Tasks marked [P] can run in parallel
274
+ - US1: T004 and T005 (CSS changes) can run in parallel
275
+ - US2: T012 and T013 (state additions) can run in parallel
276
+ - US3: T019 and T020 (imports) can run in parallel
277
+ - US4: T029 and T030 (imports) can run in parallel
278
+ - US5: T037 and T038 (hook + component scaffolding) can run in parallel
279
+ - US6: T053, T054, T055 (Tailwind config) can run in parallel
280
+ - US7: T062, T063, T064 (approach decision) can run in parallel
281
+ - **Polish (Phase 10)**: T073-T085 (all testing tasks) can run in parallel
282
+
283
+ ---
284
+
285
+ ## Parallel Example: User Story 1 (Font Size)
286
+
287
+ ```bash
288
+ # Launch CSS changes in parallel:
289
+ Task T004: "Add CSS custom property --content-font-size to :root in frontend/src/index.css"
290
+ Task T005: "Apply font-size: var(--content-font-size) to .prose class in frontend/src/index.css"
291
+
292
+ # Then create hook (sequential - needed before component integration):
293
+ Task T006: "Create useFontSize hook in frontend/src/hooks/useFontSize.ts"
294
+
295
+ # Then integrate into components (sequential - depends on hook):
296
+ Task T007: "Add font size state management to MainApp"
297
+ Task T008: "Pass fontSize props to NoteViewer"
298
+ Task T009: "Add font size buttons to NoteViewer toolbar"
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Parallel Example: User Story 5 (Table of Contents)
304
+
305
+ ```bash
306
+ # Launch hook and component scaffolding in parallel:
307
+ Task T037: "Create useTableOfContents hook in frontend/src/hooks/useTableOfContents.ts"
308
+ Task T038: "Create TableOfContents component in frontend/src/components/TableOfContents.tsx"
309
+
310
+ # Then implement markdown heading extraction (sequential):
311
+ Task T039: "Add heading ID generation to h1/h2/h3 renderers"
312
+ Task T040: "Implement slugify function"
313
+ Task T041: "Extract headings using useRef during render"
314
+
315
+ # Then integrate (sequential - depends on hook + component):
316
+ Task T042-T052: "Integrate TOC panel into NoteViewer with state/persistence/scrolling"
317
+ ```
318
+
319
+ ---
320
+
321
+ ## Implementation Strategy
322
+
323
+ ### MVP First (User Stories 1 & 2 Only - Both P1)
324
+
325
+ 1. Complete Phase 1: Setup (~5 min)
326
+ 2. Complete Phase 3: User Story 1 - Font Size (~15 min)
327
+ 3. **STOP and VALIDATE**: Test font size independently per quickstart.md
328
+ 4. Complete Phase 4: User Story 2 - Expand/Collapse All (~20 min)
329
+ 5. **STOP and VALIDATE**: Test expand/collapse independently per quickstart.md
330
+ 6. Deploy/demo if ready (MVP with 2 core features)
331
+
332
+ **Total MVP Time**: ~40 minutes for P1 features only
333
+
334
+ ### Incremental Delivery (Priority Order)
335
+
336
+ 1. **Setup (Phase 1)** β†’ Dependencies ready (~5 min)
337
+ 2. **P1 Features (Phases 3-4)** β†’ Font Size + Expand/Collapse β†’ Test β†’ Deploy (~40 min total)
338
+ 3. **P2 Features (Phases 5-7)** β†’ Wikilink Preview + Reading Time + TOC β†’ Test β†’ Deploy (~80 min total)
339
+ 4. **P3 Features (Phase 8)** β†’ Smooth Transitions β†’ Test β†’ Deploy (~10 min)
340
+ 5. **STRETCH (Phase 9)** β†’ Particle Effects β†’ Test β†’ Deploy (~45 min) - OPTIONAL
341
+ 6. **Polish (Phase 10)** β†’ Comprehensive testing + build β†’ Deploy (~30 min)
342
+
343
+ **Total Time Estimate**:
344
+ - P1 only: ~40 min (MVP)
345
+ - P1 + P2: ~120 min (recommended hackathon scope)
346
+ - P1 + P2 + P3: ~130 min (full polish)
347
+ - All features (including STRETCH): ~175 min (if time permits)
348
+
349
+ ### Parallel Team Strategy
350
+
351
+ With multiple developers (after Phase 1 setup):
352
+
353
+ **Option A: Priority-based (Recommended)**
354
+ 1. Developer A: User Story 1 (Font Size) - P1
355
+ 2. Developer B: User Story 2 (Expand/Collapse) - P1
356
+ 3. After P1 complete and tested:
357
+ - Developer A: User Story 3 (Wikilink Preview) - P2
358
+ - Developer B: User Story 4 (Reading Time) - P2
359
+ - Developer C: User Story 5 (Table of Contents) - P2
360
+
361
+ **Option B: Full parallel**
362
+ 1. Developer A: User Stories 1 & 4 (Font Size + Reading Time)
363
+ 2. Developer B: User Stories 2 & 6 (Expand/Collapse + Transitions)
364
+ 3. Developer C: User Stories 3 & 5 (Wikilink Preview + TOC)
365
+ 4. Developer D: User Story 7 (Particles - STRETCH)
366
+
367
+ **Option C: Serial (Single Developer)**
368
+ 1. Follow priority order: US1 β†’ US2 β†’ US4 β†’ US3 β†’ US5 β†’ US6 β†’ US7 (STRETCH)
369
+ 2. Stop after any checkpoint to demo/deploy incremental value
370
+
371
+ ---
372
+
373
+ ## Notes
374
+
375
+ - **[P] tasks**: Different files, no dependencies - safe to parallelize
376
+ - **[Story] label**: Maps task to specific user story for traceability
377
+ - **No tests**: Manual verification only per quickstart.md checklist
378
+ - **Independent stories**: Each story can be completed and tested without others
379
+ - **Frontend-only**: No backend changes required
380
+ - **Existing patterns**: Leverages CSS variables, React hooks, shadcn/ui, tailwindcss-animate
381
+ - **New dependencies**: Only @radix-ui/react-hover-card (installed in Phase 1)
382
+ - **Performance targets**: All documented in success criteria (SC-001 through SC-010)
383
+ - **Accessibility**: WCAG 2.2 Level AA compliance required
384
+ - **Commit strategy**: Commit after each completed user story checkpoint
385
+ - **Deployment**: Can deploy after any user story completion (incremental value)
386
+ - **STRETCH goal**: User Story 7 (Particles) only if P1/P2/P3 stable and time permits
387
+
388
+ ---
389
+
390
+ **Recommended Hackathon Scope**: P1 + P2 features (User Stories 1-5) = ~120 min implementation + 30 min testing/polish = **2.5 hours total**
391
+
392
+ This delivers maximum polish impact with minimal risk. P3 (Transitions) and STRETCH (Particles) can be added if time remains.