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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Invariants:
- Session Activity strip (Wave 4 P2) — per-sister ephemeral commands/files next to feed from mouth-stream / action ledger (no second audit DB); writes show path·size only; `docs/activity.md`; no CopilotKit; package 0.13.0.
- Peer chase / head keep-alive (Wave 5 P0) — `offerSisterHop` / `sendToAgent` park `pendingHops` envelopes; head working while hops are out; assess injects `expecting` and may hop again if unmet; empty/fail sister auto-wakes a notice; `docs/peer-chase.md`; no CopilotKit / `@ag-ui/*`; sisters stay separate; package 0.13.0.
- GPUI polish P0 (Wave 6) — toast stack + level policy (`src/chrome/toast*.tsx` / toast-store); EmptyState icon/CTA; focus chords Cmd+L / Cmd+B / Cmd+, / Cmd+J (HID + gpuix); `docs/wave6-gpui-polish.md`; no CopilotKit / gpui-kit; idle parks intact; package 0.13.0.
- GPUI polish P1 (Wave 6) — Cmd+K command palette lite (sisters / rooms / settings sections / Jobs); visible steer-queue card (remove / send-now); shared `ListRow` density + hover end-slot; `Tip` microcopy on titlebar / rail / Jobs; opaque menuFill overlays; idle parks intact; package stays 0.13.0 (no version cut in P1).
- Bezel light appearance (Wave 2 P2) — designed light (Settings → Window → Appearance; not invert of graphite); Brand-complete Settings (remaining CARD/FIELD via useChrome; update modal scrim); ToggleGroup, EmptyState, Sheet extracted. Still vendored `@gpuix/react` (Wave 3.2 is 0.6.2 + matched native). Package 0.8.0.

## Safety
Expand Down
42 changes: 36 additions & 6 deletions docs/wave6-gpui-polish.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Wave 6 — GPUI polish (toast / empty CTA / focus chords)
# Wave 6 — GPUI polish (toast / palette / steer / density)

**Band:** P0 (feedback & findability)
**Bands:** P0 (feedback & findability) + **P1** (palette, queue chrome, density)
**Package:** stays on current main (no version cut in this PR)
**Patterns only** — recipes from rgitui / ChatGPUI / Waku / Bezel. No CopilotKit, Electron, or `gpui-kit` / `gpui-component` crates.
**Patterns only** — recipes from rgitui / ChatGPUI / Waku / Bezel / Zeron. No CopilotKit, Electron, or `gpui-kit` / `gpui-component` crates.

## What shipped (P0)

