# zidane-ui/workspace/headless

Import: `import { … } from 'zidane-ui/workspace/headless'`. Source: [ui/src/workspace/headless/index.ts](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/index.ts).

## Functions

### `activeTabOf`

Function.

The tab the panel shows: the focused group's pick, else its first.

```ts
function activeTabOf(t: PanelTree): string | null
```

**Parameters**

- `t` (`PanelTree`): The stored tree.

**Returns** `string | null`

Source: [ui/src/workspace/headless/panel-tree.ts:82](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L82)

### `appendPanelPage`

Function.

`base` with `page` after it: its rows join the sections of the same id (a row already shown, by id, is not
repeated), its other sections follow, and its `more` replaces `base`'s. The header (title, controls, search)
stays `base`'s. Past [`PANEL_PAGED_ITEMS`](https://zidane.si/docs/reference/ui-workspace-headless.md#PANEL_PAGED_ITEMS) rows, the rest is dropped and there is no more.

```ts
function appendPanelPage(base: PanelContent, page: PanelContent): PanelContent
```

**Parameters**

- `base` (`PanelContent`): The content shown.
- `page` (`PanelContent`): The next page.

**Returns** `PanelContent`

Source: [ui/src/workspace/headless/pages.ts:19](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/pages.ts#L19)

### `bindingInFolder`

Function.

Whether `binding` is one of `cwd`'s.

```ts
function bindingInFolder(binding: string, cwd: string): boolean
```

**Parameters**

- `binding` (`string`): A [`bindingKey`](https://zidane.si/docs/reference/ui-workspace-headless.md#bindingKey).
- `cwd` (`string`): The folder.

**Returns** `boolean`

Source: [ui/src/workspace/headless/contents.ts:36](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/contents.ts#L36)

### `bindingKey`

Function.

The binding a panel's content belongs to: the folder it loads against, and the session (or none).

```ts
function bindingKey(cwd: string, sessionId: string | null): string
```

**Parameters**

- `cwd` (`string`): The folder.
- `sessionId` (`string | null`): The session, or null.

**Returns** `string`

Source: [ui/src/workspace/headless/contents.ts:26](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/contents.ts#L26)

### `chatFloor`

Function.

The narrowest the chat may get with `share` set or not.

```ts
function chatFloor(windowWidth: number, wide: boolean): number
```

**Parameters**

- `windowWidth` (`number`): The window's width (px).
- `wide` (`boolean`): The panel was dragged past `CHAT_MIN` (its `share` is set).

**Returns** `number`

Source: [ui/src/workspace/headless/resolve.ts:35](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L35)

### `closeTab`

Function.

Remove a tab. A group left empty collapses; when it was focused, the group before it takes the focus.

```ts
function closeTab(t: PanelTree, tabId: string): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `tabId` (`string`): The tab.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:131](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L131)

### `createLayoutMemory`

Function.

A new layout memory over `options.storage`.

```ts
function createLayoutMemory(options: LayoutMemoryOptions): LayoutMemory
```

**Parameters**

- `options` (`LayoutMemoryOptions`): The storage, and what to do when a write fails.

**Returns** `LayoutMemory`

Source: [ui/src/workspace/headless/layout-memory.ts:298](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L298)

### `createMemoryLayouts`

Function.

Layouts kept in memory for the page's life.

```ts
function createMemoryLayouts(options?: LayoutStoreOptions): MemoryLayouts
```

**Parameters**

- `options?` (`LayoutStoreOptions`, default `{}`): What an unknown scope reads.

**Returns** `MemoryLayouts`

Source: [ui/src/workspace/headless/layouts.ts:77](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L77)

### `createOpenTarget`

Function.

Builds the open decisions over the app's setting, its OS actions and its panel actions.

```ts
function createOpenTarget(deps: OpenTargetDeps): OpenTargetApi
```

**Parameters**

- `deps` (`OpenTargetDeps`): The [`OpenTargetDeps`](https://zidane.si/docs/reference/ui-workspace-headless.md#OpenTargetDeps).

**Returns** `OpenTargetApi`

Source: [ui/src/workspace/headless/open-target.ts:78](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/open-target.ts#L78)

### `createPanelContents`

Function.

Creates an empty [`PanelContents`](https://zidane.si/docs/reference/ui-workspace-headless.md#PanelContents).

```ts
function createPanelContents(): PanelContents
```

**Returns** `PanelContents`

Source: [ui/src/workspace/headless/contents.ts:64](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/contents.ts#L64)

### `createPanelFocusStore`

Function.

Creates an empty [`PanelFocusStore`](https://zidane.si/docs/reference/ui-workspace-headless.md#PanelFocusStore).

```ts
function createPanelFocusStore(): PanelFocusStore
```

**Returns** `PanelFocusStore`

Source: [ui/src/workspace/headless/panel-focus.ts:34](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-focus.ts#L34)

### `createStorageLayouts`

Function.

Layouts kept in a `Storage` under one key, as `{ version: 1, scopes }`.
Each read is validated (`parseWorkspaceLayout`); unreadable or foreign
data reads as nothing stored. A write from another tab (the `storage`
event) is read again and notified. A full or missing storage keeps the
change in memory.

```ts
function createStorageLayouts(options: StorageLayoutsOptions): WorkspaceLayouts
```

**Parameters**

- `options` (`StorageLayoutsOptions`): The storage, the key and what an unknown scope reads.

**Returns** `WorkspaceLayouts`

Source: [ui/src/workspace/headless/layouts.ts:145](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L145)

### `createWorkspaceOps`

Function.

Creates the panel actions over a layout store.

```ts
function createWorkspaceOps<S extends WorkspaceScope>(
  options: WorkspaceOpsOptions<S>,
): WorkspaceOps
```

**Type parameters**

- `S` (`extends WorkspaceScope`)

**Parameters**

- `options` (`WorkspaceOpsOptions<S>`): The store, the scope on screen and what is shown.

**Returns** `WorkspaceOps`

Source: [ui/src/workspace/headless/ops.ts:89](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/ops.ts#L89)

### `dropTab`

Function.

A dragged tab dropped. On its own strip it is a reorder: nothing else
changes (the group keeps showing what it showed). Anywhere else it moves
(or splits off) and is shown, focused, where it lands.

```ts
function dropTab(t: PanelTree, tabId: string, to: TabDrop): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `tabId` (`string`): The tab.
- `to` (`TabDrop`): Where it was dropped.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:178](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L178)

### `findGroup`

Function.

The group holding `tabId`, or null.

```ts
function findGroup(node: PanelNode, tabId: string): PanelGroup | null
```

**Parameters**

- `node` (`PanelNode`): The tree.
- `tabId` (`string`): The tab.

**Returns** `PanelGroup | null`

Source: [ui/src/workspace/headless/tree.ts:85](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L85)

### `focusedGroup`

Function.

The focused group (the first one if the stored id is stale).

```ts
function focusedGroup(t: PanelTree): PanelGroup
```

**Parameters**

- `t` (`PanelTree`): The stored tree.

**Returns** `PanelGroup`

Source: [ui/src/workspace/headless/panel-tree.ts:73](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L73)

### `focusTab`

Function.

Show `tabId` in its group and focus that group (no-op when the tree lacks it).

```ts
function focusTab(t: PanelTree, tabId: string): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `tabId` (`string`): The tab.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:98](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L98)

### `groupIdsOf`

Function.

The ids of every group under `node`, in order.

```ts
function groupIdsOf(node: PanelViewNode<WorkspaceTab>): string[]
```

**Parameters**

- `node` (`PanelViewNode<WorkspaceTab>`): The tree as shown.

**Returns** `string[]`

Source: [ui/src/workspace/headless/view.ts:154](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L154)

### `groupsOf`

Function.

Every group, depth first (top/left to bottom/right).

```ts
function groupsOf(node: PanelNode): PanelGroup[]
```

**Parameters**

- `node` (`PanelNode`): The tree.

**Returns** `PanelGroup[]`

Source: [ui/src/workspace/headless/tree.ts:66](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L66)

### `groupViews`

Function.

Every group on screen, top/left to bottom/right.

```ts
function groupViews<T extends WorkspaceTab>(node: PanelViewNode<T>): PanelGroupView<T>[]
```

**Type parameters**

- `T` (`extends WorkspaceTab`)

**Parameters**

- `node` (`PanelViewNode<T>`): The tree as shown.

**Returns** `PanelGroupView<T>[]`

Source: [ui/src/workspace/headless/view.ts:145](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L145)

### `layoutProjectKey`

Function.

The project a scope's shared layout lives under: the root, qualified by the server for a server
session (the same path on two hosts is two projects).

```ts
function layoutProjectKey(scope: LayoutScope): string
```

**Parameters**

- `scope` (`LayoutScope`): The scope.

**Returns** `string`

Source: [ui/src/workspace/headless/layout-memory.ts:101](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L101)

### `layoutScopeKey`

Function.

A scope's identity: the session key when bound, else `draft:<id>` or `project:<root>`.

```ts
function layoutScopeKey(scope: LayoutScope): string
```

**Parameters**

- `scope` (`LayoutScope`): The scope.

**Returns** `string`

Source: [ui/src/workspace/headless/layout-memory.ts:77](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L77)

### `maxColumnWidth`

Function.

Largest width a column may take while the chat keeps `chatMin`.

```ts
function maxColumnWidth(
  windowWidth: number,
  otherColumn: number,
  hardMax: number,
  chatMin?: number,
): number
```

**Parameters**

- `windowWidth` (`number`): The window's width (px).
- `otherColumn` (`number`): The other column's width (px).
- `hardMax` (`number`): The column's own maximum (px).
- `chatMin?` (`number`, default `CHAT_MIN`): The chat's floor (px).

**Returns** `number`

Source: [ui/src/workspace/headless/resolve.ts:126](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L126)

### `moveTabBy`

Function.

`tabId` moved `delta` slots within `visible` (its group's tabs on screen, in order); hidden tabs keep theirs.

```ts
function moveTabBy(
  t: PanelTree,
  tabId: string,
  visible: readonly string[],
  delta: number,
): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `tabId` (`string`): The tab.
- `visible` (`readonly string[]`): Its group's tabs on screen, in order.
- `delta` (`number`): Slots to move: negative is left.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:151](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L151)

### `panelDragResult`

Function.

What a panel drag to `width` saves: past the usual chat floor it becomes a
share of the room beside the sidebar, otherwise a plain width.

```ts
function panelDragResult(
  windowWidth: number,
  sidebarWidth: number,
  width: number,
): { panelShare: number | null; panelWidth: number }
```

**Parameters**

- `windowWidth` (`number`): The window's width (px).
- `sidebarWidth` (`number`): The sidebar's width (px).
- `width` (`number`): Where the drag ended (px).

**Returns** `{ panelShare: number | null; panelWidth: number }`

Source: [ui/src/workspace/headless/resolve.ts:138](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L138)

### `panelTabMark`

Function.

The mark a panel's tab draws: `icon.gui` as an icon, else its extension's mark. `undefined`: a tab with
neither (an app's tab without an icon).

```ts
function panelTabMark(tab: PanelTabMarkSource): ExtensionMark | undefined
```

**Parameters**

- `tab` (`PanelTabMarkSource`): The tab.

**Returns** `ExtensionMark | undefined`

Source: [ui/src/workspace/headless/tab-mark.ts:18](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tab-mark.ts#L18)

### `panelTreeOf`

Function.

One group holding `tabs`, showing `active`.

```ts
function panelTreeOf(tabs: readonly string[], active?: string | null): PanelTree
```

**Parameters**

- `tabs` (`readonly string[]`): The group's tab ids, in order.
- `active?` (`string | null`, default `null`): The tab it shows (null: its first).

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:51](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L51)

### `parseLayout`

Function.

A stored layout, validated field by field; older shapes migrate (the flat tab strip, the bottom group).

```ts
function parseLayout(raw: unknown): SessionLayout
```

**Parameters**

- `raw` (`unknown`): What was stored (parsed JSON).

**Returns** `SessionLayout`

Source: [ui/src/workspace/headless/layout-memory.ts:204](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L204)

### `parsePanelTree`

Function.

A stored panel tree, validated. Older layouts migrate once, on read: a bare
group tree (no focus, no counter) and, before groups, the flat strip
(`panelTabs` + `panelActiveId`), which becomes one group.

```ts
function parsePanelTree(
  raw: unknown,
  legacy: { active: string | null; tabs: readonly string[] },
): PanelTree
```

**Parameters**

- `raw` (`unknown`): What was stored.
- `legacy` (`{ active: string | null; tabs: readonly string[] }`): The flat strip an older record kept, used when `raw` has no tree.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:250](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L250)

### `parseTree`

Function.

A stored tree, validated; null when it is not one.

```ts
function parseTree(raw: unknown): PanelNode | null
```

**Parameters**

- `raw` (`unknown`): What was stored.

**Returns** `PanelNode | null`

Source: [ui/src/workspace/headless/tree.ts:233](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L233)

### `parseWorkspaceLayout`

Function.

A stored layout, validated field by field: what cannot be read falls back
to `fallback`'s, so stale or foreign data never shows an impossible state.

```ts
function parseWorkspaceLayout(raw: unknown, fallback?: WorkspaceLayout): WorkspaceLayout
```

**Parameters**

- `raw` (`unknown`): What was stored.
- `fallback?` (`WorkspaceLayout`, default `EMPTY_WORKSPACE_LAYOUT`): The layout a missing field takes its value from.

**Returns** `WorkspaceLayout`

Source: [ui/src/workspace/headless/layouts.ts:32](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L32)

### `pickTab`

Function.

Pin `tabId` (into `groupId`, default the focused group) and show it.

```ts
function pickTab(t: PanelTree, tabId: string, groupId?: string): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `tabId` (`string`): The tab.
- `groupId?` (`string`): The group it joins when the tree lacks it.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:121](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L121)

### `pinTab`

Function.

Add `tabId` to `groupId` (default: the focused group) unless the tree has it.

```ts
function pinTab(t: PanelTree, tabId: string, groupId?: string): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `tabId` (`string`): The tab.
- `groupId?` (`string`): The group it joins.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:110](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L110)

### `resizeSplit`

Function.

New shares for split `splitId` as shown: `shown` lists each shown child's group ids (`resizeShownSplit`).

```ts
function resizeSplit(
  t: PanelTree,
  splitId: string,
  shown: readonly readonly string[][],
  sizes: readonly number[],
): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `splitId` (`string`): The split.
- `shown` (`readonly readonly string[][]`): Each shown child's group ids.
- `sizes` (`readonly number[]`): The shown children's new shares.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:212](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L212)

### `resolveLayout`

Function.

Fits the sidebar, the chat and the side panel into the window (see the module comment for the order things give).

```ts
function resolveLayout(w: LayoutWants): LayoutResolved
```

**Parameters**

- `w` (`LayoutWants`): The window's width and each column's wanted width.

**Returns** `LayoutResolved`

Source: [ui/src/workspace/headless/resolve.ts:60](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L60)

### `sameAttention`

Function.

Whether two attentions show the same. `live` is part of the value: a flip from counting to "in use" must land.

```ts
function sameAttention(a: TabAttention, b: TabAttention): boolean
```

**Parameters**

- `a` (`TabAttention`): One attention.
- `b` (`TabAttention`): The other.

**Returns** `boolean`

Source: [ui/src/workspace/headless/view.ts:90](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L90)

### `sameWorkspaceLayout`

Function.

Whether two layouts hold the same thing (the tree compared by value).

```ts
function sameWorkspaceLayout(a: WorkspaceLayout, b: WorkspaceLayout): boolean
```

**Parameters**

- `a` (`WorkspaceLayout`): One layout.
- `b` (`WorkspaceLayout`): The other.

**Returns** `boolean`

Source: [ui/src/workspace/headless/layouts.ts:51](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L51)

### `setPendingReveal`

Function.

Remembers where to reveal `absPath` when the Files tab opens it next.

```ts
function setPendingReveal(absPath: string, at: { column?: number; line: number }): void
```

**Parameters**

- `absPath` (`string`): The file's absolute path.
- `at` (`{ column?: number; line: number }`): The position (1-based; the column defaults to 1).

Source: [ui/src/workspace/headless/reveal-at.ts:14](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/reveal-at.ts#L14)

### `splitPanelWidth`

Function.

The width a panel opens at when its project never sized it: half the room
the sidebar leaves, so the panel and the chat start side by side at 50/50.
A plain width, recomputed as the window changes: the chat's floor still
squeezes it, then floats it, in a narrow window.

```ts
function splitPanelWidth(windowWidth: number, sidebarWidth: number, min: number): number
```

**Parameters**

- `windowWidth` (`number`): The window's width (px).
- `sidebarWidth` (`number`): The sidebar's width (px), 0 when closed.
- `min` (`number`): The panel's minimum (px).

**Returns** `number`

Source: [ui/src/workspace/headless/resolve.ts:114](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L114)

### `splitTab`

Function.

Take `tabId` out into a new group beside (`right`) or below (`bottom`) its own, focused.

```ts
function splitTab(t: PanelTree, tabId: string, side: SplitSide): PanelTree
```

**Parameters**

- `t` (`PanelTree`): The stored tree.
- `tabId` (`string`): The tab.
- `side` (`SplitSide`): Where the new group goes.

**Returns** `PanelTree`

Source: [ui/src/workspace/headless/panel-tree.ts:199](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L199)

### `splitView`

Function.

The split `id` as shown, null when it is not on screen.

```ts
function splitView<T extends WorkspaceTab>(
  node: PanelViewNode<T>,
  id: string,
): PanelSplitView<T> | null
```

**Type parameters**

- `T` (`extends WorkspaceTab`)

**Parameters**

- `node` (`PanelViewNode<T>`): The tree as shown.
- `id` (`string`): The split.

**Returns** `PanelSplitView<T> | null`

Source: [ui/src/workspace/headless/view.ts:162](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L162)

### `takePendingReveal`

Function.

The position waiting for `absPath`, removed as it is returned; `null` when none waits.

```ts
function takePendingReveal(absPath: string): { column: number; line: number } | null
```

**Parameters**

- `absPath` (`string`): The file's absolute path.

**Returns** `{ column: number; line: number } | null`

Source: [ui/src/workspace/headless/reveal-at.ts:23](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/reveal-at.ts#L23)

### `treeTabs`

Function.

Every tab in the tree, group by group.

```ts
function treeTabs(node: PanelNode): string[]
```

**Parameters**

- `node` (`PanelNode`): The tree.

**Returns** `string[]`

Source: [ui/src/workspace/headless/tree.ts:75](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L75)

### `viewOf`

Function.

The tree as shown: only tabs the catalog offers here, groups left empty
collapsed, each group's active tab resolved (its pick, else its first).
With `fillEmpty` a strip whose tabs are all hidden here shows the
catalog's first tab instead (not stored until the user picks).

```ts
function viewOf<T extends WorkspaceTab>(
  tree: PanelNode,
  catalog: readonly T[],
  fillEmpty?: boolean,
): PanelViewNode<T> | null
```

**Type parameters**

- `T` (`extends WorkspaceTab`)

**Parameters**

- `tree` (`PanelNode`): The stored tree.
- `catalog` (`readonly T[]`): The tabs offered here.
- `fillEmpty?` (`boolean`, default `false`): Show the first offered tab when no stored tab is offered.

**Returns** `PanelViewNode<T> | null`

Source: [ui/src/workspace/headless/view.ts:116](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L116)

## Constants

### `CHAT_MIN`

Constant.

The window's horizontal budget: sidebar | chat | side panel.

The chat never drops below its floor (`CHAT_MIN`). When the wanted widths do not fit,
give way in this order: shrink the panel to its minimum, shrink the
sidebar to its minimum, float the panel over the chat (`overlay`), and
finally collapse the sidebar. Pure; the App applies the result.

A panel dragged wider than that floor allows is kept as a share of the
room beside the sidebar (`panel.share`): it follows the window's width,
and the chat's floor drops to `CHAT_MIN_SHARE` of the window.

```ts
const CHAT_MIN = 480
```

Source: [ui/src/workspace/headless/resolve.ts:13](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L13)

### `CHAT_MIN_SHARE`

Constant.

The chat's floor once the user has dragged the panel past `CHAT_MIN`.

```ts
const CHAT_MIN_SHARE = 0.1
```

Source: [ui/src/workspace/headless/resolve.ts:15](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L15)

### `DEFAULT_WORKSPACE_LABELS`

Constant.

The default English strings of the side panel. Override any of them through `WorkspaceProvider`.

```ts
const DEFAULT_WORKSPACE_LABELS: { panelAdd: string; panelChartLabel: string; panelClose: string; panelCloseTab: (label: string) => string; panelContentEmpty: string; panelContentNone: string; panelExpand: (expanded: boolean) => 'Exit full screen panels' | 'Full screen panels'; panelFilterClear: string; panelFiltersClear: string; panelFilterSummary: (label: string, picked: readonly string[]) => string; panelGroupTabs: (index: number) => string; panelHeatmapDay: (day: string) => string; panelHeatmapLabel: string; panelHomeLabel: string; panelLoadingMore: string; panelLoadMore: string; panelPopOut: string; panelRegion: string; panelResizeGroups: string; panelRowActions: (row: string) => string; panelRowCollapse: string; panelRowExpand: string; panelRowShowMore: string; panelSearch: string; panelSearchClear: string; panelShow: string; panelShowWaiting: (count: number) => string; panelTabInUse: string; panelTabs: string; panelTabUpdated: string; panelToggle: (open: boolean) => 'Hide side panel' | 'Show side panel'; panelToggleCount: (count: number) => string; panelUnavailable: string }
```

Source: [ui/src/workspace/headless/labels.ts:8](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/labels.ts#L8)

### `EMPTY_CONTENT_ENTRY`

Constant.

A panel with nothing loaded.

```ts
const EMPTY_CONTENT_ENTRY: PanelContentEntry
```

Source: [ui/src/workspace/headless/contents.ts:17](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/contents.ts#L17)

### `EMPTY_LAYOUT`

Constant.

A layout with every default (the side panel on Artifacts, closed).

```ts
const EMPTY_LAYOUT: SessionLayout
```

Source: [ui/src/workspace/headless/layout-memory.ts:151](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L151)

### `EMPTY_WORKSPACE_LAYOUT`

Constant.

A layout with nothing stored: closed, no tab.

```ts
const EMPTY_WORKSPACE_LAYOUT: WorkspaceLayout
```

Source: [ui/src/workspace/headless/layouts.ts:12](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L12)

### `LAYOUT_STORAGE_KEY`

Constant.

The storage key the layouts live under.

```ts
const LAYOUT_STORAGE_KEY = 'zg:layout:v2'
```

Source: [ui/src/workspace/headless/layout-memory.ts:111](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L111)

### `OVERLAY_CHAT_PEEK`

Constant.

A floating panel always leaves this much of the chat visible to click on.

```ts
const OVERLAY_CHAT_PEEK = 160
```

Source: [ui/src/workspace/headless/resolve.ts:17](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L17)

### `PANEL_PAGED_ITEMS`

Constant.

Rows kept across every page of one panel, at most: past it, Show more goes away.

```ts
const PANEL_PAGED_ITEMS = 1000
```

Source: [ui/src/workspace/headless/pages.ts:7](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/pages.ts#L7)

### `WORKSPACE_STORAGE_KEY`

Constant.

The default storage key of [`createStorageLayouts`](https://zidane.si/docs/reference/ui-workspace-headless.md#createStorageLayouts).

```ts
const WORKSPACE_STORAGE_KEY = 'zidane-ui:workspace:v1'
```

Source: [ui/src/workspace/headless/layouts.ts:122](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L122)

## Interfaces

### `LayoutMemory`

Interface.

One window's layout memory; see the top of this file.

```ts
interface LayoutMemory
```

**Properties**

- `adoptScopeLayout` (`(scope: LayoutScope) => void`): Give `scope` its own copy of the layout it shows now; no-op once it owns one.
- `forgetScopeLayout` (`(target: string | LayoutScope) => void`): Drop a session's (or scope's) own layout, e.g. when the session is deleted.
- `getScopeLayout` (`(scope: LayoutScope | null) => SessionLayout`): A scope's layout (`EMPTY_LAYOUT` without one); the same object until it changes.
- `getSessionLayout` (`(sessionId: string | null) => SessionLayout`): A session's layout (`EMPTY_LAYOUT` without one), through the mapping.
- `moveScopeLayout` (`(from: LayoutScope, to: LayoutScope) => void`): A draft became a session: the draft's own fields move onto the session's record.
- `registerSessionScope` (`(scope: LayoutScope) => void`): A session just created, before the list catches up: it resolves to its project and takes its own copy.
- `reset` (`() => void`): Forget the cache, the mapping and the snapshots (tests).
- `scopeLayoutId` (`(scope: LayoutScope) => string`): The identity a scope's composed layout is cached under (what a reader keys its memo on).
- `setSessionProjects` (`(pairs: Iterable<readonly [string, string | null | undefined]>) => void`): Session → project (cwd) pairs: the pre-scope form of `setSessionScopes` (root = cwd).
- `setSessionScopes` (`(scopes: Iterable<LayoutScope>) => void`): The session → scope mapping (every listed session's scope); layouts re-resolve when it changes.
- `subscribeLayouts` (`(listener: () => void) => () => void`): Calls `listener` after any layout changed, in this window or another. Returns the unsubscribe.
- `sweepScopeLayouts` (`(localSessions: () => Promise<readonly string[]>) => Promise<void>`): Once per launch: drop the draft records and the records of local sessions `localSessions` no longer lists.
- `updateScopeLayout` (`(scope: LayoutScope, change: Partial<SessionLayout> | ((current: SessionLayout) => LayoutPatch)) => void`): Change a scope's layout; a function `change` reads the layout stored right now (read-modify-write).
- `updateSessionLayout` (`(sessionId: string, change: Partial<SessionLayout> | ((current: SessionLayout) => LayoutPatch)) => void`): Change a session's layout, through the mapping.

Source: [ui/src/workspace/headless/layout-memory.ts:262](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L262)

### `LayoutMemoryOptions`

Interface.

Options of [`createLayoutMemory`](https://zidane.si/docs/reference/ui-workspace-headless.md#createLayoutMemory).

```ts
interface LayoutMemoryOptions
```

**Properties**

- `onSaveError?` (`(error: unknown) => void`): A write that failed (quota, disabled storage): the layout is kept for this window only. Default: a console warning.
- `storage` (`() => Pick<Storage, 'getItem' | 'setItem'>`): Where the layouts are kept, read on each access (`() => localStorage`). Reads and writes that throw are survived.

Source: [ui/src/workspace/headless/layout-memory.ts:254](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L254)

### `LayoutResolved`

Interface.

The columns' effective widths and modes.

```ts
interface LayoutResolved
```

**Properties**

- `chatMin` (`number`): The floor the chat was held to (`chatFloor`).
- `chatWidth` (`number`): The room left to the chat (px).
- `panelMode` (`'overlay' | 'docked'`): `overlay`: drawn over the chat at its wanted width instead of taking room.
- `panelWidth` (`number`): The panel's width (px): the room it takes docked, its drawn width floating; 0 when closed.
- `sidebarForcedCollapsed` (`boolean`): The sidebar had to close for the chat to fit.
- `sidebarWidth` (`number`): The sidebar's width (px); 0 when closed.

Source: [ui/src/workspace/headless/resolve.ts:40](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L40)

### `LayoutScope`

Interface.

What a layout is attached to: a folder, optionally bound to a session (or
to the new-chat draft that will become one). The desktop app's `Scope` has
this shape, so it passes its own.

```ts
interface LayoutScope
```

**Properties**

- `cwd` (`string`): Where things open.
- `draft?` (`string`): The new-chat draft's id (never together with `session`).
- `root` (`string`): The project root (worktrees fold into their main checkout).
- `session?` (`string`): Bound to this session (its key).
- `workspace?` (`string`): Where a bound session's files live when not on this machine.

Source: [ui/src/workspace/headless/layout-memory.ts:56](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L56)

### `LayoutStoreOptions`

Interface.

Options shared by the layout stores.

```ts
interface LayoutStoreOptions
```

**Properties**

- `initial?` (`(scope: WorkspaceScope) => WorkspaceLayout`): What a scope with nothing stored reads. Default: [`EMPTY_WORKSPACE_LAYOUT`](https://zidane.si/docs/reference/ui-workspace-headless.md#EMPTY_WORKSPACE_LAYOUT).

Source: [ui/src/workspace/headless/layouts.ts:61](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L61)

### `LayoutWants`

Interface.

What [`resolveLayout`](https://zidane.si/docs/reference/ui-workspace-headless.md#resolveLayout) fits: the window, and each column's wanted width and floor.

```ts
interface LayoutWants
```

**Properties**

- `panel` (`{ min: number; open: boolean; share?: number | null; width: number }`): `share`: the panel's share of the window minus the sidebar, set when dragged past `CHAT_MIN`.
- `sidebar` (`{ min: number; open: boolean; width: number }`): The sidebar: shown, its wanted width, its minimum.
- `windowWidth` (`number`): The window's width (px).

Source: [ui/src/workspace/headless/resolve.ts:20](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/resolve.ts#L20)

### `MemoryLayouts`

Interface.

A [`WorkspaceLayouts`](https://zidane.si/docs/reference/ui-workspace-headless.md#WorkspaceLayouts) kept in memory, plus a way to write as another window would.

```ts
interface MemoryLayouts extends WorkspaceLayouts
```

**Properties**

- `get` (`(scope: WorkspaceScope) => WorkspaceLayout`, from `WorkspaceLayouts`): The scope's layout as stored now. The same object while it is unchanged.
- `set` (`(key: string, layout: WorkspaceLayout) => void`): Replaces a scope's layout from outside, and notifies.
- `subscribe` (`(onChange: () => void) => () => void`, from `WorkspaceLayouts`): Called after any layout changed, here or in another window. Returns the unsubscribe.
- `update` (`(scope: WorkspaceScope, change: (stored: WorkspaceLayout) => Partial<WorkspaceLayout>) => void`, from `WorkspaceLayouts`): Applies `change` to the layout stored now (read-modify-write, so a stale
  copy is never written back over another window's change). A change that
  changes nothing writes nothing.

Source: [ui/src/workspace/headless/layouts.ts:67](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L67)

### `OpenTargetApi`

Interface.

The decisions [`createOpenTarget`](https://zidane.si/docs/reference/ui-workspace-headless.md#createOpenTarget) returns.

```ts
interface OpenTargetApi
```

**Properties**

- `diffs` (`() => void`): Reveals the Diffs tab.
- `external` (`(absPath: string) => Promise<boolean>`): Opens an absolute path in the editor whatever the setting. `false`, opening nothing, for a sandbox file.
- `file` (`(absPath: string, event?: OpenTargetEvent, at?: { column?: number; line: number }) => Promise<OpenTarget>`): Opens an absolute path. `editor` → `system.openPath`; `panel` → reveal the
  Files tab (the tab reads the file itself, hence the returned target so
  callers can hand over).
- `files` (`() => void`): Reveals the Files tab.
- `folder` (`(absPath: string, root: string | null, event?: OpenTargetEvent) => Promise<boolean>`): Opens a folder. `panel`: inside `root` (the chat's folder) the Files tab
  selects and expands it in the tree; outside it, the file manager (the
  tree only lists `root`). `editor`: the editor. `false` when nothing
  could show it.
- `resolve` (`(event?: OpenTargetEvent) => OpenTarget`): Effective target for a click; a modifier inverts the setting.

Source: [ui/src/workspace/headless/open-target.ts:44](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/open-target.ts#L44)

### `OpenTargetDeps`

Interface.

What [`createOpenTarget`](https://zidane.si/docs/reference/ui-workspace-headless.md#createOpenTarget) needs.

```ts
interface OpenTargetDeps
```

**Properties**

- `setting` (`OpenTarget`): The app's setting: where a plain click opens.
- `system?` (`Pick<FilesSystem, 'revealPath' | 'openPath'>`): Opens and reveals paths on this computer (`FilesHost.system`). Absent: editor clicks open nothing.
- `openProactive` (`(id: string) => void`): Shows a tab without yanking one the user picked since.
- `revealFile` (`(absPath: string) => void`): Shows the Files tab on an absolute path (a trailing `/`: a folder to select and expand).

Source: [ui/src/workspace/headless/open-target.ts:32](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/open-target.ts#L32)

### `OpenTargetEvent`

Interface.

A click's modifiers.

```ts
interface OpenTargetEvent
```

**Properties**

- `ctrlKey?` (`boolean`): Ctrl was held.
- `metaKey?` (`boolean`): ⌘ was held.

Source: [ui/src/workspace/headless/open-target.ts:24](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/open-target.ts#L24)

### `PanelContent`

Interface.

JSON-safe content a panel's `load` returns. The host paints it with its own components.

```ts
interface PanelContent
```

**Properties**

- `actions?` (`readonly PanelAction[]`): Buttons in the panel footer.
- `badge?` (`string | number`): Count / short tag shown on the tab. `0` and `''` hide it.
- `controls?` (`readonly PanelControl[]`): Controls in the panel's header, in order (the first starts it, the last ends it).
- `empty?` (`string`): Shown when `sections` are empty.
- `more?` (`PanelMore`): There is more after this content: a Show more button.
- `search?` (`PanelSearch`): A search field in the header.
- `sections` (`readonly PanelSection[]`): Sections to show, in order.
- `title?` (`string`): Overrides the tab label inside the panel header.

Source: [dist/index-Bq87OcGy.d.ts:27832](https://github.com/Tahul/zidane/blob/main/dist/index-Bq87OcGy.d.ts#L27832)

### `PanelContentEntry`

Interface.

An extension panel's last content, with its loading and pending flags.

```ts
interface PanelContentEntry
```

**Properties**

- `content` (`PanelContent | null`): The last content loaded, or null.
- `error` (`string | null`): Why the last load or action failed, in the extension's words; null when it did not.
- `loading` (`boolean`): A load is running.
- `pending` (`boolean`): Content changed while the tab was not visible.

Source: [ui/src/workspace/headless/view.ts:95](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L95)

### `PanelContents`

Interface.

The content cache's store.

```ts
interface PanelContents
```

**Properties**

- `begin` (`(binding: string, id: string) => number`): Starts a load of `id` in `binding`: flags it loading. Returns the load's token for `PanelContents.settle`.
- `get` (`() => PanelContentCache`): The cache now.
- `keep` (`(bindings: readonly string[]) => void`): Keeps only `bindings` (the most recently shown); the others reload when shown again.
- `markChanged` (`(cwd: string, panelId: string, skip?: string | null) => void`): `panelId` changed in `cwd`: every binding of that folder that loaded it
  flags it pending, except `skip` (the binding that reloads it now).
- `markPending` (`(binding: string, id: string) => void`): Flags an entry pending (it reloads when shown).
- `setError` (`(binding: string, id: string, error: string | null) => void`): Sets or clears an entry's error.
- `settle` (`(binding: string, id: string, token: number, result: { content: PanelContent } | { error: string }) => void`): Lands a load's result, unless a newer load of the same panel in the same binding started since.
- `subscribe` (`(onChange: () => void) => () => void`): Called after each change. Returns the unsubscribe.

Source: [ui/src/workspace/headless/contents.ts:41](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/contents.ts#L41)

### `PanelFocusEntry`

Interface.

The last focus asked for in one scope.

```ts
interface PanelFocusEntry
```

**Properties**

- `focus` (`PanelFocus`): What to focus.
- `panelId` (`string`): The panel it was asked for.
- `seq` (`number`): Bumped by every open.

Source: [ui/src/workspace/headless/panel-focus.ts:12](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-focus.ts#L12)

### `PanelFocusStore`

Interface.

The focus asked per scope.

```ts
interface PanelFocusStore
```

**Properties**

- `get` (`(scopeKey: string | null, panelId: string) => PanelFocusEntry | null`): The focus asked for `panelId` in `scopeKey`, if the last open there was for it.
- `reset` (`() => void`): Forgets everything (tests).
- `set` (`(scopeKey: string, panelId: string, focus: PanelFocus) => PanelFocusEntry`): Records `focus` for `scopeKey`'s panel `panelId` and notifies.
- `subscribe` (`(onChange: () => void) => () => void`): Calls `onChange` after every `set`. Returns the unsubscribe.

Source: [ui/src/workspace/headless/panel-focus.ts:22](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-focus.ts#L22)

### `PanelGroup`

Interface.

A group of tabs: one strip, one body showing its active tab.

```ts
interface PanelGroup
```

**Properties**

- `active` (`string | null`): The tab this group shows (null: its first).
- `id` (`string`): Its id (`g<n>`), never reused.
- `t` (`'group'`): Marks a group.
- `tabs` (`readonly string[]`): Its tab ids, in strip order.

Source: [ui/src/workspace/headless/tree.ts:21](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L21)

### `PanelGroupView`

Interface.

One group of tabs on screen: its own strip and active tab.

```ts
interface PanelGroupView<T extends WorkspaceTab = WorkspaceTab>
```

**Type parameters**

- `T` (`extends WorkspaceTab`, default `WorkspaceTab`)

**Properties**

- `activeTab` (`T`): The tab it shows.
- `id` (`string`): The stored group's id.
- `t` (`'group'`): Marks a group.
- `tabs` (`readonly T[]`): Its tabs offered here, in strip order.

Source: [ui/src/workspace/headless/view.ts:39](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L39)

### `PanelLoadContextInput`

Interface.

A load context as it arrives from across a boundary (the GUI renderer)
or from host UI state, before the host binds it: every field optional.
`normalizePanelLoadContext` reduces untrusted input to this shape;
`resolvePanelLoadContext` turns it into the `PanelLoadContext`
`load` / `onAction` receive.

```ts
interface PanelLoadContextInput
```

**Properties**

- `cursor?` (`string`): The page asked for (`PanelMore.cursor`).
- `cwd?` (`string`): Folder the panel is for.
- `focus?` (`PanelFocus`): What opened the panel, when something pointed at it.
- `global?` (`boolean`): The home of no project (`cwd` is the user's home folder).
- `search?` (`string`): The text in its search field.
- `sessionId?` (`string`): Session the panel is shown beside.
- `surface?` (`PanelSurface`): The surface asking (`'side'` when absent).
- `view?` (`Readonly<Record<string, string | readonly string[]>>`): The values of its controls.

Source: [dist/index-Bq87OcGy.d.ts:27878](https://github.com/Tahul/zidane/blob/main/dist/index-Bq87OcGy.d.ts#L27878)

### `PanelSplit`

Interface.

Groups (or splits) side by side or stacked.

```ts
interface PanelSplit
```

**Properties**

- `children` (`readonly PanelNode[]`): Its children, top/left first.
- `dir` (`SplitDir`): Side by side or stacked.
- `id` (`string`): Its id (`s<n>`), never reused.
- `sizes` (`readonly number[]`): Each child's share of the split, summing to 1.
- `t` (`'split'`): Marks a split.

Source: [ui/src/workspace/headless/tree.ts:33](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L33)

### `PanelSplitView`

Interface.

A split on screen: its children side by side (`row`) or stacked (`col`).

```ts
interface PanelSplitView<T extends WorkspaceTab = WorkspaceTab>
```

**Type parameters**

- `T` (`extends WorkspaceTab`, default `WorkspaceTab`)

**Properties**

- `children` (`readonly PanelViewNode<T>[]`): Its children on screen.
- `dir` (`SplitDir`): Side by side or stacked.
- `id` (`string`): The stored split's id.
- `resizable` (`boolean`): Its dividers map onto the stored split (`shownSlots`); one hidden tabs flattened has none.
- `sizes` (`readonly number[]`): The children's shares as stored.
- `t` (`'split'`): Marks a split.

Source: [ui/src/workspace/headless/view.ts:51](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L51)

### `PanelTree`

Interface.

The side panel's layout document: the tab groups, the focused one and the id counter.

```ts
interface PanelTree
```

**Properties**

- `focused` (`string`): The focused group's id (always one of the tree's groups).
- `root` (`PanelNode`): The groups and splits.
- `seq` (`number`): The number the next new id takes (groups `g<n>`, splits `s<n>`).

Source: [ui/src/workspace/headless/panel-tree.ts:33](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L33)

### `SessionLayout`

Interface.

How a scope's window was left: the side panel, the Files tab, the terminal drawer, the composer badges.

```ts
interface SessionLayout
```

**Properties**

- `badgeOrder` (`readonly string[]`): The composer badges in the user's dragged order (`orderComposerBadges`);
  per project, so every conversation of the folder shows them alike.
- `filesActive` (`string | null`): The Files tab's active file.
- `filesChangedOnly` (`boolean`): The Files tree shows changed files only.
- `filesOpen` (`readonly string[]`): The files open in the Files tab.
- `filesShowIgnored` (`boolean`): The Files tree shows ignored files.
- `filesTreeOpen` (`boolean`): The Files tab's tree column is shown.
- `filesTreeWidth` (`number | null`): Files tab tree column width (px); null = default.
- `panelOpen` (`boolean`): The side panel is shown.
- `panelShare` (`number | null`): Dragged past the chat's usual floor: the panel's share of the room beside the sidebar (`resolveLayout`, `zidane-ui/workspace/headless`).
- `panelTree` (`PanelTree`): The side panel's tab groups, the focused one, and its active tab (`PanelTree`, `zidane-ui/workspace/headless`).
- `panelWidth` (`number | null`): The side panel's width (px); null = default.
- `terminalHeight` (`number | null`): The terminal drawer's height (px; null = default).
- `terminalHideAgent` (`boolean`): The terminal strip leaves the agent's terminals out (its robot toggle); per project, like the Files filters.
- `terminalOpen` (`boolean`): Terminal drawer under the chat column: shown.
- `terminalOrder` (`readonly string[]`): Terminal tabs in the user's dragged order (`zidane-ui/terminal/headless` order); per session, like its terminals.

Source: [ui/src/workspace/headless/layout-memory.ts:114](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L114)

### `StorageLayoutsOptions`

Interface.

Options of [`createStorageLayouts`](https://zidane.si/docs/reference/ui-workspace-headless.md#createStorageLayouts).

```ts
interface StorageLayoutsOptions extends LayoutStoreOptions
```

**Properties**

- `initial?` (`(scope: WorkspaceScope) => WorkspaceLayout`, from `LayoutStoreOptions`): What a scope with nothing stored reads. Default: [`EMPTY_WORKSPACE_LAYOUT`](https://zidane.si/docs/reference/ui-workspace-headless.md#EMPTY_WORKSPACE_LAYOUT).
- `key?` (`string`): The storage key. Default: `zidane-ui:workspace:v1`.
- `listenExternal?` (`(onExternalWrite: (key: string | null) => void) => () => void`): Called with a listener that re-reads the storage when another tab wrote
  it (the `storage` event). Default: listens on `window` when there is one.
- `storage` (`LayoutStorage`): Where to keep them, such as `localStorage`.

Source: [ui/src/workspace/headless/layouts.ts:109](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L109)

### `TabAttention`

Interface.

A tab has something for the user. `attention` is the warn-toned, pulsing
kind (the agent is waiting on an answer); `info` is a quiet count (open
todos). Kept per scope so a badge earned on one session never shows on
another and comes back when switching back.

```ts
interface TabAttention
```

**Properties**

- `count` (`number`): The count on the badge.
- `live?` (`boolean`): Something is happening in the tab right now (the agent driving the browser): a running arc, no count.
- `tone` (`'info' | 'attention'`): Waiting on the user (warn-toned) or a quiet count.

Source: [ui/src/workspace/headless/view.ts:75](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L75)

### `WorkspaceHost`

Interface.

Where the workspace keeps its layouts and finds extension panels. Leave a member out and what needs it falls back.

```ts
interface WorkspaceHost<S extends WorkspaceScope = WorkspaceScope>
```

**Type parameters**

- `S` (`extends WorkspaceScope`, default `WorkspaceScope`)

**Properties**

- `layouts?` (`WorkspaceLayouts<S>`): Persistent layouts. Absent: layouts last as long as the page.
- `panels?` (`WorkspacePanels`): Extension panels. Absent: only the app's own tabs.

Source: [ui/src/workspace/headless/host.ts:128](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L128)

### `WorkspaceLayout`

Interface.

One scope's side panel, as stored.

```ts
interface WorkspaceLayout
```

**Properties**

- `open` (`boolean`): The panel is open.
- `share` (`number | null`): Dragged past the chat's usual floor: the panel's share of the room beside the sidebar (`resolveLayout`).
- `tree` (`PanelTree`): The tab groups, the focused one and the ids still to hand out.
- `width` (`number | null`): The width the user dragged the panel to (px); `null` until dragged.

Source: [ui/src/workspace/headless/host.ts:40](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L40)

### `WorkspaceLayouts`

Interface.

The layouts of every scope, read synchronously so a scope switch shows its layout in the same render.

```ts
interface WorkspaceLayouts<S extends WorkspaceScope = WorkspaceScope>
```

**Type parameters**

- `S` (`extends WorkspaceScope`, default `WorkspaceScope`)

**Properties**

- `get` (`(scope: S) => WorkspaceLayout`): The scope's layout as stored now. The same object while it is unchanged.
- `subscribe` (`(onChange: () => void) => () => void`): Called after any layout changed, here or in another window. Returns the unsubscribe.
- `update` (`(scope: S, change: (stored: WorkspaceLayout) => Partial<WorkspaceLayout>) => void`): Applies `change` to the layout stored now (read-modify-write, so a stale
  copy is never written back over another window's change). A change that
  changes nothing writes nothing.

Source: [ui/src/workspace/headless/host.ts:52](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L52)

### `WorkspaceOps`

Interface.

The panel's layout actions.

```ts
interface WorkspaceOps
```

**Properties**

- `close` (`() => void`): Close the panel.
- `closeTab` (`(id: string) => void`): Remove a tab from the strip; closing the last one closes the panel.
- `cycle` (`(direction: -1 | 1) => void`): Show the next (1) or previous (-1) tab of the focused group.
- `dropTab` (`(id: string, to: TabDrop) => void`): A dragged tab dropped on a strip (before a tab, null: at its end) or on a group's body (a zone).
- `moveTab` (`(id: string, delta: number) => void`): Move one tab left (-1) or right (+1) in its strip (keyboard reorder).
- `open` (`() => void`): Open the panel.
- `openProactive` (`(id: string) => void`): Agent or extension driven open; refused while the user owns the tab choice.
- `pick` (`(id: string, groupId?: string) => void`): Show a tab; one not in the strip yet joins `groupId` (default: the focused group).
- `requestAttention` (`(id: string, show: boolean) => void`): Pin `id` into the strip and, unless `show` is false, show it (a blocked run outranks earlier picks).
- `resizeSplit` (`(splitId: string, sizes: readonly number[]) => void`): New shares for a split's children as shown (a divider drag); hidden ones keep theirs.
- `splitTab` (`(id: string, side: 'right' | 'down') => void`): Take a tab out into a new group below (`down`) or beside (`right`) its own.
- `toggle` (`() => void`): Open the panel when closed, close it when open.

Source: [ui/src/workspace/headless/ops.ts:34](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/ops.ts#L34)

### `WorkspaceOpsOptions`

Interface.

Options of [`createWorkspaceOps`](https://zidane.si/docs/reference/ui-workspace-headless.md#createWorkspaceOps).

```ts
interface WorkspaceOpsOptions<S extends WorkspaceScope>
```

**Type parameters**

- `S` (`extends WorkspaceScope`)

**Properties**

- `layouts` (`() => WorkspaceLayouts<S>`): Where layouts are stored. Read on every action, so a host change applies to the next one.
- `scope` (`() => S | null`): The scope on screen, or null (actions do nothing).
- `view` (`() => WorkspaceOpsView`): What is on screen now.

Source: [ui/src/workspace/headless/ops.ts:72](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/ops.ts#L72)

### `WorkspaceOpsView`

Interface.

What the ops read from the latest render: what is on screen.

```ts
interface WorkspaceOpsView
```

**Properties**

- `openTabs` (`readonly WorkspaceTab[]`): Every tab on screen.
- `tabs` (`readonly WorkspaceTab[]`): Every tab offered here.
- `view` (`PanelViewNode | null`): The tree as shown (`viewOf`), or null.

Source: [ui/src/workspace/headless/ops.ts:62](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/ops.ts#L62)

### `WorkspacePanelChange`

Interface.

A panel's content changed in a folder.

```ts
interface WorkspacePanelChange
```

**Properties**

- `cwd` (`string`): The folder it changed in.
- `panelId` (`string`): The panel.

Source: [ui/src/workspace/headless/host.ts:98](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L98)

### `WorkspacePanels`

Interface.

Extension-contributed panels: their catalog per folder, their content and their actions.

```ts
interface WorkspacePanels
```

**Properties**

- `action` (`(cwd: string, panelId: string, actionId: string, payload: unknown, ctx: PanelLoadContextInput) => Promise<WorkspacePanelActionResult>`): Runs a panel's action.
- `list` (`(cwd: string) => Promise<readonly WorkspacePanelSummary[]>`): The extension panels offered in `cwd`.
- `load` (`(cwd: string, panelId: string, ctx: PanelLoadContextInput) => Promise<PanelContent>`): One panel's content for `ctx` (the session it shows beside, what it was opened on). Rejects when it fails.
- `subscribe?` (`(onChange: (change: WorkspacePanelChange) => void) => () => void`): Called when a panel's content changed. Returns the unsubscribe.
- `subscribeCatalog?` (`(onChange: () => void) => () => void`): Called when the catalog may have changed (an extension loaded or left). Returns the unsubscribe.
- `watch?` (`(cwd: string, panelId: string) => () => void`): Keeps the panel's own live updates on while it is shown. Returns the stop.

Source: [ui/src/workspace/headless/host.ts:106](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L106)

### `WorkspacePanelSummary`

Interface.

One extension panel an app offers in a folder.

```ts
interface WorkspacePanelSummary
```

**Properties**

- `home?` (`PanelHomeLayout`): Its default place on the project home.
- `icon?` (`PanelIcon`): The tab icon.
- `id` (`string`): The panel's id: its tab id in the strip.
- `label` (`string`): The tab label.
- `owner` (`string`): The extension that registered it.
- `ownerIcon?` (`PanelIcon`): That extension's own icon: the tab draws its mark when the panel has no `icon`.
- `ownerName?` (`string`): That extension's display name.
- `scope` (`ExtensionPanelScope`): Where its side tab shows: with a session, on a folder alone, or both.
- `surfaces?` (`readonly PanelSurface[]`): The surfaces it shows on: side tab, project-home block. Absent: the side panel only.

Source: [ui/src/workspace/headless/host.ts:66](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L66)

### `WorkspaceScope`

Interface.

What a layout belongs to: a session, a folder, a draft. The kit only reads
`key`; an app adds what its host needs to find the record (the desktop
app's scope carries the folder whose template a new session copies).

```ts
interface WorkspaceScope
```

**Properties**

- `key` (`string`, readonly): Identifies the layout: two scopes with the same key share one.

Source: [ui/src/workspace/headless/host.ts:34](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L34)

### `WorkspaceTab`

Interface.

A tab the workspace can show. An app's own kinds are `builtin: true` and
render through its `WorkspaceTabRegistry`; `builtin: false` is an
extension panel whose content loads from `WorkspaceHost.panels`.
`zidane/chat/pure`'s `PanelTab` (the desktop app's catalog) is one.

```ts
interface WorkspaceTab
```

**Properties**

- `builtin` (`boolean`): The app renders it; `false`: an extension panel.
- `extension?` (`'panel' | 'webview'`): The kit draws it from the extensions host (`ExtensionsHost`): an extension side panel (`'panel'`) or a
  webview panel (`'webview'`) the app's catalog didn't list. Absent: the app's registry draws it.
- `icon?` (`PanelIcon`): The pill's icon (`gui`: an icon class). Absent on an extension's panel: its extension's mark ([`panelTabMark`](https://zidane.si/docs/reference/ui-workspace-headless.md#panelTabMark)).
- `id` (`string`): The tab's id: unique in the catalog, what the layout stores.
- `label` (`string`): The pill's label.
- `owner?` (`string`): The extension that added it (its id): the tab draws the extension's mark when it has no `icon.gui`.
- `ownerIcon?` (`PanelIcon`): That extension's own icon.
- `ownerName?` (`string`): That extension's display name.

Source: [ui/src/workspace/headless/view.ts:16](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L16)

## Types

### `DropZone`

Type.

Where a drop lands on a group: into it, or a new group on one of its edges.

```ts
type DropZone = 'center' | 'left' | 'right' | 'top' | 'bottom'
```

Source: [ui/src/workspace/headless/tree.ts:50](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L50)

### `LayoutPatch`

Type.

A changed layout, or a function of the layout stored right now.

```ts
type LayoutPatch = Partial<SessionLayout>
```

Source: [ui/src/workspace/headless/layout-memory.ts:251](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layout-memory.ts#L251)

### `LayoutStorage`

Type.

The `Storage` calls [`createStorageLayouts`](https://zidane.si/docs/reference/ui-workspace-headless.md#createStorageLayouts) makes (`localStorage`, `sessionStorage` or a double).

```ts
type LayoutStorage = Pick<Storage, 'getItem' | 'setItem'>
```

Source: [ui/src/workspace/headless/layouts.ts:106](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/layouts.ts#L106)

### `OpenTarget`

Type.

Where a click opens: the editor (the OS) or the side panel.

```ts
type OpenTarget = 'editor' | 'panel'
```

Source: [ui/src/workspace/headless/open-target.ts:21](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/open-target.ts#L21)

### `PanelContentCache`

Type.

Every binding's entries, by panel id. Replaced (never mutated) on each change.

```ts
type PanelContentCache = Readonly<Record<string, Readonly<Record<string, PanelContentEntry>>>>
```

Source: [ui/src/workspace/headless/contents.ts:14](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/contents.ts#L14)

### `PanelNode`

Type.

A node of the group tree.

```ts
type PanelNode = PanelGroup | PanelSplit
```

Source: [ui/src/workspace/headless/tree.ts:47](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L47)

### `PanelTabMarkSource`

Type.

What [`panelTabMark`](https://zidane.si/docs/reference/ui-workspace-headless.md#panelTabMark) reads of a tab.

```ts
type PanelTabMarkSource = Pick<WorkspaceTab, 'icon' | 'owner' | 'ownerName' | 'ownerIcon'>
```

Source: [ui/src/workspace/headless/tab-mark.ts:10](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tab-mark.ts#L10)

### `PanelViewNode`

Type.

A node of the tree as shown.

```ts
type PanelViewNode<T extends WorkspaceTab = WorkspaceTab> = 
  | PanelGroupView<T>
  | PanelSplitView<T>
```

**Type parameters**

- `T` (`extends WorkspaceTab`, default `WorkspaceTab`)

Source: [ui/src/workspace/headless/view.ts:67](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/view.ts#L67)

### `SplitDir`

Type.

How a split lays out its children: side by side (`row`) or stacked (`col`).

```ts
type SplitDir = 'row' | 'col'
```

Source: [ui/src/workspace/headless/tree.ts:18](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L18)

### `SplitSide`

Type.

The edge a new group splits off at.

```ts
type SplitSide = Exclude<DropZone, 'center'>
```

Source: [ui/src/workspace/headless/tree.ts:52](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/tree.ts#L52)

### `TabDrop`

Type.

Where a dragged tab is dropped: a strip (before a tab, null: at its end), or a group's body.

```ts
type TabDrop =
  | { before: string | null; groupId: string }
  | { groupId: string; zone: DropZone }
```

Source: [ui/src/workspace/headless/panel-tree.ts:43](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/panel-tree.ts#L43)

### `WorkspaceHostError`

Type.

Why an extension panel call failed, for the panel to word through its labels.

```ts
type WorkspaceHostError = { code: 'unavailable' } | { code: 'failed'; message: string }
```

Source: [ui/src/workspace/headless/host.ts:88](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L88)

### `WorkspaceLabels`

Type.

The shape of the workspace label dictionary. Some values are functions that build a string.

```ts
type WorkspaceLabels = typeof DEFAULT_WORKSPACE_LABELS
```

Source: [ui/src/workspace/headless/labels.ts:56](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/labels.ts#L56)

### `WorkspacePanelActionResult`

Type.

What running a panel action answered.

```ts
type WorkspacePanelActionResult =
  | { ok: true }
  | { error: WorkspaceHostError; ok: false }
```

Source: [ui/src/workspace/headless/host.ts:95](https://github.com/Tahul/zidane/blob/main/ui/src/workspace/headless/host.ts#L95)
