diff --git a/apps/ui/e2e/__screens__/agent.png b/apps/ui/e2e/__screens__/agent.png index 8b371c16..f948923a 100644 Binary files a/apps/ui/e2e/__screens__/agent.png and b/apps/ui/e2e/__screens__/agent.png differ diff --git a/apps/ui/e2e/__screens__/backlog.png b/apps/ui/e2e/__screens__/backlog.png index 3e9675e8..d9f424ff 100644 Binary files a/apps/ui/e2e/__screens__/backlog.png and b/apps/ui/e2e/__screens__/backlog.png differ diff --git a/apps/ui/src/app.css b/apps/ui/src/app.css index c63bf5cd..c350034a 100644 --- a/apps/ui/src/app.css +++ b/apps/ui/src/app.css @@ -1586,6 +1586,15 @@ font-family: var(--cx-font-ui); } +/* Seat the inline branch glyph (state-dot.css §seating). */ +.usage-git-glyph { + display: inline-flex; +} + +.usage-git-glyph > svg { + display: block; +} + /* ── Shared helpers ─────────────────────────────────────────────────────── */ .muted { @@ -1640,11 +1649,17 @@ .backlog-chevron { color: var(--cx-text-faint); - font-size: 10px; + display: inline-flex; /* stylelint-disable-next-line declaration-property-value-disallowed-list -- TODO(motion migration, D9/foundation-T8): raw 0.12s/ease grandfathered by the RIG-2034 cutover lane, which never scoped the motion axis */ transition: transform 0.12s ease; } +/* Seat the inline glyph SVG (state-dot.css §seating): an inline replaced box + * otherwise rides the text baseline and overflows its box. */ +.backlog-chevron > svg { + display: block; +} + .backlog-chevron.open { transform: rotate(90deg); } @@ -3485,6 +3500,17 @@ cursor: default; } +/* The Stop control's halt-mark glyph, seated inline beside the "stop" label. */ +.obs-stop-glyph { + display: inline-flex; + vertical-align: middle; + color: currentColor; +} + +.obs-stop-glyph > svg { + display: block; +} + /* A REFUSED stop, said beside the control rather than left in the console — * same weight and colour as the ask block's refusal (.ask-error). */ .obs-error { @@ -3507,6 +3533,26 @@ color: var(--cx-text); } +/* The minimize/expand toggle's caret glyph. `disclosure` points right; the open + * panel keeps that (the old `⟩`), and the minimized rail rotates it 180° to + * point left (the old `⟨`, the expand affordance). Both states are one glyph so + * the pair can never misalign the way the old character pair could. */ +.obs-min-glyph { + display: inline-flex; + color: currentColor; + transform: rotate(180deg); + /* stylelint-disable-next-line declaration-property-value-disallowed-list -- TODO(motion migration, D9/foundation-T8): raw 0.12s/ease grandfathered by the RIG-2034 cutover lane, which never scoped the motion axis */ + transition: transform 0.12s ease; +} + +.obs-min-glyph.open { + transform: rotate(0deg); +} + +.obs-min-glyph > svg { + display: block; +} + .log-panel .obs-body { flex: 1; min-height: 0; diff --git a/apps/ui/src/comms.ts b/apps/ui/src/comms.ts index 5fbceb45..5d17d402 100644 --- a/apps/ui/src/comms.ts +++ b/apps/ui/src/comms.ts @@ -34,14 +34,16 @@ export function isDm(channel: Channel): boolean { } /** The glyph before a channel name, by kind (Discord-style: # for a channel, - * @ for a DM, a cluster glyph for a group DM). One home so the rail row and - * the channel header never drift. */ + * @ for a DM, & for a group DM — the "cluster" marker). One home so the rail + * row and the channel header never drift. All three are ASCII, covered by the + * brand face; this stays DATA (a string in the marker column), not a `` + * — a bitmap for one kind only would mix glyph and character in one column. */ export function channelGlyph(kind: Channel["kind"]): string { switch (kind) { case "dm": return "@"; case "group_dm": - return "⌗"; + return "&"; default: return "#"; } diff --git a/apps/ui/src/components/BacklogView.tsx b/apps/ui/src/components/BacklogView.tsx index b1fdf9a9..f894e754 100644 --- a/apps/ui/src/components/BacklogView.tsx +++ b/apps/ui/src/components/BacklogView.tsx @@ -2,6 +2,7 @@ import { type Component, createSignal, For, Show } from "solid-js"; import { isMultiForge, issueKey } from "../board-render"; import { useStore } from "../context"; import type { Issue } from "../stub-data"; +import { Glyph } from "./Glyph"; /** A single Linear-style issue row: id · title · priority · state · tracker. * Clicking the row selects the issue (staying on the view). Not the board @@ -63,7 +64,9 @@ const BacklogSection: Component<{ aria-controls={contentId} onClick={() => setOpen(!open())} > - + {props.title} {props.rows.length} diff --git a/apps/ui/src/components/Glyph.tsx b/apps/ui/src/components/Glyph.tsx index 60ffefdf..1784bdb2 100644 --- a/apps/ui/src/components/Glyph.tsx +++ b/apps/ui/src/components/Glyph.tsx @@ -32,7 +32,8 @@ export type GlyphName = | "gear" | "logo" | "panel-right" - | "panel-left"; + | "panel-left" + | "stop"; /** [x, y] of each lit cell (11×11, one CSS px per cell), transcribed from the * frozen ASCII grids in `design/components.md` §Glyphs (`#` = lit). Keying on @@ -911,6 +912,60 @@ const GLYPH_CELLS: Record< [8, 9], [9, 9], ], + // A filled 7×7 square (rows/cols 2..8) — the halt mark on the log-panel Stop + // control, replacing the uncovered `■` U+25A0. One solid block, deliberately + // centered with a 2-cell margin so it reads square, not full-bleed. + stop: [ + [2, 2], + [3, 2], + [4, 2], + [5, 2], + [6, 2], + [7, 2], + [8, 2], + [2, 3], + [3, 3], + [4, 3], + [5, 3], + [6, 3], + [7, 3], + [8, 3], + [2, 4], + [3, 4], + [4, 4], + [5, 4], + [6, 4], + [7, 4], + [8, 4], + [2, 5], + [3, 5], + [4, 5], + [5, 5], + [6, 5], + [7, 5], + [8, 5], + [2, 6], + [3, 6], + [4, 6], + [5, 6], + [6, 6], + [7, 6], + [8, 6], + [2, 7], + [3, 7], + [4, 7], + [5, 7], + [6, 7], + [7, 7], + [8, 7], + [2, 8], + [3, 8], + [4, 8], + [5, 8], + [6, 8], + [7, 8], + [8, 8], + ], }; /** Every glyph name, derived from the table itself so callers that enumerate diff --git a/apps/ui/src/components/LogPanel.tsx b/apps/ui/src/components/LogPanel.tsx index 4bf1dbf1..74013f58 100644 --- a/apps/ui/src/components/LogPanel.tsx +++ b/apps/ui/src/components/LogPanel.tsx @@ -2,6 +2,7 @@ import { type Component, Show } from "solid-js"; import { useStore } from "../context"; import { foldSession } from "../session-events"; import type { Agent } from "../stub-data"; +import { Glyph } from "./Glyph"; import { SessionTrace } from "./SessionTrace"; /** The trace observation pane: the selected agent's typed execution trace. The @@ -82,7 +83,10 @@ export const LogPanel: Component<{ agent: Agent }> = (props) => { } onClick={() => void store.stopAgent()} > - ■ stop + {" "} + stop {/* A refused stop (a server with no RunnerHub answers `Unavailable`, @@ -104,7 +108,12 @@ export const LogPanel: Component<{ agent: Agent }> = (props) => { title={store.logOpen() ? "Minimize log panel" : "Expand log panel"} onClick={() => store.toggleLog()} > - {store.logOpen() ? "⟩" : "⟨"} + diff --git a/apps/ui/src/components/UsageBar.tsx b/apps/ui/src/components/UsageBar.tsx index 9f7934b0..30a7e53f 100644 --- a/apps/ui/src/components/UsageBar.tsx +++ b/apps/ui/src/components/UsageBar.tsx @@ -1,5 +1,6 @@ import { type Component, For } from "solid-js"; import { STUB_USAGE } from "../stub-data"; +import { Glyph } from "./Glyph"; const fmtTokens = (n: number): string => n >= 1_000_000 @@ -41,7 +42,10 @@ export const UsageBar: Component = () => ( - compass · main + {" "} + compass · main ); diff --git a/apps/ui/src/design/components.md b/apps/ui/src/design/components.md index bf1828d6..3e0b60dd 100644 --- a/apps/ui/src/design/components.md +++ b/apps/ui/src/design/components.md @@ -729,6 +729,24 @@ toggle-right-sidebar control): ........... ``` +### T5c glyph grid (LogPanel, 11×11) + +`stop` — a filled square halt mark (replaces `■`, the log-panel Stop control): + +```text +........... +........... +..#######.. +..#######.. +..#######.. +..#######.. +..#######.. +..#######.. +..#######.. +........... +........... +``` + ### Chrome conversion audit (T5a + T5b) Every chrome site converted from a character to a ``, with the @@ -777,6 +795,38 @@ The two name-bearing rows are the hazard this table exists to catch: a bare `✓`/`✗` verdict mark reads as nothing once the glyph is hidden, so those sites carry the verdict word on the wrapper. `RightSidebar.prpane.test.tsx` pins that. +### Chrome conversion audit (T5c) + +The final chrome-pictograph slice: the remaining characters no brand face +covers (`■ ⟩ ⟨ ⎇ ⌗ ▸`) and the `→` style calls. All converted sites below are +**decorative** — adjacent text or an `aria-label` on the control names them. + +| Site | Was | Glyph | +| --- | --- | --- | +| `BacklogView` section caret | `▸` | `disclosure` | +| `LogPanel` Stop mark | `■` | `stop` | +| `LogPanel` minimize toggle | `⟩` `⟨` | `disclosure` (CSS rotates 180°) | +| `UsageBar` branch mark | `⎇` | `vcs` | + +Only `stop` is a new bitmap; the rest reuse existing glyphs. The minimize +toggle is the two-state case (record T5c brief §2): both states are the one +`disclosure` glyph, rotated by CSS — `⟩` open, `⟨` (180°) minimized — so the +pair can never misalign the way two separate characters could. + +`comms.channelGlyph` returns the group-DM marker as DATA (a string in the +marker column beside `@` and `#`), so a `` cannot go there. The old `⌗` +U+2317 was uncovered; it swaps to `&` U+0026, covered by the brand face — a +bitmap for one kind alone would mix glyph and character in one column. + +Kept as text — the `→` U+2192 is in the cmap, so a bitmap is a style call, not +a coverage fix, and the typographic arrow reads at least as well: + +| Site | Char | Why | +| --- | --- | --- | +| `Bridge` lane open cue (2) | `→` | reads no better as a bitmap at 11px | +| `SettingsView` map arrows (2) | `→` | chrome arrow; kept with the prose | +| `SettingsView` prose arrows (2) | `→` | mid-sentence — a bitmap breaks type | + ## Tabs - **Class:** `.cx-tabs` · `data-orientation="h | v"`, with `.cx-tab` items