Expand Down Expand Up @@ -33,14 +33,44 @@ GPUIx `onKeyDown` plus the same HID watch discipline as paste/copy/quit (textare
| **Cmd+J** | Toggle Jobs sheet |
| Cmd+Shift+I | Inspector (existing) |

## What shipped (P1)

### Command palette lite (Cmd+K)
- `src/chrome/command-palette.tsx` + `src/chrome/palette.ts`
- Overlay: fuzzy **Sisters** / **Rooms** / **Settings** sections / **Jobs**
- Search field keeps focus; list cursor is drawn separately
- **Esc** clears the query first, then closes
- Events only (select sister / room / open settings section / open Jobs) — no command ontology kit
- Opaque `menuFill` card

### Visible steer queue card
- `src/chrome/steer-queue.tsx` above the composer
- One opaque card; one row per queued line
- **Remove** / **Send now** (promote when busy; send that line alone when idle)
- Quiet microcopy when Send would queue and the queue is empty
- Session: `removeSteerAt` / `sendSteerNow`

### List density + hover end-slot
- Shared `ListRow` (`src/chrome/list-row.tsx`) — compact/default density
- Settings routines / skills / rooms: secondary actions reveal on hover
- Rail New / Settings footer rows use `ListRow` (compact when rail is compact)

### Tooltip microcopy
- `Tip` (`src/chrome/tooltip.tsx`) — one-line raised opaque card; no new npm lib
- Titlebar Computer / Jobs; rail New / Settings / sister hits

| Chord | Action |
| --- | --- |
| **Cmd+K** | Toggle command palette |

No full keymap file yet — P2 shortcuts-help can generate from one table later.

## Parks (unchanged)
- Idle parks / resting-motion
- Steer queue still hint-string (P1 queue card)
- Command palette (P1)
- OS Notification Center (P2)
- Pulse lease strides / overlay scrollbar (P2)
- No `bezel` / `gpui-kit` deps

## Audit
See `/workspace/automaton-wave6-gpui-polish-audit.md` (box) and `artifacts/automaton-wave6-gpui-polish-audit.md` when copied. P0 row status: **implemented on branch `feat/wave-6-p0-toast-empty-chords`**.
See `/workspace/automaton-wave6-gpui-polish-audit.md` (box) and `artifacts/automaton-wave6-gpui-polish-audit.md` when copied.
P0: shipped v0.13.0. P1: branch `feat/wave-6-p1-palette-steer-density`.
135 changes: 81 additions & 54 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
visibleAgents,
} from './domain'
import { ingestPath, insertClipboardText, pickLocalFiles, readClipboardPaths, readClipboardText } from './runtime/attachments'
import { watchCopyHotkey, watchCutHotkey, watchFocusComposerHotkey, watchJobsHotkey, watchPasteHotkey, watchQuitHotkey, watchSelectAllHotkey, watchSettingsHotkey, watchToggleRailHotkey } from './runtime/paste-hotkey'
import { watchCopyHotkey, watchCutHotkey, watchFocusComposerHotkey, watchJobsHotkey, watchPasteHotkey, watchQuitHotkey, watchSelectAllHotkey, watchSettingsHotkey, watchToggleRailHotkey, watchPaletteHotkey } from './runtime/paste-hotkey'
import { runningTests } from './runtime/test-env'
import { applyUpdate, checkForUpdate, dismissUpdate, readDismissedSha, shouldOfferUpdate, relaunchAutomaton, type UpdateOffer } from './runtime/updates'
import {
Expand Down Expand Up @@ -88,6 +88,7 @@ import {
toggleRailChord,
settingsChord,
jobsChord,
commandPaletteChord,
} from './inspector'
import { DeskStage } from './desk'
import { JobsPane } from './jobs-pane'
Expand All @@ -101,6 +102,7 @@ import { initiatorFromKickoff, mouthStreamLabel } from './runtime/mouth-stream'
import { sessionActivityForSister } from './runtime/session-activity'
import { setHumanDriving } from './runtime/driving'
import { quitAutomaton } from './runtime/quit'
import { listRooms } from './runtime/rooms'
import { ensureScreen } from './runtime/screen'
import {
addLiveAgent,
Expand All @@ -117,6 +119,8 @@ import {
dismissFanout,
dropLiveAgent,
dropPendingPath,
removeSteerAt,
sendSteerNow,
failComputer,
failJob,
failMouth,
Expand Down Expand Up @@ -153,7 +157,7 @@ import {
import { SisterBlob, framePath, markFor } from './blob'
import { railDragOrigin, railIsCompact, railWidthFromDrag, readSkin, toggleRailWidth, writeSkin } from './runtime/skin'
import { ConfirmCard, QuestionCard, SecretRequestCard } from './cards'
import { ActivityZone, Composer, EmptyState, MouthWaitBubble, Sheet, Titlebar, ToastStack, activityVisible, groupBoxStyle, pushToast, tracesFromJob } from './chrome'
import { ActivityZone, CommandPalette, Composer, EmptyState, ListRow, MouthWaitBubble, Sheet, SteerQueueCard, Tip, Titlebar, ToastStack, activityVisible, groupBoxStyle, pushToast, tracesFromJob } from './chrome'
import { connectorDisplayName } from './runtime/connectors'
import { Settings } from './settings'
import { motionTransition } from './motion'
Expand Down Expand Up @@ -265,6 +269,8 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
})
const [pane, setPane] = useState<Pane>('none')
const [composerFocusNonce, setComposerFocusNonce] = useState(0)
const [paletteOpen, setPaletteOpen] = useState(false)
const [settingsFocus, setSettingsFocus] = useState<string | null>(null)
const [planeTick, setPlaneTick] = useState(0)
const [railWidth, setRailWidth] = useState(() => readSkin().railWidth)
const [railDragging, setRailDragging] = useState(false)
Expand Down Expand Up @@ -337,6 +343,15 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
setPane((current) => (current === 'settings' ? 'none' : 'settings'))
}

const togglePalette = () => {
setPaletteOpen((open) => !open)
}

const openSettingsSection = (section?: string) => {
setSettingsFocus(section ?? null)
setPane('settings')
}

const focusComposer = () => {
setComposerFocusNonce((n) => n + 1)
}
Expand Down Expand Up @@ -855,6 +870,7 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
const stopRail = watchToggleRailHotkey(() => toggleRail())
const stopSettings = watchSettingsHotkey(() => toggleSettings())
const stopJobs = watchJobsHotkey(() => toggleJobs())
const stopPalette = watchPaletteHotkey(() => togglePalette())
return () => {
stopPaste()
stopCopy()
Expand All @@ -865,6 +881,7 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
stopRail()
stopSettings()
stopJobs()
stopPalette()
}
}, [])

