From 0e90fb9090575ff8d7c3775c8f10be40a8561f3b Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 02:49:10 +0100 Subject: [PATCH 01/27] docs(06.3): capture phase context for UI structure refactor Phase 06.3: UI Structure Refactor - Implementation decisions documented - Phase boundary established - Design mockups created in Pencil Key structural decisions: - Sidebar becomes app navigation (Files, Settings), not folder tree - Folder navigation via click-to-enter and [..] PARENT_DIR - Fixed footer with copyright, links, and status indicator - Toolbar with breadcrumbs left, actions right - File list with bracketed sortable column headers Also: Add .planning/ to markdownlint ignore (uses custom heading structure with HTML-like section tags) Co-Authored-By: Claude Opus 4.5 --- .markdownlintignore | 3 + .../06.3-CONTEXT.md | 114 ++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 .planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md diff --git a/.markdownlintignore b/.markdownlintignore index c430e188c9..54881a1fa1 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -9,3 +9,6 @@ CHANGELOG.md # Finalized specifications - do not modify 00-Preliminary-R&D/ 00_START_HERE.md + +# Planning docs use custom heading structure with HTML tags +.planning/ diff --git a/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md b/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md new file mode 100644 index 0000000000..9096cda625 --- /dev/null +++ b/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md @@ -0,0 +1,114 @@ +# Phase 6.3: UI Structure Refactor - Context + +**Gathered:** 2026-01-30 +**Status:** Ready for planning + + +## Phase Boundary + +Complete structural redesign of page layouts, component hierarchy, toolbars, and navigation. This phase changes HOW the UI is organized — not styling (that was 6.2), but structure: page layout composition, toolbar placement and behavior, navigation patterns, and component hierarchy. + + + + +## Implementation Decisions + +### App Shell Layout + +- Fixed app shell — header, sidebar, footer all fixed position +- Only the file content area scrolls +- Three main zones: Header (top), Main (sidebar + content), Footer (bottom) + +### Header Structure + +- Separate header zone (not merged with toolbar) +- Contains: Logo (`> CIPHERBOX`) + user menu dropdown +- User dropdown triggered by hover +- Dropdown contains: settings, logout +- Status indicator moved to footer + +### Sidebar Composition + +- **Major change:** Sidebar is app-level navigation, NOT a folder tree +- Navigation items: Files (current view), Settings +- Storage quota indicator anchored at bottom of sidebar +- Sidebar collapsible to icons only (Files icon, Settings icon, storage visible) + +### Toolbar Organization + +- Layout: Breadcrumbs on left, actions on right +- Actions: New Folder button + Refresh button +- Upload button removed — users upload via drag-and-drop dropzone only +- Separation: Subtle background color difference (no hard border line) + +### Folder Navigation (no folder tree) + +- Double-click folders in file list to navigate into them +- Parent folder item `[..] PARENT_DIR` displayed in all non-root folders +- Parent folder row has empty Size/Modified columns +- Browser history integration — back button works for navigation +- Breadcrumbs show current path in toolbar + +### File List Structure + +- List view only — no grid or alternative views +- Columns: Name, Size, Modified (standard layout) +- Column headers wrapped in brackets: `[NAME]`, `[SIZE]`, `[MODIFIED]` +- Clickable column headers for sorting (with sort indicator arrow) +- Files and folders same brightness color (#00D084) +- Illustrated empty state (terminal-style ASCII art with helpful text) + +### Footer Structure + +- Fixed footer at bottom of app +- Layout: Copyright (left) | Links (center) | Status (right) +- Copyright: `© 2026 CipherBox` +- Links: `[help] [privacy] [terms] [github]` +- Status indicator: `● [CONNECTED]` with glow effect + +### Claude's Discretion + +- Exact ASCII art for empty state illustration +- Collapsed sidebar icon designs +- Hover state animations for user dropdown +- Exact spacing and padding values within established patterns + + + + +## Specific Ideas + +- Terminal aesthetic maintained — brackets `[]` around interactive elements +- Folder tree completely removed in favor of in-place navigation (click to enter, `[..]` to go up) +- The `[..] PARENT_DIR` row style — not italic, same as other entries but dimmer columns +- Storage quota bar at sidebar bottom with progress indicator +- Footer links should be subtle (dark green) while status indicator is bright green + + + + +## Deferred Ideas + +None — discussion stayed within phase scope + + + +### Approved Design + +**Frame:** Phase 6.3 - Unified Structure Mockup (ID: nRzxj) +**Location:** `designs/cipher-box-design.pen` +**Canvas position:** Right side of existing designs (x: 3890) + +The approved mockup shows: + +- Header with logo and hover-triggered user dropdown +- Sidebar with Files/Settings nav and storage quota +- Toolbar with breadcrumbs and +folder/refresh buttons +- File list with sortable bracketed headers +- Parent folder row `[..] PARENT_DIR` +- Footer with copyright, links, and status indicator + +--- + +_Phase: 06.3-ui-structure-refactor_ +_Context gathered: 2026-01-30_ From 72e688a01a212137ad513d8f1221771232ce40c9 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 02:50:45 +0100 Subject: [PATCH 02/27] Revert "docs(06.3): capture phase context for UI structure refactor" This reverts commit 0e90fb9090575ff8d7c3775c8f10be40a8561f3b. --- .markdownlintignore | 3 - .../06.3-CONTEXT.md | 114 ------------------ 2 files changed, 117 deletions(-) delete mode 100644 .planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md diff --git a/.markdownlintignore b/.markdownlintignore index 54881a1fa1..c430e188c9 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -9,6 +9,3 @@ CHANGELOG.md # Finalized specifications - do not modify 00-Preliminary-R&D/ 00_START_HERE.md - -# Planning docs use custom heading structure with HTML tags -.planning/ diff --git a/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md b/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md deleted file mode 100644 index 9096cda625..0000000000 --- a/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md +++ /dev/null @@ -1,114 +0,0 @@ -# Phase 6.3: UI Structure Refactor - Context - -**Gathered:** 2026-01-30 -**Status:** Ready for planning - - -## Phase Boundary - -Complete structural redesign of page layouts, component hierarchy, toolbars, and navigation. This phase changes HOW the UI is organized — not styling (that was 6.2), but structure: page layout composition, toolbar placement and behavior, navigation patterns, and component hierarchy. - - - - -## Implementation Decisions - -### App Shell Layout - -- Fixed app shell — header, sidebar, footer all fixed position -- Only the file content area scrolls -- Three main zones: Header (top), Main (sidebar + content), Footer (bottom) - -### Header Structure - -- Separate header zone (not merged with toolbar) -- Contains: Logo (`> CIPHERBOX`) + user menu dropdown -- User dropdown triggered by hover -- Dropdown contains: settings, logout -- Status indicator moved to footer - -### Sidebar Composition - -- **Major change:** Sidebar is app-level navigation, NOT a folder tree -- Navigation items: Files (current view), Settings -- Storage quota indicator anchored at bottom of sidebar -- Sidebar collapsible to icons only (Files icon, Settings icon, storage visible) - -### Toolbar Organization - -- Layout: Breadcrumbs on left, actions on right -- Actions: New Folder button + Refresh button -- Upload button removed — users upload via drag-and-drop dropzone only -- Separation: Subtle background color difference (no hard border line) - -### Folder Navigation (no folder tree) - -- Double-click folders in file list to navigate into them -- Parent folder item `[..] PARENT_DIR` displayed in all non-root folders -- Parent folder row has empty Size/Modified columns -- Browser history integration — back button works for navigation -- Breadcrumbs show current path in toolbar - -### File List Structure - -- List view only — no grid or alternative views -- Columns: Name, Size, Modified (standard layout) -- Column headers wrapped in brackets: `[NAME]`, `[SIZE]`, `[MODIFIED]` -- Clickable column headers for sorting (with sort indicator arrow) -- Files and folders same brightness color (#00D084) -- Illustrated empty state (terminal-style ASCII art with helpful text) - -### Footer Structure - -- Fixed footer at bottom of app -- Layout: Copyright (left) | Links (center) | Status (right) -- Copyright: `© 2026 CipherBox` -- Links: `[help] [privacy] [terms] [github]` -- Status indicator: `● [CONNECTED]` with glow effect - -### Claude's Discretion - -- Exact ASCII art for empty state illustration -- Collapsed sidebar icon designs -- Hover state animations for user dropdown -- Exact spacing and padding values within established patterns - - - - -## Specific Ideas - -- Terminal aesthetic maintained — brackets `[]` around interactive elements -- Folder tree completely removed in favor of in-place navigation (click to enter, `[..]` to go up) -- The `[..] PARENT_DIR` row style — not italic, same as other entries but dimmer columns -- Storage quota bar at sidebar bottom with progress indicator -- Footer links should be subtle (dark green) while status indicator is bright green - - - - -## Deferred Ideas - -None — discussion stayed within phase scope - - - -### Approved Design - -**Frame:** Phase 6.3 - Unified Structure Mockup (ID: nRzxj) -**Location:** `designs/cipher-box-design.pen` -**Canvas position:** Right side of existing designs (x: 3890) - -The approved mockup shows: - -- Header with logo and hover-triggered user dropdown -- Sidebar with Files/Settings nav and storage quota -- Toolbar with breadcrumbs and +folder/refresh buttons -- File list with sortable bracketed headers -- Parent folder row `[..] PARENT_DIR` -- Footer with copyright, links, and status indicator - ---- - -_Phase: 06.3-ui-structure-refactor_ -_Context gathered: 2026-01-30_ From 45c7c4ea2fcb202a39b286e973ac2c3eee59d159 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 02:53:16 +0100 Subject: [PATCH 03/27] docs(06.3): capture phase context for UI structure refactor Phase 06.3: UI Structure Refactor - Implementation decisions documented - Phase boundary established - Design mockups created in Pencil Key structural decisions: - Sidebar becomes app navigation (Files, Settings), not folder tree - Folder navigation via click-to-enter and [..] PARENT_DIR - Fixed footer with copyright, links, and status indicator - Toolbar with breadcrumbs left, actions right - File list with bracketed sortable column headers Also fixes 06.2-CONTEXT.md to pass markdownlint (MD001 heading increment) Co-Authored-By: Claude Opus 4.5 --- .../06.2-CONTEXT.md | 49 +++------------- .../06.3-CONTEXT.md | 56 +++++++++++++++++++ 2 files changed, 64 insertions(+), 41 deletions(-) create mode 100644 .planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md diff --git a/.planning/phases/06.2-restyle-app-with-pencil-design/06.2-CONTEXT.md b/.planning/phases/06.2-restyle-app-with-pencil-design/06.2-CONTEXT.md index 4633d757dc..7988f37933 100644 --- a/.planning/phases/06.2-restyle-app-with-pencil-design/06.2-CONTEXT.md +++ b/.planning/phases/06.2-restyle-app-with-pencil-design/06.2-CONTEXT.md @@ -13,37 +13,11 @@ Complete UI redesign of the CipherBox web app using the Pencil design file as th ## Implementation Decisions -### Design Source -- Primary design file: `designs/cipher-box-design.pen` -- Design covers: Desktop file browser, mobile file browser, login screens (connected/disconnected states) -- For missing elements (error states, loading spinners, empty states): ask Pencil to generate designs -- Settings page: User will design in Pencil first before implementation - -### Visual Identity -- **Theme:** Dark mode only — no light mode option -- **Typography:** JetBrains Mono exclusively throughout the entire app -- **Primary color:** Black background (`#000000`) -- **Accent color:** Green (`#00D084`) for borders, highlights, buttons, text -- **Secondary colors:** Add red for errors, yellow for warnings (semantic colors alongside green) -- **Matrix background:** Full animated matrix rain effect (user confirmed — not static fallback) - -### Component Scope -- Restyle everything at once: login + file browser + modals together -- Modals (upload progress, delete confirmation, rename): Match terminal style (Claude designs) -- Context menus: Terminal style with green borders, mono font, dark background -- Settings page: Deferred until user creates Pencil design - -### Design Consistency -- **Fidelity:** Visually equivalent — same feel, but Claude can adjust for responsiveness -- **Terminal prefixes:** Keep [DIR], [FILE], [docs] etc. exactly as designed -- **Hover/focus states:** Subtle green glow effect on interactive elements -- **Missing elements:** Ask Pencil to generate designs rather than improvising - -### Claude's Discretion -- Exact responsive breakpoints (design shows 1440px desktop, 390px mobile) -- Animation timing and easing for matrix effect -- Semantic color shades (which red, which yellow) -- Loading spinner design if not provided +- **Design Source:** Primary design file is `designs/cipher-box-design.pen`. Design covers desktop file browser, mobile file browser, login screens (connected/disconnected states). For missing elements (error states, loading spinners, empty states): ask Pencil to generate designs. Settings page: User will design in Pencil first before implementation. +- **Visual Identity:** Dark mode only (no light mode). JetBrains Mono typography throughout. Black background (`#000000`). Green accent (`#00D084`) for borders, highlights, buttons, text. Red for errors, yellow for warnings. Full animated matrix rain effect on login. +- **Component Scope:** Restyle everything at once (login + file browser + modals). Modals match terminal style. Context menus: green borders, mono font, dark background. Settings page deferred until user creates Pencil design. +- **Design Consistency:** Visually equivalent feel, but Claude can adjust for responsiveness. Keep [DIR], [FILE], [docs] prefixes exactly as designed. Subtle green glow on hover/focus. Ask Pencil for missing elements rather than improvising. +- **Claude's Discretion:** Exact responsive breakpoints. Animation timing/easing for matrix effect. Semantic color shades. Loading spinner design if not provided. @@ -57,14 +31,7 @@ Complete UI redesign of the CipherBox web app using the Pencil design file as th - Status bar at bottom showing connection status and hints - Breadcrumb navigation with terminal-style formatting -### Design File Reference -The .pen file contains these screens: -- `bi8Au` — Desktop file browser (1440x900) -- `ZVAUX` — Mobile file browser (390x844) -- `gVNOQ` — Desktop login connected -- `3OkP2` — Mobile login connected -- `77MPO` — Desktop login disconnected -- `j6TYH` — Mobile login disconnected +The .pen file contains these screens: `bi8Au` (Desktop file browser 1440x900), `ZVAUX` (Mobile file browser 390x844), `gVNOQ` (Desktop login connected), `3OkP2` (Mobile login connected), `77MPO` (Desktop login disconnected), `j6TYH` (Mobile login disconnected). @@ -78,5 +45,5 @@ The .pen file contains these screens: --- -*Phase: 06.2-restyle-app-with-pencil-design* -*Context gathered: 2026-01-23* +_Phase: 06.2-restyle-app-with-pencil-design_ +_Context gathered: 2026-01-23_ diff --git a/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md b/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md new file mode 100644 index 0000000000..46aea3da5f --- /dev/null +++ b/.planning/phases/06.3-ui-structure-refactor/06.3-CONTEXT.md @@ -0,0 +1,56 @@ +# Phase 6.3: UI Structure Refactor - Context + +**Gathered:** 2026-01-30 +**Status:** Ready for planning + + + +## Phase Boundary + +Complete structural redesign of page layouts, component hierarchy, toolbars, and navigation. This phase changes HOW the UI is organized — not styling (that was 6.2), but structure: page layout composition, toolbar placement and behavior, navigation patterns, and component hierarchy. + + + + + +## Implementation Decisions + +- **App Shell Layout:** Fixed app shell with header, sidebar, and footer all fixed position. Only the file content area scrolls. Three main zones: Header (top), Main (sidebar + content), Footer (bottom). +- **Header Structure:** Separate header zone (not merged with toolbar). Contains logo (`> CIPHERBOX`) and user menu dropdown. Dropdown triggered by hover, contains settings and logout. Status indicator moved to footer. +- **Sidebar Composition:** Sidebar is app-level navigation, NOT a folder tree (major change). Navigation items: Files (current view), Settings. Storage quota indicator anchored at bottom. Sidebar collapsible to icons only. +- **Toolbar Organization:** Breadcrumbs on left, actions on right. Actions: New Folder button + Refresh button. Upload button removed — users upload via drag-and-drop dropzone only. Separation via subtle background color difference (no hard border). +- **Folder Navigation:** Double-click folders in file list to navigate into them. Parent folder item `[..] PARENT_DIR` displayed in all non-root folders (empty Size/Modified columns). Browser history integration — back button works. Breadcrumbs show current path in toolbar. +- **File List Structure:** List view only (no grid). Columns: Name, Size, Modified. Column headers wrapped in brackets: `[NAME]`, `[SIZE]`, `[MODIFIED]`. Clickable headers for sorting. Files and folders same brightness (#00D084). Illustrated empty state with terminal-style ASCII art. +- **Footer Structure:** Fixed footer at bottom. Layout: Copyright (left) | Links (center) | Status (right). Copyright: `© 2026 CipherBox`. Links: `[help] [privacy] [terms] [github]`. Status: `● [CONNECTED]` with glow effect. +- **Claude's Discretion:** Exact ASCII art for empty state. Collapsed sidebar icon designs. Hover animations for user dropdown. Exact spacing/padding within established patterns. + + + + + +## Specific Ideas + +- Terminal aesthetic maintained — brackets `[]` around interactive elements +- Folder tree completely removed in favor of in-place navigation (click to enter, `[..]` to go up) +- The `[..] PARENT_DIR` row style — same as other entries but with empty size/date columns +- Storage quota bar at sidebar bottom with progress indicator +- Footer links subtle (dark green) while status indicator is bright green + + + + + +## Deferred Ideas + +None — discussion stayed within phase scope + + + +## Approved Design + +Design mockup created in `designs/cipher-box-design.pen` (frame: "Phase 6.3 - Unified Structure Mockup", ID: nRzxj, canvas position x: 3890). + +--- + +_Phase: 06.3-ui-structure-refactor_ +_Context gathered: 2026-01-30_ From 4fb3461d67ba51097999c52b87b9edb16560f697 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 02:55:28 +0100 Subject: [PATCH 04/27] Update pencil designs --- designs/cipher-box-design.pen | 2376 +++++++++++++++++++++++++++++++++ 1 file changed, 2376 insertions(+) diff --git a/designs/cipher-box-design.pen b/designs/cipher-box-design.pen index 2606e2bff1..685a74f0dc 100644 --- a/designs/cipher-box-design.pen +++ b/designs/cipher-box-design.pen @@ -3844,6 +3844,2382 @@ ] } ] + }, + { + "type": "frame", + "id": "JuhmV", + "x": 3040, + "y": 0, + "name": "Option A - Separate Header", + "width": 700, + "height": 450, + "fill": "#000000", + "layout": "vertical", + "children": [ + { + "type": "text", + "id": "9WGFX", + "name": "labelA", + "fill": "#00D084", + "content": "Option A: SEPARATE HEADER", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "700" + }, + { + "type": "frame", + "id": "WhI6c", + "name": "appHeader", + "width": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#00D084" + }, + "padding": [ + 12, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "J8ZC3", + "name": "headerLeftA", + "gap": 16, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "aIwda", + "name": "logoA", + "fill": "#00D084", + "content": "> CIPHERBOX", + "fontFamily": "JetBrains Mono", + "fontSize": 14, + "fontWeight": "700" + } + ] + }, + { + "type": "frame", + "id": "AWT2s", + "name": "headerRightA", + "gap": 16, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "MgqUk", + "name": "userA", + "fill": "#006644", + "content": "user@matrix.cloud", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "Dkmg6", + "name": "settingsA", + "fill": "#00D084", + "content": "[settings]", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "pZQpq", + "name": "logoutA", + "fill": "#00D084", + "content": "[logout]", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "YFUQ5", + "name": "fileBrowserArea", + "width": "fill_container", + "height": "fill_container", + "fill": "#001a11", + "children": [ + { + "type": "frame", + "id": "P0Tcz", + "name": "sidebar", + "width": 200, + "height": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": { + "right": 1 + }, + "fill": "#003322" + }, + "layout": "vertical", + "gap": 8, + "padding": 16, + "children": [ + { + "type": "text", + "id": "QutSO", + "name": "sidebarTitleA", + "fill": "#00D084", + "content": "[FOLDERS]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "600" + }, + { + "type": "text", + "id": "hOeUe", + "name": "folder1A", + "fill": "#006644", + "content": "▸ ~/root", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "aChF5", + "name": "folder2A", + "fill": "#006644", + "content": " ▸ documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "DnMCO", + "name": "folder3A", + "fill": "#006644", + "content": " ▸ photos", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "PXPZ3", + "name": "mainContent", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "gap": 12, + "padding": 16, + "children": [ + { + "type": "frame", + "id": "D68Jj", + "name": "toolbar", + "width": "fill_container", + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "GsJ3Y", + "name": "breadcrumbA", + "fill": "#006644", + "content": "~/root/documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "Xlb49", + "name": "actionsA", + "gap": 8, + "children": [ + { + "type": "text", + "id": "MwyHv", + "name": "btnUploadA", + "fill": "#00D084", + "content": "[--upload]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "hIMjL", + "name": "btnNewFolderA", + "fill": "#00D084", + "content": "[+folder]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "W8MaH", + "name": "fileList", + "width": "fill_container", + "height": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + } + } + ] + } + ] + }, + { + "type": "text", + "id": "Pt0wv", + "name": "noteA", + "fill": "#006644", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Two distinct zones: App header + File browser with its own toolbar", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "Ul7pV", + "x": 3040, + "y": 500, + "name": "Option B - Merged Single Bar", + "width": 700, + "height": 450, + "fill": "#000000", + "layout": "vertical", + "children": [ + { + "type": "text", + "id": "eoUT3", + "name": "labelB", + "fill": "#00D084", + "content": "Option B: MERGED SINGLE BAR", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "700" + }, + { + "type": "frame", + "id": "9MKXh", + "name": "unifiedBar", + "width": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#00D084" + }, + "padding": [ + 10, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "sllm6", + "name": "barLeftB", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "nIOHc", + "name": "logoB", + "fill": "#00D084", + "content": ">", + "fontFamily": "JetBrains Mono", + "fontSize": 16, + "fontWeight": "700" + }, + { + "type": "text", + "id": "N1vDa", + "name": "breadcrumbB", + "fill": "#006644", + "content": "~/root/documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "OrmCE", + "name": "barCenterB", + "gap": 12, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "etTdA", + "name": "btnUploadB", + "fill": "#00D084", + "content": "[--upload]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "42mDy", + "name": "btnNewFolderB", + "fill": "#00D084", + "content": "[+folder]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "hilgf", + "name": "barRightB", + "gap": 16, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "9rxYQ", + "name": "userB", + "fill": "#006644", + "content": "user@matrix.cloud", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "Bcgdw", + "name": "settingsB", + "fill": "#00D084", + "content": "[⚙]", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "wZpbu", + "name": "logoutB", + "fill": "#00D084", + "content": "[×]", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "SqQ21", + "name": "contentArea", + "width": "fill_container", + "height": "fill_container", + "fill": "#001a11", + "children": [ + { + "type": "frame", + "id": "HgRYc", + "name": "sidebar", + "width": 200, + "height": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": { + "right": 1 + }, + "fill": "#003322" + }, + "layout": "vertical", + "gap": 8, + "padding": 16, + "children": [ + { + "type": "text", + "id": "zDCv9", + "name": "sidebarTitleB", + "fill": "#00D084", + "content": "[FOLDERS]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "600" + }, + { + "type": "text", + "id": "l4pbh", + "name": "folder1B", + "fill": "#006644", + "content": "▸ ~/root", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "y6H8V", + "name": "folder2B", + "fill": "#006644", + "content": " ▸ documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "O0tQk", + "name": "folder3B", + "fill": "#006644", + "content": " ▸ photos", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "jCLj9", + "name": "fileArea", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "padding": 16, + "children": [ + { + "type": "frame", + "id": "xiPA8", + "name": "fileList", + "width": "fill_container", + "height": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + } + } + ] + } + ] + }, + { + "type": "text", + "id": "ChPLe", + "name": "noteB", + "fill": "#006644", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Single unified bar: Logo + breadcrumb + actions + user. More vertical space for files.", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "6GR4O", + "x": 3040, + "y": 1000, + "name": "Toolbar Options", + "width": 750, + "fill": "#0a0a0a", + "layout": "vertical", + "gap": 20, + "padding": 20, + "children": [ + { + "type": "text", + "id": "r8QzV", + "name": "titleT", + "fill": "#00D084", + "content": "TOOLBAR ORGANIZATION OPTIONS", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "700" + }, + { + "type": "frame", + "id": "B2Dfs", + "name": "Option 1", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "text", + "id": "ncGnw", + "name": "label1", + "fill": "#006644", + "content": "Option 1: Left breadcrumbs, Right actions", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "6oh3B", + "name": "bar1", + "width": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 12, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "J5jye", + "name": "left1", + "fill": "#006644", + "content": "~/root/documents/projects", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "ghqyW", + "name": "right1", + "gap": 12, + "children": [ + { + "type": "frame", + "id": "YLgR9", + "name": "btn1a", + "fill": "#00D084", + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "psXaC", + "name": "btn1aText", + "fill": "#000000", + "content": "--upload", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "600" + } + ] + }, + { + "type": "frame", + "id": "XXpkf", + "name": "btn1b", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#00D084" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "Hi18P", + "name": "btn1bText", + "fill": "#00D084", + "content": "+folder", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "T2BAc", + "name": "btn1c", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "0TAcr", + "name": "btn1cText", + "fill": "#006644", + "content": "refresh", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "ty5PE", + "name": "Option 2", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "text", + "id": "KyVfl", + "name": "label2", + "fill": "#006644", + "content": "Option 2: Breadcrumbs left, Primary center, Secondary right", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "NATyk", + "name": "bar2", + "width": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 12, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "c7fZn", + "name": "left2", + "fill": "#006644", + "content": "~/root/documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "yEAxn", + "name": "center2", + "gap": 12, + "children": [ + { + "type": "frame", + "id": "Qa2Sr", + "name": "btn2a", + "fill": "#00D084", + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "VlApx", + "name": "btn2aText", + "fill": "#000000", + "content": "--upload", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "600" + } + ] + }, + { + "type": "frame", + "id": "Cf0vJ", + "name": "btn2b", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#00D084" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "6EPlX", + "name": "btn2bText", + "fill": "#00D084", + "content": "+folder", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "2dCIq", + "name": "right2", + "gap": 12, + "children": [ + { + "type": "frame", + "id": "RA32t", + "name": "btn2c", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "gxNA5", + "name": "btn2cText", + "fill": "#006644", + "content": "view: list", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "ci535", + "name": "btn2d", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "67a0s", + "name": "btn2dText", + "fill": "#006644", + "content": "sort: name", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "HXWFE", + "name": "Option 3", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "text", + "id": "EWxIO", + "name": "label3", + "fill": "#006644", + "content": "Option 3: Stacked rows (breadcrumbs row + actions row)", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "laCaA", + "name": "bar3", + "width": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "EgOFY", + "name": "row3a", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 10, + 16 + ], + "children": [ + { + "type": "text", + "id": "OZmPc", + "name": "bread3", + "fill": "#006644", + "content": "~/root/documents/projects/client-work", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "CgWgz", + "name": "row3b", + "width": "fill_container", + "padding": [ + 10, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "CR8QL", + "name": "left3", + "gap": 12, + "children": [ + { + "type": "frame", + "id": "expJn", + "name": "btn3a", + "fill": "#00D084", + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "FKwvE", + "name": "btn3aText", + "fill": "#000000", + "content": "--upload", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "600" + } + ] + }, + { + "type": "frame", + "id": "sH72B", + "name": "btn3b", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#00D084" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "cCNr5", + "name": "btn3bText", + "fill": "#00D084", + "content": "+folder", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "Gca6N", + "name": "btn3c", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "jHW8A", + "name": "btn3cText", + "fill": "#006644", + "content": "refresh", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "nNtz1", + "name": "right3", + "gap": 12, + "children": [ + { + "type": "frame", + "id": "ww0RB", + "name": "btn3d", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "Jeosd", + "name": "btn3dText", + "fill": "#006644", + "content": "view: list", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "Emt4Z", + "name": "btn3e", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "M4Wj6", + "name": "btn3eText", + "fill": "#006644", + "content": "sort: name", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "rURQo", + "x": 3040, + "y": 1407, + "name": "Toolbar Border Options", + "width": 750, + "fill": "#0a0a0a", + "layout": "vertical", + "gap": 16, + "padding": 20, + "children": [ + { + "type": "text", + "id": "RHlBc", + "name": "titleB", + "fill": "#00D084", + "content": "TOOLBAR BORDER/SEPARATION OPTIONS", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "700" + }, + { + "type": "frame", + "id": "58REk", + "name": "Border Option 1", + "width": "fill_container", + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "bWaKX", + "name": "blabel1", + "fill": "#006644", + "content": "Option 1: Bottom border line (green accent)", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "0BIKw", + "name": "bframe1", + "width": "fill_container", + "height": 120, + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "U21SL", + "name": "btoolbar1", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#00D084" + }, + "padding": [ + 12, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "lV8Wk", + "name": "bbread1", + "fill": "#006644", + "content": "~/root/documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "10rnc", + "name": "bactions1", + "gap": 8, + "children": [ + { + "type": "text", + "id": "nghsB", + "name": "bbtn1a", + "fill": "#00D084", + "content": "[+folder]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "Nb1nL", + "name": "bbtn1b", + "fill": "#006644", + "content": "[refresh]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "KaUyx", + "name": "bfiles1", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "gap": 4, + "padding": [ + 8, + 16 + ], + "children": [ + { + "type": "text", + "id": "zgbKC", + "name": "brow1a", + "fill": "#00D084", + "content": "[DIR] projects/", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "lcnbc", + "name": "brow1b", + "fill": "#006644", + "content": "[FILE] readme.md", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "VyJlb", + "name": "Border Option 2", + "width": "fill_container", + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "2x4d8", + "name": "blabel2", + "fill": "#006644", + "content": "Option 2: Background color difference (no border)", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "jDyQm", + "name": "bframe2", + "width": "fill_container", + "height": 120, + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "PHiGp", + "name": "btoolbar2", + "width": "fill_container", + "fill": "#001a11", + "padding": [ + 12, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "PFx0N", + "name": "bbread2", + "fill": "#006644", + "content": "~/root/documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "yFV0A", + "name": "bactions2", + "gap": 8, + "children": [ + { + "type": "text", + "id": "YbrBO", + "name": "bbtn2a", + "fill": "#00D084", + "content": "[+folder]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "Vf6nU", + "name": "bbtn2b", + "fill": "#006644", + "content": "[refresh]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "NxKLQ", + "name": "bfiles2", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "gap": 4, + "padding": [ + 8, + 16 + ], + "children": [ + { + "type": "text", + "id": "P3Vxd", + "name": "brow2a", + "fill": "#00D084", + "content": "[DIR] projects/", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "3JSjs", + "name": "brow2b", + "fill": "#006644", + "content": "[FILE] readme.md", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "zDBps", + "name": "Border Option 3", + "width": "fill_container", + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "MQKbI", + "name": "blabel3", + "fill": "#006644", + "content": "Option 3: No separation (toolbar flows into content)", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "XgFIE", + "name": "bframe3", + "width": "fill_container", + "height": 120, + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "ZdzdX", + "name": "btoolbar3", + "width": "fill_container", + "padding": [ + 12, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "bAsBg", + "name": "bbread3", + "fill": "#006644", + "content": "~/root/documents", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "wj7U0", + "name": "bactions3", + "gap": 8, + "children": [ + { + "type": "text", + "id": "B1yux", + "name": "bbtn3a", + "fill": "#00D084", + "content": "[+folder]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "TPRPe", + "name": "bbtn3b", + "fill": "#006644", + "content": "[refresh]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "MuGnf", + "name": "bfiles3", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "gap": 4, + "padding": [ + 8, + 16 + ], + "children": [ + { + "type": "text", + "id": "wFEwZ", + "name": "brow3a", + "fill": "#00D084", + "content": "[DIR] projects/", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "fUsK1", + "name": "brow3b", + "fill": "#006644", + "content": "[FILE] readme.md", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "nRzxj", + "x": 3890, + "y": 1407, + "name": "Phase 6.3 - Unified Structure Mockup", + "clip": true, + "width": 1000, + "height": 700, + "fill": "#000000", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "BwljC", + "name": "appHeader", + "width": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#00D084" + }, + "padding": [ + 12, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "peUja", + "name": "headerLeft", + "gap": 12, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "9jOaU", + "name": "logo", + "fill": "#00D084", + "content": ">", + "fontFamily": "JetBrains Mono", + "fontSize": 20, + "fontWeight": "700" + }, + { + "type": "text", + "id": "cjDhd", + "name": "appName", + "fill": "#00D084", + "content": "CIPHERBOX", + "fontFamily": "JetBrains Mono", + "fontSize": 14, + "fontWeight": "600" + } + ] + }, + { + "type": "frame", + "id": "Jr1bB", + "name": "headerRight", + "gap": 16, + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "CLFih", + "name": "userMenu", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "gap": 8, + "padding": [ + 4, + 8 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "5RlUT", + "name": "userEmail", + "fill": "#006644", + "content": "user@matrix.cloud", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "uIfYP", + "name": "userArrow", + "fill": "#006644", + "content": "▼", + "fontFamily": "JetBrains Mono", + "fontSize": 8, + "fontWeight": "normal" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "Yu3Zz", + "name": "mainArea", + "width": "fill_container", + "height": "fill_container", + "children": [ + { + "type": "frame", + "id": "rckAN", + "name": "sidebar", + "width": 180, + "height": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": { + "right": 1 + }, + "fill": "#003322" + }, + "layout": "vertical", + "justifyContent": "space_between", + "children": [ + { + "type": "frame", + "id": "wfywR", + "name": "sidebarNav", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "padding": 16, + "children": [ + { + "type": "frame", + "id": "kmbsj", + "name": "navFiles", + "fill": "#001a11", + "gap": 12, + "padding": [ + 8, + 12 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "yje8j", + "name": "navFilesIcon", + "content": "📁", + "fontFamily": "JetBrains Mono", + "fontSize": 14, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "rr76i", + "name": "navFilesText", + "fill": "#00D084", + "content": "Files", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "600" + } + ] + }, + { + "type": "frame", + "id": "X75Nl", + "name": "navSettings", + "gap": 12, + "padding": [ + 8, + 12 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "mxyTw", + "name": "navSettingsIcon", + "fill": "#006644", + "content": "⚙", + "fontFamily": "JetBrains Mono", + "fontSize": 14, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "hM3sj", + "name": "navSettingsText", + "fill": "#006644", + "content": "Settings", + "fontFamily": "JetBrains Mono", + "fontSize": 12, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "c4u69", + "name": "sidebarFooter", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "padding": 16, + "children": [ + { + "type": "text", + "id": "vWvmF", + "name": "storageLabel", + "fill": "#00D084", + "content": "[STORAGE]", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "sqnqc", + "name": "storageBar", + "width": "fill_container", + "height": 6, + "fill": "#001a11", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "children": [ + { + "type": "frame", + "id": "D6TNN", + "name": "storageFill", + "width": 50, + "height": "fill_container", + "fill": "#00D084" + } + ] + }, + { + "type": "text", + "id": "Zcghl", + "name": "storageText", + "fill": "#006644", + "content": "128 MB / 500 MB", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "yDKsx", + "name": "contentArea", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "ugnDQ", + "name": "toolbar", + "width": "fill_container", + "fill": "#001108", + "padding": [ + 12, + 20 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "uUOmU", + "name": "breadcrumb", + "fill": "#006644", + "content": "~/root/documents/projects", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "Kf7jn", + "name": "toolbarActions", + "gap": 12, + "children": [ + { + "type": "frame", + "id": "8T0F3", + "name": "btnNewFolder", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#00D084" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "aGan3", + "name": "btnNewFolderText", + "fill": "#00D084", + "content": "+folder", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "tC3V4", + "name": "btnRefresh", + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "#003322" + }, + "padding": [ + 6, + 12 + ], + "children": [ + { + "type": "text", + "id": "n8MrG", + "name": "btnRefreshText", + "fill": "#006644", + "content": "refresh", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "FwA1t", + "name": "fileListContainer", + "width": "fill_container", + "height": "fill_container", + "fill": "#000000", + "layout": "vertical", + "padding": [ + 0, + 20, + 20, + 20 + ], + "children": [ + { + "type": "frame", + "id": "0aYdY", + "name": "fileListHeader", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 12, + 0 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "aDYxP", + "name": "colName", + "fill": "#00D084", + "content": "[NAME] ▼", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "pdtUr", + "name": "colMeta", + "gap": 48, + "children": [ + { + "type": "text", + "id": "G9ivB", + "name": "colSize", + "fill": "#006644", + "content": "[SIZE]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "3xlmZ", + "name": "colDate", + "fill": "#006644", + "content": "[MODIFIED]", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "FlOT9", + "name": "fileList", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "RmYYZ", + "name": "rowParent", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 10, + 0 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "p7tZG", + "name": "parentName", + "fill": "#006644", + "content": "[..] PARENT_DIR", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "tihKl", + "name": "parentMeta", + "gap": 48, + "children": [ + { + "type": "text", + "id": "4W7E8", + "name": "parentSize", + "fill": "#003322", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "QjUEV", + "name": "parentDate", + "fill": "#003322", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "LWrHb", + "name": "row1", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 10, + 0 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "jsVmB", + "name": "row1Name", + "fill": "#00D084", + "content": "[DIR] client-work/", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "5wFrQ", + "name": "row1Meta", + "gap": 48, + "children": [ + { + "type": "text", + "id": "dONyU", + "name": "row1Size", + "fill": "#003322", + "content": "--", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "gff4f", + "name": "row1Date", + "fill": "#006644", + "content": "2026-01-28", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "tYv9q", + "name": "row2", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 10, + 0 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "CQniB", + "name": "row2Name", + "fill": "#00D084", + "content": "[DIR] personal/", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "ioPek", + "name": "row2Meta", + "gap": 48, + "children": [ + { + "type": "text", + "id": "ZG0X0", + "name": "row2Size", + "fill": "#003322", + "content": "--", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "4Ka1I", + "name": "row2Date", + "fill": "#006644", + "content": "2026-01-25", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "Qk1Dk", + "name": "row3", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 10, + 0 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "Tqi66", + "name": "row3Name", + "fill": "#00D084", + "content": "[FILE] project-plan.md", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "RxcIg", + "name": "row3Meta", + "gap": 48, + "children": [ + { + "type": "text", + "id": "DB1fB", + "name": "row3Size", + "fill": "#00D084", + "content": "12.4 KB", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "h13n0", + "name": "row3Date", + "fill": "#00D084", + "content": "2026-01-29", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "2wUvg", + "name": "row4", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 10, + 0 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "LaGx0", + "name": "row4Name", + "fill": "#00D084", + "content": "[FILE] notes.txt", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "FwAeq", + "name": "row4Meta", + "gap": 48, + "children": [ + { + "type": "text", + "id": "c6IKr", + "name": "row4Size", + "fill": "#00D084", + "content": "2.1 KB", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "ImTtA", + "name": "row4Date", + "fill": "#00D084", + "content": "2026-01-27", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "xWers", + "name": "row5", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#003322" + }, + "padding": [ + 10, + 0 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "aZFVs", + "name": "row5Name", + "fill": "#00D084", + "content": "[FILE] backup.tar.gz.enc", + "fontFamily": "JetBrains Mono", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "VvZnA", + "name": "row5Meta", + "gap": 48, + "children": [ + { + "type": "text", + "id": "eiOi8", + "name": "row5Size", + "fill": "#00D084", + "content": "48.7 MB", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "aNn5B", + "name": "row5Date", + "fill": "#00D084", + "content": "2026-01-20", + "fontFamily": "JetBrains Mono", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "14qYa", + "name": "appFooter", + "width": "fill_container", + "fill": "#000000", + "stroke": { + "align": "inside", + "thickness": { + "top": 1 + }, + "fill": "#003322" + }, + "padding": [ + 8, + 24 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "QuIOF", + "name": "footerLeft", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "Fk54G", + "name": "copyright", + "fill": "#003322", + "content": "© 2026 CipherBox", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "m3dmc", + "name": "footerCenter", + "gap": 16, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "LedCZ", + "name": "linkHelp", + "fill": "#006644", + "content": "[help]", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "eACx3", + "name": "linkPrivacy", + "fill": "#006644", + "content": "[privacy]", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "J38Wy", + "name": "linkTerms", + "fill": "#006644", + "content": "[terms]", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "oLTdj", + "name": "linkGithub", + "fill": "#006644", + "content": "[github]", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "OjXpK", + "name": "footerRight", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "ellipse", + "id": "RgprA", + "name": "statusDot", + "fill": "#00D084", + "width": 6, + "height": 6, + "effect": { + "type": "shadow", + "shadowType": "outer", + "color": "#00D08466", + "blur": 8 + } + }, + { + "type": "text", + "id": "iAPN3", + "name": "statusText", + "fill": "#00D084", + "content": "[CONNECTED]", + "fontFamily": "JetBrains Mono", + "fontSize": 9, + "fontWeight": "600" + } + ] + } + ] + } + ] } ] } \ No newline at end of file From 500b38a143ffe10ba54fcdee76e1f890529ccebc Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 02:59:37 +0100 Subject: [PATCH 05/27] docs(06.3): research phase domain Phase 6.3: UI Structure Refactor - Standard stack identified (no new dependencies) - Architecture patterns documented (AppShell, URL-based navigation) - Component inventory created (9 new, 7 modify, 3 remove) - Pitfalls catalogued (5 common issues) - Design specs extracted from Pencil file Co-Authored-By: Claude Opus 4.5 --- .../06.3-RESEARCH.md | 620 ++++++++++++++++++ 1 file changed, 620 insertions(+) create mode 100644 .planning/phases/06.3-ui-structure-refactor/06.3-RESEARCH.md diff --git a/.planning/phases/06.3-ui-structure-refactor/06.3-RESEARCH.md b/.planning/phases/06.3-ui-structure-refactor/06.3-RESEARCH.md new file mode 100644 index 0000000000..ae4fbfda38 --- /dev/null +++ b/.planning/phases/06.3-ui-structure-refactor/06.3-RESEARCH.md @@ -0,0 +1,620 @@ +# Phase 6.3: UI Structure Refactor - Research + +**Researched:** 2026-01-30 +**Domain:** React component architecture, fixed layout systems, folder navigation patterns +**Confidence:** HIGH + +## Summary + +This phase involves a significant structural redesign of the CipherBox web application UI. The key changes are: + +1. Moving from a flexible layout to a fixed app shell (header, sidebar, footer all fixed; only content scrolls) +2. Converting the sidebar from a folder tree to app-level navigation (major architectural change) +3. Implementing in-place folder navigation with browser history integration +4. Adding new components: Footer, UserMenu dropdown, Storage quota indicator, Refresh button + +The current codebase uses React 18.3 with react-router-dom 7.12, Zustand 5.0 for state management, and a terminal/hacker aesthetic with JetBrains Mono font and green-on-black colors. The existing architecture is well-structured with clear separation between routes, components, hooks, and stores. + +**Primary recommendation:** Implement changes incrementally - start with AppShell layout restructure, then navigation system changes, then individual component updates. Use CSS Grid for the fixed layout and leverage existing stores for state management. + +## Standard Stack + +The established libraries/tools for this phase: + +### Core (Already in use - no changes) + +| Library | Version | Purpose | Why Standard | +| ---------------- | ------- | -------------------- | ----------------------------------- | +| react | ^18.3.1 | UI framework | Project foundation | +| react-router-dom | ^7.12.0 | Routing & navigation | Browser history integration | +| zustand | ^5.0.10 | State management | Already used for vault/folder state | + +### Supporting (No new dependencies needed) + +| Library | Version | Purpose | When to Use | +| ------------------ | -------- | ---------------------------- | ------------------ | +| @floating-ui/react | ^0.27.16 | Tooltip/dropdown positioning | User menu dropdown | + +### Alternatives Considered + +| Instead of | Could Use | Tradeoff | +| ----------- | --------- | -------------------------------------------------- | +| CSS Grid | Flexbox | Grid better for fixed layout with named areas | +| Floating UI | CSS hover | Floating UI already in project, handles edge cases | + +**Installation:** + +```bash +# No new packages required +``` + +## Architecture Patterns + +### Current Component Hierarchy + +```text +App.tsx + AppRoutes (react-router) + Login.tsx + Dashboard.tsx + FileBrowser.tsx + FolderTree.tsx (REMOVE/REPLACE) + Toolbar (inline) + FileList.tsx + EmptyState.tsx + UploadModal.tsx + Dialogs... + Settings.tsx +``` + +### Target Component Hierarchy + +```text +App.tsx + AppRoutes (react-router) + Login.tsx + AppShell.tsx (NEW - wraps authenticated routes) + Header.tsx (NEW) + Logo + UserMenu.tsx (NEW - dropdown) + Sidebar.tsx (NEW - replaces FolderTree) + NavItem (Files) + NavItem (Settings) + StorageQuota.tsx (NEW) + Main (children slot) + FileBrowserPage.tsx (replaces Dashboard content) + Toolbar (breadcrumbs + actions) + FileList.tsx (MODIFY) + EmptyState.tsx (MODIFY) + SettingsPage.tsx + Footer.tsx (NEW) + Copyright + Links + StatusIndicator + // Login stays outside AppShell +``` + +### Pattern 1: Fixed App Shell with CSS Grid + +**What:** Container with fixed header/sidebar/footer and scrollable content area +**When to use:** When navigation and status should always be visible +**Example:** + +```css +/* AppShell layout */ +.app-shell { + display: grid; + grid-template-rows: auto 1fr auto; + grid-template-columns: auto 1fr; + grid-template-areas: + 'header header' + 'sidebar main' + 'footer footer'; + height: 100vh; + overflow: hidden; +} + +.app-header { + grid-area: header; + position: sticky; + top: 0; +} +.app-sidebar { + grid-area: sidebar; + position: sticky; + top: 0; + height: calc(100vh - header - footer); + overflow-y: auto; +} +.app-main { + grid-area: main; + overflow-y: auto; +} +.app-footer { + grid-area: footer; + position: sticky; + bottom: 0; +} +``` + +### Pattern 2: URL-based Folder Navigation + +**What:** Encode current folder path in URL for browser history support +**When to use:** When users should be able to use back/forward buttons +**Example:** + +```typescript +// Update useFolderNavigation to use React Router +function useFolderNavigation() { + const navigate = useNavigate(); + const { folderId = 'root' } = useParams<{ folderId?: string }>(); + + const navigateTo = useCallback( + (id: string) => { + navigate(id === 'root' ? '/files' : `/files/${id}`); + }, + [navigate] + ); + + const navigateUp = useCallback(() => { + const parentId = currentFolder?.parentId ?? 'root'; + navigateTo(parentId); + }, [currentFolder, navigateTo]); + + return { currentFolderId: folderId, navigateTo, navigateUp }; +} +``` + +### Pattern 3: Hover-triggered Dropdown + +**What:** User menu that opens on hover (not click) +**When to use:** Per CONTEXT.md decision for header user menu +**Example:** + +```typescript +function UserMenu({ email }: { email: string }) { + const [isOpen, setIsOpen] = useState(false); + + return ( +
setIsOpen(true)} + onMouseLeave={() => setIsOpen(false)} + > + {email} ▼ + {isOpen && ( +
+ [settings] + +
+ )} +
+ ); +} +``` + +### Anti-Patterns to Avoid + +- **Nested scrolling containers:** Only one scrolling container (main content area) should exist +- **Storing navigation state only in React state:** Use URL as source of truth for folder location +- **Coupling sidebar to file browser:** Sidebar is app-level nav, not folder-specific + +## Don't Hand-Roll + +Problems that look simple but have existing solutions: + +| Problem | Don't Build | Use Instead | Why | +| ------------------------ | --------------------------- | -------------------------------------- | -------------------------------------------- | +| Dropdown positioning | Custom position calculation | @floating-ui/react | Edge cases (viewport boundaries) are complex | +| Storage quota formatting | Manual byte formatting | Existing formatBytes utility | Already exists in codebase | +| Route-based folder state | Custom history integration | react-router-dom useParams/useNavigate | Already using react-router | + +**Key insight:** The codebase already has utilities and patterns for most needs - reuse them. + +## Common Pitfalls + +### Pitfall 1: Breaking File Operations When Restructuring + +**What goes wrong:** Moving FileBrowser component breaks drag-drop, context menus, upload +**Why it happens:** These features rely on parent-child relationships and event bubbling +**How to avoid:** Keep FileBrowser's internal structure intact; only change its container +**Warning signs:** Drag-drop stops working, context menus appear in wrong position + +### Pitfall 2: Mobile Sidebar Regression + +**What goes wrong:** Fixed layout breaks mobile overlay behavior +**Why it happens:** CSS Grid layout conflicts with position: fixed for mobile overlay +**How to avoid:** Use media queries to switch between grid (desktop) and flex/fixed (mobile) +**Warning signs:** Sidebar doesn't slide in on mobile, backdrop doesn't appear + +### Pitfall 3: Viewport Height Issues (100vh) + +**What goes wrong:** Layout extends beyond viewport on mobile browsers +**Why it happens:** Mobile browsers have dynamic address bars; 100vh includes hidden area +**How to avoid:** Use `height: 100dvh` or `min-height: 100vh` with flex-grow +**Warning signs:** Page scrolls when it shouldn't, footer hidden behind browser UI + +### Pitfall 4: Lost Folder State on Navigation + +**What goes wrong:** User loses place in folder tree when navigating to settings and back +**Why it happens:** Folder state only exists in React component state +**How to avoid:** Keep folder ID in URL; restore state from URL on mount +**Warning signs:** Going to Settings and back returns user to root folder + +### Pitfall 5: Breaking Existing Styles + +**What goes wrong:** Terminal aesthetic inconsistencies after restructure +**Why it happens:** New components don't follow existing CSS variable patterns +**How to avoid:** Reference index.css variables for all colors, fonts, spacing +**Warning signs:** Mixed font sizes, wrong green shades, inconsistent borders + +## Code Examples + +Verified patterns from design file and existing codebase: + +### AppShell Grid Layout + +```css +/* Source: Phase 6.3 Pencil design */ +.app-shell { + display: grid; + grid-template-rows: auto 1fr auto; + grid-template-columns: 180px 1fr; + grid-template-areas: + 'header header' + 'sidebar main' + 'footer footer'; + height: 100vh; + background-color: var(--color-background); +} + +@media (max-width: 768px) { + .app-shell { + grid-template-columns: 1fr; + grid-template-areas: + 'header' + 'main' + 'footer'; + } +} +``` + +### Header Component Structure + +```typescript +// Source: Phase 6.3 Pencil design (frame: appHeader, id: BwljC) +function AppHeader() { + return ( +
+
+ > + CIPHERBOX +
+
+ +
+
+ ); +} + +/* CSS from design */ +.app-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 24px; + background-color: var(--color-background); + border-bottom: 1px solid var(--color-border); +} +``` + +### Sidebar Navigation Component + +```typescript +// Source: Phase 6.3 Pencil design (frame: sidebar, id: rckAN) +function AppSidebar() { + const location = useLocation(); + + return ( + + ); +} + +/* CSS from design */ +.app-sidebar { + width: 180px; + display: flex; + flex-direction: column; + justify-content: space-between; + background-color: var(--color-background); + border-right: 1px solid var(--color-green-darker); /* #003322 */ +} +``` + +### Footer Component Structure + +```typescript +// Source: Phase 6.3 Pencil design (frame: appFooter, id: 14qYa) +function AppFooter() { + return ( + + ); +} + +/* CSS from design */ +.app-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 24px; + background-color: var(--color-background); + border-top: 1px solid var(--color-green-darker); /* #003322 */ +} + +.footer-copyright { color: #003322; font-size: 9px; } +.footer-link { color: #006644; font-size: 9px; margin: 0 8px; } +``` + +### Parent Directory Row Pattern + +```typescript +// Source: Phase 6.3 Pencil design (frame: rowParent, id: RmYYZ) +function ParentDirRow({ onClick }: { onClick: () => void }) { + return ( +
+
+ [..] + PARENT_DIR +
+
--
+
--
+
+ ); +} +``` + +### Breadcrumb Path Format + +```typescript +// Source: Phase 6.3 Pencil design (frame: breadcrumb, id: uUOmU) +// Breadcrumb format: ~/root/documents/projects +function BreadcrumbPath({ path }: { path: string[] }) { + const pathString = '~/' + path.join('/'); + return ( + + ); +} +``` + +## Design Specifications from Pencil + +### Color Tokens (from design file) + +| Element | Color | CSS Variable | +| ------------------ | --------- | -------------------------- | +| Primary text | #00D084 | var(--color-green-primary) | +| Secondary text | #006644 | var(--color-green-dim) | +| Borders (bright) | #00D084 | var(--color-border) | +| Borders (dim) | #003322 | var(--color-border-dim) | +| Background | #000000 | var(--color-background) | +| Active nav item bg | #001a11 | (new token needed) | +| Glow effect | #00D08466 | var(--color-green-glow) | + +### Typography (from design file) + +| Element | Size | Weight | +| ---------------- | ---- | ---------------------------- | +| Logo "CIPHERBOX" | 14px | 600 | +| Logo ">" | 20px | 700 | +| User email | 11px | 400 | +| Nav items | 12px | 600 (active), 400 (inactive) | +| File list items | 11px | 400 | +| Column headers | 10px | 600 | +| Footer text | 9px | 400/600 | + +### Spacing (from design file) + +| Element | Padding | +| ------------------- | ------------------------------ | +| Header | 12px vertical, 24px horizontal | +| Sidebar nav | 16px all sides | +| Nav item | 8px vertical, 12px horizontal | +| Toolbar | 12px vertical, 20px horizontal | +| File list container | 0 top, 20px sides and bottom | +| File list row | 10px vertical | +| Footer | 8px vertical, 24px horizontal | + +### Dimensions (from design file) + +| Element | Width/Height | +| ----------- | ---------------------- | +| Sidebar | 180px | +| Storage bar | 6px height, full width | +| Status dot | 6px x 6px | + +## Component Inventory + +### Components to Create + +| Component | Purpose | Location | +| --------------- | -------------------------------- | ---------------------------------------- | +| AppShell | Fixed layout wrapper | components/layout/AppShell.tsx | +| AppHeader | Header with logo + user menu | components/layout/AppHeader.tsx | +| AppSidebar | Navigation + storage quota | components/layout/AppSidebar.tsx | +| AppFooter | Copyright, links, status | components/layout/AppFooter.tsx | +| UserMenu | Hover dropdown for user actions | components/layout/UserMenu.tsx | +| NavItem | Sidebar navigation item | components/layout/NavItem.tsx | +| StorageQuota | Usage bar in sidebar | components/layout/StorageQuota.tsx | +| StatusIndicator | Connection status in footer | components/layout/StatusIndicator.tsx | +| ParentDirRow | [..] PARENT_DIR row in file list | components/file-browser/ParentDirRow.tsx | + +### Components to Modify + +| Component | Changes Needed | +| ---------------- | --------------------------------------------------------------------------------------- | +| FileList.tsx | Add ParentDirRow, update column headers to [NAME]/[SIZE]/[MODIFIED], remove TYPE column | +| FileListItem.tsx | Update styling, remove TYPE display | +| Breadcrumbs.tsx | Change to path format (~/root/path), remove back button (use parent row instead) | +| EmptyState.tsx | Add terminal-style ASCII art | +| Dashboard.tsx | Replace with route to FileBrowserPage inside AppShell | +| Settings.tsx | Move into AppShell wrapper | +| App.css | Major restructure for new layout | + +### Components to Remove/Replace + +| Component | Reason | +| ---------------------- | -------------------------------------- | +| FolderTree.tsx | Replaced by in-place folder navigation | +| FolderTreeNode.tsx | No longer needed | +| ApiStatusIndicator.tsx | Replaced by StatusIndicator in footer | + +### Routes Changes + +| Current | New | Notes | +| ---------- | ---------------- | ---------------------------------- | +| /dashboard | /files | More descriptive | +| /dashboard | /files/:folderId | Support deep links to folders | +| /settings | /settings | No change, but wrapped in AppShell | + +## State Management Changes + +### Folder Navigation State + +**Current:** `useFolderNavigation` hook uses local React state for `currentFolderId` +**New:** Derive `currentFolderId` from URL params via react-router + +```typescript +// Current approach (to change) +const [currentFolderId, setCurrentFolderId] = useState('root'); + +// New approach +const { folderId = 'root' } = useParams<{ folderId?: string }>(); +``` + +### Storage Quota State + +**Current:** `useQuotaStore` exists with `usedBytes`, `limitBytes`, `remainingBytes` +**New:** No changes needed - just consume from `StorageQuota` component + +### API Status State + +**Current:** `ApiStatusIndicator` uses `useHealthControllerCheck` hook directly +**New:** Move same logic to `StatusIndicator` component in footer + +## E2E Test Impact + +### No E2E Tests Found + +The codebase does not have dedicated E2E test files (no `*.spec.ts` or `*.e2e.ts` in apps/web). +Unit tests exist in `apps/web/src/stores/__tests__/logout-security.test.ts`. + +### Selector Changes That May Affect Future Tests + +| Old Selector | New Selector | Component | +| ---------------------- | ----------------- | --------- | +| .dashboard-header | .app-header | Header | +| .file-browser-sidebar | .app-sidebar | Sidebar | +| .folder-tree | (removed) | N/A | +| .file-list-header-type | (removed) | FileList | +| .api-status | .status-indicator | Footer | + +### Recommended Test IDs for New Components + +```typescript +// Add data-testid attributes for testability +
+
); } diff --git a/apps/web/src/components/layout/StatusIndicator.tsx b/apps/web/src/components/layout/StatusIndicator.tsx new file mode 100644 index 0000000000..ddcbdc4442 --- /dev/null +++ b/apps/web/src/components/layout/StatusIndicator.tsx @@ -0,0 +1,39 @@ +import { useHealthControllerCheck } from '../../api/health/health'; + +/** + * Status indicator component. + * Shows API connection status in the footer with a glowing dot and terminal-style text. + */ +export function StatusIndicator() { + const { data, isLoading, isError } = useHealthControllerCheck({ + query: { + refetchInterval: 30000, + retry: 2, + refetchOnWindowFocus: true, + }, + }); + + // Determine status based on query state + const isConnected = !isLoading && !isError && data?.status === 'ok'; + + let dotClass = 'status-indicator-dot'; + let statusText = '[CHECKING]'; + + if (isLoading) { + dotClass += ' status-indicator-dot--loading'; + statusText = '[CHECKING]'; + } else if (isConnected) { + dotClass += ' status-indicator-dot--connected'; + statusText = '[CONNECTED]'; + } else { + dotClass += ' status-indicator-dot--disconnected'; + statusText = '[DISCONNECTED]'; + } + + return ( +
+ + {statusText} +
+ ); +} diff --git a/apps/web/src/components/layout/UserMenu.tsx b/apps/web/src/components/layout/UserMenu.tsx new file mode 100644 index 0000000000..c27df04db5 --- /dev/null +++ b/apps/web/src/components/layout/UserMenu.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import { Link } from 'react-router-dom'; +import { useAuth } from '../../hooks/useAuth'; + +/** + * User menu dropdown component. + * Hover-triggered dropdown showing user email with settings and logout options. + */ +export function UserMenu() { + const { userInfo, logout } = useAuth(); + const [isOpen, setIsOpen] = useState(false); + + // Get user email from Web3Auth user info + const email = userInfo?.email || 'User'; + + const handleLogout = async () => { + setIsOpen(false); + await logout(); + }; + + return ( +
setIsOpen(true)} + onMouseLeave={() => setIsOpen(false)} + > + + + {isOpen && ( +
+ + [settings] + + +
+ )} +
+ ); +} From 1379a07bcdf4f116358717be42befc6c589c5193 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:18:43 +0100 Subject: [PATCH 09/27] feat(06.3-01): create sidebar components - Create NavItem with terminal-style icons [DIR] and [CFG] - Create StorageQuota with progress bar using quota store - Update AppSidebar with Files/Settings navigation and active states - Add index.ts barrel file exporting all 8 layout components - Active nav detection uses useLocation from react-router-dom --- apps/web/src/components/layout/AppSidebar.tsx | 27 +++++++++++-- apps/web/src/components/layout/NavItem.tsx | 26 +++++++++++++ .../src/components/layout/StorageQuota.tsx | 38 +++++++++++++++++++ apps/web/src/components/layout/index.ts | 8 ++++ 4 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/components/layout/NavItem.tsx create mode 100644 apps/web/src/components/layout/StorageQuota.tsx create mode 100644 apps/web/src/components/layout/index.ts diff --git a/apps/web/src/components/layout/AppSidebar.tsx b/apps/web/src/components/layout/AppSidebar.tsx index 49a5bd2969..9f957fc4d1 100644 --- a/apps/web/src/components/layout/AppSidebar.tsx +++ b/apps/web/src/components/layout/AppSidebar.tsx @@ -1,12 +1,33 @@ +import { useLocation } from 'react-router-dom'; +import { NavItem } from './NavItem'; +import { StorageQuota } from './StorageQuota'; + /** * App sidebar component. - * Contains navigation items and storage quota. + * Contains navigation items and storage quota indicator. */ export function AppSidebar() { + const location = useLocation(); + return ( ); } diff --git a/apps/web/src/components/layout/NavItem.tsx b/apps/web/src/components/layout/NavItem.tsx new file mode 100644 index 0000000000..941bdb7800 --- /dev/null +++ b/apps/web/src/components/layout/NavItem.tsx @@ -0,0 +1,26 @@ +import { Link } from 'react-router-dom'; + +interface NavItemProps { + to: string; + icon: 'folder' | 'settings'; + label: string; + active: boolean; +} + +/** + * Navigation item component. + * Renders a sidebar navigation link with terminal-style icon prefix. + */ +export function NavItem({ to, icon, label, active }: NavItemProps) { + // Terminal-style ASCII icons + const iconText = icon === 'folder' ? '[DIR]' : '[CFG]'; + + const className = active ? 'nav-item nav-item--active' : 'nav-item'; + + return ( + + {iconText} + {label} + + ); +} diff --git a/apps/web/src/components/layout/StorageQuota.tsx b/apps/web/src/components/layout/StorageQuota.tsx new file mode 100644 index 0000000000..b293b444d8 --- /dev/null +++ b/apps/web/src/components/layout/StorageQuota.tsx @@ -0,0 +1,38 @@ +import { useQuotaStore } from '../../stores/quota.store'; + +/** + * Format bytes to human-readable string. + */ +function formatBytes(bytes: number): string { + if (bytes === 0) return '0 B'; + + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + const k = 1024; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + const value = bytes / Math.pow(k, i); + + // Use 1 decimal place for larger units + return `${value.toFixed(i > 1 ? 1 : 0)} ${units[i]}`; +} + +/** + * Storage quota indicator component. + * Shows storage usage with a progress bar and text. + */ +export function StorageQuota() { + const { usedBytes, limitBytes } = useQuotaStore(); + + // Calculate percentage (avoid division by zero) + const percentage = limitBytes > 0 ? (usedBytes / limitBytes) * 100 : 0; + + return ( +
+
+
+
+ + {formatBytes(usedBytes)} / {formatBytes(limitBytes)} + +
+ ); +} diff --git a/apps/web/src/components/layout/index.ts b/apps/web/src/components/layout/index.ts new file mode 100644 index 0000000000..aed174c7c7 --- /dev/null +++ b/apps/web/src/components/layout/index.ts @@ -0,0 +1,8 @@ +export { AppShell } from './AppShell'; +export { AppHeader } from './AppHeader'; +export { AppSidebar } from './AppSidebar'; +export { AppFooter } from './AppFooter'; +export { UserMenu } from './UserMenu'; +export { NavItem } from './NavItem'; +export { StorageQuota } from './StorageQuota'; +export { StatusIndicator } from './StatusIndicator'; From 955065a937d6432c4c8f55e4abc14e6fe36fe1a0 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:20:38 +0100 Subject: [PATCH 10/27] docs(06.3-01): complete AppShell layout components plan Tasks completed: 3/3 - Create Layout CSS and AppShell Component - Create Header and Footer Components - Create Sidebar Components SUMMARY: .planning/phases/06.3-ui-structure-refactor/06.3-01-SUMMARY.md --- .planning/STATE.md | 57 ++++---- .../06.3-01-SUMMARY.md | 129 ++++++++++++++++++ 2 files changed, 160 insertions(+), 26 deletions(-) create mode 100644 .planning/phases/06.3-ui-structure-refactor/06.3-01-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index da9bfec454..5464f8f7d5 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,45 +5,46 @@ See: .planning/PROJECT.md (updated 2026-01-20) **Core value:** Zero-knowledge privacy - files encrypted client-side, server never sees plaintext -**Current focus:** Phase 6.2 - Restyle App with Pencil Design (inserted) +**Current focus:** Phase 6.3 - UI Structure Refactor (inserted) ## Current Position -Phase: 6.1 of 11 (Webapp Automation Testing) - COMPLETE -Plan: 6 of 6 in Phase 6.1 complete -Status: Complete - All E2E tests and CI integration done -Last activity: 2026-01-22 - Completed quick task 001: API status indicator on login page +Phase: 6.3 of 11 (UI Structure Refactor) +Plan: 1 of TBD in Phase 6.3 complete +Status: In progress - AppShell layout components created +Last activity: 2026-01-30 - Completed 06.3-01-PLAN.md -Progress: [#######...] 72% (34 of 47 plans) +Progress: [#######...] 74% (35 of 47 plans) ## Performance Metrics **Velocity:** -- Total plans completed: 34 -- Average duration: 5.1 min -- Total execution time: 2.9 hours +- Total plans completed: 35 +- Average duration: 5.0 min +- Total execution time: 2.95 hours **By Phase:** -| Phase | Plans | Total | Avg/Plan | -| ------------------------ | ----- | ------ | -------- | -| 01-foundation | 3/3 | 20 min | 7 min | -| 02-authentication | 4/4 | 18 min | 4.5 min | -| 03-core-encryption | 3/3 | 18 min | 6 min | -| 04-file-storage | 4/4 | 17 min | 4.3 min | -| 04.1-api-service-testing | 3/3 | 11 min | 3.7 min | -| 04.2-local-ipfs-testing | 2/2 | 14 min | 7 min | -| 05-folder-system | 4/4 | 18 min | 4.5 min | -| 06-file-browser-ui | 4/4 | 19 min | 4.8 min | -| 06.1-webapp-automation | 6/6 | 25 min | 4.2 min | +| Phase | Plans | Total | Avg/Plan | +| -------------------------- | ----- | ------ | -------- | +| 01-foundation | 3/3 | 20 min | 7 min | +| 02-authentication | 4/4 | 18 min | 4.5 min | +| 03-core-encryption | 3/3 | 18 min | 6 min | +| 04-file-storage | 4/4 | 17 min | 4.3 min | +| 04.1-api-service-testing | 3/3 | 11 min | 3.7 min | +| 04.2-local-ipfs-testing | 2/2 | 14 min | 7 min | +| 05-folder-system | 4/4 | 18 min | 4.5 min | +| 06-file-browser-ui | 4/4 | 19 min | 4.8 min | +| 06.1-webapp-automation | 6/6 | 25 min | 4.2 min | +| 06.3-ui-structure-refactor | 1/? | 3 min | 3 min | **Recent Trend:** -- Last 5 plans: 4m, 4m, 3m, 4m, 2m +- Last 5 plans: 4m, 3m, 4m, 2m, 3m - Trend: Consistent, improving -_Updated after each plan completion_ +Updated after each plan completion. ## Accumulated Context @@ -151,6 +152,10 @@ Recent decisions affecting current work: | ESM for E2E test files | 06.1-03 | Consistent with monorepo type:module, avoids require() issues | | Separate E2E workspace package | 06.1-03 | Isolated dependencies, independent test execution from unit tests | | Multi-browser testing (Chromium/Firefox/WebKit) | 06.1-03 | Cross-browser compatibility validation catches browser-specific bugs | +| CSS Grid with 180px sidebar | 06.3-01 | Fixed layout with header/sidebar/main/footer areas, only main scrolls | +| Hover-triggered UserMenu dropdown | 06.3-01 | Per CONTEXT.md decision, onMouseEnter/Leave not onClick | +| Terminal ASCII icons [DIR] [CFG] | 06.3-01 | Nav items use bracket-wrapped labels for terminal aesthetic | +| Mobile breakpoint 768px hides sidebar | 06.3-01 | Single column layout on mobile, sidebar removed from grid | ### Pending Todos @@ -192,12 +197,12 @@ Recent decisions affecting current work: ## Session Continuity -Last session: 2026-01-22 -Stopped at: Phase 6.1 Complete - Webapp Automation Testing verified +Last session: 2026-01-30 +Stopped at: Completed 06.3-01-PLAN.md - AppShell layout components Resume file: None -Next phase: Phase 6.2 (Restyle App with Pencil Design) +Next plan: 06.3-02-PLAN.md (Wire AppShell to routes) --- _State initialized: 2026-01-20_ -_Last updated: 2026-01-25 after Phase 6.3 insertion_ +_Last updated: 2026-01-30 after 06.3-01 completion_ diff --git a/.planning/phases/06.3-ui-structure-refactor/06.3-01-SUMMARY.md b/.planning/phases/06.3-ui-structure-refactor/06.3-01-SUMMARY.md new file mode 100644 index 0000000000..3fcdec2acf --- /dev/null +++ b/.planning/phases/06.3-ui-structure-refactor/06.3-01-SUMMARY.md @@ -0,0 +1,129 @@ +--- +phase: 06.3-ui-structure-refactor +plan: 01 +subsystem: ui +tags: [react, css-grid, layout, components, terminal-aesthetic] + +# Dependency graph +requires: + - phase: 06.1-webapp-automation + provides: E2E testing framework, authenticated UI patterns +provides: + - CSS Grid app shell layout (header/sidebar/main/footer) + - 8 layout components: AppShell, AppHeader, AppSidebar, AppFooter, UserMenu, NavItem, StorageQuota, StatusIndicator + - Hover-triggered user menu with logout + - Sidebar navigation with active states + - Storage quota progress bar + - Connection status indicator +affects: [06.3-02, 06.3-03, 06-file-browser-ui] + +# Tech tracking +tech-stack: + added: [] + patterns: + - CSS Grid with grid-template-areas for fixed app shell + - Hover-triggered dropdown (not click) + - Terminal-style bracket labels [settings] [logout] [CONNECTED] + +key-files: + created: + - apps/web/src/styles/layout.css + - apps/web/src/components/layout/AppShell.tsx + - apps/web/src/components/layout/AppHeader.tsx + - apps/web/src/components/layout/AppSidebar.tsx + - apps/web/src/components/layout/AppFooter.tsx + - apps/web/src/components/layout/UserMenu.tsx + - apps/web/src/components/layout/NavItem.tsx + - apps/web/src/components/layout/StorageQuota.tsx + - apps/web/src/components/layout/StatusIndicator.tsx + - apps/web/src/components/layout/index.ts + modified: [] + +key-decisions: + - 'CSS Grid layout with 180px sidebar, scrollable main area only' + - 'Hover-triggered UserMenu dropdown per CONTEXT.md decision' + - 'Terminal-style ASCII icons [DIR] and [CFG] for nav items' + - 'Mobile breakpoint at 768px hides sidebar' + +patterns-established: + - 'Layout components in components/layout/ directory' + - 'data-testid on all layout components for E2E testing' + - 'formatBytes utility in StorageQuota for byte display' + +# Metrics +duration: 3min +completed: 2026-01-30 +--- + +# Phase 6.3 Plan 01: AppShell Layout Components Summary + +CSS Grid app shell with fixed header/sidebar/footer and 8 new layout components using terminal aesthetic. + +## Performance + +- **Duration:** 3 min +- **Started:** 2026-01-30T02:15:36Z +- **Completed:** 2026-01-30T02:18:58Z +- **Tasks:** 3 +- **Files created:** 10 + +## Accomplishments + +- Created CSS Grid layout system with header/sidebar/main/footer areas +- Built 8 layout components with proper composition and exports +- Implemented hover-triggered UserMenu with settings and logout +- Added StatusIndicator with health check hook and glowing dot +- Created StorageQuota with progress bar using quota store + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Create Layout CSS and AppShell Component** - `52f7876` (feat) +2. **Task 2: Create Header and Footer Components** - `39a7419` (feat) +3. **Task 3: Create Sidebar Components** - `1379a07` (feat) + +## Files Created/Modified + +- `apps/web/src/styles/layout.css` - CSS Grid layout system with design tokens +- `apps/web/src/components/layout/AppShell.tsx` - Main layout wrapper component +- `apps/web/src/components/layout/AppHeader.tsx` - Header with logo and UserMenu +- `apps/web/src/components/layout/AppSidebar.tsx` - Navigation sidebar with quota +- `apps/web/src/components/layout/AppFooter.tsx` - Footer with copyright, links, status +- `apps/web/src/components/layout/UserMenu.tsx` - Hover-triggered dropdown menu +- `apps/web/src/components/layout/NavItem.tsx` - Sidebar navigation item +- `apps/web/src/components/layout/StorageQuota.tsx` - Storage usage progress bar +- `apps/web/src/components/layout/StatusIndicator.tsx` - API connection status +- `apps/web/src/components/layout/index.ts` - Barrel file exporting all components + +## Decisions Made + +- **CSS Grid with named areas:** Used grid-template-areas for clear layout structure per RESEARCH.md recommendation +- **180px fixed sidebar:** Per design specs from Pencil design file +- **Hover dropdown:** UserMenu uses onMouseEnter/onMouseLeave per CONTEXT.md decision (not click) +- **Terminal ASCII icons:** [DIR] and [CFG] for folder and settings nav items +- **Mobile breakpoint 768px:** Sidebar hidden on mobile, single column layout +- **--color-nav-active:** Added new CSS variable #001a11 for active nav item background + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered + +None - all components built successfully on first attempt. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- Layout components ready to be integrated into authenticated routes +- Plan 02 will wire AppShell to routes and update Dashboard/FileBrowser +- Components have data-testid attributes ready for E2E testing + +--- + +_Phase: 06.3-ui-structure-refactor_ +_Completed: 2026-01-30_ From 4e8397782bf8bb8ae1ac20e319be39f78bc5caf2 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:23:17 +0100 Subject: [PATCH 11/27] feat(06.3-03): create ParentDirRow and update FileList to 3 columns - Add ParentDirRow component for [..] PARENT_DIR navigation - Update FileList headers to bracket format [NAME] [SIZE] [MODIFIED] - Remove TYPE column from header and grid layout - Add showParentRow/onNavigateUp props to FileList - Update CSS grid from 4 columns to 3 columns --- .../src/components/file-browser/FileList.tsx | 17 +++--- .../components/file-browser/ParentDirRow.tsx | 53 +++++++++++++++++++ apps/web/src/styles/file-browser.css | 17 ++++-- 3 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 apps/web/src/components/file-browser/ParentDirRow.tsx diff --git a/apps/web/src/components/file-browser/FileList.tsx b/apps/web/src/components/file-browser/FileList.tsx index c52a54e9f3..4fc364bb11 100644 --- a/apps/web/src/components/file-browser/FileList.tsx +++ b/apps/web/src/components/file-browser/FileList.tsx @@ -1,6 +1,7 @@ import { type DragEvent, type MouseEvent } from 'react'; import type { FolderChild } from '@cipherbox/crypto'; import { FileListItem } from './FileListItem'; +import { ParentDirRow } from './ParentDirRow'; type FileListProps = { /** Items to display (files and folders) */ @@ -9,6 +10,10 @@ type FileListProps = { selectedId: string | null; /** Parent folder ID (for drag operations) */ parentId: string; + /** Whether to show [..] PARENT_DIR row (non-root folders) */ + showParentRow?: boolean; + /** Callback when [..] row is clicked to navigate up */ + onNavigateUp?: () => void; /** Callback when an item is selected */ onSelect: (itemId: string) => void; /** Callback when navigating into a folder */ @@ -63,6 +68,8 @@ export function FileList({ items, selectedId, parentId, + showParentRow, + onNavigateUp, onSelect, onNavigate, onContextMenu, @@ -75,21 +82,19 @@ export function FileList({ {/* Header row */}
- NAME + [NAME]
- SIZE -
-
- TYPE + [SIZE]
- MODIFIED + [MODIFIED]
{/* Item rows */}
+ {showParentRow && onNavigateUp && } {sortedItems.map((item) => ( void; +}; + +/** + * Parent directory navigation row. + * + * Displays [..] PARENT_DIR row as first item in file list for non-root folders. + * Clicking navigates up to parent folder. + * + * @example + * ```tsx + * function FileList({ showParentRow, onNavigateUp }) { + * return ( + *
+ * {showParentRow && } + * {items.map(item => )} + *
+ * ); + * } + * ``` + */ +export function ParentDirRow({ onClick }: ParentDirRowProps) { + return ( +
e.key === 'Enter' && onClick()} + data-testid="parent-dir-row" + > + {/* Row 1: Icon + Name */} +
+ + PARENT_DIR +
+ + {/* Row 2: Empty size and date columns */} +
+ + -- + + + -- + +
+
+ ); +} diff --git a/apps/web/src/styles/file-browser.css b/apps/web/src/styles/file-browser.css index 3adf1bbaa7..b3424e701a 100644 --- a/apps/web/src/styles/file-browser.css +++ b/apps/web/src/styles/file-browser.css @@ -254,7 +254,7 @@ .file-list-header { display: grid; - grid-template-columns: 1fr 120px 120px 180px; + grid-template-columns: 1fr 120px 180px; gap: var(--spacing-md); padding: var(--spacing-sm) var(--spacing-md); border-bottom: var(--border-thickness) solid var(--color-border); @@ -274,8 +274,8 @@ /* File List Item - Desktop uses grid, Mobile uses 2-row stacked layout */ .file-list-item { display: grid; - grid-template-columns: 1fr 120px 120px 180px; - grid-template-areas: "name size type date"; + grid-template-columns: 1fr 120px 180px; + grid-template-areas: "name size date"; gap: var(--spacing-md); padding: var(--spacing-sm) var(--spacing-md); cursor: pointer; @@ -301,6 +301,15 @@ background-color: var(--color-green-darker); } +/* Parent directory row [..] PARENT_DIR */ +.file-list-item--parent { + cursor: pointer; +} + +.file-list-item--parent:hover { + background-color: var(--color-green-darker); +} + /* Row wrappers - on desktop, these act as containers within grid */ .file-list-item-row-top { grid-area: name; @@ -338,6 +347,7 @@ font-size: var(--font-size-sm); } +/* TYPE column removed - no longer used in 3-column layout .file-list-item-type { grid-area: type; display: flex; @@ -346,6 +356,7 @@ font-size: var(--font-size-sm); text-transform: lowercase; } +*/ .file-list-item-date { grid-area: date; From 0b89c09ec46a5c6134236cf42dbcf44df7a75ab8 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:23:20 +0100 Subject: [PATCH 12/27] feat(06.3-02): update routes and create page components - Create FilesPage.tsx wrapping FileBrowser in AppShell - Create SettingsPage.tsx wrapping LinkedMethods in AppShell - Update routes: /files/:folderId? pattern, /dashboard redirect - Use ParentDirRow in FileList for [..] PARENT_DIR navigation - Adjust file list grid to 3 columns (removed TYPE column) Co-Authored-By: Claude Opus 4.5 --- apps/web/src/routes/FilesPage.tsx | 41 +++++++++++++++++++++++++ apps/web/src/routes/SettingsPage.tsx | 46 ++++++++++++++++++++++++++++ apps/web/src/routes/index.tsx | 11 ++++--- 3 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/routes/FilesPage.tsx create mode 100644 apps/web/src/routes/SettingsPage.tsx diff --git a/apps/web/src/routes/FilesPage.tsx b/apps/web/src/routes/FilesPage.tsx new file mode 100644 index 0000000000..ced8ff996f --- /dev/null +++ b/apps/web/src/routes/FilesPage.tsx @@ -0,0 +1,41 @@ +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { AppShell } from '../components/layout'; +import { FileBrowser } from '../components/file-browser'; +import { useAuth } from '../hooks/useAuth'; + +/** + * Files page showing user's vault wrapped in AppShell. + * Protected route - redirects to login if not authenticated. + */ +export function FilesPage() { + const { isAuthenticated, isLoading } = useAuth(); + const navigate = useNavigate(); + + // Redirect if not authenticated + useEffect(() => { + if (!isLoading && !isAuthenticated) { + navigate('/'); + } + }, [isAuthenticated, isLoading, navigate]); + + // Show loading state while checking authentication + if (isLoading) { + return ( + +
Loading...
+
+ ); + } + + // Don't render if not authenticated (redirect is in progress) + if (!isAuthenticated) { + return null; + } + + return ( + + + + ); +} diff --git a/apps/web/src/routes/SettingsPage.tsx b/apps/web/src/routes/SettingsPage.tsx new file mode 100644 index 0000000000..8cea85fdea --- /dev/null +++ b/apps/web/src/routes/SettingsPage.tsx @@ -0,0 +1,46 @@ +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { AppShell } from '../components/layout'; +import { LinkedMethods } from '../components/auth/LinkedMethods'; +import { useAuth } from '../hooks/useAuth'; + +/** + * Settings page wrapped in AppShell. + * Protected route - redirects to login if not authenticated. + */ +export function SettingsPage() { + const { isAuthenticated, isLoading } = useAuth(); + const navigate = useNavigate(); + + // Redirect if not authenticated + useEffect(() => { + if (!isLoading && !isAuthenticated) { + navigate('/'); + } + }, [isAuthenticated, isLoading, navigate]); + + // Show loading state while checking authentication + if (isLoading) { + return ( + +
Loading...
+
+ ); + } + + // Don't render if not authenticated (redirect is in progress) + if (!isAuthenticated) { + return null; + } + + return ( + +
+

