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

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

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

258 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# 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_