Expand Down Expand Up @@ -922,6 +939,7 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
if (toggleRailChord(event)) toggleRail()
if (settingsChord(event)) toggleSettings()
if (jobsChord(event)) toggleJobs()
if (commandPaletteChord(event)) togglePalette()
if (pasteChord(event)) enqueueClipboard()
if (selectAllChord(event)) feedApi.current?.selectAll()
if (copyChord(event)) copyFeedIfIdle()
Expand All @@ -930,6 +948,23 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
}}
>
<Titlebar name={active?.name ?? PRODUCT} version={installedVersion} onInspect={toggleInspector} onJobs={toggleJobs} />
<CommandPalette
open={paletteOpen}
agents={visibleAgents(session.agents)}
rooms={listRooms()}
onClose={() => setPaletteOpen(false)}
onSelectSister={(id) => {
setSession((current) => (runningTests() ? setActive(current, id) : playIntro(setActive(current, id), id)))
setPane('none')
}}
onSelectRoom={(_roomId) => {
openSettingsSection('rooms')
}}
onOpenSettings={(section) => openSettingsSection(section)}
onOpenJobs={() => {
setPane('jobs')
}}
/>
<div
style={{
display: 'flex',
Expand Down Expand Up @@ -1113,6 +1148,21 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
/>
) : null}
<ToastStack style={{ paddingLeft: T.space.lg, paddingRight: T.space.lg, paddingBottom: T.space.sm }} />
<SteerQueueCard
lines={overlayBusy ? [] : (thread?.steerQueue ?? [])}
queueing={Boolean(
!overlayBusy &&
thread &&
shouldQueueSteer(thread.mouth, thread.computerBusy === true) &&
(thread.steerQueue?.length ?? 0) === 0,
)}
onRemove={(index) =>
setSession((current) => removeSteerAt(current, current.activeAgentId, index))
}
onSendNow={(index) =>
setSession((current) => sendSteerNow(current, current.activeAgentId, index))
}
/>
<Composer
value={overlayBusy ? '' : (thread?.draft ?? '')}
agents={session.agents}
Expand Down Expand Up @@ -1194,7 +1244,11 @@ function StaffApp({ store: providedStore }: { store?: StaffStore } = {}) {
<Settings
metrics={store.metrics()}
agents={visibleAgents(session.agents)}
onClose={() => setPane('none')}
focusSection={settingsFocus}
onClose={() => {
setSettingsFocus(null)
setPane('none')
}}
onToast={(level, message) => pushToast({ level, message })}
onPlaneChange={() => setPlaneTick((n) => n + 1)}
onSkinChange={() => {
Expand Down Expand Up @@ -1629,8 +1683,8 @@ function Rail({
const family = modelFamily(pin)
const at = row ? lastItemAt(row.items) : null
return (
<Tip key={agent.id} testId={`agent-tip-${agent.id}`} label={compact ? agent.name : `${agent.name} — right-click for menu`} side="right">
<div
key={agent.id}
testId={`agent-${agent.id}`}
style={{
display: 'flex',
Expand Down Expand Up @@ -1709,60 +1763,33 @@ function Rail({
)}
</div>
</div>
</Tip>
)
})}
<div style={{ flexGrow: 1 }} />
<div
testId="new-agent"
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: compact ? 'center' : 'flex-start',
gap: compact ? 0 : T.space.sm,
paddingLeft: rowPad,
paddingRight: rowPad,
paddingTop: T.space.sm,
paddingBottom: T.space.sm,
marginLeft: rowMargin,
marginRight: rowMargin,
marginBottom: T.space.xxs,
borderRadius: T.radius.md,
minHeight: T.blob.slot,
...HIT,
backgroundColor: T.clear,
hover: { backgroundColor: T.raised },
}}
onClick={onCreate}
>
<PlusMark />
{compact ? null : <div style={{ fontSize: T.type.sm, color: T.secondary }}>New automaton</div>}
<div style={{ marginLeft: rowMargin, marginRight: rowMargin, marginBottom: T.space.xxs }}>
<Tip testId="rail-new-tip" label="New automaton" side="top">
<ListRow
testId="new-agent"
density={compact ? 'compact' : 'default'}
startSlot={<PlusMark />}
onClick={onCreate}
>
{compact ? null : <div style={{ fontSize: T.type.sm, color: T.secondary }}>New automaton</div>}
</ListRow>
</Tip>
</div>
<div
testId="settings-open"
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: compact ? 'center' : 'flex-start',
gap: compact ? 0 : T.space.sm,
paddingLeft: rowPad,
paddingRight: rowPad,
paddingTop: T.space.sm,
paddingBottom: T.space.sm,
marginLeft: rowMargin,
marginRight: rowMargin,
marginBottom: T.space.md,
borderRadius: T.radius.md,
minHeight: T.blob.slot,
...HIT,
backgroundColor: T.clear,
hover: { backgroundColor: T.raised },
}}
onClick={onSettings}
>
<GearMark />
{compact ? null : <div style={{ fontSize: T.type.sm, color: T.secondary }}>Settings</div>}
<div style={{ marginLeft: rowMargin, marginRight: rowMargin, marginBottom: T.space.md }}>
<Tip testId="rail-settings-tip" label="Settings (Cmd+,)" side="top">
<ListRow
testId="settings-open"
density={compact ? 'compact' : 'default'}
startSlot={<GearMark />}
onClick={onSettings}
>
{compact ? null : <div style={{ fontSize: T.type.sm, color: T.secondary }}>Settings</div>}
</ListRow>
</Tip>
</div>
</div>
)
Expand Down
Loading
Loading