Skip to content

The outliner

Every page in Subspace, notes, agent definitions, meeting minutes, CRM records, is an outline: a tree of nodes (bullets), each with text as its source of truth and optionally an element (a table, code cell, terminal, custom function, or file) rendered in a node frame. The outliner is the surface you spend the most time in, and almost every other feature, links and embeds, page metadata, tables and formulas, agent runs, renders as bullets inside it.

An outliner page with a table element

Every line is a bullet; a bullet can carry a table, a code cell, or a file.

An empty page shows “Click to start writing…”. Click any bullet’s text to start editing it in place.

Key Effect
Enter New sibling below the current node, or the node’s first child if the cursor is on a parent
Tab Indent: make the node a child of its previous sibling
Shift+Tab Outdent: promote the node to be a sibling of its parent
Backspace on an empty node Delete the node and move focus up
/ Move focus to the previous / next visible row
Escape Commit the edit and blur
⌘Z / ⌘⇧Z Undo / redo your own edits

Pressing Enter on a folded parent unfolds it, but only in the window where you pressed it; other open windows on the same page keep their own fold state, because folds are view state, not document state (see Folds below).

Drag a bullet by its dot to reorder it among siblings or reparent it under a new parent; drop position (above, below, or onto another bullet) determines whether it becomes a sibling or a child. Node order is stored as a fractional rank key per node, so reordering one bullet never touches its siblings’ rows.

Type / at the start of a bullet to open a popover of registered commands (custom functions today; agents join the same menu), each with a description and a type badge. Filter by typing, then press Enter or click to insert name( and hand off to the argument UI. See Custom functions and Agents overview.

Hover any row and click the chevron that appears in the left gutter (▾ expanded, ▸ collapsed) to fold or unfold its children. A collapsed parent shows a halo around its bullet so you can tell it is hiding content.

Folding a bullet only hides its subtree in that view; it has no effect on search, backlinks, or what an agent’s kb.read tool sees.

Click a bullet’s dot to zoom into it: that node becomes the view’s root, and only its subtree renders, as if it were its own page. A breadcrumb across the top (page › ancestors › node) shows your position and lets you zoom back out one level at a time.

Zoom state is URL-addressable: /p/<slug>?zoom=<nodeId>. That means a zoomed view is a shareable link and survives a reload, a refresh, or opening the same URL on another device. Zooming ignores the root node’s own fold (you always see its direct children even if it was collapsed in the outer view); descendants below the root still fold normally.

In the desktop app, you can work on two parts of a page (or two different pages) side by side without leaving the outliner:

  • ⌥↵ (Option/Alt+Enter) with your cursor on a line opens that bullet, zoomed, in a side-by-side shell pane. -clicking the bullet dot does the same thing.
  • ⌥⇧↵ promotes the bullet to a full new tab instead, the same way the terminal’s ⇱ button promotes a pane to a tab.

Both the origin outline and the new pane or tab are live, independent, routed views of the same underlying page: edits in either one sync through the shared reducer, so typing in the pane updates the bullet in the original outline immediately. The pane/tab layout persists per window in kb.view_state, so it survives a reload. Click the × on a pane to fold it back to a single view.

Without the desktop shell (on web or mobile), both shortcuts fall back to in-place zoom navigation instead of opening a second view.

Edits apply optimistically: the client runs your command through the same pure reducer the server uses, updates the screen instantly, and holds the command in a pending list until the server echoes it back over /ws. Multiple windows and multiple users converge to the same document, last-write-wins in server commit order. The header’s save indicator (data-dirty) shows whether you have un-acknowledged commands in flight.

⌘Z / ⌘⇧Z undo and redo your own commands by issuing their reducer-emitted inverses as ordinary new commands, so undo is itself just another convergent edit. See the command model for how the reducer, optimistic apply, and rebase fit together.

The outliner’s visual language is a small, fixed token set (from the design system’s Foundations sheet), exposed as CSS custom properties:

Token Value Used for
--canvas #fafaf8 Page background
--surface #ffffff Cards, popovers, node frames
--hover-wash #f4f3f0 Row hover background
--hairline #e9e7e2 Default dividers
--chrome-hairline #f0eee9 Chrome/toolbar dividers
--guide #eceae5 Indent guides
--faint-ink #9e9c94 Placeholder / de-emphasized text
--secondary-ink #6e6c66 Secondary text
--ink #1c1b1a Primary text
--accent-link #3e63c4 [[links]], the brand blue
--accent-agent #7b5bc4 Agent affordances, the brand purple
--accent-ok #4c8a62 Success / normal-severity states
--accent-attention #b07c22 Important-severity states
--accent-urgent #b4453c Ultra-severity states
--bracket #b9c4e0 [[ ]] bracket glyphs
--tag-ink / --tag-bg #6b7ca8 / #eef0f6 #tag chips
--embed-rule / --embed-bg / --embed-head #d8deef / #fafbfd / #8a94ae ((embed)) blocks
--font-ui 'Source Sans 3', -apple-system, 'Segoe UI', sans-serif UI and node text
--font-mono 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace Code cells, terminals
--indent-step 22px Horizontal offset per nesting level
--shadow 0 1px 3px rgba(28, 27, 26, 0.05) Elevated surfaces