Skip to content

UI polish: facepile, dialogs, UI paths, the share menu, and an icon-subset guard - #118

Merged
alcor merged 4 commits into
mainfrom
feat/ui-polish
Sep 14, 2026
Merged

alcor merged 4 commits into
mainfrom
feat/ui-polish

Conversation

@alcor

@alcor alcor commented Sep 14, 2026

Copy link
Copy Markdown
Member

A round of interface work on the document chrome, plus a guard for the failure that prompted half of it. No issue: the list came from Nicholas reading the UI.

Facepile

Your own face sat 6px clear of the pile while every other face overlapped by 8px. It overlaps like the rest now.

Faces for people who are not here were dimmed with opacity-50, which let the faces underneath show through where they overlap. They take grayscale(1) plus a step of brightness away from the page instead: 1.28 in light, 0.62 in dark, keyed off [data-theme] with a prefers-color-scheme fallback for auto. Opaque, and in the right direction for each theme.

The PDF row drew the word "print"

Material Symbols is loaded as a named subset in root.tsx, and print was not in it, so the ligature rendered as its own name. That is the third time an icon has shipped that way.

There is now a test that reads every Icon name out of app/ and fails if the subset does not list it, and a second that keeps the list sorted so the next addition has an obvious place. It caught both print call sites before I touched anything.

Dialogs

Anchored 60px from the top rather than centred. Their content still changes height between tabs — 420px to 548px across the six in Invite an agent — but centring split every change both ways and moved the dialog under the pointer. Measured: top holds at 60 on all six.

UI paths

The bold paths through another product's interface (Settings → Connectors → Add custom connector) lost their underline, and the pop-out mark went from a grey 24px to the 12.25px and the colour of the text it sits in.

It was 24px because the chrome rule for menu and dialog glyphs is two classes deep and outranked the utility class on the element; .material-symbols-outlined.icon-inline matches that weight.

WakeSection carried a second copy of the same markup and was missed on the first pass, so both call sites now render one UiPath component. One definition to change next time.

Other is More

The tab is "More" with more_horiz, since it is not a product. agent-protocol.ts:157 already excluded other from the client-label path, so nothing else reads that string.

Three help surfaces still told people to mint an access token under "Share → Invite an agent → Other": the /mcp page, /llms.txt, and docs/self-hosting.md. Two of the three were in the same file, which is why the first sweep caught only one.

Share menu

Three groups: invite an agent; the ways to hand the document to someone (share link, copy link, send to device); then a DOWNLOAD header over the three formats. The formats are labelled Markdown, PDF and EPUB rather than repeating the verb the header already carries.

One commit is not mine

fc00bbf was in the working tree when this session picked it back up. I recorded it rather than authoring it. It reclassifies READ from readOnlyHint: true, openWorldHint: false to readOnlyHint: false, openWorldHint: true — a read enrolls or refreshes the caller in the roster, and any public-by-link document is reachable from its id — moves patch from WRITE to DESTRUCTIVE, and brings chatgpt-app-submission.json in line, adding the two tools written since it last changed. It is self-consistent and the tests were updated with it. If it was not meant to ship, that is the commit to revert.

Verification

Measured in a browser at 800px against a local dev instance: the share menu reads Invite an agent, Copy link, Send to device, DOWNLOAD, Markdown, PDF, EPUB; the printer glyph renders 24x24; the dialog holds at top 60 across all six tabs; the UI path has text-decoration: none with text and mark both 12.25px and both rgb(26, 26, 26); .face-away resolves to brightness(1.28) in light and brightness(0.62) in dark.

968 tests run, 952 pass. The 16 failures are the three localStorage suites on Node 25, which fail identically on main and which CLAUDE.md records as an environment issue. Lint and typecheck clean.

Not verified: the WakeSection instance of UiPath needs a signed-in session and was never seen rendered; it is covered by the component's own tests and by being the same component. The facepile's overlap and away styling were measured through the unit tests and the computed filter, not with a second person actually present.

This branch is deployed to vapor.fyi as bb6e35d5-e2a9-4062-a776-431ff2ada0c9, ahead of merge, at Nicholas's direction.

🤖 Generated with Claude Code

alcor and others added 4 commits September 13, 2026 22:50
Your own face sat 6px clear of the pile while every other face overlapped by
8px. It overlaps like the rest now. Faces for people who are not here were
dimmed with alpha, which let the faces underneath show through where they
overlap; they take grayscale plus a step of brightness away from the page's
own instead, opaquely and in the right direction for each theme.

"Print or save as PDF" drew the word "print". Material Symbols is loaded as a
named subset in root.tsx and `print` was not in it. That is the third time an
icon has shipped as its own ligature text, so there is now a test that reads
every Icon name out of app/ and checks the subset lists it, and a second that
keeps the list sorted so the next addition has an obvious place.

Dialogs are anchored 60px from the top rather than centred. Their content
still changes height between tabs (420px to 548px in Invite an agent), but
centring split every change both ways and moved the dialog under the pointer.

UI paths lost their underline, and the pop-out arrow beside them went from
24px to the 12.25px of the text it sits in. It was 24px because the chrome
rule for menu and dialog glyphs is two classes deep and outranked the utility
on the element; `.material-symbols-outlined.icon-inline` matches that weight.

"Other" is "More" in Invite an agent, with more_horiz rather than a mark: it
is not a product.

The share menu is three groups now: invite an agent, then the ways to hand
the document to someone (link, copy, device), then a Download header over the
three formats, labelled Markdown, PDF and EPUB rather than repeating the verb.

Verified in a browser: the menu reads Invite an agent, Copy link, Send to
device, DOWNLOAD, Markdown, PDF, EPUB; the printer glyph renders 24x24; the
dialog holds at top 60 across all six tabs; the arrow measures 12px; and
.face-away resolves to brightness(1.28) in light and brightness(0.62) in dark.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… same

The last pass changed the underline and the pop-out in AgentsPanel and missed
WakeSection, which carried its own copy of the same markup: a bold path, a
14px grey open_in_new, and `underline decoration-border underline-offset-2`.
Both call sites now render `UiPath`, so there is one definition to change.

The pop-out is no longer grey. It takes the sentence's colour along with its
size, which puts it in the line instead of hanging off it as furniture.

The /mcp help page still told people to look under "Other" for an access
token; that tab is "More" now.

Measured in the dialog: the path reads Settings → Connectors → Add custom
connector, text-decoration none, text and mark both 12.25px, and both
rgb(26, 26, 26). The WakeSection instance needs a signed-in session and was
not seen rendered; it is the same component, covered by the component's tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The /mcp page and /llms.txt each told people to mint an access token under
"Share → Invite an agent → Other", and so did docs/self-hosting.md. The tab
has been called More since 876e523. Two of the three were in the same file,
which is why the first pass caught only one of them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
These edits were in the working tree when this session picked it back up;
they are not mine, and I am recording them rather than authoring them.

`READ` claimed readOnlyHint and a closed world. Neither holds: a read
enrolls or refreshes the caller in the document's agent roster, and any
public-by-link document is reachable from its id alone. `patch` moves from
WRITE to DESTRUCTIVE, which is what a tool that deletes blocks is.

chatgpt-app-submission.json is brought in line and gains the two tools added
since it was written, read_changes and patch, with their justifications.

Rebased onto 989bd69.
@alcor
alcor merged commit 4a2b4ab into main Sep 14, 2026
3 checks passed
@alcor
alcor deleted the feat/ui-polish branch September 14, 2026 06:00
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