From 59321d1616254bc0c6137b8221b26f5011242207 Mon Sep 17 00:00:00 2001 From: kate bonner Date: Tue, 28 Jul 2026 18:25:49 -0400 Subject: [PATCH 1/2] style(rail): at-rest chevron on the pending-but-clickable pulse chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pulse chip opens the Run Inspector while still un-banked (766e201e7), but it kept the dotted border — the exact grammar every OTHER pending chip uses to mean inert — so its clickability was only discoverable on hover. Add an always-visible trailing chevron (neutral muted ink, bumps to base on hover/focus) so the shape signals the action at rest; the dotted border still tells the truth about not-yet-banked state. Also retire the stale comment claiming the recorded chips' soft-yellow fill (removed 2026-07-24). Co-Authored-By: Claude Fable 5 --- packages/ui/src/amicode/amicode.css | 30 +++++++++++++++++-------- packages/ui/src/amicode/entity-rail.tsx | 9 ++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/ui/src/amicode/amicode.css b/packages/ui/src/amicode/amicode.css index cf2066cb33..65bba90fa9 100644 --- a/packages/ui/src/amicode/amicode.css +++ b/packages/ui/src/amicode/amicode.css @@ -650,10 +650,11 @@ padding: 0; } -/* Rail entity chips (Kate 2026-07-24): STATIC label only — no value, no chevron - or + icon. A recorded (clickable) chip carries the soft-yellow fill as its - affordance and opens the current version; a not-yet-recorded chip is inert - with a dotted border. Focus ring comes from the shared rail button rule below. */ +/* Rail entity chips (Kate 2026-07-24): STATIC label only — no value, no + icon. + A recorded (clickable) chip is a solid-bordered pill on the layer-01 surface + (the yellow fill was retired 2026-07-24 — neutral surface, hover carries the + feedback) and opens the current version; a not-yet-recorded chip is inert with + a dotted border. Focus ring comes from the shared rail button rule below. */ [data-component="amicode-entity-rail"] .amc-rail-chip { display: inline-flex; align-items: center; @@ -707,17 +708,28 @@ } /* …except a not-recorded chip that IS a button (the pulse chip, which opens the Run Inspector before a pulse is banked): keep the dotted not-yet look, but it - must feel clickable. Hover feedback already comes from button.amc-rail-chip. - It also carries the recorded chips' ink — full-strength colour at weight 600 — - so its glyph and label read as bold as its neighbours instead of dimming out; - faint ink is how a chip says "inert", and this one isn't. The glyph inherits - currentColor, so the colour bump bolds the icon along with the label. The + must READ clickable at rest — hover feedback alone failed the glance test + (Kate 2026-07-28: "doesn't appear clickable"). Three signals: full-strength + ink at weight 600 (faint ink is how a chip says "inert", and this one isn't; + the glyph inherits currentColor so it bolds with the label), an always-visible + trailing chevron (the shape says "this goes somewhere" — same glyph family as + the receipt card's, but never hover-gated here), and the pointer cursor. The dotted border still carries the not-yet-banked state on its own. */ [data-component="amicode-entity-rail"] button.amc-rail-chip.is-empty { cursor: pointer; color: var(--v2-text-text-base); font-weight: 600; } +[data-component="amicode-entity-rail"] .amc-rail-chip .amc-chev { + color: var(--v2-text-text-muted); + font-size: var(--font-size-md); + line-height: 1; + transition: color 0.16s ease; +} +[data-component="amicode-entity-rail"] button.amc-rail-chip:hover .amc-chev, +[data-component="amicode-entity-rail"] button.amc-rail-chip:focus-visible .amc-chev { + color: var(--v2-text-text-base); +} [data-component="amicode-entity-rail"] button:focus-visible, [data-component="amicode-ask-card"] button:focus-visible, [data-component="amicode-run-window"] button:focus-visible { diff --git a/packages/ui/src/amicode/entity-rail.tsx b/packages/ui/src/amicode/entity-rail.tsx index c2b7352ca0..2e5b4bc88e 100644 --- a/packages/ui/src/amicode/entity-rail.tsx +++ b/packages/ui/src/amicode/entity-rail.tsx @@ -296,6 +296,15 @@ export function AmicodeEntityRail(props: { > {chip.label} + {/* At-rest chevron on the pending-but-clickable chip: the dotted + border alone reads "inert" (that's what it means on every + other pending chip), so the shape — not hover — carries the + "this goes somewhere" signal (Kate 2026-07-28). */} + + + )} From 2f7c9e157512b5532e056c078de37eb8f8a86e4e Mon Sep 17 00:00:00 2001 From: kate bonner Date: Tue, 28 Jul 2026 19:01:26 -0400 Subject: [PATCH 2/2] =?UTF-8?q?refactor(rail):=20drop=20the=20Inspect=20Ru?= =?UTF-8?q?n=20button=20=E2=80=94=20the=20pulse=20chip=20is=20the=20one=20?= =?UTF-8?q?inspector=20entry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chip and the button opened the same panel from the same row; with the chip now carrying an at-rest affordance, the button was redundant chrome (Kate 2026-07-28). Comments referencing the button updated to name the pulse chip as the rail's inspector entry. Co-Authored-By: Claude Fable 5 --- .../session/use-amicode-commands.test.ts | 4 +- .../pages/session/use-amicode-commands.tsx | 4 +- packages/ui/src/amicode/amicode.css | 3 -- packages/ui/src/amicode/entity-rail.tsx | 40 +++---------------- 4 files changed, 10 insertions(+), 41 deletions(-) diff --git a/packages/app/src/pages/session/use-amicode-commands.test.ts b/packages/app/src/pages/session/use-amicode-commands.test.ts index 602ba8d10c..0c768fd021 100644 --- a/packages/app/src/pages/session/use-amicode-commands.test.ts +++ b/packages/app/src/pages/session/use-amicode-commands.test.ts @@ -1,8 +1,8 @@ import { describe, expect, spyOn, test } from "bun:test" import { inAmicode, postAmicode } from "./use-amicode-commands" -// The "Inspect Run" button (entity rail) and the Amico command palette both -// reach the VS Code extension through postAmicode(). chat_panel.ts relays the +// The entity rail's pulse chip (its one inspector entry) and the Amico command +// palette both reach the VS Code extension through postAmicode(). chat_panel.ts relays the // envelope to the host and executes ONLY commands on its BRIDGE_ALLOWED_COMMANDS // allowlist — so the exact envelope shape and command string are a contract. describe("postAmicode bridge envelope", () => { diff --git a/packages/app/src/pages/session/use-amicode-commands.tsx b/packages/app/src/pages/session/use-amicode-commands.tsx index 760680081b..df4bf2fe7c 100644 --- a/packages/app/src/pages/session/use-amicode-commands.tsx +++ b/packages/app/src/pages/session/use-amicode-commands.tsx @@ -10,8 +10,8 @@ import { useCommand, type CommandOption } from "@/context/command" // Each command posts {source:"amicode",kind:"command",command} to window.parent; // chat_panel.ts relays it to an ALLOWLISTED vscode command. -// Exported so non-palette surfaces (e.g. the "Inspect Run" button on the entity -// rail) can fire the same host-bridged commands. inAmicode() gates them out of +// Exported so non-palette surfaces (e.g. the pulse chip on the entity rail — +// the rail's one inspector entry) can fire the same host-bridged commands. inAmicode() gates them out of // the public web/share build, where there is no extension host to relay to. export const inAmicode = () => typeof window !== "undefined" && window.self !== window.top diff --git a/packages/ui/src/amicode/amicode.css b/packages/ui/src/amicode/amicode.css index 65bba90fa9..4fb938f0a7 100644 --- a/packages/ui/src/amicode/amicode.css +++ b/packages/ui/src/amicode/amicode.css @@ -680,16 +680,13 @@ height: 14px; flex-shrink: 0; } -[data-slot="amicode-rail-inspect"], [data-slot="amicode-rail-retry"] { transition: background 0.16s ease, border-color 0.16s ease; } -[data-slot="amicode-rail-inspect"]:hover, [data-slot="amicode-rail-retry"]:hover { background: var(--v2-background-bg-layer-02); border-color: var(--v2-border-border-strong); } -[data-slot="amicode-rail-inspect"]:focus-visible, [data-slot="amicode-rail-retry"]:focus-visible { outline: 1px solid var(--v2-border-border-focus); outline-offset: 1px; diff --git a/packages/ui/src/amicode/entity-rail.tsx b/packages/ui/src/amicode/entity-rail.tsx index 2e5b4bc88e..3d7129b8db 100644 --- a/packages/ui/src/amicode/entity-rail.tsx +++ b/packages/ui/src/amicode/entity-rail.tsx @@ -115,9 +115,10 @@ export function AmicodeEntityRail(props: { widgetHost?: AmicodeWidgetHost onOpenEntity: (kind: string, seq?: number) => void onAsk?: (text: string) => void - // Bridge-agnostic: fired when the user clicks "Inspect Run". The app wires it - // to the host (postAmicode → amicode.openInspector) and passes it only when - // framed in Amicode, so the button stays hidden everywhere else. + // Bridge-agnostic: fired when the user clicks the pulse chip (the rail's one + // inspector entry). The app wires it to the host (postAmicode → + // amicode.openInspector) and passes it only when framed in Amicode, so the + // chip falls back to dialog/inert behavior everywhere else. onInspectRun?: () => void retryLabel: string unavailableLabel: string @@ -207,15 +208,10 @@ export function AmicodeEntityRail(props: { // Which chips hand off to the Run Inspector instead of the entity dialog. // Only the pulse chip, and only when the host actually wired an inspector — // standalone opencode has none, so there the chip keeps its dialog behavior. + // The pulse chip is the ONLY inspector entry on the rail — the separate + // "Inspect Run" button was redundant chrome next to it (Kate 2026-07-28). const opensInspector = (kind: string) => kind === "pulse" && props.onInspectRun !== undefined - // Whether there is a run to inspect — gates the "Inspect Run" button so it - // appears alongside the live run chip, not before any solve has started. - const hasRun = createMemo(() => { - const snapshot = state() - return snapshot.kind === "ready" && snapshot.view.runs.length > 0 - }) - return ( 0}>
@@ -309,30 +305,6 @@ export function AmicodeEntityRail(props: { )} - - -