feat: add tab-scoped base directory with smart auto-detection
Overview
This PR adds per-tab base directory context that allows all components within a tab (terminals, file previews, widgets) to inherit a common working directory. This creates a cohesive workspace experience where related work shares the same base path.
Problem
Currently, each terminal, file preview, and widget in Wave Terminal operates independently with its own working directory. This creates friction when:
- Opening multiple terminals that should work in the same project
- Switching between widgets and terminals that need to reference the same files
- Maintaining consistent context across tab restarts
Solution
This PR introduces tab-level base directory metadata with:
- Smart auto-detection from terminal directory changes
- Visual indicators showing the current base directory
- Lock/unlock mechanism for manual control
- Automatic inheritance by new terminals and widgets
Features
1. Tab Metadata Fields
-
tab:basedir- Stores the current base directory path -
tab:basedirlock- Controls whether auto-detection is enabled
2. Visual UI Indicators
- Folder icon with shortened path displayed on tabs
- Clickable lock/unlock icons to control auto-detection
- Tooltip showing full path on hover
3. Tab Context Menu
Right-click on any tab to:
- Set base directory manually (file picker)
- Choose from presets (defined in
presets/tabvars.json) - Clear base directory
- Toggle lock/unlock
4. Smart Auto-Detection
When auto-detection is enabled (unlocked):
- Terminal
cdcommands automatically update the tab's base directory - Uses OSC 7 sequence (standard terminal protocol)
- Only updates if base directory is unset or set to
~ - Respects lock status to preserve manually-set directories
5. Component Integration
New Terminals:
// Inherits tab base directory as initial cmd:cwd
const tabBaseDir = tabData?.meta?.["tab:basedir"];
if (tabBaseDir) {
termBlockDef.meta["cmd:cwd"] = tabBaseDir;
}
Widgets:
// Widgets inherit base directory for file operations
if (blockDef?.meta?.view === "term" && !blockDef.meta["cmd:cwd"]) {
blockDef.meta["cmd:cwd"] = tabBaseDir;
}
File Previews:
// File preview defaults to tab base directory
if (blockDef?.meta?.view === "preview" && blockDef.meta.file === "~") {
blockDef.meta.file = tabBaseDir;
}
6. Tab Variables Configuration
New configuration section in Wave Config:
- Path:
presets/tabvars.json - Allows defining preset base directories
- JSON validation ensures correct structure
Example preset:
{
"tabvar@my-project": {
"tab:basedir": "~/Code/my-project",
"tab:basedirlock": true
}
}
Implementation Details
Backend Changes
pkg/waveobj/wtypemeta.go:
type WaveObjMeta struct {
// ... existing fields ...
TabBaseDir string `json:"tab:basedir,omitempty"`
TabBaseDirLock bool `json:"tab:basedirlock,omitempty"`
}
pkg/wconfig/defaultconfig/presets/tabvars.json:
- Example presets for common project structures
- Demonstrates locked vs unlocked directories
Frontend Changes
frontend/app/tab/tab.tsx:
- Added visual base directory indicator below tab name
- Lock/unlock icons with click handlers
- Context menu items for base directory management
- Path shortening for space efficiency (
/very/long/path→.../path)
frontend/app/tab/tab.scss:
.tab-basedir {
display: flex;
gap: 3px;
font-size: 9px;
opacity: 0.7;
.tab-basedir-lock {
cursor: pointer;
&:hover {
opacity: 1;
}
}
}
frontend/app/view/term/termwrap.ts:
- Enhanced OSC 7 handler to update tab metadata
- Checks lock status before auto-detection
- Integrates with existing terminal directory tracking
frontend/app/store/keymodel.ts:
- Modified
getDefaultNewBlockDef()to check tab base directory - Falls back to focused block's cwd if no tab base directory
frontend/app/workspace/widgets.tsx:
- Updated
handleWidgetSelect()to inherit tab base directory - Handles both terminal and preview widgets
UI/UX Design Decisions
-
Subtle Visual Indicators: Small font size and opacity prevent visual clutter while remaining informative
-
Path Shortening: Display
.../waveterminstead of full path to save horizontal space -
Clickable Lock Icons: Direct manipulation feels more intuitive than menu-only access
-
Smart Auto-Detection Logic: Only updates when not locked AND (not set OR set to
~)- Respects user's manual choices
- Provides helpful defaults for new tabs
-
Context Menu Organization: Base Directory submenu keeps related actions grouped
Testing
Manual Testing
- Create a new tab
- Open a terminal and
cdto a project directory - Observe tab base directory auto-detection
- Click lock icon to disable auto-detection
- Create new terminal - should inherit locked base directory
- Right-click tab → Base Directory → Set manually
- Verify widgets use the base directory
Edge Cases Tested
- Terminal size mismatch during restoration
- Empty or invalid base directory paths
- Switching between locked/unlocked states
- Multiple terminals in same tab with different cwds
Screenshots
Tab with Base Directory Indicator
Show tab with folder icon and path
Lock/Unlock States
Show locked vs unlocked visual states
Context Menu
Show right-click menu with Base Directory options
Migration
No migration needed - this is a new feature with backward compatibility:
- Existing tabs without
tab:basedircontinue to work normally - Auto-detection only activates when base directory is unset
Documentation
Updated CLAUDE.md with:
- Feature description
- Configuration examples
- Development guidelines
Related Issues
Closes #XXXX (if applicable)
Checklist
- [x] Code follows project conventions
- [x] TypeScript types generated from Go types
- [x] Visual design matches Wave Terminal aesthetic
- [x] Context menu integration tested
- [x] Auto-detection logic validated
- [x] Lock/unlock mechanism works correctly
- [x] Component inheritance tested (terminals, widgets, previews)
Co-Authored-By: Claude Sonnet 4.5 [email protected]
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.
Walkthrough
This pull request introduces a tab-level base directory feature across frontend and backend components. The changes enable users to set a default working directory for each tab through UI controls, store this configuration as metadata (tab:basedir and tab:basedirlock), and apply it automatically when creating term blocks or handling terminal output. New term blocks inherit the tab's base directory as their working directory when not explicitly set. An OSC 7 handler can auto-detect and update the tab base directory from terminal responses if not locked. Configuration support includes type definitions, validators, and default presets for tab variables.
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Details
Walkthrough context:
- Frontend UI additions in tab.tsx include context menu options for setting/clearing base directories, toggling lock state, and displaying base directory information alongside tab names
- Styling updates in tab.scss introduce a new tab-basedir block with folder icon and lock/unlock indicator
- Backend type definitions and constants added to support the new metadata fields
- Auto-detection logic in termwrap.ts updates tab base directory from OSC 7 terminal responses when conditions are met
- Default block creation in keymodel.ts falls back to tab base directory for working directory when not specified
- Widget selection logic in widgets.tsx applies base directory to newly selected term or preview blocks
- Configuration schema and validator added for tab variable presets
Complexity drivers:
- tab.tsx (+140 lines) contains multiple new state handlers, context menu expansions, and UI rendering logic that require careful review for correctness
- tab.scss (+53 lines) introduces new styling structure with layout, typography, and hover behaviors
- termwrap.ts conditional logic for OSC 7 handling requires understanding of the terminal protocol and metadata update conditions
- Remaining files contain straightforward type/constant additions and configuration data following established patterns
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title 'feat: add tab-scoped base directory with smart auto-detection' accurately and concisely describes the main change—adding per-tab base directory functionality with auto-detection capabilities. |
| Description check | ✅ Passed | The description comprehensively explains the feature, problem statement, solution, implementation details, testing approach, and UI/UX decisions—all directly related to the changeset. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing touches
- [ ] 📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
Closing as superseded by #2788 which includes this feature along with security fixes, validation improvements, and VS Code style tab redesign.