← Corpus / corpora-builder / spec

Header Chrome

The workspace becomes a switcher, not a label. The mode toggle shows one icon and says what the next click gets you.

Path
specs/Header-Chrome.md
Authors
Michael Staton
Augmented with
Claude Code on Claude Opus 5 (1M context)
Tags
Spec · Corpora-Builder · Frontend · Theme-System

Header Chrome

Why

The header says corpora reach-edu (reach-edu). That is a slug printed twice and a bucket name leaking into the product, and it is not a control — there is no way to see or change which workspace you are in.

Both halves already exist elsewhere in the tree and neither needs inventing.

Behaviours

1. The workspace is a switcher, in augment-it’s shape

Ported from augment-it/shell/src/WorkspaceSwitcher.svelte — the same trigger, the same listbox, the same interaction contract. Pattern port, not a shared dependency, per the no-shared-dependency-across-ai-labs-apps convention.

  • The trigger shows the display name only: Reach Edu.
  • The slug appears only inside the dropdown, on its row, beside the name — which is where an operator goes when they need to know exactly which corpus they are pointed at.
  • Dot, label, chevron. aria-haspopup="listbox", rows are role="option" with aria-selected, the active row carries a check.
  • Escape closes; a pointer-down outside closes.

Top right, because that is where it sits in augment-it and a person who uses both should not have to look in two places.

2. A display name is derived, not demanded

display_name currently defaults to the slug, which is how reach-edu (reach-edu) happened. When nothing is configured it is humanised from the slug instead: reach-edu → Reach Edu.

CORPORA_WORKSPACE_NAME still wins when set, because a derived name is a reasonable guess and not an authority — ncad-forge should be able to say NCAD-Forge.

3. The API hands over structure, not a rendered string

/api/meta returned label: "reach-edu (reach-edu)" — a formatted string the client could only print. It now returns the workspace as fields, so the client decides what to show where. A server that pre-renders a label has made a layout decision on the client’s behalf and taken away the only thing the client is for.

4. One mode icon, and it says what comes next

The canonical three icons — sun, crescent, four-point star — are lifted verbatim from ai-labs/splash/src/components/ModeToggle.astro, where the same three modes have the same three glyphs.

Splash pages show all three and glow the active one. Here it is a straight swap: one icon, the current mode, and clicking advances. A three-button group is right on a marketing page where the modes are part of the pitch; in a dense tool it is three targets to do one thing.

The tooltip is what makes a single icon honest. An icon that changes on click without saying what it becomes is a guessing game:

Light mode · click for Dark
Dark mode · click for Vibrant
Vibrant mode · click for Light

The cycle is light → dark → vibrant → light, and the tooltip is generated from it rather than written out, so the two cannot disagree.

5. A partial payload degrades to the name we have

label — the server’s own name for the corpus — predates the workspace field and is still sent. When a client is holding a meta older than that field, the trigger shows label rather than an em dash.

This was reported from the running app: Vite HMR swapped the new component in without re-running the initial fetch, so meta came from a sidecar that had no workspace key. A hard reload fixes that instance; rendering nothing while a usable name sat unused beside it is the part that was actually wrong.

And a missing workspace is not the same as a local folder. Saying “local folder” because a field was absent is a confident wrong answer about where somebody’s corpus lives, so the footer says nothing instead.

Tests

IDGiven / When / Then
HEADER-01Given a slug and no configured name, when the workspace resolves, then the display name is the humanised slug and the slug itself is unchanged
HEADER-02Given CORPORA_WORKSPACE_NAME, when the workspace resolves, then the configured name wins over the derived one
HEADER-03Given a workspace, when /api/meta is called, then slug and display name are separate fields rather than one rendered label
HEADER-04Given each mode in turn, when the next mode is asked for, then the cycle is light → dark → vibrant → light
HEADER-05Given each mode, when its tooltip is generated, then it names the current mode and the one a click produces, and never disagrees with the cycle
HEADER-06Given a payload with no workspace field, when the trigger renders, then it falls back to label; and the footer claims a storage location only when one was actually reported
  • augment-it/shell/src/WorkspaceSwitcher.svelte — the switcher this ports
  • ai-labs/splash/src/components/ModeToggle.astro — the three canonical icons
  • app/DESIGN.md — the three-mode contract and the control primitive
  • context-v/specs/Browse-Corpus.md — /api/meta, whose shape this changes