# Textile tiling workspace This document specifies how **Textile’s main editing area is tiled**, in the spirit of **Obsidian** and **VS Code**: a hierarchy of splits, draggable dividers, and one **workspace** region that consumes the chrome not reserved for navigation and status UI. It pairs with [**PRODUCT.md**](PRODUCT.md): multi-pane layout supports **living inside Textile**, **frictionless access**, and keeps the **editor performant** by isolating resize and layout logic from document editing. --- ## Layout model: binary split tree The workspace is represented as a **full binary tree** of splits: - Each **internal node** is either: - **Horizontal** (`row`): children are tiled **left** and **right**. - **Vertical** (`column`): children are tiled **above** and **below**. - Each **leaf** is a **tile** (eventually a stack of tabs; today a single content surface such as **EditPad**). Each split stores a **ratio** in `(0, 1)`: the proportion of the parent’s axis allocated to its **first** child (the second receives `1 - ratio`). Layout **pixels are derived at render time** from the workspace rectangle—so **window resize preserves proportions** automatically. Splitting adjusts the tree: - **Split right** (default): introduce or extend a horizontal split focused on the **active tile**. - **Split down**: introduce or extend a vertical split. When another tile is added “to the right,” the tree stays strictly binary by **nesting** new split nodes—same structural idea as VS Code, rather than widening a node beyond two children. ```mermaid flowchart TD shell [AppShell_future] left [LeftNav_future_placeholder] center [CentralColumn] bottom [BottomBar_future_placeholder] ws [Workspace] shell --> left shell --> center shell --> bottom center --> ws ws --> rootSplit [Split_root_H_or_V] rootSplit --> leafA [Tile_leaf_A] rootSplit --> inner [Split_nested] inner --> leafB [Tile_leaf_B] inner --> leafC [Tile_leaf_C] ``` **Rejected for v1:** arbitrary grids, floating windows—not needed to mirror Obsidian/VS Code and harder to specify and resize correctly. --- ## Terminology | Term | Meaning | | --------------- | ------------------------------------------------------------------------------------------------------------------------------- | | **Shell** | The full Electron window: future **left sidebar**, optional **top/right** strips, optional **bottom bar**, and everything else. | | **Workspace** | The remaining rectangle devoted to tiles—the **split tree root** is laid out here. | | **Tile** | A leaf: one **pane**. Later: tab strip inside the pane; for now conceptual. | | **Split node** | Internal node with **exactly two** children and one **ratio** along the split axis. | | **Active tile** | The focused leaf; splits and tab actions attach here unless multi-select appears later (out of scope). | --- ## Focus - Exactly **one active tile** in the workspace. - **Click** (or equivalent) inside a tile moves focus when tiling UI exists. --- ## Creating tiles - **Split right**: On the active leaf, replace it with a **horizontal** split: **left** = previous tile content, **right** = new empty tile. - **Split down**: Same with a **vertical** split: **top** = previous, **bottom** = new. - **Initial ratio**: fixed **50/50 (`0.5`)** for every new split (predictable behavior). - **Target**: Always the **active tile** unless a future multi-pane selection model is introduced. --- ## Minimum tile sizes - Each tile defines **minimum width** and **minimum height** (pixels). **Policies** - While **dragging a splitter**, **clamp** the ratio so no tile violates minimums. - If a requested **split** cannot satisfy minimums given the current workspace size, **refuse** the operation and give **clear user feedback**. --- ## Resizing splitters - Only **borders between two sibling children** expose a draggable **splitter**. - Dragging adjusts that parent node’s **ratio**; **nothing else** in the tree is reinterpreted besides layout pass. - A **vertical** divider adjusts **horizontal** apportionment; a **horizontal** divider adjusts **vertical** apportionment. **Possible later enhancement:** double-click a splitter reset to **50/50** (not committed for first implementation). --- ## Window resize - On container size change (window or future chrome collapse), **recompute geometry** while **preserving stored ratios**. - Subtract reserved areas for sidebar and bottom bar once they exist—**workspace** receives the leftover box only. - **No automatic equalization** unless a deliberate **reset layout** command exists in the future. --- ## Controls (conceptual—not all implemented yet) Users will manipulate tiles via: 1. **In-app UI** (buttons, toolbar, pane headers). 2. **Menu bar** (Electron). 3. **Keyboard shortcuts**. 4. **Tab drag-and-drop** (after tabs exist)—drag **tabs**, not tiles, to move editors between tiles or create splits according to drag targets. Drag-and-drop is specified here but deferred until tabs exist. ### Command identifiers (single source of truth) All surfaces should converge on stable **workspace commands**, for example: | Command ID | Intended behavior | | ---------------------------- | --------------------------------------------------------------- | | `workspace.splitActiveRight` | Split active tile to the right. | | `workspace.splitActiveDown` | Split active tile downward. | | `workspace.closeActiveTile` | Close / merge active pane (behavior TBD when one tile remains). | | `workspace.focusNextTile` | Cycle focus between tiles (order TBD). | | `workspace.setActiveTile` | Focus tile by id/path (called from clicks). | Ratio updates during splitter drags belong to **`workspace.resizeDivider`** (or equivalently mutate split state keyed by splitter id)—exact API can follow concrete state shape. These IDs should ultimately register in the broader **actions** system described under [Related: command surface](#related-command-surface-shortcuts-menu-palette). --- ## Near-term non-goals The following are **explicitly postponed** relative to earliest tiling milestones: - Full **file tree** in the left sidebar (placeholder region only at first). - **Bottom bar** content (word count, etc.). - **Top/right** decorative or tool regions. - **Tabs** UI and **tab dragging**. They stay in shell/wireframe form until corresponding product phases land. --- ## Implementation plan | Phase | Scope | | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | **1** | **Shell layout:** reserve regions for future left nav and bottom bar (may be zero-width placeholders); **workspace** consumes the remainder. | | **2** | **Split tree + render:** persist tree state; layout via flex or absolute geometry from ratios; leaves mount real content (**EditPad**) or placeholders. | | **3** | **Splitters:** pointer drag on dividers; clamp to min tile sizes; cursors and hit targets. | | **4** | **Commands + keyboard shortcuts:** wired to workspace command IDs; focus moves and close semantics. | | **5** | **Electron menu hooks:** menus dispatch the same commands as keyboard and UI. | | **6** | **Tabs per tile** plus **drag-and-drop** of tabs across tiles/split hints. | | **7** | **Persist layout** to vault or global settings—format TBD. | --- ## Related: command surface (shortcuts, menu, palette) **Next architectural step (after tiling implementation begins):** Introduce a single **catalog of actions/commands** whose entries carry at least: - Stable **id** (including `workspace.*` above). - Human-readable **title** / description. - **Handler** runnable from renderer (or delegated to main where needed). - Optional **shortcut** metadata for registration with OS/Electron accelerator tables. - **Menu** grouping for the application menu. - Optional **`enabled`** predicate (`when`) **Electron menus**, **global shortcuts**, contextual UI triggers, and a future **command palette** should all enumerate and invoke this catalog so labels, accelerator strings, and behavior live in **one place**. Tiling and editor features register side by side (e.g. “Toggle sidebar,” “Split right”) so the palette can search everything. Details are intentionally out of scope for this tiling spec; document here only so product and roadmap stay aligned. --- ## Decisions (record) | Decision | Choice | | --------------------------- | ------------------------------ | | Tree shape | Strict **binary** splits only. | | New split ratio | Fixed **0.5** initially. | | Min size violation on drag | **Clamp** ratios. | | Min size violation on split | **Refuse** with feedback. | --- ## Changelog mindset Iterate this file when UX rules tighten (e.g. tab drag targets, persisted layout schema, or sidebar width behavior). Implementation PRs should reference updated sections rather than scattering layout rules elsewhere.