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>
This commit is contained in:
2026-03-18 09:12:05 -04:00
parent 806e2f1ec6
commit 0d38bd3108
78 changed files with 8175 additions and 421 deletions

View File

@@ -0,0 +1,44 @@
# Product Brief: CalcText
> The strategic foundation — why this product exists, who it serves, and what success looks like.
**Created:** 2026-03-17
**Phase:** 1 — Product Brief
**Agent:** Saga (Analyst)
---
## What Belongs Here
The Product Brief answers five strategic questions:
1. **Why** does this product exist? (Vision & business goals)
2. **Who** is it for? (Target users and their context)
3. **What** does it need to do? (Core capabilities)
4. **How** will we know it works? (Success metrics)
5. **What** are the constraints? (Platform requirements, tech stack)
Everything downstream — trigger maps, scenarios, page specs, design system — traces back to decisions made here. This is the North Star.
---
## For Agents
**Workflow:** `_bmad/wds/workflows/1-project-brief/workflow.md`
**Agent trigger:** `PB` (Saga)
**Templates:** `_bmad/wds/workflows/1-project-brief/templates/`
---
## Documents
_This section will be updated as documents are created during Phase 1._
| # | Document | Status |
|---|----------|--------|
| 01 | Project Brief (Simplified) | Complete |
| 02 | Brownfield Analysis | Complete |
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,205 @@
# CalcText Web — Brownfield Analysis
**Date:** 2026-03-17
**Agent:** Freya (WDS Designer)
**Scope:** calcpad-web current state + cross-platform patterns from macOS
---
## 1. Current Architecture
| Layer | Tech | Status |
|-------|------|--------|
| Framework | React 19 + TypeScript | Solid |
| Editor | CodeMirror 6 (custom language, extensions) | Solid |
| Engine | Rust WASM in Web Worker (JS fallback for dev) | Solid |
| Build | Vite 7, ES2022 target, code splitting | Solid |
| PWA | vite-plugin-pwa, service worker, manifest | Solid |
| Styling | CSS custom properties, no framework | Flexible — ready for theme system |
---
## 2. Current Layout
```
┌─────────────────────────────────────────┐
│ Header (logo + subtitle + status dot) │ 52px
├─────────────────────────────────────────┤
│ Align Toolbar (editor + results) │ 28px
├───────────────────────┬─┬───────────────┤
│ │ │ │
│ Editor (flex: 3) │D│ Results │
│ CodeMirror 6 │i│ Panel │
│ 15px mono, 24px line │v│ (flex: 1) │
│ │ │ │
└───────────────────────┴─┴───────────────┘
```
**Mobile (< 640px):** Results panel, toolbar, divider, subtitle — all hidden. Editor only.
---
## 3. CSS Design Tokens (Current)
### Colors
| Token | Light | Dark |
|-------|-------|------|
| --text | #6b6375 | #9ca3af |
| --text-h | #08060d | #f3f4f6 |
| --bg | #fff | #16171d |
| --bg-secondary | #f8f9fa | #1a1b23 |
| --border | #e5e4e7 | #2e303a |
| --code-bg | #f4f3ec | #1f2028 |
| --accent | #6366f1 | #818cf8 |
| --accent-bg | rgba(99,102,241,0.1) | rgba(129,140,248,0.15) |
| --accent-border | rgba(99,102,241,0.5) | rgba(129,140,248,0.5) |
| --warning | #f59e0b | (same) |
| --success | #10b981 | (same) |
| --error | #e53e3e | (same) |
### Typography
| Context | Font | Size | Weight | Line Height |
|---------|------|------|--------|-------------|
| Body | system-ui | 16px | 400 | 1.5 |
| Editor/Results | ui-monospace | 15px | 400/600 | 1.6 (24px) |
| Header title | system-ui | 20px | — | — |
| Header subtitle | system-ui | 13px | — | — |
| Status | system-ui | 12px | — | — |
| Toolbar labels | system-ui | 11px | — | — |
| Buttons | system-ui | 14px | 500 | — |
### Spacing
| Context | Value |
|---------|-------|
| Header padding | 12px 24px |
| Editor line padding | 0 16px |
| Editor content padding | 12px 0 |
| Results line padding | 0 16px |
| Results content padding | 12px 0 |
| Toolbar height | 28px |
| Toolbar padding | 0 12px |
| Button gaps | 4px |
| Divider width | 5px (9px hit area) |
### Breakpoints
| Width | Behavior |
|-------|----------|
| > 640px | Full two-column layout |
| <= 640px | Single column, results/toolbar/divider hidden |
| <= 480px | Install prompt stacks vertically |
### Z-Index
| Element | Value |
|---------|-------|
| Install prompt | 100 |
| (everything else) | Default stacking |
### Animations
| Element | Type | Duration |
|---------|------|----------|
| Divider hover | Transition | 0.15s |
| Toolbar buttons | Transition | 0.1s |
| Install button | Transition | 0.2s |
| Status dot (loading) | Pulse keyframe | 1.5s |
| Offline icon | Pulse keyframe | 2s |
---
## 4. Component Inventory
| Component | Location | Props | Purpose |
|-----------|----------|-------|---------|
| App | App.tsx | — | Shell, state, layout |
| CalcEditor | editor/CalcEditor.tsx | initialDoc, onDocChange, results, debounceMs, onViewReady | CodeMirror wrapper |
| ResultsPanel | components/ResultsPanel.tsx | results, align, style | Right-side results display |
| AlignToolbar | components/AlignToolbar.tsx | editorAlign, resultsAlign, onChange handlers | Text alignment controls |
| OfflineBanner | components/OfflineBanner.tsx | isOnline | Offline status |
| InstallPrompt | components/InstallPrompt.tsx | promptEvent, isInstalled, onInstall, onDismiss | PWA install |
### Editor Extensions
| Extension | File | Purpose |
|-----------|------|---------|
| calcpadLanguage | calcpad-language.ts | Syntax highlighting (keywords, functions, operators, currencies) |
| errorDisplayExtension | error-display.ts | Wavy underlines + gutter markers for errors |
| stripedLinesExtension | inline-results.ts | Even-line zebra striping |
| calcpadEditorTheme | CalcEditor.tsx | Base theme (font, padding, colors) |
### Hooks
| Hook | File | State |
|------|------|-------|
| useEngine | engine/useEngine.ts | ready, results, error, evalSheet() |
| useOnlineStatus | hooks/useOnlineStatus.ts | isOnline |
| useInstallPrompt | hooks/useInstallPrompt.ts | promptEvent, isInstalled, handlers |
---
## 5. Accessibility Audit
| Feature | Status | Notes |
|---------|--------|-------|
| Semantic HTML | Partial | Header, main present. Missing nav, regions |
| ARIA roles | Partial | OfflineBanner (status), InstallPrompt (complementary) |
| Focus indicators | Missing | No custom :focus-visible styles |
| Keyboard nav | Default | CodeMirror defaults only, toolbar not keyboard-navigable |
| Screen reader | Partial | Results panel has no aria-live for updates |
| Color contrast | OK | Meets AA for most combinations |
| Font scaling | Missing | Fixed 15px, ignores system preferences |
| Reduced motion | Missing | No prefers-reduced-motion media query |
---
## 6. Cross-Platform Comparison (macOS vs Web)
| Aspect | macOS | Web | Recommendation |
|--------|-------|-----|----------------|
| Spacing | Compact (8px) | Spacious (12-16px) | Tighten to match native feel |
| Results weight | Regular, secondary color | Bold 600, accent color | Reduce to secondary — results are too loud |
| Font scaling | System Dynamic Type | Fixed 15px | Respect system preferences |
| Min dimensions | 800x600 window | No minimum | Set min-width guidance |
| Text substitutions | All disabled | Browser default | Disable smart quotes/dashes |
| Stripe pattern | Synced, 4% opacity | Synced, 2.5-3.5% opacity | Already close |
| Error display | Editor only | Editor only | Already matched |
| Scroll sync | Binding-driven | Event-driven (passive) | Both work well |
---
## 7. What's Missing for Workspace Vision
| Feature | Exists? | Effort | Priority |
|---------|---------|--------|----------|
| Theme engine (multi-theme) | No (only system dark/light) | Medium | P0 |
| Theme switcher UI | No | Low | P0 |
| Document model (multi-doc) | No (single string in state) | Medium | P0 |
| Tab bar | No | Medium | P0 |
| File sidebar | No | Medium | P1 |
| localStorage persistence | No | Low | P0 |
| Folder organization | No | Medium | P1 |
| Templates library | No | Low | P2 |
| Dark mode toggle (manual) | No (system preference only) | Low | P0 |
| Mobile results view | No (hidden on mobile) | Medium | P1 |
| Keyboard shortcuts panel | No | Low | P2 |
| Status bar | No | Low | P1 |
---
## 8. Strengths to Preserve
1. **Engine isolation** — Web Worker keeps UI responsive
2. **PWA foundation** — Offline-ready, installable
3. **CSS custom properties** — Perfect base for theme system
4. **CodeMirror extensibility** — Custom language, errors, striping
5. **Scroll sync** — Reliable cross-pane synchronization
6. **Light/dark adaptation** — prefers-color-scheme already works
7. **Code splitting** — React and CodeMirror in separate chunks
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,94 @@
# CalcText — Simplified Product Brief
**Date:** 2026-03-17
**Type:** Brownfield — Workspace Evolution
**Brief Level:** Simplified
---
## Project Scope
CalcText is a cross-platform notepad calculator (Web, macOS, Windows) powered by a shared Rust calculation engine with 200+ unit conversions, 180+ fiat currencies, 50+ crypto, date/time math, and 50+ built-in functions.
The web app is evolving from a single-document, two-column calculator into a **full workspace application** with:
- **Multi-theme system** — Light, Dark, Matrix, and custom user-defined themes
- **Tabbed documents** — Multiple calctext files open simultaneously
- **File sidebar** — Left panel with folder organization, recent files, favorites, templates
- **localStorage persistence** — Documents survive page reloads without backend
- **Elevated results panel** — Richer, subtler result display
**Platform strategy:** Web-first design. Once established, replicate the UX patterns to macOS (SwiftUI) and Windows (iced/Rust). The design must be portable — avoid web-only paradigms that don't translate to native.
---
## Challenge & Opportunity
CalcText has a powerful engine trapped in a basic single-document UI. The current web app is functional but doesn't reflect the depth of what the engine can do, and doesn't invite users to stay.
**The opportunity:** No competitor (Soulver, Numi, Numbr, PCalc) offers a true multi-document workspace with theming and file organization for calculations. CalcText can own this gap — becoming the app users open daily and live in, not just visit for a quick calculation.
**The gap:** Current UI is a text editor with results. The vision is a professional workspace where calculations are organized, styled, and persistent.
---
## Design Goals
### Functional
- Multi-theme engine with preset themes and custom theme support
- Tab bar for multi-document management
- Collapsible file sidebar with tree view, folders, and templates
- Document model with create, rename, delete, organize
- localStorage persistence for all documents and preferences
### Experience
- Professional workspace feel — "VS Code/Notion for numbers"
- Complete experience shipped as one coherent product, not incremental phases
- Matrix theme as a brand differentiator and personality statement
- Intuitive for first-time users, powerful for daily users
### Business
- Web-first design that establishes reusable UX patterns
- Cross-platform portable design language
- Foundation for future premium features (cloud sync, accounts, collaboration)
---
## Constraints
### Technical (locked in)
- React 19 + CodeMirror 6 + Vite 7
- Rust WASM engine running in Web Worker
- Custom CSS with CSS custom properties (no Tailwind, no shadcn)
- PWA architecture (service worker, manifest, offline support)
### Deferred (out of scope this phase)
- User accounts and authentication
- Cloud save and sync (Supabase planned)
- Collaborative real-time editing (CRDT)
- Shareable URL links
- Embeddable widget
### Cross-platform
- Design patterns must translate to SwiftUI (macOS) and iced (Windows)
- Avoid web-only interactions that don't port to native
- Shared engine means consistent calculation behavior across platforms
### Timeline & Budget
- Flexible — no hard deadline
- Stakes: enterprise/high — quality over speed
---
## Current State Reference
See `01-brownfield-analysis.md` for complete codebase scan including:
- All CSS custom properties and values
- Component inventory and props
- Responsive breakpoints and behavior
- Accessibility audit
- Cross-platform comparison (macOS vs Web)
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,42 @@
# UX Scenarios: CalcText
> Design experiences, not screens — every page serves a user with a goal and an emotion.
**Created:** 2026-03-17
**Phase:** 3 (Scenario Outline) + Phase 4 (UX Design)
**Agents:** Freya (Page Specifications)
---
## Scenarios
| # | Scenario | Pages | Description |
|---|----------|-------|-------------|
| 01 | Workspace Shell | App Shell, Status Bar | The overall 3-panel layout that contains everything |
| 02 | Calculation Experience | Editor, Results Panel | The core — typing calculations, seeing results |
| 03 | Document Management | Tab Bar, New Document, Rename/Delete | Multi-document workflow |
| 04 | File Organization | Sidebar, Folder Tree, Templates, Recent/Favorites | Organizing calctext files |
| 05 | Theming | Theme Picker, Preset Themes, Custom Theme, Accent Color | Personalizing the workspace |
| 06 | Mobile Experience | Mobile Shell, Mobile Results, Mobile Sidebar | Responsive/touch adaptation |
---
## Page Index
_Updated as page specifications are created during Phase 4._
| Scenario | Page | Status | File |
|----------|------|--------|------|
| 01 | App Shell | Specified | 01-workspace-shell/1.1-app-shell/1.1-app-shell.md |
| 01 | Status Bar | Specified | 01-workspace-shell/1.2-status-bar/1.2-status-bar.md |
| 02 | Editor | Specified | 02-calculation-experience/2.1-editor/2.1-editor.md |
| 02 | Results Panel | Specified | 02-calculation-experience/2.2-results-panel/2.2-results-panel.md |
| 03 | Tab Bar & Document Lifecycle | Specified | 03-document-management/3.1-tab-bar/3.1-tab-bar.md |
| 04 | Sidebar & File Organization | Specified | 04-file-organization/4.1-sidebar/4.1-sidebar.md |
| 04 | Templates | Specified | 04-file-organization/4.2-templates/4.2-templates.md |
| 05 | Theme System | Specified | 05-theming/5.1-theme-system/5.1-theme-system.md |
| 06 | Mobile Experience | Specified | 06-mobile-experience/6.1-mobile-shell/6.1-mobile-shell.md |
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,558 @@
# 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_

