Workspace Mode - Vision & Design Decisions
This document captures the key design decisions and vision for workspace mode.
Vision Summary
Transform fac into a workspace-aware editor where:
- Workspace = directory + persistent editor state (tabs, panes, positions)
- Fortress = navigator UI for finding workspaces and files
- State persists across sessions
- Single-file mode still works unchanged
Key Concepts
Workspace
A workspace is created when opening a directory:
fac . # Open cwd as workspace
fac ~/projects/myapp # Open that directory as workspace
fac # Opens Fortress welcome menu
Workspace contains:
- Tabs and which files are open
- Pane splits and layouts
- Cursor positions per file
- Viewport scroll positions
- Fuss mode state (open/closed)
- Dirty buffer backups
Persisted in: .fac/workspace.json in workspace root
Fortress Navigator (Ctrl-O)
Dual-pane file/directory browser for:
- Opening new workspaces - navigate to directory, press Enter
- Opening orphan files - navigate to file (outside workspace), press Enter
Features:
- Dual-pane display (parent 30% | current 70%)
- Navigate with arrow keys
- Enter on directory → switch to that workspace
- Enter on file → open as orphan tab in current workspace
- '8' key → toggle between favorites and recents view
- 'f' key → add current directory to favorites
- ESC/q → cancel and return
Orphan Tabs
Files opened from outside the workspace via Fortress:
- Displayed in greyish/subtle color in tab bar
- NOT persisted in workspace state
- If closed, hard to reopen (need Fortress again)
- Similar to VSCode's non-workspace files
Favorites & Recents
Stored in ~/.config/fac/:
- favorites.json - manually marked favorite workspaces
- recents.json - automatically tracked recent workspaces (last 10-20)
- Press '8' in Fortress to toggle between favorites/recents view
File Structure
Workspace Root
~/projects/myapp/
.fac/
workspace.json # Editor state (tabs, panes, positions)
backups/ # Dirty buffer backups
main.f90.bak
test.f90.bak
.backup-metadata.json
src/
main.f90
...
User Config
~/.config/fac/
favorites.json # User-marked favorites
recents.json # Auto-tracked recent workspaces
Command Line Behavior
| Command | Behavior |
|---|---|
fac |
Open Fortress welcome menu (favorites/recents) |
fac . |
Open cwd as workspace (create if new) |
fac ~/dir |
Open that directory as workspace |
fac file.txt |
Single-file mode (check parent for workspace) |
Key Workflows
1. Start Working on Project
cd ~/projects/myapp
fac .
# Opens workspace (or creates if first time)
# Restores tabs/panes from last session
2. Switch to Another Project
While in fac:
Ctrl-O
# Navigate to ~/projects/other-project
# Press Enter
# Saves current workspace state
# Opens new workspace with its state
3. Open Orphan File
While in fac workspace:
Ctrl-O
# Navigate to /etc/hosts (outside workspace)
# Press Enter
# Opens in new tab (grey color, not persisted)
4. Quick Edit Single File
fac /etc/hosts
# Single-file mode (no workspace)
# Works like current fac
Design Decisions
1. Orphan Tabs
- Visual: Greyish/subtle color in tab bar
- Persistence: NOT saved in workspace.json
- Rationale: Similar to VSCode - you can open any file, but it won't be restored next session
2. Favorites vs Recents
- Both available, like VSCode
- Toggle with '8' key between views
- Favorites: Manual (user adds with 'f' key)
- Recents: Automatic (last 10-20 opened workspaces)
3. Workspace Discovery
facwith no args → Fortress welcome menufac path→ Try to load workspace from that path- Silent creation (no prompt) when creating new workspace
4. Backup Strategy
- On quit: Prompt to save dirty buffers
- If 'n': Create backup in
.fac/backups/ - On open: Detect backups, prompt to restore
- Options: [r]estore / [i]gnore / [d]iff
- Always show diff option for safety
5. Storage Location
- Workspace state:
.fac/workspace.jsonin workspace root- Rationale: Self-contained, obvious location, easy to delete
- User config:
~/.config/fac/- Rationale: Standard XDG location for user preferences
6. File Paths in workspace.json
- Workspace files: Relative paths (e.g.,
src/main.f90) - Orphan files: Absolute paths (e.g.,
/etc/hosts) - Rationale: Workspace portable if directory moves
7. Missing Files
- Behavior: Show warning, skip that tab, continue loading
- Warning: "File not found: src/missing.f90"
- Rationale: Don't block workspace load due to one missing file
8. Fortress Integration
- Copy Fortress modules into fac (not shell out)
- Use: Filesystem, terminal, UI modules
- Omit: Git integration (fuss menu has this), multiselect (for now)
- Rationale: Single binary, no Python dependency, full control
State Persistence
What Gets Saved in workspace.json
✅ Tab list with file paths ✅ Pane splits and layouts ✅ Cursor positions per file ✅ Viewport scroll positions per file ✅ Active tab index ✅ Active pane per tab ✅ Fuss mode state (open/closed, width)
❌ Orphan tabs (not persisted) ❌ Undo history (too large, unnecessary) ❌ Search history (not critical) ❌ Yank stack (session-specific)
workspace.json Schema
{
"version": "1.0",
"workspace_path": "/home/user/project",
"last_opened": "2025-01-05T10:30:00Z",
"tabs": [
{
"label": "main.f90",
"panes": [
{
"file": "src/main.f90",
"cursor": {"line": 42, "col": 10},
"viewport": {"line": 30, "col": 1},
"modified": false
}
],
"split_type": "none|vertical|horizontal",
"active_pane": 0
}
],
"active_tab": 0,
"fuss_mode": {
"active": true,
"width": 30
}
}
Fortress Keybindings
Navigation
↑/↓: Move cursor→: Enter directory←: Go to parent~: Jump to home directory/: Jump to root directoryEnter: Select (directory = switch workspace, file = open as orphan)q/ESC: Cancel and return to editor
Favorites & Recents
8: Toggle between favorites and recents viewf: Add current directory to favoritesr/x: Remove favorite (in favorites view)
Future (not Phase 1)
s: Fuzzy search with fzf (maybe later).: Toggle dotfiles (maybe later)
Backwards Compatibility
Critical: Single-file mode must work unchanged
fac file.txtbehaves exactly as before- Ctrl-B shows parent directory tree
- No workspace created
- No persistence
Testing: Extensive regression tests for non-workspace mode
Open Questions / Future Enhancements
Not in Initial Implementation
- Fuzzy search within workspace (fzf integration)
- Multiselect in Fortress
- Git indicators in Fortress (fuss menu has this)
- Multiple workspaces open simultaneously
- Workspace templates
- Per-workspace editor settings
Maybe Later
- Workspace sharing (commit .fac/ to git?)
- Remote workspaces
- Workspace import/export
- Workspace groups
Success Criteria
The implementation is successful when:
- ✅
faclaunches Fortress welcome menu - ✅
fac .creates/loads workspace seamlessly - ✅ Ctrl-O navigates to workspaces and files
- ✅ All tabs, panes, positions persist across sessions
- ✅ Orphan tabs work and are visually distinct
- ✅ Favorites and recents track correctly
- ✅ Backups save/restore with diff option
- ✅ Single-file mode works unchanged
- ✅ No regressions in existing features
- ✅ Documentation is complete
References
- See
WORKSPACE_ROADMAP.mdfor implementation phases - See
WORKSPACE_TASKS.mdfor task tracking - See
workspace_spec.mdfor technical specifications (Phase 0 deliverable)
Last Updated: 2025-01-05
View source
| 1 | # Workspace Mode - Vision & Design Decisions |
| 2 | |
| 3 | This document captures the key design decisions and vision for workspace mode. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Vision Summary |
| 8 | |
| 9 | Transform `fac` into a workspace-aware editor where: |
| 10 | - **Workspace** = directory + persistent editor state (tabs, panes, positions) |
| 11 | - **Fortress** = navigator UI for finding workspaces and files |
| 12 | - State persists across sessions |
| 13 | - Single-file mode still works unchanged |
| 14 | |
| 15 | --- |
| 16 | |
| 17 | ## Key Concepts |
| 18 | |
| 19 | ### Workspace |
| 20 | A workspace is created when opening a directory: |
| 21 | ```bash |
| 22 | fac . # Open cwd as workspace |
| 23 | fac ~/projects/myapp # Open that directory as workspace |
| 24 | fac # Opens Fortress welcome menu |
| 25 | ``` |
| 26 | |
| 27 | **Workspace contains:** |
| 28 | - Tabs and which files are open |
| 29 | - Pane splits and layouts |
| 30 | - Cursor positions per file |
| 31 | - Viewport scroll positions |
| 32 | - Fuss mode state (open/closed) |
| 33 | - Dirty buffer backups |
| 34 | |
| 35 | **Persisted in:** `.fac/workspace.json` in workspace root |
| 36 | |
| 37 | ### Fortress Navigator (Ctrl-O) |
| 38 | Dual-pane file/directory browser for: |
| 39 | 1. **Opening new workspaces** - navigate to directory, press Enter |
| 40 | 2. **Opening orphan files** - navigate to file (outside workspace), press Enter |
| 41 | |
| 42 | **Features:** |
| 43 | - Dual-pane display (parent 30% | current 70%) |
| 44 | - Navigate with arrow keys |
| 45 | - Enter on directory → switch to that workspace |
| 46 | - Enter on file → open as orphan tab in current workspace |
| 47 | - '8' key → toggle between favorites and recents view |
| 48 | - 'f' key → add current directory to favorites |
| 49 | - ESC/q → cancel and return |
| 50 | |
| 51 | ### Orphan Tabs |
| 52 | Files opened from outside the workspace via Fortress: |
| 53 | - Displayed in **greyish/subtle color** in tab bar |
| 54 | - NOT persisted in workspace state |
| 55 | - If closed, hard to reopen (need Fortress again) |
| 56 | - Similar to VSCode's non-workspace files |
| 57 | |
| 58 | ### Favorites & Recents |
| 59 | Stored in `~/.config/fac/`: |
| 60 | - **favorites.json** - manually marked favorite workspaces |
| 61 | - **recents.json** - automatically tracked recent workspaces (last 10-20) |
| 62 | - Press '8' in Fortress to toggle between favorites/recents view |
| 63 | |
| 64 | --- |
| 65 | |
| 66 | ## File Structure |
| 67 | |
| 68 | ### Workspace Root |
| 69 | ``` |
| 70 | ~/projects/myapp/ |
| 71 | .fac/ |
| 72 | workspace.json # Editor state (tabs, panes, positions) |
| 73 | backups/ # Dirty buffer backups |
| 74 | main.f90.bak |
| 75 | test.f90.bak |
| 76 | .backup-metadata.json |
| 77 | src/ |
| 78 | main.f90 |
| 79 | ... |
| 80 | ``` |
| 81 | |
| 82 | ### User Config |
| 83 | ``` |
| 84 | ~/.config/fac/ |
| 85 | favorites.json # User-marked favorites |
| 86 | recents.json # Auto-tracked recent workspaces |
| 87 | ``` |
| 88 | |
| 89 | --- |
| 90 | |
| 91 | ## Command Line Behavior |
| 92 | |
| 93 | | Command | Behavior | |
| 94 | |---------|----------| |
| 95 | | `fac` | Open Fortress welcome menu (favorites/recents) | |
| 96 | | `fac .` | Open cwd as workspace (create if new) | |
| 97 | | `fac ~/dir` | Open that directory as workspace | |
| 98 | | `fac file.txt` | Single-file mode (check parent for workspace) | |
| 99 | |
| 100 | --- |
| 101 | |
| 102 | ## Key Workflows |
| 103 | |
| 104 | ### 1. Start Working on Project |
| 105 | ```bash |
| 106 | cd ~/projects/myapp |
| 107 | fac . |
| 108 | # Opens workspace (or creates if first time) |
| 109 | # Restores tabs/panes from last session |
| 110 | ``` |
| 111 | |
| 112 | ### 2. Switch to Another Project |
| 113 | While in fac: |
| 114 | ``` |
| 115 | Ctrl-O |
| 116 | # Navigate to ~/projects/other-project |
| 117 | # Press Enter |
| 118 | # Saves current workspace state |
| 119 | # Opens new workspace with its state |
| 120 | ``` |
| 121 | |
| 122 | ### 3. Open Orphan File |
| 123 | While in fac workspace: |
| 124 | ``` |
| 125 | Ctrl-O |
| 126 | # Navigate to /etc/hosts (outside workspace) |
| 127 | # Press Enter |
| 128 | # Opens in new tab (grey color, not persisted) |
| 129 | ``` |
| 130 | |
| 131 | ### 4. Quick Edit Single File |
| 132 | ```bash |
| 133 | fac /etc/hosts |
| 134 | # Single-file mode (no workspace) |
| 135 | # Works like current fac |
| 136 | ``` |
| 137 | |
| 138 | --- |
| 139 | |
| 140 | ## Design Decisions |
| 141 | |
| 142 | ### 1. Orphan Tabs |
| 143 | - **Visual**: Greyish/subtle color in tab bar |
| 144 | - **Persistence**: NOT saved in workspace.json |
| 145 | - **Rationale**: Similar to VSCode - you can open any file, but it won't be restored next session |
| 146 | |
| 147 | ### 2. Favorites vs Recents |
| 148 | - **Both available**, like VSCode |
| 149 | - **Toggle with '8' key** between views |
| 150 | - **Favorites**: Manual (user adds with 'f' key) |
| 151 | - **Recents**: Automatic (last 10-20 opened workspaces) |
| 152 | |
| 153 | ### 3. Workspace Discovery |
| 154 | - `fac` with no args → Fortress welcome menu |
| 155 | - `fac path` → Try to load workspace from that path |
| 156 | - Silent creation (no prompt) when creating new workspace |
| 157 | |
| 158 | ### 4. Backup Strategy |
| 159 | - **On quit**: Prompt to save dirty buffers |
| 160 | - **If 'n'**: Create backup in `.fac/backups/` |
| 161 | - **On open**: Detect backups, prompt to restore |
| 162 | - **Options**: [r]estore / [i]gnore / [d]iff |
| 163 | - **Always show diff option** for safety |
| 164 | |
| 165 | ### 5. Storage Location |
| 166 | - **Workspace state**: `.fac/workspace.json` in workspace root |
| 167 | - Rationale: Self-contained, obvious location, easy to delete |
| 168 | - **User config**: `~/.config/fac/` |
| 169 | - Rationale: Standard XDG location for user preferences |
| 170 | |
| 171 | ### 6. File Paths in workspace.json |
| 172 | - **Workspace files**: Relative paths (e.g., `src/main.f90`) |
| 173 | - **Orphan files**: Absolute paths (e.g., `/etc/hosts`) |
| 174 | - **Rationale**: Workspace portable if directory moves |
| 175 | |
| 176 | ### 7. Missing Files |
| 177 | - **Behavior**: Show warning, skip that tab, continue loading |
| 178 | - **Warning**: "File not found: src/missing.f90" |
| 179 | - **Rationale**: Don't block workspace load due to one missing file |
| 180 | |
| 181 | ### 8. Fortress Integration |
| 182 | - **Copy Fortress modules into fac** (not shell out) |
| 183 | - **Use**: Filesystem, terminal, UI modules |
| 184 | - **Omit**: Git integration (fuss menu has this), multiselect (for now) |
| 185 | - **Rationale**: Single binary, no Python dependency, full control |
| 186 | |
| 187 | --- |
| 188 | |
| 189 | ## State Persistence |
| 190 | |
| 191 | ### What Gets Saved in workspace.json |
| 192 | ✅ Tab list with file paths |
| 193 | ✅ Pane splits and layouts |
| 194 | ✅ Cursor positions per file |
| 195 | ✅ Viewport scroll positions per file |
| 196 | ✅ Active tab index |
| 197 | ✅ Active pane per tab |
| 198 | ✅ Fuss mode state (open/closed, width) |
| 199 | |
| 200 | ❌ Orphan tabs (not persisted) |
| 201 | ❌ Undo history (too large, unnecessary) |
| 202 | ❌ Search history (not critical) |
| 203 | ❌ Yank stack (session-specific) |
| 204 | |
| 205 | ### workspace.json Schema |
| 206 | ```json |
| 207 | { |
| 208 | "version": "1.0", |
| 209 | "workspace_path": "/home/user/project", |
| 210 | "last_opened": "2025-01-05T10:30:00Z", |
| 211 | "tabs": [ |
| 212 | { |
| 213 | "label": "main.f90", |
| 214 | "panes": [ |
| 215 | { |
| 216 | "file": "src/main.f90", |
| 217 | "cursor": {"line": 42, "col": 10}, |
| 218 | "viewport": {"line": 30, "col": 1}, |
| 219 | "modified": false |
| 220 | } |
| 221 | ], |
| 222 | "split_type": "none|vertical|horizontal", |
| 223 | "active_pane": 0 |
| 224 | } |
| 225 | ], |
| 226 | "active_tab": 0, |
| 227 | "fuss_mode": { |
| 228 | "active": true, |
| 229 | "width": 30 |
| 230 | } |
| 231 | } |
| 232 | ``` |
| 233 | |
| 234 | --- |
| 235 | |
| 236 | ## Fortress Keybindings |
| 237 | |
| 238 | ### Navigation |
| 239 | - `↑/↓`: Move cursor |
| 240 | - `→`: Enter directory |
| 241 | - `←`: Go to parent |
| 242 | - `~`: Jump to home directory |
| 243 | - `/`: Jump to root directory |
| 244 | - `Enter`: Select (directory = switch workspace, file = open as orphan) |
| 245 | - `q/ESC`: Cancel and return to editor |
| 246 | |
| 247 | ### Favorites & Recents |
| 248 | - `8`: Toggle between favorites and recents view |
| 249 | - `f`: Add current directory to favorites |
| 250 | - `r/x`: Remove favorite (in favorites view) |
| 251 | |
| 252 | ### Future (not Phase 1) |
| 253 | - `s`: Fuzzy search with fzf (maybe later) |
| 254 | - `.`: Toggle dotfiles (maybe later) |
| 255 | |
| 256 | --- |
| 257 | |
| 258 | ## Backwards Compatibility |
| 259 | |
| 260 | **Critical**: Single-file mode must work unchanged |
| 261 | - `fac file.txt` behaves exactly as before |
| 262 | - Ctrl-B shows parent directory tree |
| 263 | - No workspace created |
| 264 | - No persistence |
| 265 | |
| 266 | **Testing**: Extensive regression tests for non-workspace mode |
| 267 | |
| 268 | --- |
| 269 | |
| 270 | ## Open Questions / Future Enhancements |
| 271 | |
| 272 | ### Not in Initial Implementation |
| 273 | - Fuzzy search within workspace (fzf integration) |
| 274 | - Multiselect in Fortress |
| 275 | - Git indicators in Fortress (fuss menu has this) |
| 276 | - Multiple workspaces open simultaneously |
| 277 | - Workspace templates |
| 278 | - Per-workspace editor settings |
| 279 | |
| 280 | ### Maybe Later |
| 281 | - Workspace sharing (commit .fac/ to git?) |
| 282 | - Remote workspaces |
| 283 | - Workspace import/export |
| 284 | - Workspace groups |
| 285 | |
| 286 | --- |
| 287 | |
| 288 | ## Success Criteria |
| 289 | |
| 290 | The implementation is successful when: |
| 291 | - ✅ `fac` launches Fortress welcome menu |
| 292 | - ✅ `fac .` creates/loads workspace seamlessly |
| 293 | - ✅ Ctrl-O navigates to workspaces and files |
| 294 | - ✅ All tabs, panes, positions persist across sessions |
| 295 | - ✅ Orphan tabs work and are visually distinct |
| 296 | - ✅ Favorites and recents track correctly |
| 297 | - ✅ Backups save/restore with diff option |
| 298 | - ✅ Single-file mode works unchanged |
| 299 | - ✅ No regressions in existing features |
| 300 | - ✅ Documentation is complete |
| 301 | |
| 302 | --- |
| 303 | |
| 304 | ## References |
| 305 | |
| 306 | - See `WORKSPACE_ROADMAP.md` for implementation phases |
| 307 | - See `WORKSPACE_TASKS.md` for task tracking |
| 308 | - See `workspace_spec.md` for technical specifications (Phase 0 deliverable) |
| 309 | |
| 310 | --- |
| 311 | |
| 312 | Last Updated: 2025-01-05 |