7.4 KiB
Finalize TOML Schema for Tab Configs — Tech Spec
Linear: APP-3575
Problem
The recursive [layout] / [[layout.panes]] TOML format becomes unreadable at depth 3+. We need to replace it with a flat [[panes]] array where nodes reference children by string ID, add tab color support, and ship a bundled Oz skill for generating configs.
Relevant code
app/src/tab_configs/tab_config.rs—TabConfig,TabConfigPaneNode,render_tab_config,resolve_pane_treeapp/src/tab_configs/tab_config_tests.rs— existing tests for parsing and renderingapp/src/tab_configs/mod.rs— public exportsapp/src/workspace/view.rs:4702-4780—open_tab_config_with_params,open_tab_config,create_and_open_new_tab_configapp/src/launch_configs/launch_config.rs—PaneTemplateType,SplitDirection,CommandTemplate(the output types tab configs produce)app/src/pane_group/mod.rs:1181-1333—pane_tree_from_template(consumesPaneTemplateTypeto create panes)app/src/user_config/native.rs:228-232—load_tab_configsapp/src/user_config/util.rs:167-180—parse_tab_config_dir_entry(TOML parsing)app/resources/tab_configs/new_tab_config_template.toml— default templateresources/bundled/skills/tab-configs/SKILL.md— bundled Oz skillapp/src/ai/skills/skill_manager.rs— bundled skill loading fromresources/bundled/skills/
Current state
Tab configs are TOML files in ~/.warp/tab_configs/. They previously used a recursive TabConfigLayout struct with nested panes: Vec<TabConfigLayout> for child splits, which became impractical at depth 3+.
There is no color field on TabConfig — launch configs support color via TabTemplate.color but tab configs don't.
There is no bundled skill for generating tab configs.
Proposed changes
1. New struct: TabConfigPaneNode
A flat node in the [[panes]] array. Distinguished as split vs leaf by the presence of split + children. No type field — all leaves are terminal panes (a type discriminator will be added when non-terminal pane types are supported).
Fields: id, pane_type (serde-renamed to type), split, children, is_focused, cwd, commands, worktree_name_autogenerated.
worktree_name_autogenerated (bool, default false): when true, the param-fill UI auto-generates the worktree branch name instead of showing a free-text input. The app infers "is this a worktree config?" by scanning commands for git worktree — no separate worktree flag is needed.
1b. New enum: TabConfigPaneType
Terminal / Agent / Cloud — maps to PaneMode at render time.
1c. New enum: PaneMode (in launch_config.rs)
Terminal (default) / Agent / Cloud — added as a field on PaneTemplateType::PaneTemplate with #[serde(default)] so launch config deserialization is unaffected.
2. Updated TabConfig
Add two fields:
color: Option<AnsiColorIdentifier>— tab color, applied after tab creation (matching the launch config pattern inopen_launch_config_window).panes: Vec<TabConfigPaneNode>— flat pane list. The first entry is the root of the pane tree.
Remove the old layout: TabConfigLayout field and the TabConfigLayout struct entirely. The legacy [layout] format is no longer supported.
3. resolve_pane_tree function
Converts the flat panes list into a PaneTemplateType tree:
- Index all nodes by ID. Reject duplicate IDs.
- Root = first entry.
- Recursively resolve: split nodes look up children by ID; leaf nodes produce
PaneTemplate { cwd, commands, is_focused }. - Focus handling: if any pane has explicit
is_focused = true, use it. Otherwise auto-focus the first leaf (matching legacyrender_layoutbehavior). - On error (missing refs, <2 children), return
Errand the caller falls back to a single empty terminal.
4. Updated render_tab_config
Always calls resolve_pane_tree. On error (empty panes, missing refs, missing type on a leaf, etc.), falls back to a single empty terminal pane with a logged warning. Leaf resolution maps TabConfigPaneType to PaneMode and sets it on the produced PaneTemplate.
5. Updated open_tab_config_with_params in workspace view
After add_tab_with_pane_layout, apply tab_config.color to the new tab's selected_color — the same pattern launch configs use at workspace/view.rs:2386.
6. Updated template
app/resources/tab_configs/new_tab_config_template.toml uses the new flat format. Active (uncommented) content is a single-pane terminal with commands = []. Commented examples show two-pane split, 2x2 grid, and parameterized configs.
7. Bundled Oz skill
resources/bundled/skills/tab-configs/SKILL.md contains the full schema reference, examples, validation rules, and common natural-language-to-layout mappings. The build script (script/prepare_bundled_resources) copies it into the app bundle automatically.
End-to-end flow
- User creates/edits a
.tomlfile in~/.warp/tab_configs/. - Filesystem watcher in
WarpConfigdetects the change and callsload_tab_configs. parse_tab_config_dir_entrycallstoml::from_str::<TabConfig>(). Thepanesfield deserializes from[[panes]]entries.- User selects the config from the
+menu →open_tab_configis called. - If params exist, the param-fill modal opens. Otherwise
open_tab_config_with_paramsis called directly. render_tab_configbuilds param contexts, callsresolve_pane_tree, returns(Option<String>, PaneTemplateType).- Workspace calls
add_tab_with_pane_layout(PanesLayout::Template(pane_template), ...). PaneGroup::pane_tree_from_templaterecursively creates panes from thePaneTemplateTypetree. ForPaneMode::Agent, it creates a terminal session then enters agent mode. ForPaneMode::Cloud, it creates an ambient agent pane viacreate_ambient_agent_terminal.- If
tab_config.coloris set, the tab'sselected_coloris updated.
Risks and mitigations
- Flat pane list validation: Invalid configs (missing refs, cycles) are caught at render time with descriptive error messages logged and a fallback to a single terminal pane. The user's tab config file is never modified.
- Skill generating invalid TOML: The skill embeds validation rules and examples. The schema is simple enough (terminal-only, no type field) that generation errors are unlikely.
Testing and validation
- Unit tests in
tab_config_tests.rs:- Parse single flat pane.
- Parse and render flat two-pane split.
- Parse and render flat 2x2 grid.
- Explicit
is_focusedhonored. - Auto-focus first leaf when no explicit focus.
- Invalid flat pane tree (missing child ref) returns error.
- Duplicate IDs rejected.
- Tab color deserialized correctly.
- Manual: build and run Warp, create tab configs, verify pane layouts open correctly.
- Manual: invoke the
tab-configsskill, confirm it generates a working config.
Follow-ups
- Add non-terminal pane types (notebook, code, settings, etc.) with a
typefield discriminator. - Consider cycle detection in validation (currently not implemented — the recursive resolver will stack overflow on cycles, but configs are small enough that this is not a practical concern).
- Consider orphan pane warnings (panes not referenced by any
childrenand not the root). - Investigate whether the Oz skill should auto-open the created config in the user's editor.