View File

@@ -0,0 +1,124 @@
# 1.2 — Status Bar
**Previous Step:** ← [App Shell](../1.1-app-shell/1.1-app-shell.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 01 — Workspace Shell |
| **Page Number** | 1.2 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Persistent UI Strip (embedded in App Shell) |
| **Viewport** | All breakpoints |
---
## Overview
**Page Purpose:** Provide at-a-glance contextual information about the current document, engine status, and workspace state. Acts as a persistent information bar at the bottom of the workspace.
**User Situation:** User is working in the editor and glances down for context — current line/column, whether the engine is ready, what theme is active, and document stats.
**Success Criteria:**
- Information readable at a glance without interrupting flow
- No interactive elements that accidentally trigger (info-only, with 2 clickable shortcuts)
- Consistent across all themes
---
## Layout Structure
```
┌──────────────────────────────────────────────────────────────┐
│ [Ln 12, Col 8] [42 lines] │ [Engine ● Ready] [Dark 🎨] │
│ ← left-aligned info │ right-aligned info → │
└──────────────────────────────────────────────────────────────┘
```
Height: 24px. Full width. Background: var(--bg-secondary). Border-top: 1px solid var(--border).
---
## Spacing
| Property | Token | Pixels |
|----------|-------|--------|
| Height | — | 24px |
| Padding horizontal | space-md | 12px |
| Item gap | space-md | 12px |
| Dot size (engine status) | — | 6px |
| Dot margin-right | space-2xs | 4px |
---
## Typography
| Element | Size | Weight | Typeface | Color |
|---------|------|--------|----------|-------|
| All status text | text-3xs | 400 | system mono | var(--text) |
| Engine status (ready) | text-3xs | 400 | system mono | var(--success) |
| Engine status (loading) | text-3xs | 400 | system mono | var(--warning) |
| Theme name | text-3xs | 500 | system mono | var(--text) |
---
## Status Items
### Left Group
**OBJECT ID:** `statusbar-left`
| Item | Content | Update Trigger |
|------|---------|----------------|
| Cursor position | `Ln {line}, Col {col}` | Cursor movement |
| Line count | `{n} lines` | Document change |
| Selection (conditional) | `{n} selected` | When text selected |
### Right Group
**OBJECT ID:** `statusbar-right`
| Item | Content | Behavior |
|------|---------|----------|
| Engine status | `● Ready` (green dot) or `◌ Loading...` (amber, pulse) | Auto-updates on engine state change |
| Theme indicator | `{Theme Name} 🎨` | Click → opens theme picker (same as header button) |
| Offline (conditional) | `📡 Offline` | Only visible when offline |
---
## States
| State | Engine Indicator | Additional |
|-------|-----------------|------------|
| Ready | ● green dot, "Ready" | — |
| Loading | ◌ amber dot, pulse animation, "Loading..." | — |
| Error | ● red dot, "Engine error" | Tooltip with error message |
| Offline | ● green dot, "Ready" | + "📡 Offline" appended |
---
## Responsive
| Breakpoint | Behavior |
|------------|----------|
| >= 768px | Full status bar, all items visible |
| < 768px | Simplified: cursor position + engine dot only. Theme and line count hidden. |
---
## Technical Notes
- Status bar reads from editor state (cursor, selection, line count) and engine hook (ready, error)
- Theme indicator is the only clickable element keeps status bar non-disruptive
- On macOS/Windows: maps to native status bar or window title bar info
---
**Previous Step:** [App Shell](../1.1-app-shell/1.1-app-shell.md)
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,302 @@
# 2.1 — Editor
**Next Step:** → [Results Panel](../2.2-results-panel/2.2-results-panel.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 02 — Calculation Experience |
| **Page Number** | 2.1 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Embedded Panel (within App Shell main area) |
| **Viewport** | All breakpoints |
| **Interaction** | Keyboard-primary, mouse secondary |
---
## Overview
**Page Purpose:** The editor is where calculations happen. Users type natural-language math expressions, define variables, add comments, and organize their thinking. It must feel like a fast, responsive text editor — not a calculator widget.
**User Situation:** User is actively thinking through numbers. They're writing a budget, converting units, figuring out a mortgage, or doing quick math. The editor must never get in their way — every keystroke should feel instant.
**Success Criteria:**
- Typing latency < 16ms (60fps)
- Results update within 50ms of pause (debounce)
- Syntax highlighting aids comprehension without distraction
- Errors are visible but non-intrusive
- Visual hierarchy makes 50-line documents scannable
**Entry Points:**
- Opening a document (tab click, sidebar double-click, new document)
- Returning to active document
**Exit Points:**
- Switching tabs (editor content swaps)
- Closing document
---
## Reference Materials
**Existing Implementation:**
- `calcpad-web/src/editor/CalcEditor.tsx` Current CodeMirror wrapper
- `calcpad-web/src/editor/calcpad-language.ts` Syntax highlighting
- `calcpad-web/src/editor/error-display.ts` Error underlines + gutter
- `calcpad-web/src/editor/inline-results.ts` Zebra striping
**Design System:**
- [Spacing Scale](../../../D-Design-System/00-design-system.md#spacing-scale)
- [Type Scale](../../../D-Design-System/00-design-system.md#type-scale)
---
## Layout Structure
```
┌──────────────────────────────────────────┐
│ [Gutter] [Line Content] │
│ │
│ 1 │ # Monthly Budget │ ← heading (bold, larger)
│ 2 │ │ ← empty line
│ 3 │ // Income │ ← comment (muted)
│ 4 │ salary = 5000 │ ← variable assignment
│ 5 │ freelance = 1200 │ ← variable assignment
│ 6 │ total_income = salary + freelance │ ← expression
│ 7 │ │
│ 8 │ // Expenses │ ← comment
│ 9 │ rent = 1500 │
│ 10 │ groceries = 400 │
│ 11 │ utilities = rent * 5% ̰ ̰ ̰ ̰ │ ← error underline
│ 12 │ │
│ 13 │ total_expenses = sum │ ← aggregator
│ 14 │ savings = total_income - total_exp │
│ │ │
│ │ │
└──────────────────────────────────────────┘
Zebra striping on alternating lines
```
---
## Spacing
| Property | Token | Pixels |
|----------|-------|--------|
| Content padding top/bottom | space-sm | 8px |
| Line padding horizontal | space-md | 12px |
| Gutter width | | 40px (auto-expands for > 999 lines) |
| Gutter padding right | space-xs | 6px |
| Line height | — | 24px (15px font × 1.6) |
| Error gutter width | — | 20px |
**Change from current:** Reduced line padding from 16px → 12px to match macOS's tighter feel.
---
## Typography
| Element | Size | Weight | Typeface | Color |
|---------|------|--------|----------|-------|
| Line content | text-md (15px) | 400 | system mono | var(--text) |
| Headings (# lines) | text-md (15px) | 700 | system mono | var(--text-h) |
| Comments (// lines) | text-md (15px) | 400 italic | system mono | var(--text) at 50% opacity |
| Variable names | text-md (15px) | 400 | system mono | var(--syntax-variable) |
| Numbers | text-md (15px) | 400 | system mono | var(--syntax-number) |
| Operators | text-md (15px) | 400 | system mono | var(--syntax-operator) |
| Keywords | text-md (15px) | 500 | system mono | var(--syntax-keyword) |
| Functions | text-md (15px) | 400 | system mono | var(--syntax-function) |
| Currency symbols | text-md (15px) | 400 | system mono | var(--syntax-currency) |
| Line numbers (gutter) | text-xs (13px) | 400 | system mono | var(--text) at 40% opacity |
| Active line number | text-xs (13px) | 600 | system mono | var(--text) |
---
## Syntax Highlighting Tokens
New CSS custom properties for syntax colors, per-theme:
| Token | Light | Dark | Matrix |
|-------|-------|------|--------|
| --syntax-variable | #4f46e5 (indigo-600) | #a5b4fc (indigo-300) | #00ff41 |
| --syntax-number | #0d9488 (teal-600) | #5eead4 (teal-300) | #00cc33 |
| --syntax-operator | #6b6375 (text) | #9ca3af (text) | #00ff41 |
| --syntax-keyword | #7c3aed (violet-600) | #c4b5fd (violet-300) | #39ff14 |
| --syntax-function | #2563eb (blue-600) | #93c5fd (blue-300) | #00ff41 |
| --syntax-currency | #d97706 (amber-600) | #fcd34d (amber-300) | #ffff00 |
| --syntax-comment | rgba(text, 0.5) | rgba(text, 0.5) | rgba(#00ff41, 0.4) |
| --syntax-heading | var(--text-h) | var(--text-h) | #00ff41 |
| --syntax-error | #e53e3e | #fc8181 | #ff0000 |
---
## Visual Hierarchy Improvements
### Headings (`# lines`)
| Property | Value |
|----------|-------|
| Font weight | 700 (bold) |
| Color | var(--text-h) — strongest text color |
| Top margin | 8px extra (visual section break) — only if preceded by non-empty line |
| Bottom margin | 0 (heading belongs to content below) |
| Background | None (clean) |
### Comments (`// lines`)
| Property | Value |
|----------|-------|
| Font style | Italic |
| Opacity | 50% of text color |
| No background stripe | Comments skip zebra striping (visually distinct already) |
### Empty Lines
| Property | Value |
|----------|-------|
| Height | 24px (same as content lines) |
| Background | Normal zebra stripe pattern |
| Purpose | Visual breathing room, section separators |
### Active Line
| Property | Value |
|----------|-------|
| Background | var(--accent-bg) — subtle accent tint |
| Gutter | Line number bold + full opacity |
| Transition | background 0.1s |
---
## Error Display
### Error Underline
**OBJECT ID:** `editor-error-underline`
| Property | Value |
|----------|-------|
| Style | wavy underline |
| Color | var(--syntax-error) |
| Thickness | 1.5px |
| Scope | Underlines the specific token/expression that errored |
### Error Gutter Marker
**OBJECT ID:** `editor-error-gutter`
| Property | Value |
|----------|-------|
| Icon | ⚠ (warning triangle) |
| Size | 14px |
| Color | var(--syntax-error) |
| Position | Error gutter column (20px wide, left of line numbers) |
| Hover | Tooltip with error message text |
### Error Tooltip
**OBJECT ID:** `editor-error-tooltip`
| Property | Value |
|----------|-------|
| Trigger | Hover over error gutter marker OR underlined text |
| Background | var(--bg-secondary) |
| Border | 1px solid var(--border) |
| Border-radius | 4px |
| Padding | 4px 8px |
| Font | text-xs, system sans, var(--syntax-error) |
| Shadow | 0 2px 8px rgba(0,0,0,0.15) |
| Max width | 300px |
| Position | Below the error line, left-aligned with gutter |
---
## Zebra Striping
| Property | Value |
|----------|-------|
| Pattern | Even-numbered lines (matching current implementation) |
| Light mode | rgba(0, 0, 0, 0.02) — reduced from 0.025 |
| Dark mode | rgba(255, 255, 255, 0.025) — reduced from 0.035 |
| Matrix mode | rgba(0, 255, 65, 0.03) — green tint |
| Skip on | Comment lines (already visually distinct) |
---
## Autocomplete
**OBJECT ID:** `editor-autocomplete`
| Property | Value |
|----------|-------|
| Trigger | Typing 2+ characters that match a variable, function, or keyword |
| Panel | Dropdown below cursor, var(--bg) bg, 1px border, 4px radius |
| Max items | 8 visible, scroll for more |
| Item height | 28px |
| Active item | var(--accent-bg) highlight |
| Categories | Variables (with last value), Functions (with signature), Keywords, Units, Currencies |
| Keyboard | ↑↓ to navigate, Tab/Enter to accept, Esc to dismiss |
| Auto-dismiss | On cursor movement away |
---
## Page States
| State | When | Behavior |
|-------|------|----------|
| **Active editing** | User is typing | Debounced eval (50ms). Results update. Auto-save (500ms). |
| **Idle** | User paused | All results current. Document saved. |
| **Read-only** | Template preview (future) | No cursor, no editing. Gray overlay on gutter. |
| **Engine loading** | WASM initializing | Editor is fully editable. Results show "—" until engine ready. |
| **Large document** | > 500 lines | Viewport rendering only (CodeMirror handles this). Performance warning in status bar if > 1000 lines. |
---
## Interactions
| Action | Behavior |
|--------|----------|
| Type expression | Debounced eval → results update in 50ms |
| Define variable (`x = 5`) | Variable registered, available for autocomplete on subsequent lines |
| Reference variable | Autocomplete suggests matching variables with their current values |
| Use aggregator (`sum`, `total`) | Aggregates all numeric results above (up to previous heading or empty line block) |
| Line reference (`#3`) | References result of line 3 |
| Comment (`// text`) | Line excluded from evaluation, styled as comment |
| Heading (`# text`) | Section header, not evaluated, used for visual grouping and aggregator scoping |
| Select text | Selection count shown in status bar |
| Cmd/Ctrl+Z | Undo (per-document history preserved while tab is open) |
| Cmd/Ctrl+Shift+Z | Redo |
| Cmd/Ctrl+D | Duplicate current line |
| Cmd/Ctrl+/ | Toggle comment on current line |
| Alt+↑/↓ | Move line up/down |
---
## Technical Notes
- **CodeMirror instance management:** One instance per active tab. Inactive tabs store EditorState (preserves undo history). On tab switch, create new EditorView with stored state.
- **Eval debounce:** 50ms (current). Consider making configurable in settings (0200ms range).
- **Syntax highlighting performance:** StreamLanguage parser runs synchronously — fine for < 1000 lines. For very large documents, consider switching to Lezer grammar.
- **Font scaling:** Expose font size setting (1224px range) in settings. Current 15px is good default. Store in localStorage.
- **Cross-platform:** CodeMirror handles keyboard differences (Cmd vs Ctrl). Same extension stack works everywhere via WASM.
---
## Open Questions
| # | Question | Context | Status |
|---|----------|---------|--------|
| 1 | Should headings have extra top margin? | Creates visual sections but breaks 1:1 line alignment with results panel | 🟡 In Discussion likely yes, results panel adjusts |
| 2 | Should comments skip zebra striping? | Makes them more visually distinct but breaks the pattern | 🔴 Open |
| 3 | Font size as user preference? | 1224px range slider in settings | 🟢 Resolved: Yes, default 15px |
---
**Next Step:** [Results Panel](../2.2-results-panel/2.2-results-panel.md)
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,302 @@
# 2.2 — Results Panel
**Previous Step:** ← [Editor](../2.1-editor/2.1-editor.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 02 — Calculation Experience |
| **Page Number** | 2.2 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Embedded Panel (within App Shell main area) |
| **Viewport** | Desktop + tablet (side panel), mobile (bottom tray) |
---
## Overview
**Page Purpose:** Display calculation results aligned line-by-line with the editor. Results are the reason the product exists — they must be clear, informative, and subtly styled so they complement the editor without competing for attention.
**User Situation:** User is typing in the editor and their eyes flick right to see results. This happens dozens of times per session. The results must be instantly scannable — the eye should find the answer in milliseconds.
**Success Criteria:**
- Every result line aligns pixel-perfectly with its editor line
- Results are readable but visually secondary to the editor
- Different result types are distinguishable at a glance
- Scroll synchronization is seamless
**Entry Points:**
- Always visible on desktop/tablet (side panel)
- Toggle on mobile (bottom tray)
---
## Layout Structure
### Desktop/Tablet (side panel)
```
┌──────────────────────────┐
│ │
│ ──── │ ← heading (no result)
│ │ ← empty
│ ──── │ ← comment (no result)
│ 5,000 │ ← number
│ 1,200 │ ← number
│ 6,200 │ ← expression result
│ │ ← empty
│ ──── │ ← comment
│ 1,500 │ ← number
│ 400 │ ← number
│ ⚠ error │ ← error (muted, not red)
│ │ ← empty
│ 1,900 │ ← aggregator result
│ 4,300 │ ← expression result
│ │
└──────────────────────────┘
Right-aligned, zebra striping matches editor
```
### Mobile (bottom tray)
```
┌─────────────────────────────┐
│ ═══ (drag handle) │ 48px collapsed
│ Last result: 4,300 │
├─────────────────────────────┤
│ Ln 5: salary = 5000 │ ← expanded: shows
│ Ln 6: freelance = 1200 │ line + result pairs
│ Ln 7: total_income = 6200 │ scrollable
│ ... │
└─────────────────────────────┘
```
---
## Spacing
| Property | Token | Pixels |
|----------|-------|--------|
| Content padding top/bottom | space-sm | 8px (matches editor) |
| Result line padding horizontal | space-md | 12px (matches editor) |
| Result line height | — | 24px (matches editor exactly) |
| Mobile tray collapsed height | — | 48px |
| Mobile tray expanded height | — | 40vh |
| Mobile drag handle | — | 32px × 4px pill |
---
## Typography
| Element | Size | Weight | Typeface | Color |
|---------|------|--------|----------|-------|
| Numeric result | text-md (15px) | 400 | system mono | var(--result-number) |
| Unit value | text-md (15px) | 400 | system mono | var(--result-unit) |
| Currency value | text-md (15px) | 400 | system mono | var(--result-currency) |
| DateTime result | text-md (15px) | 400 | system mono | var(--result-datetime) |
| Boolean result | text-md (15px) | 400 | system mono | var(--result-boolean) |
| Error hint | text-xs (13px) | 400 | system sans | var(--text) at 30% opacity |
| Non-result lines | — | — | — | Empty (no text rendered) |
**Design change from current:** Weight reduced from 600 → 400. Color changed from var(--accent) to type-specific semantic colors. This makes results secondary to the editor content, matching macOS behavior.
---
## Result Type Colors
New CSS custom properties per theme:
| Token | Light | Dark | Matrix | Purpose |
|-------|-------|------|--------|---------|
| --result-number | #374151 (gray-700) | #d1d5db (gray-300) | #00ff41 | Plain numbers |
| --result-unit | #0d9488 (teal-600) | #5eead4 (teal-300) | #00cc33 | Values with units (5 kg, 3.2 m) |
| --result-currency | #d97706 (amber-600) | #fcd34d (amber-300) | #ffff00 | Currency values ($50, €42) |
| --result-datetime | #7c3aed (violet-600) | #c4b5fd (violet-300) | #39ff14 | Dates and times |
| --result-boolean | #6366f1 (indigo-500) | #818cf8 (indigo-400) | #00ff41 | true/false |
| --result-error | var(--text) at 30% | var(--text) at 30% | rgba(#ff0000, 0.3) | Error hint text |
**Why type-specific colors:** Users scanning results can instantly distinguish "that's a currency" from "that's a unit" from "that's a date" without reading the value. The colors are muted (not saturated) so they don't compete with the editor.
---
## Result Display Format
| Result Type | Display Format | Example |
|-------------|----------------|---------|
| Number | Formatted with thousand separators | `6,200` |
| Unit value | Value + unit abbreviation | `2.2 kg` · `156.2 mi` |
| Currency | Symbol + value | `$4,300` · `€3,857.20` |
| DateTime | Locale-formatted | `Mar 25, 2026` · `14:30` |
| TimeDelta | Human-readable | `3 days, 4 hours` |
| Boolean | Lowercase | `true` · `false` |
| Comment | Empty line (dash marker) | `────` (subtle horizontal line) |
| Heading | Empty line (dash marker) | `────` |
| Empty | Empty line | (blank) |
| Error | Muted hint | `· error` (tiny, de-emphasized) |
| Variable assignment | Show assigned value | `5,000` |
**Change from current:** Removed the `= ` prefix before results. Just show the value. Cleaner.
### Comment/Heading Marker
| Property | Value |
|----------|-------|
| Content | `────` (4 em dashes, or CSS border-bottom) |
| Color | var(--border) at 50% opacity |
| Purpose | Visual separator showing this line has no numeric result |
| Width | 60% of panel width, right-aligned |
### Error Hint
| Property | Value |
|----------|-------|
| Content | `· error` or `· invalid` |
| Color | var(--text) at 30% opacity |
| Purpose | Subtle indicator that something went wrong — details are in the editor gutter |
| Font | text-xs (13px), system sans |
---
## Zebra Striping
| Property | Value |
|----------|-------|
| Pattern | Matches editor exactly (same even-line pattern) |
| Colors | Same as editor per theme |
| Sync | Uses line index, not DOM position, for consistency |
---
## Scroll Synchronization
| Property | Value |
|----------|-------|
| Direction | Editor drives, results follows |
| Method | `scrollTop` mirroring via passive scroll listener |
| Latency | < 1 frame (requestAnimationFrame if needed) |
| Results panel | `overflow-y: hidden` no independent scrolling |
| Edge case | If editor has heading margins (extra space), results panel inserts matching spacers |
---
## Result Hover Interaction
**OBJECT ID:** `results-hover`
| Property | Value |
|----------|-------|
| Trigger | Mouse hover over a result line |
| Behavior | Show full precision + unit details in tooltip |
| Tooltip content | Raw value (full precision), type label, conversion hint |
| Example | Hover `$4,300` "4300.00 USD (United States Dollar)" |
| Example | Hover `2.2 kg` "2.20462 kg (kilogram) · 4.85 lb" |
| Style | Same as error tooltip (bg-secondary, border, 4px radius) |
| Delay | 500ms hover delay (don't trigger on casual mouse movement) |
---
## Result Click Interaction
**OBJECT ID:** `results-click`
| Property | Value |
|----------|-------|
| Trigger | Single click on a result value |
| Behavior | Copy raw value to clipboard |
| Feedback | Brief flash (0.3s) result text turns var(--success) then fades back |
| Tooltip | "Copied!" appears for 1.5s |
| Accessibility | aria-label="Copy result: {value}" |
---
## Mobile Bottom Tray
**OBJECT ID:** `results-mobile-tray`
### Collapsed State (48px)
| Property | Value |
|----------|-------|
| Content | Drag handle + last non-empty result value |
| Drag handle | 32px × 4px pill, var(--border), centered |
| Result text | "Last: {value}" or "No results" if empty |
| Font | text-xs, var(--text) |
| Background | var(--bg-secondary) |
| Border | top 1px solid var(--border) |
| Interaction | Tap or swipe up expand |
### Expanded State (40vh)
| Property | Value |
|----------|-------|
| Content | Scrollable list of all line results paired with their expressions |
| Item format | `Ln {n}: {expression} → {result}` |
| Item height | 36px (larger for touch) |
| Active line | Highlighted with var(--accent-bg) |
| Tap result | Copy to clipboard (same as desktop click) |
| Interaction | Swipe down collapse. Tap backdrop collapse. |
| Scroll | Independent scroll (not synced with editor in mobile) |
### Transitions
| Property | Value |
|----------|-------|
| Expand/collapse | 200ms ease-out |
| Spring | Optional subtle overshoot on expand |
---
## Page States
| State | When | Results Display |
|-------|------|-----------------|
| **Normal** | Engine ready, results computed | Type-colored values per line |
| **Engine loading** | WASM initializing | All result lines show `—` in var(--text) at 20% |
| **Empty document** | No lines in editor | Panel is blank |
| **All errors** | Every line has errors | All lines show muted `· error` hints |
| **Stale results** | Document changed, eval pending | Previous results stay visible (no flash/flicker) |
---
## Accessibility
| Feature | Implementation |
|---------|---------------|
| ARIA | `role="complementary"`, `aria-label="Calculation results"` |
| Live updates | `aria-live="polite"` on result container announces new results |
| Screen reader | Each result: `aria-label="Line {n}: {expression} equals {result}"` |
| Color-blind | Result types distinguishable by position + format, not just color |
| Click feedback | `aria-label="Copied"` announced on clipboard copy |
| Reduced motion | No flash animation; instant color change for copy feedback |
---
## Technical Notes
- **Result alignment:** Line height must match editor exactly (24px). If editor adds heading margins, results panel must add matching spacer divs.
- **Rendering:** ResultsPanel receives `EngineLineResult[]` from useEngine hook. Re-renders only changed lines (React key by line index).
- **Copy to clipboard:** Use `navigator.clipboard.writeText()`. Fall back to textarea trick for older browsers.
- **Hover tooltip positioning:** Position below the result line, right-aligned with panel. Flip above if near viewport bottom.
- **Mobile tray:** Use CSS `transform: translateY()` for smooth expand/collapse. Touch events for swipe gesture.
- **Cross-platform:** Side panel native split view (macOS/Windows). Mobile tray not applicable on desktop native.
---
## Open Questions
| # | Question | Context | Status |
|---|----------|---------|--------|
| 1 | Should heading lines in results show `────` markers? | Helps visual alignment but adds visual noise | 🔴 Open |
| 2 | Should copy-on-click copy formatted or raw value? | `$4,300` vs `4300` | 🟡 Likely raw (more useful for pasting) |
| 3 | Result hover tooltip show conversion alternatives? | "2.2 kg · 4.85 lb" on hover | 🟢 Resolved: Yes, useful for unit/currency results |
---
**Previous Step:** [Editor](../2.1-editor/2.1-editor.md)
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,257 @@
# 3.1 — Tab Bar & Document Lifecycle
**Next Step:** → [Sidebar](../../04-file-organization/4.1-sidebar/4.1-sidebar.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 03 — Document Management |
| **Page Number** | 3.1 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Embedded Strip (within App Shell, between header and editor) |
---
## Overview
**Page Purpose:** Enable multi-document workflow. Users can open, create, close, reorder, and rename documents via tabs. The tab bar is the primary navigation between open documents.
**Success Criteria:**
- Switching tabs feels instant (< 50ms)
- Users never lose work (auto-save before switch)
- Tab state survives page reload
- 15+ tabs remain usable (horizontal scroll)
---
## Layout Structure
```
┌──────────────────────────────────────────────────────────┐
│ [● Budget ×] [ Invoice ×] [ Unit Conv ×] [+] ··· │ 36px
└──────────────────────────────────────────────────────────┘
● = unsaved changes dot
Active tab: connected to editor (no bottom border)
[+] = new document button
··· = overflow gradient when scrollable
```
---
## Spacing
| Property | Token | Pixels |
|----------|-------|--------|
| Tab bar height | | 36px |
| Tab padding horizontal | space-sm | 8px |
| Tab gap | | 0px (tabs are flush, separated by 1px border) |
| Tab min width | | 100px |
| Tab max width | | 200px |
| Close button size | | 16px × 16px |
| Close button margin-left | space-xs | 6px |
| Modified dot size | | 6px |
| Modified dot margin-right | space-2xs | 4px |
| New tab button width | | 36px (square) |
---
## Typography
| Element | Size | Weight | Color |
|---------|------|--------|-------|
| Tab label (active) | text-xs | 500 | var(--text-h) |
| Tab label (inactive) | text-xs | 400 | var(--text) |
| Close × | text-xs | 400 | var(--text) at 50%, hover var(--text) |
| New + icon | text-sm | 300 | var(--text), hover var(--accent) |
---
## Tab States
### Active Tab
| Property | Value |
|----------|-------|
| Background | var(--bg) same as editor, creates visual connection |
| Border bottom | None tab "opens into" the editor |
| Border left/right | 1px solid var(--border) |
| Border top | 2px solid var(--accent) active indicator |
| Label | Weight 500, var(--text-h) |
| Close button | Always visible |
### Inactive Tab
| Property | Value |
|----------|-------|
| Background | var(--bg-secondary) |
| Border bottom | 1px solid var(--border) closed off from editor |
| Border left/right | 1px solid var(--border) |
| Border top | 2px solid transparent |
| Label | Weight 400, var(--text) |
| Close button | Visible on hover only |
### Hover (Inactive)
| Property | Value |
|----------|-------|
| Background | Blend between bg-secondary and bg (subtle lighten) |
| Transition | background 0.1s |
### Dragging
| Property | Value |
|----------|-------|
| Appearance | Tab lifts with subtle shadow (0 2px 8px rgba(0,0,0,0.15)) |
| Opacity | 90% |
| Placeholder | 2px var(--accent) vertical line at insertion point |
| Cursor | grabbing |
---
## Interactions
| Action | Behavior |
|--------|----------|
| **Click tab** | Switch to document. Auto-save current. Restore editor state (content, cursor, scroll, undo). |
| **Click +** | Create "Untitled" doc, open in new tab, focus editor. |
| **Click ×** | Close tab. If unsaved and modified, no prompt (auto-saved). Remove from openTabIds. |
| **Middle-click tab** | Close tab (same as ×). |
| **Double-click tab** | Inline rename label becomes input field, Enter confirms, Esc cancels. |
| **Drag tab** | Reorder. Drop position shown by accent line indicator. |
| **Ctrl/Cmd+Tab** | Next tab (wraps). |
| **Ctrl/Cmd+Shift+Tab** | Previous tab (wraps). |
| **Ctrl/Cmd+W** | Close active tab. If last tab, create new "Untitled". |
| **Ctrl/Cmd+N** | New document + tab. |
| **Ctrl/Cmd+19** | Jump to tab by position. |
| **Mouse wheel on tab bar** | Horizontal scroll when tabs overflow. |
---
## Tab Overflow
| Property | Value |
|----------|-------|
| Trigger | Total tab width > container width |
| Scroll | Horizontal, smooth, via mouse wheel or trackpad |
| Indicators | 16px fade gradient on left/right edges when scrollable |
| Active tab | Auto-scrolls into view when selected via keyboard |
| New tab button | Sticky right — always visible outside scroll area |
---
## Document Lifecycle
### Create
| Trigger | Behavior |
|---------|----------|
| Click [+] | New doc: `{ id: uuid(), title: "Untitled", content: "", folderId: null }` |
| Sidebar template click | New doc with template content and suggested title |
| Ctrl/Cmd+N | Same as [+] |
Title auto-increments: "Untitled", "Untitled 2", "Untitled 3"...
### Rename
| Trigger | Behavior |
|---------|----------|
| Double-click tab | Label becomes `<input>`, pre-selected text, 200px max width |
| Sidebar right-click → Rename | Same inline editing in sidebar |
| Enter | Confirm rename, update document and sidebar |
| Esc | Cancel, revert to previous name |
| Blur | Confirm (same as Enter) |
| Empty name | Revert to "Untitled" |
### Delete
| Trigger | Behavior |
|---------|----------|
| Sidebar right-click → Delete | Confirmation: "Delete '{title}'? This cannot be undone." |
| Confirm | Remove from documents[], close tab if open, remove from folder |
| Cancel | No action |
| Undo | 5-second toast: "Document deleted. [Undo]" — restores document if clicked |
### Duplicate
| Trigger | Behavior |
|---------|----------|
| Sidebar right-click → Duplicate | New doc: same content, title = "{original} (copy)", same folder |
| Opens in new tab automatically |
### Auto-Save
| Property | Value |
|----------|-------|
| Trigger | Document content change |
| Debounce | 500ms after last keystroke |
| Storage | localStorage (calctext-documents) |
| Indicator | Modified dot (●) appears immediately on change, disappears on save |
| Manual save | Ctrl/Cmd+S shows brief checkmark animation in tab (visual confirmation) |
---
## Modified Indicator
| Property | Value |
|----------|-------|
| Shape | Filled circle, 6px |
| Color | var(--text) at 60% |
| Position | Before tab label, 4px gap |
| Appears | On first character change |
| Disappears | On auto-save completion (500ms debounce) |
| Animation | Fade in 0.2s |
---
## Context Menu (Right-Click Tab)
| Item | Action |
|------|--------|
| Close | Close this tab |
| Close Others | Close all tabs except this one |
| Close to the Right | Close all tabs to the right |
| — | (separator) |
| Rename | Inline rename |
| Duplicate | Duplicate document |
| — | (separator) |
| Reveal in Sidebar | Scroll sidebar to show this file |
---
## Mobile Adaptations
| Property | Value |
|----------|-------|
| Tab bar | Horizontal scroll, touch-friendly |
| Tab height | 40px (larger touch target) |
| Close button | Hidden — swipe left on tab to reveal close |
| New tab | [+] button at far right |
| Rename | Long-press → context menu → Rename |
| Reorder | Long-press + drag |
---
## Page States
| State | When | Behavior |
|-------|------|----------|
| **Single tab** | Only one document open | Tab still shown (establishes pattern). Close creates new "Untitled". |
| **Many tabs (>10)** | Heavy usage | Horizontal scroll. Active tab auto-scrolls into view. |
| **All tabs closed** | User closed everything | Auto-create "Untitled" tab (workspace never empty). |
| **First launch** | No localStorage | Single "Welcome" tab with demo content. |
---
## Technical Notes
- **Tab switching performance:** Store `EditorState` per tab in memory. On switch: destroy current EditorView, create new with stored state. Content swap < 20ms.
- **Tab order persistence:** `openTabIds: string[]` in localStorage maintains order.
- **Cross-platform:** Maps to native tab bars. macOS: NSTabView or custom tab strip. Windows: iced tabs widget.
- **Max tabs:** Soft limit at 20 with performance hint in status bar. No hard limit.
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,328 @@
# 4.1 — Sidebar & File Organization
**Previous Step:** ← [Tab Bar](../../03-document-management/3.1-tab-bar/3.1-tab-bar.md)
**Next Step:** → [Templates](../4.2-templates/4.2-templates.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 04 — File Organization |
| **Page Number** | 4.1 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Collapsible Side Panel (within App Shell) |
---
## Overview
**Page Purpose:** Organize and navigate calctext documents. The sidebar provides a persistent file tree with folders, recent files, favorites, and templates — transforming CalcText from a single-use calculator into a workspace where calculations are organized and retrievable.
**Success Criteria:**
- Users find any document in < 3 seconds
- Folder hierarchy is intuitive (create, nest, rename, delete)
- Recent and Favorites provide quick access without browsing
- Sidebar never feels cluttered even with 50+ documents
---
## Layout Structure
```
┌──────────────────────┐
│ 🔍 Search... │ 32px — search bar
├──────────────────────┤
│ │
│ ▸ Recent │ section header (collapsible)
│ 📄 Budget │
│ 📄 Quick Math │
│ 📄 Invoice #42 │
│ │
│ ▸ Favorites │ section header
│ ⭐ Monthly Budget │
│ ⭐ Tax Calculator │
│ │
│ ▾ Files │ section header (expanded)
│ 📁 Work │ folder
│ │ 📄 Budget │ file in folder
│ │ 📄 Invoice │
│ 📁 Personal │ folder
│ │ 📁 Travel │ nested folder
│ │ │ 📄 Trip Cost │
│ 📄 Scratch │ root-level file
│ │
│ ▸ Templates │ section header
│ │
├──────────────────────┤
│ [+ Doc] [+ Folder] │ sticky footer
└──────────────────────┘
```
---
## Spacing
| Property | Token | Pixels |
|----------|-------|--------|
| Sidebar padding top | space-xs | 6px |
| Search bar height | | 32px |
| Search bar margin | space-xs | 6px all sides |
| Section header height | | 28px |
| Section header padding left | space-sm | 8px |
| File item height | | 28px |
| File item padding left (root) | space-md | 12px |
| File item indent per depth | | 16px |
| File icon size | | 16px |
| File icon-to-label gap | space-xs | 6px |
| Section gap | space-xs | 6px |
| Footer height | | 40px |
| Footer padding | space-xs | 6px |
---
## Typography
| Element | Size | Weight | Color |
|---------|------|--------|-------|
| Search placeholder | text-xs | 400 | var(--text) at 50% |
| Section header | text-2xs | 600 | var(--text) at 70% |
| File name | text-xs | 400 | var(--text) |
| File name (active) | text-xs | 500 | var(--text-h) |
| Folder name | text-xs | 500 | var(--text) |
| Footer buttons | text-2xs | 400 | var(--text), hover var(--accent) |
| File count badge | text-3xs | 400 | var(--text) at 40% |
---
## Search Bar
**OBJECT ID:** `sidebar-search`
| Property | Value |
|----------|-------|
| Placeholder | "Search documents..." |
| Background | var(--bg) |
| Border | 1px solid var(--border), focus var(--accent-border) |
| Border radius | 4px |
| Icon | 🔍 magnifier, 14px, var(--text) at 40% |
| Padding | 4px 8px 4px 28px (icon offset) |
| Behavior | Filters file tree in real-time as user types |
| Clear | × button appears when text entered |
| Keyboard | Ctrl/Cmd+P opens/focuses search (like VS Code quick open) |
| Results | Flat list of matching files, ranked by recency. Highlights matching text. |
| Empty state | "No documents match '{query}'" |
---
## Section: Recent
**OBJECT ID:** `sidebar-recent`
| Property | Value |
|----------|-------|
| Content | Last 5 opened documents, sorted by lastOpened timestamp |
| Collapsible | Yes, chevron toggle |
| Default state | Expanded on first launch, remembers toggle |
| Item display | File icon + name only (no folder path) |
| Empty state | "No recent documents" in text-3xs, muted |
| Update trigger | Opening any document pushes it to top, bumps oldest |
---
## Section: Favorites
**OBJECT ID:** `sidebar-favorites`
| Property | Value |
|----------|-------|
| Content | User-pinned documents, ordered manually (drag) |
| Collapsible | Yes |
| Default state | Collapsed if empty, expanded if has items |
| Item display | icon + name |
| Add to favorites | Right-click file "Add to Favorites", or drag file into section |
| Remove | Right-click "Remove from Favorites" |
| Empty state | "Drag files here or right-click Add to Favorites" |
---
## Section: Files (Tree)
**OBJECT ID:** `sidebar-files`
| Property | Value |
|----------|-------|
| Content | Complete folder tree with all documents |
| Collapsible | Yes (section level) |
| Default state | Expanded |
| Sort | Folders first, then files. Alphabetical within each group |
| Max depth | 3 levels (root folder subfolder files). Prevents over-nesting |
### Folder Item
**OBJECT ID:** `sidebar-folder`
| Property | Value |
|----------|-------|
| Icon | 📁 (closed) / 📂 (open) or chevron ▸/▾ |
| Click | Toggle expand/collapse |
| Double-click | Rename inline |
| Right-click | Context menu |
| Drag | Reorder within parent. Drop files into folder. |
| Drop target | Highlight with var(--accent-bg) + 2px dashed var(--accent) border |
| Badge | File count in parentheses: `Work (3)` text-3xs, muted |
### File Item
**OBJECT ID:** `sidebar-file`
| Property | Value |
|----------|-------|
| Icon | 📄 (default) could be themed per type later |
| Click | Open in tab (or switch to existing tab if already open) |
| Double-click | Open + rename inline |
| Hover | var(--accent-bg) background |
| Active | var(--accent-bg) + left 2px solid var(--accent) border |
| Active = | Currently open in active tab |
| Open indicator | Subtle dot or underline if open in any tab (even if not active) |
| Drag | Move between folders. Drag to tab bar to open. |
| Right-click | Context menu |
### File Context Menu
| Item | Action |
|------|--------|
| Open | Open in new tab |
| Open in New Tab | Open without closing current |
| | separator |
| Rename | Inline rename |
| Duplicate | Copy with "(copy)" suffix |
| Add to Favorites | Toggle |
| | separator |
| Move to... | Submenu with folder list |
| | separator |
| Delete | Confirm dialog 5-second undo toast |
### Folder Context Menu
| Item | Action |
|------|--------|
| New Document Here | Create file inside this folder |
| New Subfolder | Create nested folder (max depth 3) |
| | separator |
| Rename | Inline rename |
| | separator |
| Delete Folder | Must be empty. If not: "Move contents to root first." |
---
## Section: Templates
**OBJECT ID:** `sidebar-templates`
| Property | Value |
|----------|-------|
| Collapsible | Yes |
| Default state | Expanded on first launch |
| Content | Pre-built starting documents |
Full specification in [4.2 — Templates](../4.2-templates/4.2-templates.md).
---
## Sidebar Footer
**OBJECT ID:** `sidebar-footer`
| Property | Value |
|----------|-------|
| Position | Sticky bottom |
| Background | var(--bg-secondary) |
| Border top | 1px solid var(--border) |
| Layout | Two buttons side-by-side |
| Buttons | `[+ Document]` `[+ Folder]` ghost style |
| New Document | Creates at root level, opens in tab |
| New Folder | Creates at root level, inline rename active |
---
## Drag and Drop
| Drag Source | Drop Target | Behavior |
|-------------|-------------|----------|
| File | Folder | Move file into folder |
| File | Between files | Reorder within same folder |
| File | Tab bar | Open file in new tab |
| File | Favorites section | Add to favorites |
| Folder | Between folders | Reorder at same depth |
| Tab | Sidebar folder | Move document to folder |
### Drop Visual Feedback
| State | Appearance |
|-------|------------|
| Valid target hover | var(--accent-bg) background, 2px dashed var(--accent) border |
| Invalid target | No visual change (drop not accepted) |
| Insertion line | 2px solid var(--accent) horizontal line at insertion point |
| Dragging item | 60% opacity, subtle shadow |
---
## Resize Handle
| Property | Value |
|----------|-------|
| Position | Right edge of sidebar |
| Width | 1px visible, 8px hit area |
| Cursor | col-resize |
| Color | var(--border), hover/drag var(--accent) |
| Constraints | Min 180px, max 400px |
| Double-click | Reset to default 240px |
| Persistence | Width stored in localStorage |
---
## Responsive Behavior
| Breakpoint | Behavior |
|------------|----------|
| >= 1024px | Persistent side panel, resizable |
| 7681023px | Overlay drawer, hamburger toggle in header |
| < 768px | Full-screen drawer (85vw, max 320px) |
### Mobile Drawer
| Property | Value |
|----------|-------|
| Trigger | Hamburger menu (≡) in header |
| Width | 85vw, max 320px |
| Overlay | 50% black backdrop |
| Animation | Slide from left, 200ms ease-out |
| Close | Tap backdrop, swipe left, or × button top-right |
| File tap | Opens document, auto-closes drawer |
---
## Page States
| State | When | Behavior |
|-------|------|----------|
| **Empty** | No documents or folders | Show: "Welcome! Create your first document or pick a template." + prominent buttons |
| **Few files** (<5) | Early usage | All sections visible, Templates expanded to encourage exploration |
| **Many files** (>20) | Power user | Search becomes critical. Sections collapsed by default except Files |
| **Search active** | User typed in search | Tree replaced by flat filtered list. Sections hidden. |
| **Dragging** | File/folder being moved | Drop targets highlighted. Invalid areas dimmed. |
---
## Technical Notes
- **Virtual scrolling:** Not needed until 500+ items. Standard DOM rendering is fine for typical usage.
- **Folder persistence:** `folders: Folder[]` in localStorage with `parentId` for tree structure.
- **Sort stability:** Alphabetical sort is stable — user manual ordering within a folder stored as `order` field.
- **Cross-platform:** Maps to NSOutlineView (macOS), Tree widget (iced/Windows). Same data model.
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,219 @@
# 4.2 — Templates
**Previous Step:** ← [Sidebar](../4.1-sidebar/4.1-sidebar.md)
**Next Step:** → [Theme System](../../05-theming/5.1-theme-system/5.1-theme-system.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 04 — File Organization |
| **Page Number** | 4.2 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Sidebar Section + Modal (template preview) |
---
## Overview
**Page Purpose:** Provide ready-made starting documents that showcase CalcText's capabilities and help users get productive immediately. Templates are the product's best onboarding tool — they show by example.
**Success Criteria:**
- First-time user finds a relevant template within 10 seconds
- Templates demonstrate the product's unique features (variables, units, currencies, aggregators)
- Using a template creates a new document (never modifies the template)
---
## Template Library
| Template | Description | Showcases |
|----------|-------------|-----------|
| **Budget** | Monthly income/expenses with categories | Variables, aggregators (sum, total), percentages |
| **Invoice** | Service invoice with line items and tax | Variables, multiplication, percentages, currency |
| **Unit Converter** | Common conversions with examples | Unit expressions (kg to lb, km to mi, °C to °F) |
| **Trip Planner** | Travel budget with currency conversion | Currency conversion, date math, variables |
| **Loan Calculator** | Mortgage/loan with monthly payments | Financial functions, percentages, variables |
| **Blank** | Empty document | — (clean start) |
---
## Sidebar Templates Section
**OBJECT ID:** `sidebar-templates`
| Property | Value |
|----------|-------|
| Section header | "Templates" with 📋 icon |
| Collapsible | Yes |
| Default | Expanded on first launch, collapsed after first document created |
| Item height | 32px (slightly taller than files — more padding) |
| Item icon | Colored dot per template (visual distinction) |
| Item label | Template name, text-xs |
| Item sublabel | Brief description, text-3xs, muted, truncated |
### Template Item Interaction
| Action | Behavior |
|--------|----------|
| Click | Create new document from template. Title = template name. Opens in new tab. |
| Hover | Show full description in tooltip |
| Right-click | "Preview" option |
---
## Template Colors (Icon Dots)
| Template | Dot Color |
|----------|-----------|
| Budget | #10b981 (emerald) |
| Invoice | #6366f1 (indigo) |
| Unit Converter | #0d9488 (teal) |
| Trip Planner | #f59e0b (amber) |
| Loan Calculator | #7c3aed (violet) |
| Blank | var(--border) (gray) |
---
## Template Content
### Budget Template
```
# Monthly Budget
// Income
salary = 5000
freelance = 1200
total_income = salary + freelance
// Housing
rent = 1500
utilities = 150
insurance = 80
// Living
groceries = 400
transport = 120
subscriptions = 45
// Summary
total_expenses = sum
savings = total_income - total_expenses
savings_rate = savings / total_income
```
### Invoice Template
```
# Invoice #001
// Client: [Client Name]
// Date: [Date]
// Services
web_design = 2500
development = 4000
consulting = 150 * 8
// Expenses
hosting = 29.99
domain = 12.00
subtotal = sum
// Tax
tax_rate = 10%
tax = subtotal * tax_rate
total = subtotal + tax
```
### Unit Converter Template
```
# Unit Converter
// Weight
75 kg in lb
2.5 lb in kg
100 g in oz
// Distance
10 km in mi
26.2 mi in km
5280 ft in m
// Temperature
100 °C in °F
72 °F in °C
0 °C in K
// Data
1 GB in MB
500 MB in GB
1 TB in GB
```
### Trip Planner Template
```
# Trip Planner
// Budget
budget = $3000
// Flights
flight_out = $450
flight_back = $380
// Hotel
nights = 7
rate_per_night = $120
hotel_total = nights * rate_per_night
// Daily expenses
daily_food = $50
daily_transport = $20
daily_activities = $35
daily_total = daily_food + daily_transport + daily_activities
trip_expenses = daily_total * nights
// Summary
total_cost = flight_out + flight_back + hotel_total + trip_expenses
remaining = budget - total_cost
```
### Loan Calculator Template
```
# Loan Calculator
// Loan Details
principal = 250000
annual_rate = 6.5%
years = 30
// Monthly Calculation
monthly_rate = annual_rate / 12
num_payments = years * 12
// Monthly Payment
monthly_payment = principal * (monthly_rate * (1 + monthly_rate) ^ num_payments) / ((1 + monthly_rate) ^ num_payments - 1)
// Total Cost
total_paid = monthly_payment * num_payments
total_interest = total_paid - principal
// Summary
interest_ratio = total_interest / principal
```
---
## Technical Notes
- Templates are hardcoded in the app (not fetched from server). Stored as string constants.
- Creating from template: deep copy content, assign new id/title, save to documents array.
- Future: user-created templates (save document as template). Defer to v2.
- Cross-platform: same template content across all platforms.
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,371 @@
# 5.1 — Theme System
**Previous Step:** ← [Templates](../../04-file-organization/4.2-templates/4.2-templates.md)
**Next Step:** → [Mobile Experience](../../06-mobile-experience/6.1-mobile-shell/6.1-mobile-shell.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 05 — Theming |
| **Page Number** | 5.1 |
| **Platform** | Web (PWA), portable to macOS/Windows |
| **Page Type** | Dropdown Panel + Settings Section |
---
## Overview
**Page Purpose:** Enable users to personalize their workspace with preset themes and custom accent colors. Theming is a personality differentiator — especially the Matrix theme, which gives CalcText a unique identity in the calculator app market.
**Success Criteria:**
- Theme switches instantly (< 16ms, no flash of unstyled content)
- Matrix theme makes users want to screenshot and share
- Custom accent color gives ownership feeling
- Theme persists across sessions
---
## Theme Architecture
All theming works through CSS custom properties on `:root`. Switching themes = swapping property values. No component changes, no re-renders beyond what CSS handles natively.
```typescript
interface ThemeDefinition {
id: string
name: string
icon: string // emoji or svg
tokens: {
// Backgrounds
bg: string
bgSecondary: string
codeBg: string
// Text
text: string
textH: string
// Borders
border: string
// Accent
accent: string
accentBg: string
accentBorder: string
// Semantic
success: string
warning: string
error: string
// Syntax highlighting
syntaxVariable: string
syntaxNumber: string
syntaxOperator: string
syntaxKeyword: string
syntaxFunction: string
syntaxCurrency: string
syntaxComment: string
syntaxHeading: string
// Result colors
resultNumber: string
resultUnit: string
resultCurrency: string
resultDatetime: string
resultBoolean: string
// Stripes
stripe: string
// Special
fontOverride?: string // for Matrix: force monospace everywhere
specialEffect?: string // CSS class for effects like scanlines
}
}
```
---
## Preset Themes
### Light
| Token | Value |
|-------|-------|
| --bg | #ffffff |
| --bg-secondary | #f8f9fa |
| --text | #6b6375 |
| --text-h | #08060d |
| --border | #e5e4e7 |
| --accent | #6366f1 |
| --stripe | rgba(0, 0, 0, 0.02) |
| Feel | Clean, airy, professional |
### Dark
| Token | Value |
|-------|-------|
| --bg | #16171d |
| --bg-secondary | #1a1b23 |
| --text | #9ca3af |
| --text-h | #f3f4f6 |
| --border | #2e303a |
| --accent | #818cf8 |
| --stripe | rgba(255, 255, 255, 0.025) |
| Feel | Calm, focused, modern |
### Matrix
| Token | Value |
|-------|-------|
| --bg | #0a0a0a |
| --bg-secondary | #0f1a0f |
| --text | #00ff41 |
| --text-h | #33ff66 |
| --border | #003300 |
| --accent | #00ff41 |
| --stripe | rgba(0, 255, 65, 0.03) |
| --syntax-* | Green spectrum (#00cc33 to #39ff14) |
| --result-currency | #ffff00 (yellow stands out in green) |
| fontOverride | `'Courier New', 'Fira Code', monospace` |
| specialEffect | `matrix-scanlines` |
| Feel | Iconic, hackery, fun |
#### Matrix Special Effects
**Scanlines (optional, subtle):**
```css
.matrix-scanlines::after {
content: '';
position: fixed;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.08) 2px,
rgba(0, 0, 0, 0.08) 4px
);
z-index: 9999;
}
```
**Cursor glow:**
```css
.matrix-theme .cm-cursor {
border-color: #00ff41;
box-shadow: 0 0 4px #00ff41, 0 0 8px rgba(0, 255, 65, 0.3);
}
```
**Text glow (subtle):**
```css
.matrix-theme .result-value {
text-shadow: 0 0 2px rgba(0, 255, 65, 0.3);
}
```
### Midnight
| Token | Value |
|-------|-------|
| --bg | #0f172a |
| --bg-secondary | #1e293b |
| --text | #94a3b8 |
| --text-h | #e2e8f0 |
| --border | #334155 |
| --accent | #38bdf8 (sky-400) |
| --stripe | rgba(56, 189, 248, 0.03) |
| Feel | Deep blue, serene, late-night coding |
### Warm
| Token | Value |
|-------|-------|
| --bg | #fffbf5 |
| --bg-secondary | #fef3e2 |
| --text | #78716c |
| --text-h | #1c1917 |
| --border | #e7e5e4 |
| --accent | #f97316 (orange-500) |
| --stripe | rgba(249, 115, 22, 0.03) |
| Feel | Paper-like, warm, comfortable for long sessions |
---
## Theme Picker Dropdown
**OBJECT ID:** `theme-picker`
| Property | Value |
|----------|-------|
| Trigger | Click theme icon in header or status bar, or Ctrl+Shift+T |
| Position | Dropdown below header button, right-aligned |
| Width | 240px |
| Background | var(--bg) |
| Border | 1px solid var(--border) |
| Border radius | 8px |
| Shadow | 0 4px 16px rgba(0, 0, 0, 0.15) |
| Padding | space-xs (6px) |
| Animation | Scale from 95% + fade, 150ms ease-out |
| Dismiss | Click outside, Esc, or select theme |
### Theme Picker Layout
```
┌──────────────────────────┐
│ Themes │ section header
│ │
│ ☀️ Light ✓ │ active indicator
│ 🌙 Dark │
│ 💻 Matrix │
│ 🌊 Midnight │
│ 📜 Warm │
│ │
│ ────────────────────── │ separator
│ │
│ Accent Color │ section header
│ [●][●][●][●][●][●][●] │ color swatches
│ │
│ ────────────────────── │
│ ⚙️ System (auto) │ follows OS preference
└──────────────────────────┘
```
### Theme Item
| Property | Value |
|----------|-------|
| Height | 36px |
| Padding | 8px 12px |
| Layout | Icon + name + optional checkmark |
| Hover | var(--accent-bg) background, border-radius 4px |
| Active | Checkmark on right side, weight 500 |
| Click | Apply theme instantly, close picker |
| Preview | On hover, show a 4-color mini-swatch (bg, text, accent, secondary) |
### Color Swatch Preview (on hover)
| Property | Value |
|----------|-------|
| Size | 4 circles, 12px each, 4px gap |
| Colors | bg, bg-secondary, accent, text of the hovered theme |
| Position | Inline after theme name |
---
## Accent Color Picker
**OBJECT ID:** `theme-accent-picker`
| Property | Value |
|----------|-------|
| Location | Inside theme picker dropdown, below themes |
| Presets | 7 color swatches in a row |
| Swatch size | 20px circles, 6px gap |
| Active swatch | 2px ring in var(--text-h) |
| Custom | Last swatch is rainbow gradient opens native color picker |
| Behavior | Overrides --accent, --accent-bg, --accent-border for current theme |
| Persistence | Stored as `accentColor` in localStorage |
### Preset Accent Colors
| Name | Value | Hex |
|------|-------|-----|
| Indigo (default) | Indigo 500/400 | #6366f1 / #818cf8 |
| Teal | Teal 500/400 | #14b8a6 / #2dd4bf |
| Rose | Rose 500/400 | #f43f5e / #fb7185 |
| Amber | Amber 500/400 | #f59e0b / #fbbf24 |
| Emerald | Emerald 500/400 | #10b981 / #34d399 |
| Sky | Sky 500/400 | #0ea5e9 / #38bdf8 |
| Custom | User pick | Via `<input type="color">` |
Each preset has light/dark variants. The correct variant is selected based on current base theme.
---
## System Theme Option
**OBJECT ID:** `theme-system`
| Property | Value |
|----------|-------|
| Behavior | Follows OS `prefers-color-scheme` |
| Matches | Light Light theme, Dark Dark theme |
| Override | Selecting any specific theme disables system following |
| Re-enable | Select "System (auto)" in picker |
| Label | "⚙ System (auto)" with current resolved theme name |
---
## Theme Application Flow
```
1. User clicks theme → store in localStorage
2. Apply CSS class to <html>: `data-theme="matrix"`
3. CSS variables resolve from theme class
4. All components instantly update (CSS-only, no React re-render)
5. CodeMirror theme needs manual reconfiguration (dispatch theme compartment)
6. PWA theme-color meta tag updates for status bar color
```
### CodeMirror Theme Sync
| Step | Action |
|------|--------|
| 1 | Theme changes dispatch new baseTheme to EditorView |
| 2 | Syntax highlighting colors update via CSS variables (no extension swap) |
| 3 | Stripe colors update via CSS |
| 4 | Active line highlight updates via CSS |
| 5 | Only the base theme extension needs reconfiguration |
---
## Responsive
| Breakpoint | Theme Picker |
|------------|-------------|
| >= 768px | Dropdown panel below header button |
| < 768px | Bottom sheet (slides up from bottom, 60vh max height) |
### Mobile Bottom Sheet
| Property | Value |
|----------|-------|
| Width | 100vw |
| Max height | 60vh |
| Border radius | 12px 12px 0 0 (top corners) |
| Drag handle | 32px × 4px pill, centered |
| Backdrop | 50% black overlay |
| Animation | Slide up 200ms ease-out |
| Close | Swipe down, tap backdrop |
---
## Page States
| State | When | Behavior |
|-------|------|----------|
| **First launch** | No theme preference | Follow OS (system). If OS is dark, use Dark. |
| **Theme selected** | User picked a theme | Applied instantly, persisted. System mode disabled. |
| **System mode** | User selected "System (auto)" | Listens to OS changes in real-time. |
| **Custom accent** | User picked accent color | Overrides accent tokens. Works with any base theme. |
| **Matrix active** | Matrix theme selected | Font override applied. Scanline effect enabled. Green cursor glow. |
---
## Technical Notes
- **No Flash of Unstyled Content (FOUC):** Load theme from localStorage in `<script>` in `<head>` BEFORE React mounts. Set `data-theme` on `<html>` synchronously.
- **CSS structure:** Each theme is a `[data-theme="name"]` selector block overriding `:root` variables.
- **Matrix performance:** Scanline effect uses `pointer-events: none` and is pure CSS. No performance impact.
- **Cross-platform:** macOS/Windows use native appearance APIs. Theme names map to native equivalents. Matrix theme = custom dark palette on all platforms.
- **PWA:** Update `<meta name="theme-color">` dynamically when theme changes for native status bar color.
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,278 @@
# 6.1 — Mobile Experience
**Previous Step:** ← [Theme System](../../05-theming/5.1-theme-system/5.1-theme-system.md)
---
## Page Metadata
| Property | Value |
|----------|-------|
| **Scenario** | 06 — Mobile Experience |
| **Page Number** | 6.1 |
| **Platform** | Web (PWA) — mobile viewport |
| **Page Type** | Responsive Adaptation (all app shell components) |
| **Viewport** | < 768px |
| **Interaction** | Touch-first |
---
## Overview
**Page Purpose:** Define how the entire CalcText workspace adapts to mobile. This is NOT a separate app it's the same app intelligently restructured for touch and small screens. Every feature remains accessible; nothing is hidden or removed.
**Current Problem:** The existing web app hides results, toolbar, and divider on mobile effectively removing the product's value on the most common device type.
**Success Criteria:**
- All features accessible on mobile (calculations, results, file management, themes)
- Touch targets >= 44px
- One-handed operation possible for core flow (type → see result)
- Feels like a native mobile app when installed as PWA
---
## Mobile Layout (< 768px)
```
┌─────────────────────────────┐
│ Header [≡] CalcText [🎨]│ 44px (touch-sized)
├─────────────────────────────┤
│ Tab Bar (horizontal scroll) │ 40px
├─────────────────────────────┤
│ │
│ │
│ Editor │
│ (full width) │
│ │
│ │
│ │
├─────────────────────────────┤
│ ═══ Results Tray │ 48px collapsed
│ Last: $4,300 │
├─────────────────────────────┤
│ Status (simplified) │ 24px
└─────────────────────────────┘
```
---
## Component Adaptations
### Header (Mobile)
| Property | Desktop | Mobile |
|----------|---------|--------|
| Height | 40px | 44px (touch target) |
| Left content | Logo + "CalcText" | [≡] hamburger + "CalcText" |
| Right content | Theme + Settings + ⌘ | [🎨] theme only |
| Padding | 12px 12px | 8px 12px |
| Hamburger | N/A | 44px × 44px touch target, opens sidebar drawer |
### Tab Bar (Mobile)
| Property | Desktop | Mobile |
|----------|---------|--------|
| Height | 36px | 40px |
| Tab min width | 100px | 80px (more compact) |
| Close button | Visible on hover/active | Hidden — swipe left to reveal |
| New tab (+) | After last tab | Sticky far-right |
| Scroll | Mouse wheel | Touch swipe horizontal |
| Active indicator | Top 2px accent border | Bottom 2px accent border (thumb reachable) |
### Editor (Mobile)
| Property | Desktop | Mobile |
|----------|---------|--------|
| Width | Flex (shared with results) | 100vw |
| Line padding | 12px | 12px |
| Gutter | 40px (line numbers) | 32px (compact numbers) |
| Font size | 15px | 15px (same — readable on mobile) |
| Line height | 24px | 24px (same) |
| Keyboard | Physical | Virtual — editor scrolls above keyboard |
#### Virtual Keyboard Handling
| Property | Value |
|----------|-------|
| Viewport | Uses `100dvh` (dynamic viewport height) to account for keyboard |
| Scroll | Editor auto-scrolls to keep cursor visible above keyboard |
| Results tray | Hides when keyboard is open (not enough space) |
| Status bar | Hides when keyboard is open |
### Results Tray (Mobile)
Replaces the side panel with a bottom tray.
| State | Height | Content |
|-------|--------|---------|
| **Collapsed** | 48px | Drag handle + last non-empty result |
| **Expanded** | 40vh (max 60vh) | Full scrollable results list |
| **Hidden** | 0px | When virtual keyboard is open |
#### Collapsed Tray
| Property | Value |
|----------|-------|
| Background | var(--bg-secondary) |
| Border top | 1px solid var(--border) |
| Drag handle | 32px × 4px pill, var(--border), centered |
| Content | "Last: {value}" — last non-empty result, text-xs |
| Tap | Expand tray |
| Swipe up | Expand tray |
#### Expanded Tray
| Property | Value |
|----------|-------|
| Content | All results paired with expressions |
| Item format | Line number + expression snippet + result value |
| Item height | 44px (touch-friendly) |
| Active line | var(--accent-bg) highlight |
| Tap result | Copy to clipboard + brief feedback |
| Swipe down | Collapse tray |
| Tap drag handle | Collapse |
| Scroll | Independent vertical scroll |
#### Tray Interaction
```
┌──────────────────────────────┐
│ ═══ (drag handle) │ Swipe up to expand
│ Last: $4,300 │
├──────────────────────────────┤ ← expanded state below
│ 5 salary 5,000 │
│ 6 freelance 1,200 │
│ 7 total_income 6,200 │ ← highlighted (active line)
│ 9 rent 1,500 │
│ 10 groceries 400 │
│ 13 total_expenses 1,900 │
│ 14 savings 4,300 │
└──────────────────────────────┘
```
### Sidebar Drawer (Mobile)
| Property | Value |
|----------|-------|
| Trigger | Hamburger [≡] in header |
| Width | 85vw, max 320px |
| Position | Fixed left, full height |
| Background | var(--bg) |
| Backdrop | rgba(0, 0, 0, 0.5) — tap to close |
| Animation | translateX(-100%) → translateX(0), 200ms ease-out |
| Close | Tap backdrop, swipe left, × button (top-right, 44px target) |
| Content | Same sections as desktop sidebar (search, recent, favorites, files, templates) |
| File tap | Opens document → auto-closes drawer |
| Search | Full width, 44px height (touch target) |
| File items | 44px height (touch target, up from 28px desktop) |
### Theme Picker (Mobile)
| Property | Value |
|----------|-------|
| Trigger | [🎨] button in header |
| Style | Bottom sheet (slides from bottom) |
| Height | Auto (content-driven), max 60vh |
| Border radius | 12px 12px 0 0 |
| Drag handle | 32px × 4px pill, centered |
| Items | 48px height per theme (touch targets) |
| Accent swatches | 32px circles, 8px gap (larger for touch) |
| Close | Swipe down, tap backdrop |
### Status Bar (Mobile)
| Property | Value |
|----------|-------|
| Height | 24px (same) |
| Content | Cursor position + engine status dot only |
| Hidden items | Line count, theme indicator (accessible elsewhere) |
| Hidden | When virtual keyboard is open |
---
## Touch Gestures
| Gesture | Context | Action |
|---------|---------|--------|
| Swipe left on tab | Tab bar | Reveal close button |
| Swipe up on results tray | Results | Expand tray |
| Swipe down on results tray | Results | Collapse tray |
| Swipe left from right edge | Sidebar drawer | Close drawer |
| Swipe down on bottom sheet | Theme picker | Close sheet |
| Long press on file | Sidebar | Show context menu |
| Long press on tab | Tab bar | Drag to reorder |
| Tap result | Results tray | Copy to clipboard |
| Pinch | Editor | Zoom font size (optional) |
---
## Breakpoint Details
| Width | Classification | Key Adaptations |
|-------|---------------|-----------------|
| **>= 1024px** | Desktop | Full 3-panel layout |
| **7681023px** | Tablet | Sidebar → overlay drawer. Editor + results side-by-side. |
| **480767px** | Mobile | Single column. Results tray. Sidebar drawer. Touch targets 44px. |
| **< 480px** | Small mobile | Same as mobile. Tab labels may truncate. Logo text hidden. |
### Tablet Specifics (7681023px)
| Component | Behavior |
|-----------|----------|
| Sidebar | Overlay drawer (hamburger toggle) instead of persistent panel |
| Editor + Results | Side-by-side with divider (same as desktop) |
| Tab bar | Same as desktop (enough width) |
| Header | Show hamburger [≡] instead of persistent sidebar |
| Theme picker | Dropdown (same as desktop) |
---
## PWA Mobile Enhancements
| Feature | Implementation |
|---------|---------------|
| Standalone display | `display: standalone` no browser chrome |
| Status bar color | `theme-color` meta tag updates per theme |
| Safe areas | `env(safe-area-inset-*)` for notched devices |
| Splash screen | Theme-colored background + CalcText logo |
| Home screen icon | App icon with accent color ring |
| Orientation | Portrait preferred, landscape supported |
### Safe Area Padding
```css
.calcpad-app {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
```
---
## Page States (Mobile-Specific)
| State | When | Behavior |
|-------|------|----------|
| **Keyboard open** | User tapped editor | Results tray + status bar hide. Editor fills available space. |
| **Keyboard closed** | User tapped outside or pressed Done | Results tray + status bar reappear. |
| **Drawer open** | Hamburger tapped | Sidebar overlays. Backdrop captures tap to close. |
| **Tray expanded** | User swiped up | 40vh results list. Editor partially visible above. |
| **Offline** | No network | Status bar shows offline indicator. All features work. |
---
## Technical Notes
- **Viewport units:** Use `dvh` (dynamic viewport height) not `vh` to handle mobile browser chrome and virtual keyboard.
- **Touch events:** Use `touchstart`/`touchmove`/`touchend` for swipe gestures. Consider `passive: true` for scroll performance.
- **Overscroll:** Disable `overscroll-behavior: none` on app container to prevent pull-to-refresh interference.
- **iOS safe areas:** Test with iPhone notch and dynamic island. Apply `env(safe-area-inset-*)`.
- **Android back button:** In PWA mode, back button should close drawers/sheets before navigating back.
- **Font scaling:** Respect system font size on mobile. Use relative units where possible.
- **Cross-platform:** Mobile web layout does NOT need to port to native mobile (that's a separate app). But interaction patterns (swipe, long-press) inform native mobile design if built later.
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,104 @@
# Design System: CalcText
> Components, tokens, and patterns that grow from actual usage — not upfront planning.
**Created:** 2026-03-17
**Phase:** 7 — Design System (optional)
**Agent:** Freya (Designer)
---
## What Belongs Here
The Design System captures reusable patterns that emerge during UX Design (Phase 4). It is not designed upfront — it crystallizes from real page specifications.
**What goes here:**
- **Design Tokens** — Colors, spacing, typography, shadows
- **Components** — Buttons, inputs, cards, navigation elements
- **Patterns** — Layouts, form structures, content blocks
- **Visual Design** — Mood boards, design concepts, color and typography explorations
- **Assets** — Logos, icons, images, graphics
---
## Folder Structure
```
D-Design-System/
├── 00-design-system.md <- This file (hub + guide)
├── 01-Visual-Design/
│ ├── mood-boards/
│ ├── design-concepts/
│ ├── color-exploration/
│ └── typography-tests/
├── 02-Assets/
│ ├── logos/
│ ├── icons/
│ ├── images/
│ └── graphics/
└── components/
├── interactive/
├── form/
├── layout/
├── content/
├── feedback/
└── navigation/
```
---
## Spacing Scale
_Will be defined during first design session._
| Token | Value | Use |
|-------|-------|-----|
| space-3xs | — | Hairline gaps |
| space-2xs | — | Minimal spacing |
| space-xs | — | Tight spacing |
| space-sm | — | Small gaps |
| **space-md** | — | **Default element spacing** |
| space-lg | — | Comfortable spacing |
| space-xl | — | Section padding |
| space-2xl | — | Section gaps |
| space-3xl | — | Page-level breathing room |
---
## Type Scale
_Will be defined during first design session._
| Token | Value | Use |
|-------|-------|-----|
| text-3xs | — | Fine print |
| text-2xs | — | Metadata |
| text-xs | — | Captions |
| text-sm | — | Labels |
| text-md | — | Body text |
| text-lg | — | Emphasis |
| text-xl | — | Subheadings |
| text-2xl | — | Section titles |
| text-3xl | — | Hero headings |
---
## Tokens
_Additional design tokens will be documented here as they emerge from page specifications._
---
## Patterns
_Patterns will be documented here as spacing objects recur across pages._
---
## Components
_Components will be documented here as patterns emerge across scenarios._
---
_Created using Whiteport Design Studio (WDS) methodology_

View File

@@ -0,0 +1,117 @@
# Design Log
**Project:** CalcText
**Started:** 2026-03-17
**Method:** Whiteport Design Studio (WDS)
---
## Backlog
> Business-value items. Add links to detail files if needed.
- [x] Create simplified product brief (brownfield) — Phase 1
- [x] Define workspace scenarios — Phase 3
- [x] Design app shell (3-panel layout: sidebar + editor + results) — Phase 4
- [x] Design theme engine (Light, Dark, Matrix, custom) — Phase 4
- [x] Design tab system for multi-document — Phase 4
- [x] Design file sidebar with folders — Phase 4
- [x] Design elevated results panel — Phase 4
- [x] Design mobile experience — Phase 4
---
## Current
| Task | Started | Agent |
|------|---------|-------|
| — | — | — |
> Brownfield codebase scan completed 2026-03-17. See `A-Product-Brief/01-brownfield-analysis.md`
**Rules:** Mark what you start. Complete it when done (move to Log). One task at a time per agent.
---
## Design Loop Status
> Per-page design progress. Updated by agents at every design transition.
| Scenario | Step | Page | Status | Updated |
|----------|------|------|--------|---------|
| 01-workspace-shell | 1.1 | App Shell | specified | 2026-03-17 |
| 01-workspace-shell | 1.2 | Status Bar | specified | 2026-03-17 |
| 02-calc-experience | 2.1 | Editor | specified | 2026-03-17 |
| 02-calc-experience | 2.2 | Results Panel | specified | 2026-03-17 |
| 03-doc-management | 3.1 | Tab Bar & Lifecycle | specified | 2026-03-17 |
| 04-file-org | 4.1 | Sidebar | specified | 2026-03-17 |
| 04-file-org | 4.2 | Templates | specified | 2026-03-17 |
| 05-theming | 5.1 | Theme System | specified | 2026-03-17 |
| 06-mobile | 6.1 | Mobile Shell | specified | 2026-03-17 |
**Status values:** `discussed` -> `wireframed` -> `specified` -> `explored` -> `building` -> `built` -> `approved` | `removed`
**How to use:**
- **Append a row** when a page reaches a new status (do not overwrite — latest row per page is current status)
- **Read on startup** to see where the project stands and what to suggest next
---
## Log
### 2026-03-18 — Full workspace implementation complete
- Theme system: 5 presets (Light, Dark, Matrix, Midnight, Warm) + 6 accent colors
- Document model with localStorage persistence
- Tab bar with multi-document support, rename, keyboard shortcuts
- 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 redesign: type-specific colors, click-to-copy
- Format toolbar: H, B, I, //, color labels
- Live Preview toggle (markdown → formatted)
- Syntax highlighting using theme CSS variables
- Error hover tooltips
- Mobile experience: bottom results tray, sidebar drawer, touch targets, safe areas
- Alignment toolbar re-integrated in header
### 2026-03-17 — All 6 scenarios designed (Phase 4 complete)
- 8 page specifications created across 6 scenarios
- App Shell, Status Bar, Editor, Results Panel, Tab Bar, Sidebar, Templates, Theme System, Mobile
- 5 preset themes defined (Light, Dark, Matrix, Midnight, Warm)
- 7 accent color presets
- Complete localStorage schema
- Mobile experience redesigned: bottom results tray, sidebar drawer, touch gestures
- 14 keyboard shortcuts defined
- 5 templates with full content
### 2026-03-17 — Simplified product brief completed (Phase 1)
- Scope: workspace evolution (themes, tabs, sidebar, persistence)
- Strategy: web-first, then replicate to macOS and Windows
- Deferred: accounts, cloud sync, collaboration (backend needed)
- Saved to `A-Product-Brief/project-brief.md`
### 2026-03-17 — Brownfield codebase scan completed
- Full web app scan: 6 components, 4 editor extensions, 3 hooks, 15 CSS tokens
- Cross-platform comparison with macOS native app
- Key findings: results panel too loud, no manual dark mode toggle, mobile experience gutted, no document model
- Analysis saved to `A-Product-Brief/01-brownfield-analysis.md`
### 2026-03-17 — Project initialized (Phase 0)
- Type: brownfield
- Complexity: complex (web application)
- Tech stack: React 19 + CodeMirror 6 + Vite 7 + WASM
- Component library: Custom CSS (CSS custom properties)
- Brief level: simplified
- Stakes: enterprise/high
- Involvement: autonomous execution
- Brainstorm session established product vision: evolve from single-document calculator to full workspace with themes, tabs, sidebar, file management
---
## About This Folder
- **This file** — Single source of truth for project progress
- **agent-experiences/** — Compressed insights from design discussions (dated files)
- **wds-project-outline.yaml** — Project configuration from Phase 0 setup
**Do not modify `wds-project-outline.yaml`** — it is the source of truth for project configuration.

View File

@@ -0,0 +1,50 @@
# WDS Project Outline
# Generated: 2026-03-17
# Phase 0: Project Setup
project_name: CalcText
project_type: brownfield
product_complexity: complex
tech_stack: react
component_library: custom
root_folder: "_bmad-output"
brief_level: simplified
design_system_mode: recommended
existing_materials:
has_materials: false
notes: "Brainstorm insights from party mode session available in conversation context"
project_context:
stakes: enterprise
description: "Cross-platform notepad calculator evolving into a full workspace application"
working_relationship:
involvement: autonomous
user_role: project_manager
recommendation_style: direct_guidance
stakeholder_notes: null
# Brownfield Context
brownfield:
existing_product: "CalcText Web App"
tech_stack_detail: "React 19 + CodeMirror 6 + Vite 7 + WASM engine"
current_state: "Two-column editor (input + results), PWA, light/dark theme"
evolution_goals:
- "Multi-theme system (Light, Dark, Matrix, custom)"
- "Tab system for multiple calctext documents"
- "File sidebar with folders and organization"
- "User accounts and cloud persistence (deferred - backend)"
- "Elevated results panel design"
- "Mobile experience redesign"
# Phase Routing
phases:
phase_0: completed
phase_1: "simplified brief (brownfield)"
phase_2: skipped
phase_3: "scenarios from brownfield analysis"
phase_4: "UX design with Freya"
phase_5: "custom component development"
phase_7: "design system extraction"
phase_8: "product evolution entry point"