Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/app/src/pages/session/use-amicode-commands.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/app/src/pages/session/use-amicode-commands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
33 changes: 21 additions & 12 deletions packages/ui/src/amicode/amicode.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -679,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;
Expand All @@ -707,17 +705,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 {
Expand Down
49 changes: 15 additions & 34 deletions packages/ui/src/amicode/entity-rail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 (
<Show when={amicodeParts().any > 0}>
<div data-component="amicode-entity-rail">
Expand Down Expand Up @@ -296,34 +292,19 @@ export function AmicodeEntityRail(props: {
>
<Icon name={chipIcon(chip.kind)} size="small" />
{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). */}
<Show when={chip.pending}>
<span class="amc-chev" aria-hidden="true">
</span>
</Show>
</button>
</Show>
)}
</For>
<Show when={props.onInspectRun && hasRun()}>
<button
type="button"
data-slot="amicode-rail-inspect"
style={{
display: "inline-flex",
"align-items": "center",
"flex-shrink": "0",
gap: "4px",
border: "1px solid var(--v2-border-border-base)",
"border-radius": "var(--radius-md)",
background: "none",
color: "var(--v2-text-text-accent)",
padding: "2px 8px",
font: "inherit",
"font-weight": "600",
cursor: "pointer",
}}
title="Open the Run Inspector panel"
onClick={() => props.onInspectRun?.()}
>
Inspect Run
</button>
</Show>
</div>
</Show>
</div>
Expand Down
Loading