Skip to content

Workspace round 3: the map, newest-first, one-field composer, Temper everywhere, no layout shift - #12

Merged
cailmdaley merged 22 commits into
mainfrom
design/round3
Oct 5, 2026
Merged

cailmdaley merged 22 commits into
mainfrom
design/round3

Conversation

@cailmdaley

Copy link
Copy Markdown
Owner

Round 3 of the document workspace, from Cail's feedback on round 2. The headline changes:

  • the map of pages is back, as a band of tile faces under the page;
  • a channel lists its newest sends first;
  • the page sits beside the sidebar instead of across a dead band;
  • the composer is one field that never moves when you touch it;
  • Meeting is a switch;
  • Temper and Discard reach every card without a verdict;
  • opening a channel never jumps you to another page once it has painted.

A layout-shift guard now enforces "nothing moves when you touch it" across the board.

Claude Opus 5.5 on behalf of Cail. Every screenshot below comes from the mocked-daemon harness.

The map of pages

Cail lost his place without a row of thumbnails, so the reader's map is back.

  • The band. It sits under the stage: one row of 80×56 tiles, with the selected tile lifted, a serif caption on hover, and "n / N" at the end.
  • Tile faces. Every tile shows its declared title and a kind mark straight away. Audio tiles add a waveform sketch once peaks are known. A live thumbnail carries its title on a paper strip at its foot, so a flat image or a dense report still names itself.
  • Order. A channel lists §, then sent documents by newest receipt, then embeds that were never sent. A re-send moves to the front. Selection follows the document, never the slot.
  • The head drops its text index, so pages are listed once.
  • The phone gets one tick per page along the bottom bar's top edge.

Geometry

  • With the sidebar open, the page sits one 24 px gutter from it and shrinks before that gutter grows. It used to centre itself, leaving a dead band of up to 443 px at 1920.
  • The sidebar is 320 px wide and has its own ground and edge.

The composer and Meeting

  • One bordered field, the same height at rest and focused. Its verbs sit inside its right edge. Only wrapped text grows it, and it grows downward.
  • Focus never scrolls the page. r had scrolled it 7–12 px with the sidebar open, which cut off the kicker.
  • Meeting is a switch beside the verbs, off by default. Switched on, it shows the kind (Call · Room · Phone) and one verb, Start meeting.
desktop, at rest desktop, Meeting on
phone, at rest phone, Meeting on

Temper everywhere

The head's Temper · Discard shows for every fiber without a verdict: drafts, in flight and awaiting review.

  • t/x work in the same columns.
  • In-flight Desk cards reveal the pair on hover and keyboard selection.
  • A live worker still confirms once at the gesture, and every verdict goes through the 6 s undo queue.
  • The fiber page's ⋯ duplicate is gone.
in-flight fiber Desk hover

No jump after paint

  • Selection is settled before the first paint, in this order: the route, else memory, else the report the Board's receipt feed already names. A wanted page that isn't listed yet gets a frame at once.
  • show() no longer writes selection. Late documents, metadata and polls never move the page.
  • A routed page that doesn't exist falls back to the report.
  • Receipts stamped with a session id instead of a fiber uid are filed as Unfiled rather than re-read. That stops the no fiber found storm against the daemon.

Nothing moves when you touch it

ui/e2e/layoutShift.mjs records every visible box, applies one interaction, and fails on anything else moving by more than 0.5 px. The interactions are focus, typing, hover, popovers, page selection, a poll repaint and a worker-state change, swept at 1440, 1000 and 390 px across the act zone, the head, the label bar, the band, the sidebar, the Desk and the Board. It found, and this PR fixes:

  • the composer's growth on focus;
  • the Desk's needs-you edge;
  • non-reduced-motion hover lifts under reduced motion;
  • the worker control's width change between states.

No known-shift exceptions remain.

Gates

  • tsc is clean.
  • vitest: 1,562 tests pass under America/Los_Angeles and Europe/Paris (--maxWorkers=2).
  • e2e: 104 passed, 0 failed. The layout-shift guard passed 7/7 after the final helper cleanup.
  • No daemon files changed since Make the board responsive: connection lanes, deduped reads, audio and Desk fixes #10.
  • CI is out of the loop while GitHub Actions is degraded, so this merges on local gates plus an independent review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3

cailmdaley and others added 22 commits October 5, 2026 19:56
A re-send moves its document to the front, just after the constitution,
so the page you came back for sits beside §. Selection follows the
document, so a reorder never changes what you are reading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The head's text index goes; the head keeps the return, the name, the
verdicts and the worker. The pages are indexed by a band of tiles under
the stage, centred under the selected page, with the page count at its
end and the hovered tile's title as a caption in the gap above.

Every tile has a designed face at once: its title in the serif and a
kind mark, the constitution as its § alone, a recording with a sketch of
its peaks (or its length) once a listening page has learned them.
Reports, pictures and PDFs fade in live over the face; the hover
preview card is retired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tick per page rides the bottom bar's top edge over the title, the
current one in ink: where you are, without more chrome. The browser
suite now checks the band's faces, lift, centring and caption, the
re-send moving its tile to the front, and the phone's ticks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rif faces

