Skip to content

The four surfaces

Epic #268 (Phase 7 of #262).

Open ZettelFlow, not Obsidian. The ~12 sidebar views that ZettelFlow grew were consolidated into four surfaces, each hosting the former views as modes behind a segmented control. One front door, everything reachable, nothing deleted.

One ribbon button

Purpose-led work (#401) lives inside the existing Home → Cultivate mode. A small inquiry activation intent selects start/resume/ordinary; private purpose text is never put into workspace view state. The runtime singleton owns draft/checkpoint continuity across renderer remounts. No new surface or command is registered; the ordinary mode keeps its recipe and friction settings.

There is a single all-in-one ribbon button ("Open ZettelFlow"). Its menu leads with Create note (also the hotkey-bindable Open workflow command), then the system-adoption actions, then the surfaces. Note creation is no longer its own ribbon icon.

The surfaces and their modes

Surface Modes Folds in (former views)
Home Home · Cultivate · Recent ZettelFlow Home (+ a "What to do next" recommendation surface, #273) + Cultivate (#309) + What ZettelFlow changed (#454)
Health Health · Timeline · Momentum Slip-box health + the knowledge dashboard folded in (#314) + Evolution timeline + Thinking heatmap
(Discovery — dissolved, #504) — its four modes answered two questions that already had homes: see below
Explore (one mode, so no mode bar) Ask your graph + the retired Graph surface (#484), whose 3D view is now one of Explore's lenses

The count went 4 → 3 → 4 → 3, and the honest reading is not "boxes were saved".

484 absorbed the Graph surface: it hosted one mode, answering what shape is my knowledge? while

Explore answered which notes match this? — the same question in two places, with no way to carry an answer across. That merge stands.

487 then gave Explore its own room. Discovery's four modes are narrow lists — a handful of

pairs, a few resurfaced notes — which is why people keep that surface in a side panel. Explore is facets, chips, an answer, a lens bar and a 3D graph, and a mode cannot be moved out of a pane without dragging the four lists with it. The principle was never "fewer boxes": it is one home per capability, and no two places answering the same question.

504 then dissolved Discovery, whose four modes were never about discovery:

Mode What it really was Where it went
Connections findDiscoveries, a ranked pair-finder Home — which already rendered it, from the same function
Questions a global list of what is unanswered Home
Forgotten notes near the active note you have not revisited the note's own view
Challenges the active note's supports and contradictions the note's own view

Two were recommendations — what should I do next, which is what Home is for — and two were about the note you had open, which is what the This note mode of Health already was. A surface whose modes answer other surfaces' questions is a door onto four things that live somewhere else.

A surface with a single mode draws no mode bar — a bar offering one choice is not a choice, and an ARIA tablist of one is noise for a screen reader too.

Every mode reuses the retired view's rendering verbatim — same numbers, same behaviour — mounted inside the surface as a KnowledgeModeRenderer (an Obsidian Component, so its listeners are cleaned up on every mode switch).

One primary action per header (#577)

A mode's header may carry one control that opens a capability. A refresh, a filter, and moving around inside the mode are not that.

The reason is measured rather than aesthetic. The Lab drew its legend and the mechanic that starts a collision through one private helper, with one class and one weight — so nothing on screen said which of them mattered, and think before you look sat in that row for a release without being found. It is the same shape #509 found in the move buttons ("the thing the picker exists to avoid, put back by hand") and #542 in the 3D view (subtract, then a gear), one level down.

ModeHeader is the shared helper, and its three calls are the three ranks:

call what it draws for
primary() one mod-cta button, with an icon the control that opens something. Calling it twice throws
nav() a plain, muted button, in the header next idea, refresh, a filter — navigation inside the mode
secondary() an item in one overflow menu everything else the header used to carry

What the rule does not reach: a cancel that appears while a composer is armed, an undo drawn where the card was, a clear beside the filter it clears. Those are controls on the thing they act upon, and two of them were placed deliberately, against a toast, for reasons written down at the time.

The three headers it re-ranked:

mode primary header navigation overflow
Lab start a collision — the legend
Cultivate work on your own question another idea think about this instead
This note (timeline) share the idea card refresh, cognitive-only filter —

Crystallize is deliberately not the Lab's primary. It lives in the picked bar, on the selection it acts upon; promoting it would put a permanently inert button in the header, which is exactly the clutter #542 removed.

onePrimaryAction.test.ts counts the primary() calls per renderer. It cannot tell a capability from navigation — that is a judgement — but it makes the judgement singular and visible in a diff, and ModeHeader throws on the second call, so the two halves cover each other.

A dashboard that fills the pane (#620)

The Home surface's modes are a minimalist, responsive dashboard rather than a tall single column. The shared primitives live in src/styles/components/dashboard.scss: a root that fills the pane (centred only past a generous rem cap, never a fixed-pixel cage) and a dashboard-grid of repeat(auto-fit, minmax(…, 1fr)) — one column when the pane is docked narrow, two or three when Obsidian's side panels are collapsed, with no breakpoint and no script. The retired cages (.cultivate 44rem, .inquiry 52rem, .lab 780px, each margin: 0 auto) are gone.

  • Home leads with a header strip (greeting + a single Capture a thought primary + a fixed, icon-only Ask your graph door that deep-links to Explore) and three hero tiles — what to do next (the one accent card), cultivate an idea, ready to look at again. Everything else (new ideas, main concepts, review, gaps, open questions, pinned queries, the 3D-graph teaser) folds behind one "show everything" disclosure, collapsed by default.
  • Cultivate wears the same shape: the idea under cultivation is the one accent card, Notes by stage is a card that is also the stage filter, and the five moves reflow as a grid.
  • Think (the Thought Lab) keeps its composer and thread list; the list reflows into grid columns on a wide pane (grid, not CSS columns, so the newest-first order still reads left-to-right).

The Ask your graph door is an icon-only nav() variant of ModeHeader (icon + tooltip + aria-label, no visible label) — a fixed door where you already are, never a second primary().

Open as tabs

Surfaces open as normal main-area tabs (getLeaf('tab')), not only in the right sidebar — so you can move, split or pin them like any Obsidian document.

No visible breakage (§XI)

  • The 12 retired show-* opener commands are kept as aliases that open the owning surface at the right mode (e.g. show-slipbox-health → Health/Health, resurface-related-notes → Discovery/Forgotten, show-notes-history → Home/Recent). Bind hotkeys to them as before.
  • The Graph surface's own view type joined that alias list when it was retired (#484), so a workspace saved before the merge reopens Explore instead of an empty pane, and show-graph / explore-in-3d keep their ids and their hotkeys.
  • A mode that moved house is handed over rather than dropped: relocateMode maps zettelflow-discovery:ask to the Explore surface (#487), so a workspace saved while Explore was still a Discovery mode reopens Explore — instead of quietly showing Connections, which is the failure that looks like nothing went wrong.
  • A user's saved or pinned leaf of a retired view type is transparently redirected: a thin LegacyRedirectView transforms that leaf into the surface it now lives in, so no "no view of type X" pane ever appears.

Where it lives

  • Registry + back-compat maps (pure): architecture/components/core/surface/{surfaceRegistry,legacyTargets}.ts.
  • Host + renderer contract: architecture/components/core/surface/{ModeHostView,KnowledgeModeRenderer,LegacyRedirectView}.ts.
  • The three surfaces: architecture/components/core/surface/{Home,Health,Explore}SurfaceView.ts.
  • Deep-linking to a mode: activateSurface(app, surfaceType, mode) in architecture/plugin/services/ViewActivation.ts.
  • The dashboard primitives + header: src/styles/components/dashboard.scss (fluid grid + cards) and architecture/components/core/surface/ModeHeader.ts (one primary(), plus the icon-only nav() door, #620).