Requirements archived to .planning/milestones/v0.2-REQUIREMENTS.md.
Fresh REQUIREMENTS.md will be created by /gsd:new-milestone for v0.3.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Archive v0.2 (UI Overhaul and Optimization) to milestones/:
- milestones/v0.2-ROADMAP.md — full phase archive (Phases 8–11, 33 plans)
- milestones/v0.2-REQUIREMENTS.md — all 40 requirements marked complete
- milestones/v0.2-MILESTONE-AUDIT.md — audit artifact (passed, 40/40)
- MILESTONES.md — new living milestone index
- RETROSPECTIVE.md — new living retrospective with v0.2 section
- PROJECT.md — full evolution review: v0.2 requirements moved to Validated, 5 new Key Decisions added
- STATE.md — updated to milestone-complete status
- ROADMAP.md — v0.2 phases collapsed into <details> with progress table updated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Retroactive-STRIDE audit for Phase 11 (visual-design-responsive-layout-cleanup).
No plan-time threat model existed; register built from implementation files.
All 13 threats closed: 7 mitigated, 6 accepted with documented rationale.
threats_open: 0 — phase 11 security gate passes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Expand/collapse button, router-link label, and button label all get focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-1
- :style skeleton width replaced with :class w-12/w-16/w-20 (same fix applied to AppSidebar in 11-05)
- Closes VISUAL-01 and VISUAL-03 gaps found by verifier
Task 2 — admin test classification:
- AdminAiConfigTab.test.js: RETAINED (tests AdminAiView.vue, a live component)
- AdminQuotasTab.test.js: RETAINED (tests AdminQuotasView.vue, a live component)
- AdminUsersTab.test.js: RETAINED (tests AdminUsersView.vue, a live component)
Task 3 — confirmed absent: HomeView.vue, FolderView.vue, AdminView.vue all absent
Task 4 — dead-code scan:
- FolderRow.vue: DELETED — no import in any live component; StorageBrowser renders
folder rows inline; FolderRow had no active route or active import (CLAUDE.md rule)
Task 5 — stale test removal:
- dropdown.test.js: removed 2 FolderRow tests (tested dead component);
kept 2 DocumentCard Teleport tests (protect live surface)
- No unused named imports found in live components
268/268 tests pass.
- Run ANALYZE=true npm run build after all Phase 11 optimizations
- Main bundle: 183.62 kB raw / 64.84 kB gzip (was 264.63 kB / 89.34 kB)
- SettingsView, TopicsView, DocumentView all now in separate lazy chunks
- 25 chunks total vs 15 at baseline
- AccountView.vue had no active route component reference
- /account path uses redirect: '/settings' with no component import
- No imports found across the entire codebase
- Satisfies CODE-07: no unreferenced route views remain
- Normalize page titles to text-2xl font-semibold (was font-bold in TopicsView, DocumentView, SharedView)
- Normalize section titles to text-lg font-semibold (was text-xl in SettingsPreferencesTab, SettingsAiTab, SettingsCloudTab, BackupCodesDisplay, CloudCredentialModal)
- Normalize panel headings to text-sm font-semibold (SettingsAccountTab, DocumentView)
- Normalize AdminOverviewView page title from text-xl to text-2xl font-semibold
- Replace decorative sidebar skeleton inline styles with static Tailwind width classes (w-12/w-16/w-20)
- Add focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-1 to all interactive buttons across the app
- Add active:bg-* states to primary and secondary buttons for coherent press feedback
- Update nav-link scoped CSS in AppSidebar and AdminSidebar to include active:bg-gray-200 and focus-visible ring
- Add 7 new tests: VISUAL-01 skeleton class invariant, VISUAL-04 focus-visible invariant, VISUAL-03 typography invariant (36 test files, 270 tests pass)
- Add max-h-[90vh] overflow-y-auto to ShareModal, CloudCredentialModal,
FolderDeleteModal, and DocumentView cloud-delete modal panels
- Add responsive px-4 sm:px-6 to DocumentPreviewModal header for narrow viewports
- Add data-test attributes to all modal panels for testability
- Confirm @tailwindcss/forms active in tailwind.config.js (no drift found)
- Audit forms: focus:outline-none focus:ring-2 pattern is consistent throughout
- Add 4 test files covering mobile-safe modal classes and form baseline:
ShareModal.mobile.test.js, CloudCredentialModal.mobile.test.js,
FolderDeleteModal.mobile.test.js, DocumentPreviewModal.mobile.test.js
VISUAL-02, RESP-04
- Ran ANALYZE=true npm run build; copied stats.html to .planning/perf/phase11-baseline.html
- Added phase11-baseline-summary.md: chunk sizes, route lazy-load audit,
responsive/modal/typography/dead-code findings for plans 11-02..11-06
- Main bundle: 264.63 kB raw / 89.34 kB gzip; 5 user routes still synchronous
- Admin/auth routes already lazy-loaded; CSS 98.74 kB raw (expected for full UI)
- Wrapped config in async defineConfig factory so visualizer can be dynamically
imported only when ANALYZE=true; normal builds incur zero overhead
- Added frontend/stats.html to .gitignore (ephemeral artifact; canonical copy
lives in .planning/perf/)
- keyboard.test.js: Gap 4 test — matched.find(r => r.instances?.default) resolves to FileManagerView
- TreeItem.test.js: Gap 1 tests — animate-pulse present, 'Loading' text absent during shimmer state
- StorageBrowser.showSearch.test.js: Gap 2 tests — showSearch true for local+cloud at root
- All 219 tests pass (8 new, 211 existing)
- SearchBar.vue: add .prevent.stop to @keydown.escape to suppress browser native blur (Gap 5)
- OsDragOverlay.vue: register window drop listener with capture=true to fire before folder-row handlers (Gap 6)
- App.vue: add v-else-if branch for admin routes that renders only router-view (no AppSidebar)
- App.vue: replace routeViewRef with getFileManagerInstance() using matched.find(r => r.instances?.default)
- App.vue: remove unused ref import (Gap 3 + Gap 4)
- TreeItem.vue: replace 'Loading…' text with 3 animate-pulse shimmer rows
- StorageBrowser.vue: showSearch now true for mode=local OR mode=cloud (Gap 1 + Gap 2)
- App.vue: add v-else-if branch for admin routes that renders only router-view (no AppSidebar)
- App.vue: replace routeViewRef with getFileManagerInstance() using matched.find(r => r.instances?.default)
- App.vue: remove unused ref import (Gap 3 + Gap 4)
- TreeItem.vue: replace 'Loading…' text with 3 animate-pulse shimmer rows
- StorageBrowser.vue: showSearch now true for mode=local OR mode=cloud (Gap 1 + Gap 2)
- loading initialised to true so skeleton rows show immediately on mount
instead of the empty state flashing before data arrives
- @file-open no longer a silent no-op; shows an info toast explaining the
file must be opened via the cloud provider directly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WR-04: AdminAuditView.vue had a local formatTimestamp() duplicating shared
util logic — moved to utils/formatters.js and imported from there per CLAUDE.md
shared-module rule.
WR-03: Remove unused onUnmounted import from FileManagerView.vue.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CR-01: DocumentCard.vue `@unshared="doc.is_shared = false"` mutated a
defineProps prop (readonly in Vue 3). Replace with a local `isShared` ref
watched against prop changes so the Shared pill hides correctly after revoke.
CR-02: App.vue keyboard handler fired U/N/Escape shortcuts even when a modal
was open (no input focused). Add `role="dialog"` guard so all shortcuts are
suppressed while any dialog is in the DOM.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add menuTriggerEl ref, menuStyle, and updateMenuPosition() using getBoundingClientRect
- Wrap three-dot menu in Teleport to body with data-test="folder-row-menu"
- toggleMenu() captures position before opening
- Add window scroll/resize listeners with cleanup in onUnmounted
- Update outside-click handler to also exclude teleported menu element
- All 4 UX-13 dropdown tests now pass; full test suite 208/208 green
- Add pickerTriggerEl ref and pickerStyle computed from getBoundingClientRect
- Wrap folder picker dropdown in Teleport to body with data-test attribute
- Add updatePickerPosition() called on toggle, scroll, and resize
- Add window scroll/resize listeners with cleanup in onUnmounted
- 2 DocumentCard dropdown tests now pass
- 6 real tests for UX-11 drag-to-move (click guard, drop emits, amber ring, dragend reset, toast wiring)
- 4 real tests for UX-13 Teleport dropdowns (DocumentCard, FolderRow position + scroll reposition)
- All dropdown tests RED (Teleport not yet implemented)
- Click-suppression test RED (guard not yet implemented)
- Options API component; emits files-dropped with file list
- Depth-counter pattern (D-16) prevents flicker across child element boundaries
- Guards against in-app drags via dataTransfer.types.includes('Files') check
- window addEventListener/removeEventListener in mounted/beforeUnmount
- Teleport to body; z-[9998] keeps overlay below ToastContainer z-[9999]
- Fade transition via scoped CSS (.fade-enter-active/.fade-leave-active)
- 8/8 tests GREEN
- Replace 7 it.todo stubs with 8 real tests covering UX-09 contract
- Tests dispatch DragEvent/Event with synthetic dataTransfer on window
- Covers: hidden by default, Files type guard, depth counter, nested enter/leave,
drop emit, drop reset, z-[9998] class on overlay element
- All 8 tests RED (OsDragOverlay.vue does not yet exist)
- Replace 11 it.todo stubs with 9 real it() assertions
- Tests verify FileManagerView exposes focusSearch, clearSearch, triggerUpload, startNewFolder
- All 9 tests are RED — defineExpose not yet added to FileManagerView
- UX-14: remove startNewFolder/cancelNewFolder/submitNewFolder + all related state
(showNewFolderInput, newFolderName, newFolderError) from script and template
- UX-14: remove <button @click="startNewFolder">New</button> from folder section header
- UX-14: remove inline new-folder <div v-if="showNewFolderInput"> input block
- UX-03: replace Loading… divs with animate-pulse skeleton rows (3-row pattern) in
all three sections (folders, cloud, topics)
- UX-01 sidebar micro: wire EmptyState size=sm for each empty section
- folders: icon=folder headline="Create a folder in the file manager"
- cloud: icon=cloud headline="Connect in Settings" with #cta Settings link
- topics: icon=tag headline="No topics yet"
- Add EmptyState import; StorageBrowser.startNewFolder untouched (invariant)
- Update tests to work with script setup (Composition API) reality; all 9 GREEN