Themes lose tab-preview and gain page-band and tab-tip; tab is a tile
whose face is a thumbnail. Blueprint's mono uppercase index rules
are reduced to their ink, since a tile's face names its page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ground

With the sidebar open the selected page sits one page gap (24 px) from
the column and shrinks before that gutter grows; the right neighbour has
the room left over, so there is no dead band of veil. The map holds the
selected tile under the page wherever it sits. The column takes the
reader's paper ground over the veil and one hairline at the stage, so it
reads as a column rather than cards in fog. Without the sidebar the page
is centred as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The verbs sit inside the field's right edge on its text line; only wrapped
text grows it, and on the phone the verbs take a row inside the border.
Meeting becomes a worded verb beside New session and Resume.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
…ines

At 1000 px the docked page still reads at 628 px; at 1440 and 1920 it
keeps its preferred width beside the column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Off by default. On, it shows the meeting's kind (Call, Room, Phone) and
one verb, Start meeting, in place of New session and Resume; Enter starts
it too. A recording elsewhere holds the switch off and names it on hover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
A tile whose live thumbnail has loaded hides its face, so a flat figure
or a report that reads as texture lost its name; the title now rides
the tile's foot on a strip of paper. The worker plate sets its dot,
state and time as three spaced words wherever it is drawn, so sidebar
cards read "● ALOFT 4 H 6 M" instead of "ALOFT4 H 6 M".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
e2e/layoutShift.mjs records every visible box, applies one interaction and
reports anything that moved or resized beyond 0.5 px outside the touched
control and its intended dependants. The suite sweeps the act zone, the
head, label bar, sidebar, Desk and Board on desktop, a 1000 px window and
the phone. The meeting kinds lose a border that grew the composer 2 px,
and keep 44 px targets on the phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
Hover shading in the composer applies only where a pointer hovers, and the
guard exempts a tab preview's dismissal, which any press does by design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
The r key focused the field with the browser's own scroll-into-view,
which with the sidebar open left the fiber page scrolled a few pixels and
its kicker cut at the top. The dock focuses with preventScroll and scrolls
only a field out of view, to the page's top when it fits there. The guard
covers both cases, with the sidebar open and closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
Opening a channel showed the fiber's page. When the documents arrived,
up to a second later and only after a file-info read for every page,
the reader cut to the report or the remembered page. Workspace.show had
already overwritten the wanted page with the fallback, because that page
was not yet in the channel.

A channel's page is now decided once, before the first paint:
- the routed page, else the remembered one, else the report the Board's
  receipt feed already names;
- a wanted page the channel has not listed yet gets a provisional frame
  at once;
- with nothing named, the fiber's own page stays;
- show() no longer writes the selection;
- late data never moves it; only a click, a key, a route or Back does;
- a routed page the loaded channel does not hold falls back to the
  report, else the fiber's page.

File-info reads no longer gate the page list: they label pages when
they land.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The daemon's "no fiber found" lines (about 4,800 for 6-7 ids) came from
the Board. Files sent from a session with no fiber arrive in the receipt
feed with the session's UUID in the receipt's uid. The overview took
that UUID for a fiber uid and asked for /api/v1/fibers/<uuid>. The
owner answered "not found", and each poll asked again.

A receipt whose uid is its session id is now filed as Unfiled on its
host with no read. A fiber its owner reports missing is not asked for
again until the next visit to the Board.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Needs-you gold edge is painted inside the card's border, so crossing
bands changes colour, never size. Card and sidebar hover lifts are
suppressed under reduced motion. The head's worker state and time keep
widths for the longest state, so a change never moves the index. The guard
drops its known-shift list and enforces all three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
perf/ws-perf c5b04481 moves owner file-time reads behind the first show
with its own request lane; this branch's version of that change is
withdrawn so the two merge cleanly. Selection no longer depends on it:
the wanted page paints from a provisional frame either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eipts as Unfiled

Keeps perf's channel load (file times read in the quiet lane after the
first rebuild, re-show only when a time moved); the periodic refresh keeps
perf's slow-lane read and takes bugs3's shown() for the history entry.
The perf test that opens on the report now feeds the report name first,
since late data no longer moves the selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
The head's verdict pair shows for drafts, work in flight and work awaiting
review; a live worker still asks once at the gesture and every verdict
still waits out the undo queue. t and x follow the same rule. The fiber
page leads with its pair only while the fiber awaits review; elsewhere
the head carries it and the act zone's ⋯ menu is gone. On the fiber page
under review the head's pair keeps its place unseen, so nothing beside it
moves. Desk cards in flight reveal the pair on hover and selection, left
of the worker pill so a pill change never moves it; touch screens show it
only on cards awaiting review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
…one ticks

Merged cleanly. The sidebar-toggle test now feeds the report name before
opening, so the report it inspects is the selected page under the
selection stopgap rather than a mounted neighbour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
… guard

reader.css: keeps act's held widths for the head's worker state and
elapsed time, and drops act's .ws-head-position rule, since the map lane
replaced the head's page count with the band of tile faces. The guard's
allowances for the removed hover preview and page count go too, along
with still()'s unused known-shift list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTQ9CSGaUs1DbFHxME9BB3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cailmdaley
cailmdaley merged commit af01cc1 into main Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant