Spaces:
Running
Running
bigwolfeman
commited on
Commit
Β·
3c7dbf6
1
Parent(s):
a7c5777
init
Browse files- CLAUDE.md +2 -0
- specs/006-ui-polish/checklists/requirements.md +48 -0
- specs/006-ui-polish/data-model.md +277 -0
- specs/006-ui-polish/plan.md +339 -0
- specs/006-ui-polish/quickstart.md +348 -0
- specs/006-ui-polish/research.md +312 -0
- specs/006-ui-polish/spec.md +191 -0
- specs/006-ui-polish/tasks.md +392 -0
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.
|