Files
calctext/_bmad-output/C-UX-Scenarios/01-workspace-shell/1.1-app-shell/1.1-app-shell.md
C. Cassel 0d38bd3108 feat(web): implement complete workspace with themes, tabs, sidebar, and mobile
Transform CalcText from a single-document calculator into a full workspace
application with multi-document support, theming, and responsive mobile experience.

- Theme system: 5 presets (Light, Dark, Matrix, Midnight, Warm) + accent colors
- Document model with localStorage persistence and auto-save
- Tab bar with keyboard shortcuts (Ctrl+N/W/Tab/1-9), rename, close
- Sidebar with search, recent, favorites, folders, templates, drag-and-drop
- 5 templates: Budget, Invoice, Unit Converter, Trip Planner, Loan Calculator
- Status bar with cursor position, engine status, dedication to Igor Cassel
- Results panel: type-specific colors, click-to-copy, error hints
- Format toolbar: H, B, I, //, color labels with live preview toggle
- Syntax highlighting using theme CSS variables
- Error hover tooltips
- Mobile: bottom results tray, sidebar drawer, touch targets, safe areas
- Docker multi-stage build (Rust WASM + Vite + Nginx)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:12:05 -04:00

559 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 1.1 — App Shell
**Next Step:** → [Status Bar](../1.2-status-bar/1.2-status-bar.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 01 — Workspace Shell |
| **Page Number** | 1.1 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Full Application Shell |
| **Viewport** | Desktop-first, responsive to mobile |
| **Interaction** | Mouse + keyboard (primary), touch (secondary) |
| **Visibility** | Public (no auth required) |
---
## Overview
**Page Purpose:** The app shell is the top-level container that defines the spatial structure of the entire CalcText workspace. It arranges all panels (sidebar, editor, results), the tab bar, header, and status bar into a cohesive layout that persists across all user interactions.
**User Situation:** User opens CalcText in a browser. They expect a professional workspace where they can write calculations, manage files, and personalize their environment. First-time users should immediately understand the layout. Returning users should find their documents and preferences exactly as they left them.
**Success Criteria:**
- User understands the 3-panel layout within 3 seconds
- All panels are resizable and collapsible
- Layout state persists across sessions (sidebar width, panel visibility)
- Feels native — not like a web page
**Entry Points:**
- Direct URL (calctext.app or localhost)
- PWA launch from desktop/dock
- Returning session (restores last state from localStorage)
**Exit Points:**
- Close browser/PWA (state auto-saves)
- All navigation is within the shell (no page transitions)
---
## Reference Materials
**Strategic Foundation:**
- [Product Brief](../../../A-Product-Brief/project-brief.md) — Workspace evolution, web-first
- [Brownfield Analysis](../../../A-Product-Brief/01-brownfield-analysis.md) — Current CSS tokens, components, gaps
**Related Pages:**
- [Status Bar](../1.2-status-bar/1.2-status-bar.md) — Bottom status strip
- Tab Bar (Scenario 03) — Document tabs above editor
- Sidebar (Scenario 04) — File tree panel
- Editor (Scenario 02) — Calculation editor
- Results Panel (Scenario 02) — Results display
---
## Layout Structure
The app shell uses a 4-region layout: Header, Sidebar, Main Area (tabs + editor + results), and Status Bar.
### Desktop (>= 768px)
```
┌─────────────────────────────────────────────────────────┐
│ Header [Logo] [Theme] [Settings] [⌘] │ 40px
├────────┬────────────────────────────────────────────────┤
│ │ Tab Bar [Budget ×] [Invoice ×] [+] │ 36px
│ ├─────────────────────────┬──┬──────────────────┤
│ Side │ │ │ │
│ bar │ Editor │ D│ Results │
│ │ (CodeMirror 6) │ iv│ Panel │
│ 240px │ │ │ │
│ (coll │ │ │ │
│ apsi │ │ │ │
│ ble) │ │ │ │
│ │ │ │ │
├────────┴─────────────────────────┴──┴──────────────────┤
│ Status Bar [Ln 12, Col 8] [Engine ●] [Dark 🎨] │ 24px
└─────────────────────────────────────────────────────────┘
```
### Tablet (768px — 1024px)
```
┌─────────────────────────────────────────┐
│ Header [Logo] [Theme] [≡] │ 40px
├─────────────────────────────────────────┤
│ Tab Bar [Budget ×] [Invoice ×] [+] │ 36px
├─────────────────────────┬──┬────────────┤
│ │ │ │
│ Editor │ D│ Results │
│ (CodeMirror 6) │ iv│ Panel │
│ │ │ │
├─────────────────────────┴──┴────────────┤
│ Status Bar │ 24px
└─────────────────────────────────────────┘
Sidebar: overlay drawer via hamburger [≡]
```
### Mobile (< 768px)
```
┌─────────────────────────┐
│ Header [Logo] [≡] │ 44px (touch)
├─────────────────────────┤
│ Tab Bar (scrollable) │ 36px
├─────────────────────────┤
│ │
│ Editor │
│ (full width) │
│ │
│ │
├─────────────────────────┤
│ Results Tray (toggle) │ 48px collapsed / 40vh expanded
├─────────────────────────┤
│ Status Bar │ 24px
└─────────────────────────┘
Sidebar: full-screen drawer
Results: bottom tray with drag handle
```
---
## Spacing
**Scale:** [Spacing Scale](../../../D-Design-System/00-design-system.md#spacing-scale)
| Property | Token | Pixels (proposed) |
|----------|-------|--------------------|
| Page padding (horizontal) | space-zero | 0px (panels fill edge-to-edge) |
| Header padding (horizontal) | space-md | 12px |
| Header padding (vertical) | space-xs | 6px |
| Sidebar width (default) | — | 240px |
| Sidebar min width | — | 180px |
| Sidebar max width | — | 400px |
| Divider width (sidebar ↔ editor) | — | 1px visible, 8px hit area |
| Divider width (editor ↔ results) | — | 1px visible, 8px hit area |
| Tab bar height | — | 36px |
| Tab padding (horizontal) | space-sm | 8px |
| Status bar height | — | 24px |
| Status bar padding (horizontal) | space-md | 12px |
---
## Typography
**Scale:** [Type Scale](../../../D-Design-System/00-design-system.md#type-scale)
| Element | Semantic | Size | Weight | Typeface |
|---------|----------|------|--------|----------|
| App title (header) | — | text-sm | 600 | system sans |
| Tab label | — | text-xs | 400 (normal), 500 (active) | system sans |
| Status bar text | — | text-3xs | 400 | system mono |
| Sidebar section title | H3 | text-2xs | 600 | system sans |
| Sidebar file name | — | text-xs | 400 | system sans |
| Editor content | — | text-md | 400 | system mono |
| Results value | — | text-md | 400 | system mono |
---
## Page Sections
### Section: Header
**OBJECT ID:** `shell-header`
| Property | Value |
|----------|-------|
| Purpose | App identity, global actions, theme quick-switch |
| Height | 40px (desktop/tablet), 44px (mobile — touch target) |
| Background | var(--bg) |
| Border | bottom 1px solid var(--border) |
| Layout | Horizontal: logo-left, actions-right |
#### Logo Group
**OBJECT ID:** `shell-header-logo`
| Property | Value |
|----------|-------|
| Content | CalcText icon + wordmark |
| Icon size | 20px × 20px |
| Wordmark | "CalcText" in text-sm, weight 600 |
| Gap | space-xs (6px) between icon and wordmark |
| Mobile | Wordmark hidden < 480px, icon only |
#### Header Actions
**OBJECT ID:** `shell-header-actions`
| Property | Value |
|----------|-------|
| Layout | Horizontal, space-xs gap |
| Items | Theme toggle, Settings button, Keyboard shortcuts (⌘) |
| Button size | 28px × 28px (icon buttons) |
| Icon size | 16px |
| Style | Ghost buttons transparent bg, var(--text) icon, hover var(--accent-bg) |
#### Theme Toggle (Header)
**OBJECT ID:** `shell-header-theme-toggle`
| Property | Value |
|----------|-------|
| Component | Icon button with dropdown |
| Icon | Sun (light), Moon (dark), Terminal (matrix), Palette (custom) |
| Click | Opens theme picker dropdown |
| Tooltip | "Switch theme (Ctrl+Shift+T)" |
#### ↕ `shell-header-v-space-zero` — Header sits flush against tab bar below
---
### Section: Sidebar
**OBJECT ID:** `shell-sidebar`
| Property | Value |
|----------|-------|
| Purpose | File navigation, document organization, templates |
| Width | 240px default, resizable (180400px), collapsible to 0 |
| Background | var(--bg-secondary) |
| Border | right 1px solid var(--border) |
| Toggle | Cmd/Ctrl+B to show/hide |
| Resize | Drag right edge, cursor col-resize |
| Persistence | Width and visibility stored in localStorage |
#### Sidebar Sections
**OBJECT ID:** `shell-sidebar-sections`
| Section | Icon | Content |
|---------|------|---------|
| **Recent** | 🕐 | Last 5 opened documents, sorted by time |
| **Favorites** | | User-pinned documents |
| **Files** | 📁 | Full folder tree with nested structure |
| **Templates** | 📋 | Pre-built starting points (Budget, Invoice, Unit Converter, Trip Planner, Blank) |
Each section is collapsible with a chevron toggle.
#### File Tree Item
**OBJECT ID:** `shell-sidebar-file-item`
| Property | Value |
|----------|-------|
| Height | 28px |
| Padding left | 12px + (depth × 16px) for nesting |
| Icon | 📄 file / 📁 folder (closed) / 📂 folder (open) |
| Label | File/folder name, text-xs, ellipsis on overflow |
| Hover | var(--accent-bg) background |
| Active | var(--accent-bg) + left 2px accent border |
| Right-click | Context menu: Rename, Delete, Duplicate, Move to folder, Add to favorites |
| Double-click | Opens document in new tab |
| Drag | Reorder within folder, drag between folders |
#### Sidebar Footer
**OBJECT ID:** `shell-sidebar-footer`
| Property | Value |
|----------|-------|
| Content | [+ New Document] button, [+ New Folder] button |
| Layout | Horizontal, full width, space-xs gap |
| Button style | Ghost, text-2xs, var(--text), hover var(--accent) |
| Position | Sticky bottom of sidebar |
| Padding | space-sm |
| Border | top 1px solid var(--border) |
---
### Section: Tab Bar
**OBJECT ID:** `shell-tabbar`
| Property | Value |
|----------|-------|
| Purpose | Multi-document navigation, quick tab management |
| Height | 36px |
| Background | var(--bg-secondary) |
| Border | bottom 1px solid var(--border) |
| Layout | Horizontal scroll when tabs overflow |
| Position | Between header and editor area |
#### Tab Item
**OBJECT ID:** `shell-tabbar-tab`
| Property | Value |
|----------|-------|
| Min width | 100px |
| Max width | 200px |
| Padding | 0 space-sm (0 8px) |
| Height | 36px (fills bar) |
| Label | Document title, text-xs, ellipsis on overflow |
| Modified indicator | Dot (6px) before title when unsaved changes |
| Close button | × icon, 14px, visible on hover or active tab |
| Active state | var(--bg) background, no bottom border (connected to editor) |
| Inactive state | var(--bg-secondary), bottom border 1px solid var(--border) |
| Hover (inactive) | var(--bg-secondary) lightened slightly |
| Drag | Reorder tabs via drag-and-drop |
| Middle-click | Close tab |
| Double-click | Rename document inline |
#### New Tab Button
**OBJECT ID:** `shell-tabbar-new`
| Property | Value |
|----------|-------|
| Icon | + (16px) |
| Size | 36px × 36px (square, fills bar height) |
| Style | Ghost, var(--text), hover var(--accent) |
| Click | Creates "Untitled" document and switches to it |
| Position | After last tab, before overflow |
#### Tab Overflow
**OBJECT ID:** `shell-tabbar-overflow`
| Property | Value |
|----------|-------|
| Trigger | When tabs exceed container width |
| Behavior | Horizontal scroll with mouse wheel or trackpad |
| Indicators | Fade gradient on edges when scrollable |
| Keyboard | Ctrl+Tab / Ctrl+Shift+Tab to cycle tabs |
---
### Section: Main Area
**OBJECT ID:** `shell-main`
| Property | Value |
|----------|-------|
| Purpose | Contains the active document's editor and results panel |
| Layout | Horizontal flex: editor (flex: 3) + divider + results (flex: 1) |
| Background | var(--bg) |
| Content | Delegates to Scenario 02 (Editor + Results Panel) |
#### Editor Pane
**OBJECT ID:** `shell-main-editor`
| Property | Value |
|----------|-------|
| Flex | 3 (default ~75%) |
| Min width | 300px |
| Content | CodeMirror 6 instance (see Scenario 02) |
| Overflow | Vertical scroll (editor handles internally) |
#### Pane Divider (Editor ↔ Results)
**OBJECT ID:** `shell-main-divider`
| Property | Value |
|----------|-------|
| Width | 1px visible line |
| Hit area | 8px (invisible padding for easy grabbing) |
| Cursor | col-resize |
| Color | var(--border), hover var(--accent) |
| Transition | background 0.15s |
| Double-click | Reset to default 75/25 split |
| Persistence | Position stored in localStorage |
#### Results Pane
**OBJECT ID:** `shell-main-results`
| Property | Value |
|----------|-------|
| Flex | 1 (default ~25%) |
| Min width | 120px |
| Content | Results panel (see Scenario 02) |
| Scroll sync | Mirrors editor scroll position |
---
### Section: Status Bar
**OBJECT ID:** `shell-statusbar`
| Property | Value |
|----------|-------|
| Purpose | Contextual info: cursor position, engine status, theme indicator |
| Height | 24px |
| Background | var(--bg-secondary) |
| Border | top 1px solid var(--border) |
| Font | text-3xs, monospace |
| Color | var(--text) |
| Layout | Horizontal: left-info + right-info |
Full specification in [1.2 — Status Bar](../1.2-status-bar/1.2-status-bar.md).
---
## Page States
| State | When | Appearance | Actions |
|-------|------|------------|---------|
| **First Launch** | No localStorage data | Sidebar open with Templates section expanded. Single tab "Welcome" with demo calctext. Theme follows OS preference. | User can start typing, explore templates, or create new doc |
| **Returning User** | localStorage has documents | Restores: last open tabs, active tab, sidebar state, theme, divider positions | Resume exactly where they left off |
| **Empty Workspace** | All documents deleted | Editor shows subtle placeholder: "Create a new document or choose a template to get started" | + New Document button prominent, Templates section highlighted |
| **Engine Loading** | WASM initializing | Status bar shows "Engine loading..." with pulse animation. Editor is editable. Results show "—" | Editor works, results appear once engine is ready |
| **Offline** | No network | Subtle indicator in status bar. All features work (localStorage). Currency rates may be stale. | Full functionality, offline banner only if currency conversion attempted |
---
## Interactions & Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| Cmd/Ctrl + B | Toggle sidebar |
| Cmd/Ctrl + N | New document |
| Cmd/Ctrl + W | Close active tab |
| Cmd/Ctrl + Tab | Next tab |
| Cmd/Ctrl + Shift + Tab | Previous tab |
| Cmd/Ctrl + 19 | Switch to tab N |
| Cmd/Ctrl + Shift + T | Open theme picker |
| Cmd/Ctrl + , | Open settings |
| Cmd/Ctrl + S | Force save (visual confirmation auto-save is default) |
---
## Responsive Behavior
| Breakpoint | Layout Changes |
|------------|----------------|
| **>= 1024px** | Full 3-panel: sidebar + editor + results |
| **7681023px** | Sidebar becomes overlay drawer (hamburger toggle). Editor + results remain side-by-side |
| **< 768px** | Single column. Sidebar = full-screen drawer. Results = bottom tray (collapsible). Tabs = horizontal scroll. Header = 44px (touch). |
### Mobile Results Tray
| Property | Value |
|----------|-------|
| Collapsed height | 48px (shows last result + drag handle) |
| Expanded height | 40vh |
| Drag handle | 32px × 4px pill, centered, var(--border) |
| Swipe up | Expand tray |
| Swipe down | Collapse tray |
| Tap collapsed | Expand tray |
### Mobile Sidebar Drawer
| Property | Value |
|----------|-------|
| Width | 85vw (max 320px) |
| Background | var(--bg) |
| Overlay | 50% black backdrop |
| Animation | Slide from left, 200ms ease-out |
| Close | Tap backdrop, swipe left, or X button |
---
## Theme Integration
The app shell's colors are entirely driven by CSS custom properties. Switching themes means swapping the property values on `:root`. No component changes needed.
| Theme | --bg | --bg-secondary | --text | --accent | Special |
|-------|------|----------------|--------|----------|---------|
| **Light** | #fff | #f8f9fa | #6b6375 | #6366f1 | |
| **Dark** | #16171d | #1a1b23 | #9ca3af | #818cf8 | |
| **Matrix** | #0a0a0a | #0f0f0f | #00ff41 | #00ff41 | Monospace everywhere. Subtle scanline overlay. Cursor blink green. |
| **Custom** | User-defined | User-defined | User-defined | User-defined | Accent color picker + base tone (warm/cool/neutral) |
Theme selection persisted in localStorage as `calctext-theme`.
---
## localStorage Schema
```typescript
interface CalcTextStorage {
// Documents
documents: Document[]
folders: Folder[]
activeTabId: string
openTabIds: string[]
// Layout
sidebarWidth: number
sidebarVisible: boolean
dividerPosition: number // percentage
// Preferences
theme: 'light' | 'dark' | 'matrix' | string // string for custom
customTheme?: ThemeTokens
accentColor?: string
// State
lastOpenedAt: string // ISO timestamp
}
interface Document {
id: string
title: string
content: string
folderId: string | null
isFavorite: boolean
createdAt: string
updatedAt: string
}
interface Folder {
id: string
name: string
parentId: string | null
order: number
}
```
---
## Technical Notes
- **Cross-platform portability:** All layout patterns map to native equivalents. Sidebar NavigationView (SwiftUI) / side_panel (iced). Tab bar TabView. Status bar standard OS status bar pattern.
- **Performance:** Only the active tab's CodeMirror instance should be in DOM. Inactive tabs store content in memory, restore on switch.
- **Auto-save:** Documents save to localStorage on every change (debounced 500ms). No explicit "save" needed, but Cmd+S provides visual confirmation.
- **Sidebar resize:** Use ResizeObserver + mouse events. Store width in localStorage. Minimum 180px, maximum 400px.
- **Tab management:** Maximum suggested tabs: 20. Beyond that, show "too many tabs" hint. No hard limit.
---
## Open Questions
| # | Question | Context | Status |
|---|----------|---------|--------|
| 1 | Should Matrix theme have a subtle CRT scanline effect? | Could be fun but might impact readability | 🔴 Open |
| 2 | Should we support tab groups / workspaces? | Multiple sets of tabs for different projects | 🔴 Open defer to v2 |
| 3 | Max localStorage size for documents? | ~5MB browser limit. Need strategy for large collections. | 🟡 In Discussion may need IndexedDB |
---
## Checklist
- [x] Page purpose clear
- [x] All Object IDs assigned
- [x] Layout structure defined (desktop, tablet, mobile)
- [x] Spacing tokens documented
- [x] Typography scale applied
- [x] States documented (first launch, returning, empty, loading, offline)
- [x] Keyboard shortcuts defined
- [x] Responsive breakpoints specified
- [x] Theme integration documented
- [x] localStorage schema defined
- [x] Cross-platform portability noted
- [x] Open questions captured
---
**Next Step:** [Status Bar](../1.2-status-bar/1.2-status-bar.md)
---
_Created using Whiteport Design Studio (WDS) methodology_