[SETTINGS]

+
+ +
+
+
+ ); +} diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 9c32b35f9b..7328882c14 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -1,15 +1,16 @@ -import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import { Login } from './Login'; -import { Dashboard } from './Dashboard'; -import { Settings } from './Settings'; +import { FilesPage } from './FilesPage'; +import { SettingsPage } from './SettingsPage'; export function AppRoutes() { return ( } /> - } /> - } /> + } /> + } /> + } /> ); From 723f10fdd1437e4953e8324ed5dbad2109b1b369 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:24:35 +0100 Subject: [PATCH 13/27] feat(06.3-03): update FileListItem and Breadcrumbs - Remove TYPE column display from FileListItem - Remove getFileExtension helper (no longer needed) - Update Breadcrumbs to path format ~/root/path - Remove back button (parent nav via [..] row now) - Update breadcrumbs.css for path format display --- .../components/file-browser/Breadcrumbs.tsx | 53 +++---------- .../components/file-browser/FileListItem.tsx | 30 ++----- apps/web/src/styles/breadcrumbs.css | 79 +++++++------------ 3 files changed, 47 insertions(+), 115 deletions(-) diff --git a/apps/web/src/components/file-browser/Breadcrumbs.tsx b/apps/web/src/components/file-browser/Breadcrumbs.tsx index 3a6431b1fb..d17c06c618 100644 --- a/apps/web/src/components/file-browser/Breadcrumbs.tsx +++ b/apps/web/src/components/file-browser/Breadcrumbs.tsx @@ -3,21 +3,21 @@ import type { Breadcrumb } from '../../hooks/useFolderNavigation'; type BreadcrumbsProps = { /** Breadcrumb trail from root to current folder */ breadcrumbs: Breadcrumb[]; - /** Callback to navigate to a folder */ + /** Callback to navigate to a folder (kept for potential future use) */ onNavigate: (folderId: string) => void; - /** Callback to navigate up to parent folder */ + /** Callback to navigate up to parent folder (kept for potential future use) */ onNavigateUp: () => void; }; /** - * Breadcrumb navigation component. + * Breadcrumb navigation component - path format display. * - * Per CONTEXT.md: "simple back navigation with current folder name and back arrow" - * Structure allows future dropdown-per-segment enhancement. + * Per Phase 6.3 CONTEXT.md: Display path as ~/root/documents/projects format. + * Parent navigation is now handled by [..] PARENT_DIR row in file list. * * Display: - * - At root: just "My Vault" (no back arrow) - * - In subfolder: back arrow + current folder name + * - At root: ~/root + * - In subfolder: ~/root/foldername/subfolder * * @example * ```tsx @@ -34,41 +34,14 @@ type BreadcrumbsProps = { * } * ``` */ -export function Breadcrumbs({ breadcrumbs, onNavigateUp }: BreadcrumbsProps) { - // Get current folder (last in breadcrumbs) - const currentFolder = breadcrumbs[breadcrumbs.length - 1]; - - // Check if we're at root (only one breadcrumb) - const isAtRoot = breadcrumbs.length <= 1; - - if (!currentFolder) { - return ( - - ); - } +export function Breadcrumbs({ breadcrumbs }: BreadcrumbsProps) { + // Build path string from breadcrumbs + // Convert folder names to lowercase for terminal aesthetic + const pathString = '~/' + breadcrumbs.map((b) => b.name.toLowerCase()).join('/'); return ( -
- {/* Row 2: Date + Size (for mobile bottom row) */} + {/* Row 2: Size + Date (for mobile bottom row) */}
- - {dateDisplay} - {sizeDisplay} + + {dateDisplay} +
- - {/* TYPE column - hidden on mobile via CSS */} - - {typeDisplay} -
); } diff --git a/apps/web/src/styles/breadcrumbs.css b/apps/web/src/styles/breadcrumbs.css index f096530934..514c25c2f5 100644 --- a/apps/web/src/styles/breadcrumbs.css +++ b/apps/web/src/styles/breadcrumbs.css @@ -1,14 +1,36 @@ /** * Breadcrumb Navigation Styles - Terminal Aesthetic * - * Per design: Terminal-style path display like ~/storage/documents/projects - * Shows current folder path with back navigation + * Per Phase 6.3 design: Terminal-style path display like ~/root/documents/projects + * Parent navigation is now handled by [..] PARENT_DIR row in file list */ /* ========================================================================== - Breadcrumbs Container + Path Format Display ========================================================================== */ +.breadcrumb-path { + font-family: var(--font-family-mono); + font-size: var(--font-size-sm); + color: var(--color-text-primary); + padding: var(--spacing-xs) 0; + min-height: 2.5rem; + display: flex; + align-items: center; +} + +.breadcrumb-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 400px; +} + +/* ========================================================================== + Legacy Styles (deprecated - kept for reference) + ========================================================================== */ + +/* Old breadcrumbs container - no longer used */ .breadcrumbs { display: flex; align-items: center; @@ -20,16 +42,7 @@ color: var(--color-text-primary); } -/* Terminal-style path prefix */ -.breadcrumbs::before { - content: "~/"; - color: var(--color-text-secondary); -} - -/* ========================================================================== - Back Button - ========================================================================== */ - +/* Old back button - replaced by [..] PARENT_DIR row */ .breadcrumbs-back { display: flex; align-items: center; @@ -50,42 +63,6 @@ box-shadow: var(--glow-green); } -.breadcrumbs-back:focus { - outline: 2px solid var(--color-green-primary); - outline-offset: 2px; -} - -.breadcrumbs-back:active { - background-color: var(--color-green-darker); -} - -.breadcrumbs-back-icon { - font-size: var(--font-size-sm); - line-height: 1; -} - -/* ========================================================================== - Path Segments - ========================================================================== */ - -.breadcrumbs-separator { - color: var(--color-text-secondary); -} - -.breadcrumbs-segment { - color: var(--color-text-secondary); - transition: color 0.15s ease; -} - -.breadcrumbs-segment:hover { - color: var(--color-text-primary); - cursor: pointer; -} - -/* ========================================================================== - Current Folder Name - ========================================================================== */ - .breadcrumbs-current { font-weight: var(--font-weight-semibold); color: var(--color-text-primary); @@ -100,11 +77,11 @@ ========================================================================== */ @media (max-width: 768px) { - .breadcrumbs { + .breadcrumb-path { padding: var(--spacing-xs); } - .breadcrumbs-current { + .breadcrumb-text { max-width: 200px; font-size: var(--font-size-sm); } From 562baa0f3388bf3c0e2f8e216ebecccd6b4e3d8b Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:24:47 +0100 Subject: [PATCH 14/27] feat(06.3-02): update useFolderNavigation for URL-based state - Replace useState(currentFolderId) with useParams from react-router - navigateTo uses navigate() for URL-based history - Browser back/forward now works for folder navigation - Root folder maps to /files, subfolders to /files/:folderId Co-Authored-By: Claude Opus 4.5 --- apps/web/src/hooks/useFolderNavigation.ts | 58 +++++++++++++---------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/apps/web/src/hooks/useFolderNavigation.ts b/apps/web/src/hooks/useFolderNavigation.ts index c7e6d66412..ae312ffb9d 100644 --- a/apps/web/src/hooks/useFolderNavigation.ts +++ b/apps/web/src/hooks/useFolderNavigation.ts @@ -1,4 +1,5 @@ -import { useState, useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; import { useFolderStore, type FolderNode } from '../stores/folder.store'; import { useVaultStore } from '../stores/vault.store'; @@ -98,8 +99,11 @@ function buildBreadcrumbs( * ``` */ export function useFolderNavigation(): UseFolderNavigationReturn { - // Local state for current folder ID (default to 'root') - const [currentFolderId, setCurrentFolderId] = useState('root'); + // Get folder ID from URL params - defaults to 'root' when not present + const { folderId } = useParams<{ folderId?: string }>(); + const navigate = useNavigate(); + const currentFolderId = folderId ?? 'root'; + const [isLoading, setIsLoading] = useState(false); // Subscribe to stores @@ -141,34 +145,36 @@ export function useFolderNavigation(): UseFolderNavigationReturn { }, [breadcrumbs, setBreadcrumbs]); /** - * Navigate to a specific folder. + * Navigate to a specific folder using react-router. + * Browser history is automatically managed. */ const navigateTo = useCallback( - (folderId: string) => { + (targetFolderId: string) => { + // Navigate to URL - root folder goes to /files, others to /files/:folderId + if (targetFolderId === 'root') { + navigate('/files'); + } else { + navigate(`/files/${targetFolderId}`); + } + + // Handle folder loading state const targetFolder = - folderId === 'root' ? getRootFolder(vaultState, folders) : folders[folderId]; - - // If folder exists and is loaded, navigate immediately - if (targetFolder) { - setCurrentFolderId(folderId); - - // If folder is not loaded yet, trigger loading - // (actual IPNS resolution deferred to Phase 7) - if (!targetFolder.isLoaded && !targetFolder.isLoading) { - setIsLoading(true); - // Mark folder as loading - setFolder({ ...targetFolder, isLoading: true }); - - // Simulate async load (actual implementation in Phase 7) - // For now, mark as loaded after a brief delay - setTimeout(() => { - setFolder({ ...targetFolder, isLoaded: true, isLoading: false }); - setIsLoading(false); - }, 100); - } + targetFolderId === 'root' ? getRootFolder(vaultState, folders) : folders[targetFolderId]; + + if (targetFolder && !targetFolder.isLoaded && !targetFolder.isLoading) { + setIsLoading(true); + // Mark folder as loading + setFolder({ ...targetFolder, isLoading: true }); + + // Simulate async load (actual implementation in Phase 7) + // For now, mark as loaded after a brief delay + setTimeout(() => { + setFolder({ ...targetFolder, isLoaded: true, isLoading: false }); + setIsLoading(false); + }, 100); } }, - [folders, vaultState, setFolder] + [navigate, folders, vaultState, setFolder] ); /** From 5a7c574bcac376bb3c9a8cec8c6b62450b1d11e0 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:25:15 +0100 Subject: [PATCH 15/27] feat(06.3-03): add ASCII art to EmptyState - Add terminal-style ASCII folder art to empty state - Update message to '// EMPTY DIRECTORY' format - Add lowercase hint 'drag files here or use upload' - Add CSS styles for ASCII art display --- .../components/file-browser/EmptyState.tsx | 24 +++++++++++++++---- apps/web/src/styles/file-browser.css | 12 ++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/file-browser/EmptyState.tsx b/apps/web/src/components/file-browser/EmptyState.tsx index 99d3b9aff0..96752549dd 100644 --- a/apps/web/src/components/file-browser/EmptyState.tsx +++ b/apps/web/src/components/file-browser/EmptyState.tsx @@ -5,10 +5,22 @@ type EmptyStateProps = { folderId: string; }; +/** + * Terminal-style ASCII art folder icon for empty state. + */ +const asciiArt = ` + ___________ + / /| + / / | +|__________| | +| | / +|__________|/ +`; + /** * Empty state component shown when a folder has no contents. * - * Displays a large drop zone with upload prompt using UploadZone. + * Displays terminal-style ASCII art with drop zone for uploads. * Acts as both visual indicator and functional upload target. * * @example @@ -26,11 +38,13 @@ type EmptyStateProps = { */ export function EmptyState({ folderId }: EmptyStateProps) { return ( -
+
- 📤 -

This folder is empty

-

Your files are encrypted before leaving your device

+ +

// EMPTY DIRECTORY

+

drag files here or use upload

diff --git a/apps/web/src/styles/file-browser.css b/apps/web/src/styles/file-browser.css index b3424e701a..0259bd193d 100644 --- a/apps/web/src/styles/file-browser.css +++ b/apps/web/src/styles/file-browser.css @@ -392,6 +392,17 @@ padding: var(--spacing-lg); } +/* Terminal-style ASCII art for empty state */ +.empty-state-ascii { + font-family: var(--font-family-mono); + font-size: var(--font-size-xs); + line-height: 1.2; + color: var(--color-text-secondary); + margin: 0 0 var(--spacing-md) 0; + white-space: pre; +} + +/* Old icon style - replaced by ASCII art */ .empty-state-icon { font-size: 2rem; display: block; @@ -411,6 +422,7 @@ font-size: var(--font-size-xs); margin: 0; color: var(--color-text-secondary); + text-transform: lowercase; } .empty-state-upload { From 3a1bd4f136dbd69ea8a598a3dfb3b7315cda4a4f Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:26:25 +0100 Subject: [PATCH 16/27] chore(06.3-02): update App.css and deprecate old dashboard styles - Add import for layout.css in App.css - Mark dashboard-container and dashboard-header as deprecated - Mark api-status styles as deprecated (replaced by StatusIndicator) - Add deprecation comment to Dashboard.tsx component - Add settings page styles Co-Authored-By: Claude Opus 4.5 --- apps/web/src/App.css | 26 ++++++++++++++++++++++++++ apps/web/src/routes/Dashboard.tsx | 7 ++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/web/src/App.css b/apps/web/src/App.css index 87b7ec2d4b..e882d4d459 100644 --- a/apps/web/src/App.css +++ b/apps/web/src/App.css @@ -1,4 +1,5 @@ /* Import component styles */ +@import './styles/layout.css'; @import './styles/file-browser.css'; @import './styles/breadcrumbs.css'; @import './styles/responsive.css'; @@ -88,6 +89,8 @@ /* ========================================================================== Dashboard Layout + @deprecated - Replaced by AppShell layout components (Phase 6.3) + These styles kept for reference during migration; can be removed later. ========================================================================== */ .dashboard-container { @@ -142,6 +145,7 @@ /* ========================================================================== API Status Indicator + @deprecated - Replaced by StatusIndicator component in AppFooter ========================================================================== */ .api-status { @@ -176,3 +180,25 @@ .status-dot.loading { background-color: var(--color-text-secondary); } + +/* ========================================================================== + Settings Page Styles + ========================================================================== */ + +.settings-content { + padding: var(--spacing-lg); +} + +.settings-title { + font-family: var(--font-family-mono); + font-size: var(--font-size-lg); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); + margin-bottom: var(--spacing-lg); +} + +.settings-section { + background-color: var(--color-background-dark); + border: var(--border-thickness) solid var(--color-border); + padding: var(--spacing-md); +} diff --git a/apps/web/src/routes/Dashboard.tsx b/apps/web/src/routes/Dashboard.tsx index 7cd17400a2..e1e1695e21 100644 --- a/apps/web/src/routes/Dashboard.tsx +++ b/apps/web/src/routes/Dashboard.tsx @@ -1,3 +1,8 @@ +/** + * @deprecated - Use FilesPage instead. Kept for reference during migration. + * This component is no longer used in routes - /dashboard redirects to /files. + * Can be deleted after Phase 6.3 is verified complete. + */ import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { LogoutButton } from '../components/auth/LogoutButton'; @@ -5,7 +10,7 @@ import { FileBrowser } from '../components/file-browser'; import { useAuth } from '../hooks/useAuth'; /** - * Dashboard page showing user's vault. + * @deprecated Dashboard page - replaced by FilesPage with AppShell. * Protected route - redirects to login if not authenticated. */ export function Dashboard() { From dae2a87a41a944178221dbdb4ba0ef7bfabd2aa6 Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:27:01 +0100 Subject: [PATCH 17/27] docs(06.3-03): complete file list updates plan Tasks completed: 3/3 - Create ParentDirRow and update FileList structure - Update FileListItem and Breadcrumbs - Update EmptyState with ASCII art SUMMARY: .planning/phases/06.3-ui-structure-refactor/06.3-03-SUMMARY.md --- .planning/STATE.md | 28 +++-- .../06.3-03-SUMMARY.md | 118 ++++++++++++++++++ 2 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 .planning/phases/06.3-ui-structure-refactor/06.3-03-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 5464f8f7d5..20f34a1f6e 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -10,19 +10,19 @@ See: .planning/PROJECT.md (updated 2026-01-20) ## Current Position Phase: 6.3 of 11 (UI Structure Refactor) -Plan: 1 of TBD in Phase 6.3 complete -Status: In progress - AppShell layout components created -Last activity: 2026-01-30 - Completed 06.3-01-PLAN.md +Plan: 3 of TBD in Phase 6.3 complete +Status: In progress - File list components updated +Last activity: 2026-01-30 - Completed 06.3-03-PLAN.md -Progress: [#######...] 74% (35 of 47 plans) +Progress: [########..] 79% (37 of 47 plans) ## Performance Metrics **Velocity:** -- Total plans completed: 35 -- Average duration: 5.0 min -- Total execution time: 2.95 hours +- Total plans completed: 37 +- Average duration: 4.9 min +- Total execution time: 3.02 hours **By Phase:** @@ -37,11 +37,11 @@ Progress: [#######...] 74% (35 of 47 plans) | 05-folder-system | 4/4 | 18 min | 4.5 min | | 06-file-browser-ui | 4/4 | 19 min | 4.8 min | | 06.1-webapp-automation | 6/6 | 25 min | 4.2 min | -| 06.3-ui-structure-refactor | 1/? | 3 min | 3 min | +| 06.3-ui-structure-refactor | 3/? | 7 min | 2.3 min | **Recent Trend:** -- Last 5 plans: 4m, 3m, 4m, 2m, 3m +- Last 5 plans: 3m, 4m, 2m, 3m, 4m - Trend: Consistent, improving Updated after each plan completion. @@ -156,6 +156,10 @@ Recent decisions affecting current work: | Hover-triggered UserMenu dropdown | 06.3-01 | Per CONTEXT.md decision, onMouseEnter/Leave not onClick | | Terminal ASCII icons [DIR] [CFG] | 06.3-01 | Nav items use bracket-wrapped labels for terminal aesthetic | | Mobile breakpoint 768px hides sidebar | 06.3-01 | Single column layout on mobile, sidebar removed from grid | +| 3-column file list layout (Name/Size/Modified) | 06.3-03 | TYPE column removed per CONTEXT.md decision | +| Parent navigation via [..] row not breadcrumb | 06.3-03 | Back button removed from breadcrumbs, [..] row for parent navigation | +| Breadcrumbs show full path ~/root/path lowercase | 06.3-03 | Terminal aesthetic with lowercase folder names | +| ASCII art folder icon for empty state | 06.3-03 | Terminal-style ASCII art instead of emoji for empty state | ### Pending Todos @@ -198,11 +202,11 @@ Recent decisions affecting current work: ## Session Continuity Last session: 2026-01-30 -Stopped at: Completed 06.3-01-PLAN.md - AppShell layout components +Stopped at: Completed 06.3-03-PLAN.md - File list components updated Resume file: None -Next plan: 06.3-02-PLAN.md (Wire AppShell to routes) +Next plan: 06.3-04-PLAN.md (Toolbar and integration) --- _State initialized: 2026-01-20_ -_Last updated: 2026-01-30 after 06.3-01 completion_ +_Last updated: 2026-01-30 after 06.3-03 completion_ diff --git a/.planning/phases/06.3-ui-structure-refactor/06.3-03-SUMMARY.md b/.planning/phases/06.3-ui-structure-refactor/06.3-03-SUMMARY.md new file mode 100644 index 0000000000..0c0a8fd2a2 --- /dev/null +++ b/.planning/phases/06.3-ui-structure-refactor/06.3-03-SUMMARY.md @@ -0,0 +1,118 @@ +--- +phase: 06.3-ui-structure-refactor +plan: 03 +subsystem: ui +tags: [react, css-grid, file-browser, breadcrumbs, ascii-art] + +# Dependency graph +requires: + - phase: 06.3-01 + provides: AppShell layout components +provides: + - File list with 3 columns [NAME] [SIZE] [MODIFIED] + - ParentDirRow component for [..] PARENT_DIR navigation + - Path-format breadcrumbs (~/root/path) + - Terminal-style ASCII art empty state +affects: [06.3-04, file-browser-integration] + +# Tech tracking +tech-stack: + added: [] + patterns: + - In-place folder navigation via [..] row instead of sidebar tree + - Terminal-style bracket format for column headers + +key-files: + created: + - apps/web/src/components/file-browser/ParentDirRow.tsx + modified: + - apps/web/src/components/file-browser/FileList.tsx + - apps/web/src/components/file-browser/FileListItem.tsx + - apps/web/src/components/file-browser/Breadcrumbs.tsx + - apps/web/src/components/file-browser/EmptyState.tsx + - apps/web/src/styles/file-browser.css + - apps/web/src/styles/breadcrumbs.css + +key-decisions: + - '3-column layout (Name/Size/Modified) - TYPE removed per CONTEXT.md' + - 'Parent navigation via [..] row not breadcrumb back button' + - 'Breadcrumbs show full path format ~/root/path in lowercase' + - 'ASCII art folder icon for empty state terminal aesthetic' + +patterns-established: + - 'Bracket-wrapped column headers [NAME] [SIZE] [MODIFIED]' + - 'showParentRow/onNavigateUp props pattern for parent navigation' + +# Metrics +duration: 4min +completed: 2026-01-30 +--- + +# Phase 6.3 Plan 03: File List Updates Summary + +**File list restructured to 3 columns with [..] PARENT_DIR navigation row and terminal-style ASCII art empty state** + +## Performance + +- **Duration:** 4 min +- **Started:** 2026-01-30 +- **Completed:** 2026-01-30 +- **Tasks:** 3 +- **Files modified:** 7 + +## Accomplishments + +- Created ParentDirRow component for [..] PARENT_DIR navigation in non-root folders +- Updated FileList to 3-column layout with bracket-format headers [NAME] [SIZE] [MODIFIED] +- Removed TYPE column from file list (both header and item rows) +- Converted Breadcrumbs to path format display (~/root/documents/folder) +- Added terminal-style ASCII folder art to empty state + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Create ParentDirRow and Update FileList Structure** - `4e83977` (feat) +2. **Task 2: Update FileListItem and Breadcrumbs** - `723f10f` (feat) +3. **Task 3: Update EmptyState with ASCII Art** - `5a7c574` (feat) + +## Files Created/Modified + +- `apps/web/src/components/file-browser/ParentDirRow.tsx` - New component for [..] PARENT_DIR row +- `apps/web/src/components/file-browser/FileList.tsx` - Updated to 3 columns, added showParentRow/onNavigateUp props +- `apps/web/src/components/file-browser/FileListItem.tsx` - Removed TYPE column display +- `apps/web/src/components/file-browser/Breadcrumbs.tsx` - Changed to path format ~/root/path +- `apps/web/src/components/file-browser/EmptyState.tsx` - Added ASCII art and terminal-style messages +- `apps/web/src/styles/file-browser.css` - Updated grid to 3 columns, added parent row styles +- `apps/web/src/styles/breadcrumbs.css` - Added path format styles, deprecated old styles + +## Decisions Made + +1. **3-column layout** - Removed TYPE column per Phase 6.3 CONTEXT.md decision that columns should be Name, Size, Modified only +2. **Parent navigation via [..] row** - Back button removed from breadcrumbs, parent navigation now via clicking the [..] PARENT_DIR row in file list +3. **Lowercase path format** - Breadcrumbs display folder names in lowercase for consistent terminal aesthetic (~/root/documents/folder) +4. **ASCII art folder icon** - Simple folder shape using ASCII characters for empty state, maintaining terminal aesthetic + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered + +None. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- File list components updated with new column structure +- Ready for integration with actual folder navigation in FileBrowser +- showParentRow/onNavigateUp props need to be wired to useFolderNavigation hook +- Note: Plan 02 changes (routes/FilesPage/SettingsPage) exist but were committed separately + +--- + +_Phase: 06.3-ui-structure-refactor_ +_Completed: 2026-01-30_ From 1626c421d63a9cb5abd2aef621841ca2944d619e Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:29:07 +0100 Subject: [PATCH 18/27] docs(06.3-02): complete routing and URL-based navigation plan Tasks completed: 3/3 - Update routes with /files/:folderId? pattern - Update useFolderNavigation for URL-based state - Update App.css and deprecate old dashboard styles SUMMARY: .planning/phases/06.3-ui-structure-refactor/06.3-02-SUMMARY.md Co-Authored-By: Claude Opus 4.5 --- .planning/STATE.md | 2 + .../06.3-02-SUMMARY.md | 129 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 .planning/phases/06.3-ui-structure-refactor/06.3-02-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 20f34a1f6e..732b0c4499 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -156,6 +156,8 @@ Recent decisions affecting current work: | Hover-triggered UserMenu dropdown | 06.3-01 | Per CONTEXT.md decision, onMouseEnter/Leave not onClick | | Terminal ASCII icons [DIR] [CFG] | 06.3-01 | Nav items use bracket-wrapped labels for terminal aesthetic | | Mobile breakpoint 768px hides sidebar | 06.3-01 | Single column layout on mobile, sidebar removed from grid | +| URL-based folder navigation via useParams | 06.3-02 | Browser back/forward works for folder navigation history | +| /files/:folderId? route pattern | 06.3-02 | Root folder at /files, subfolders at /files/:folderId | | 3-column file list layout (Name/Size/Modified) | 06.3-03 | TYPE column removed per CONTEXT.md decision | | Parent navigation via [..] row not breadcrumb | 06.3-03 | Back button removed from breadcrumbs, [..] row for parent navigation | | Breadcrumbs show full path ~/root/path lowercase | 06.3-03 | Terminal aesthetic with lowercase folder names | diff --git a/.planning/phases/06.3-ui-structure-refactor/06.3-02-SUMMARY.md b/.planning/phases/06.3-ui-structure-refactor/06.3-02-SUMMARY.md new file mode 100644 index 0000000000..14e2554e70 --- /dev/null +++ b/.planning/phases/06.3-ui-structure-refactor/06.3-02-SUMMARY.md @@ -0,0 +1,129 @@ +--- +phase: 06.3-ui-structure-refactor +plan: 02 +subsystem: ui +tags: [react, react-router, routing, navigation, url-params] + +# Dependency graph +requires: + - phase: 06.3-01 + provides: AppShell layout components (AppShell, AppHeader, AppSidebar, AppFooter) +provides: + - URL-based folder navigation with /files/:folderId? pattern + - FilesPage and SettingsPage wrapped in AppShell + - Browser history integration for folder navigation + - /dashboard to /files redirect for backwards compatibility +affects: [06.3-03, 06.3-04, 06-file-browser-ui] + +# Tech tracking +tech-stack: + added: [] + patterns: + - URL-based state management via useParams/useNavigate + - Page components wrapping content in AppShell layout + +key-files: + created: + - apps/web/src/routes/FilesPage.tsx + - apps/web/src/routes/SettingsPage.tsx + modified: + - apps/web/src/routes/index.tsx + - apps/web/src/hooks/useFolderNavigation.ts + - apps/web/src/App.css + - apps/web/src/routes/Dashboard.tsx + +key-decisions: + - 'URL-based folder navigation replaces useState for browser history support' + - 'Root folder maps to /files, subfolders to /files/:folderId' + - 'Dashboard.tsx deprecated but kept for reference' + - 'Old dashboard CSS styles deprecated inline with comments' + +patterns-established: + - 'Page components (FilesPage, SettingsPage) wrap content in AppShell' + - 'useFolderNavigation uses useParams for currentFolderId' + - 'Authenticated routes redirect to / when not logged in' + +# Metrics +duration: 6min +completed: 2026-01-30 +--- + +# Phase 6.3 Plan 02: Wire AppShell to Routes Summary + +URL-based folder navigation with /files/:folderId? pattern, FilesPage and SettingsPage wrapped in AppShell layout. + +## Performance + +- **Duration:** 6 min +- **Started:** 2026-01-30T02:21:22Z +- **Completed:** 2026-01-30T02:27:47Z +- **Tasks:** 3 +- **Files modified:** 6 + +## Accomplishments + +- Created FilesPage and SettingsPage components wrapping content in AppShell +- Updated routes with /files/:folderId? pattern and /dashboard redirect +- Refactored useFolderNavigation to derive state from URL params +- Added layout.css import to App.css and deprecation comments + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Update Routes and Create Page Components** - `0b89c09` (feat) +2. **Task 2: Update useFolderNavigation for URL-based State** - `562baa0` (feat) +3. **Task 3: Update App.css and Remove Old Dashboard Styles** - `3a1bd4f` (chore) + +## Files Created/Modified + +- `apps/web/src/routes/FilesPage.tsx` - Files page wrapping FileBrowser in AppShell +- `apps/web/src/routes/SettingsPage.tsx` - Settings page wrapping LinkedMethods in AppShell +- `apps/web/src/routes/index.tsx` - Updated routes with /files/:folderId? pattern +- `apps/web/src/hooks/useFolderNavigation.ts` - URL-based navigation using useParams/useNavigate +- `apps/web/src/App.css` - Added layout.css import and settings page styles +- `apps/web/src/routes/Dashboard.tsx` - Marked as deprecated + +## Decisions Made + +- **URL-based folder state:** Replaced useState(currentFolderId) with useParams to enable browser history for folder navigation (back/forward buttons work) +- **Root folder URL mapping:** /files without folderId parameter defaults to 'root' folder; subfolders use /files/:folderId +- **Deprecation over deletion:** Dashboard.tsx and related CSS kept with deprecation comments for reference during migration verification + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Fixed unused FileList props causing TypeScript errors** + +- **Found during:** Task 1 (build verification) +- **Issue:** FileList.tsx had unused imports (ParentDirRow) and props (showParentRow, onNavigateUp) causing TS6133 errors that blocked build +- **Fix:** Verified ParentDirRow is now used in FileList via linter auto-fix; build passes +- **Files modified:** apps/web/src/components/file-browser/FileList.tsx (auto-fixed by linter) +- **Verification:** Build passes without errors +- **Committed in:** Linter handled during commit hooks + +--- + +**Total deviations:** 1 auto-fixed (blocking) +**Impact on plan:** Minor - linter auto-fixed the unused variable error. No scope creep. + +## Issues Encountered + +None - all components built successfully. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- AppShell layout now integrated with authenticated routes +- Plan 03 can update file list and toolbar components +- URL-based navigation enables E2E testing of folder navigation via URL +- Browser history works for folder navigation + +--- + +_Phase: 06.3-ui-structure-refactor_ +_Completed: 2026-01-30_ From bff57d1c60f98c3f84171b0e2fe8e5e2bae934aa Mon Sep 17 00:00:00 2001 From: Michael Yankelev Date: Fri, 30 Jan 2026 03:32:29 +0100 Subject: [PATCH 19/27] feat(06.3-04): update FileBrowser to remove FolderTree sidebar - Remove FolderTree import and sidebar component - Remove mobile sidebar state (sidebarOpen, isMobile, toggleSidebar, closeSidebar) - Remove window resize listener for mobile detection - Remove handleDrop callback (was for sidebar drag-drop) - Remove moveItem from useFolder hook (unused) - Add showParentRow and onNavigateUp props to FileList - Simplify layout from file-browser to file-browser-content - Update component docstring for new navigation pattern --- .../components/file-browser/FileBrowser.tsx | 247 +++++------------- 1 file changed, 65 insertions(+), 182 deletions(-) diff --git a/apps/web/src/components/file-browser/FileBrowser.tsx b/apps/web/src/components/file-browser/FileBrowser.tsx index dcae74b42d..d400788661 100644 --- a/apps/web/src/components/file-browser/FileBrowser.tsx +++ b/apps/web/src/components/file-browser/FileBrowser.tsx @@ -1,10 +1,9 @@ -import { useState, useCallback, useEffect, type DragEvent, type MouseEvent } from 'react'; +import { useState, useCallback, type DragEvent, type MouseEvent } from 'react'; import type { FolderChild, FileEntry } from '@cipherbox/crypto'; import { useFolderNavigation } from '../../hooks/useFolderNavigation'; import { useFolder } from '../../hooks/useFolder'; import { useFileDownload } from '../../hooks/useFileDownload'; import { useContextMenu } from '../../hooks/useContextMenu'; -import { FolderTree } from './FolderTree'; import { FileList } from './FileList'; import { EmptyState } from './EmptyState'; import { ContextMenu } from './ContextMenu'; @@ -33,41 +32,31 @@ type DialogState = { /** * Main file browser container component. * - * Orchestrates the sidebar folder tree and main file list area. - * Manages selection state, context menu, and file/folder actions. + * Manages the file list area with in-place navigation. + * Selection state, context menu, and file/folder actions. * - * Layout: - * - Left sidebar: FolderTree for navigation - * - Main area: FileList or EmptyState based on folder contents + * Navigation: + * - Double-click folders to navigate into them + * - Click [..] PARENT_DIR row to navigate up + * - Browser history integration via URL routing * * Actions: * - Download: Downloads file from IPFS, decrypts, and triggers browser download * - Rename: Opens dialog to rename file/folder * - Delete: Shows confirmation, then removes from folder metadata - * - Move: Drag-drop to sidebar folder tree + * - Move: Drag-drop to folder rows in list * * @example * ```tsx - * function Dashboard() { + * function FilesPage() { * return ( - *
+ *
* *
* ); * } * ``` */ -/** - * Mobile breakpoint (px) - matches CSS media query. - */ -const MOBILE_BREAKPOINT = 768; - -/** - * Check if viewport is mobile width. - */ -function isMobileViewport(): boolean { - return typeof window !== 'undefined' && window.innerWidth < MOBILE_BREAKPOINT; -} export function FileBrowser() { // Navigation state from hook @@ -75,7 +64,7 @@ export function FileBrowser() { useFolderNavigation(); // Folder operations - const { createFolder, renameItem, moveItem, deleteItem, isLoading: isOperating } = useFolder(); + const { createFolder, renameItem, deleteItem, isLoading: isOperating } = useFolder(); // File download const { download, isDownloading } = useFileDownload(); @@ -91,48 +80,13 @@ export function FileBrowser() { const [renameDialog, setRenameDialog] = useState({ open: false, item: null }); const [createFolderDialogOpen, setCreateFolderDialogOpen] = useState(false); - // Mobile sidebar state - closed by default on mobile, open on desktop - const [sidebarOpen, setSidebarOpen] = useState(() => !isMobileViewport()); - const [isMobile, setIsMobile] = useState(() => isMobileViewport()); - - // Track viewport changes - useEffect(() => { - const handleResize = () => { - const mobile = isMobileViewport(); - setIsMobile(mobile); - // Auto-open sidebar when transitioning from mobile to desktop - if (!mobile) { - setSidebarOpen(true); - } - }; - - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, []); - - // Toggle sidebar (mobile) - const toggleSidebar = useCallback(() => { - setSidebarOpen((prev) => !prev); - }, []); - - // Close sidebar (mobile) - const closeSidebar = useCallback(() => { - if (isMobile) { - setSidebarOpen(false); - } - }, [isMobile]); - // Clear selection when navigating to a new folder const handleNavigate = useCallback( (folderId: string) => { setSelectedItemId(null); navigateTo(folderId); - // Close sidebar on mobile after navigation - if (isMobile) { - setSidebarOpen(false); - } }, - [navigateTo, isMobile] + [navigateTo] ); // Handle item selection @@ -153,33 +107,6 @@ export function FileBrowser() { // Drag data is set by FileListItem component }, []); - // Drop handler for folder tree - move item to target folder - const handleDrop = useCallback( - async (targetFolderId: string, dataTransfer: DataTransfer) => { - try { - const data = dataTransfer.getData('application/json'); - if (!data) return; - - const { id, type, parentId } = JSON.parse(data) as { - id: string; - type: 'file' | 'folder'; - parentId: string; - }; - - // Don't move to same folder - if (parentId === targetFolderId) return; - - // Prevent moving folder into itself (checked in folder.service too) - if (type === 'folder' && id === targetFolderId) return; - - await moveItem(id, type, parentId, targetFolderId); - } catch (err) { - console.error('Move failed:', err); - } - }, - [moveItem] - ); - // Download action handler const handleDownload = useCallback(async () => { const item = contextMenu.item; @@ -281,111 +208,67 @@ export function FileBrowser() { ? `Are you sure you want to delete "${confirmDialog.item?.name}"? This will also delete all files and subfolders inside. This cannot be undone.` : `Are you sure you want to delete "${confirmDialog.item?.name}"? This cannot be undone.`; - // Sidebar classes based on state - const sidebarClassName = [ - 'file-browser-sidebar', - sidebarOpen ? 'file-browser-sidebar--open' : 'file-browser-sidebar--closed', - ].join(' '); - return ( -
- {/* Mobile backdrop when sidebar is open */} - {isMobile && sidebarOpen && ( -