Skip to content

fix(ux): consolidate Convert into a single coral chip + restore agent on reload + #16 follow-up - #22

Merged
joeylee12629-star merged 3 commits into
mainfrom
fix/ux-toolbar-trio
May 15, 2026
Merged

fix(ux): consolidate Convert into a single coral chip + restore agent on reload + #16 follow-up#22
joeylee12629-star merged 3 commits into
mainfrom
fix/ux-toolbar-trio

Conversation

@joeylee12629-star

Copy link
Copy Markdown
Contributor

Summary

Three small UX/agent-layer fixes that landed at the same time. All
discovered while validating main after #17 merged.

Issue Fix Commit
Convert action duplicated between toolbar and floating chip; users hit one or the other and got confused Drop the toolbar pair, leave the chip as the single Convert source, recolor it from var(--ink) to var(--coral) so it reads as a primary CTA fix(ux): consolidate Convert into a single coral chip
Agent toolbar chip shows red "Select agent" after a hard reload, even though the persisted selection is intact Fetch /api/agents on mount in the home page (after hydration) so the chip can resolve selectedAgent to a label without waiting for the user to open Settings/Welcome fix(home): detect agents on mount so the agent chip restores after reload
#17 fixed invokeAgent's win32 spawn but missed the matching one inside resolveOpenclawAgentId — same root cause, same fix shell: process.platform === "win32" on the second spawn. Closes the rest of #16. fix(agents): use shell on win32 in resolveOpenclawAgentId (#16 follow-up)

Detail

Convert chip consolidation

Before:

  • Top toolbar: ⚡ Convert to HTML + Stop button + ⌘/Ctrl+Enter
    global shortcut (in Toolbar's useEffect).
  • Editor/preview divider: floating ConvertChip doing the same thing,
    near-black var(--ink).

Two CTAs for one action read as either "is this two different things?"
or "did I miss something?". The toolbar pair gets removed; the chip
inherits the keyboard shortcut so muscle memory survives.

Color: var(--coral) (#c96442) idle, var(--coral-hover) (#b25737)
running, with a coral-tinted shadow to match. Disabled state still
renders coral at 0.4 opacity. Stop variant in running state stays the
same shape — pulse-dot + label — only the coral-hover background
distinguishes it.

Agent restoration on reload

The store partializer persists selectedAgent but never agents[]
(correct — agents is a transient capability list, not user state).
The only paths that called /api/agents were the modal mount effects
in welcome-modal.tsx and settings-modal.tsx. So a user who hard-
reloaded without opening either modal saw agents = [], and the
toolbar's agents.find(a => a.id === selectedAgent) returned undefined
→ red "Select agent" fallback rendered, even though selection was
fine in localStorage.

Fix: fire the same fetch from the home page on mount, behind a
hydrated gate so SSR isn't impacted. On failure (offline, agent
backend hiccup) the fallback paths in the modals still cover retry.

Win32 spawn (resolveOpenclawAgentId)

@lux237859-boop's report on #16 identified two spawn call sites that
needed shell: true on win32. PR #17 fixed invokeAgent's; the
resolveOpenclawAgentId one was missed. The argv is a fixed
["agents", "list"] (no user input), so flipping shell: true on
win32 here doesn't widen the shell-injection surface — same safety
analysis as #17.

Verified

  • pnpm exec tsc --noEmit clean.
  • Manual dev-server pass on macOS arm64:
    • Toolbar shows brand · agent chip · template · layout · settings · export — no Convert/Stop button.
    • Floating ConvertChip renders coral, deepens to coral-hover when running, animates to Stop. ⌘+Enter triggers from the editor pane and from blank focus.
    • Hard-reloaded the page (Cmd+Shift+R) on a tab where Claude Code was previously selected — agent chip rendered the agent's label immediately (no "Select agent" flash longer than first-paint).
  • Win32 path (resolveOpenclawAgentId) is by inspection — does not regress unix.

Test plan for reviewers

Out of scope

  • Draft button copy ("✨ Draft") stays as-is — separate naming discussion deferred.
  • No i18n changes; the chip already uses convertChip.label / toolbar.stop keys.
  • The toolbar agent-chip "Select agent" copy itself is unchanged — only its trigger condition is correctly satisfied now.

🤖 Generated with Claude Code

joeylee12629-star and others added 3 commits May 15, 2026 16:45
The Toolbar carried its own Convert/Stop button (and the ⌘/Ctrl+Enter
shortcut) while a floating ConvertChip rendered the same action over the
editor / preview divider. Two buttons, identical action — confusing.

Drop the toolbar pair, leave the chip as the single source of Convert
truth, and migrate the keyboard shortcut onto it. Recolor the chip from
the previous near-black `var(--ink)` to `var(--coral)` (idle) /
`var(--coral-hover)` (running) so the primary CTA is visually distinct
from the muted toolbar surface and from text content. Disabled state
still renders coral at 0.4 opacity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…load

The store persists `selectedAgent` across hard reloads but not the
`agents[]` list. Today that list is only populated when Settings or
Welcome modals open and call `/api/agents`. So a user who reloads
without opening either modal sees the toolbar agent chip render the
red "Select agent" fallback even though their selection is intact —
because `agents.find(a => a.id === selectedAgent)` returns undefined
against an empty list.

Fire the same `/api/agents` fetch from the home page on mount (after
hydration, so it doesn't fight SSR). Failures fall through silently —
Settings / Welcome retain their own retry-on-open paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-up)

PR #17 fixed the win32 spawn EINVAL in `invokeAgent` but missed the
matching `spawn` inside `resolveOpenclawAgentId`. The same root cause
applies — npm-installed openclaw on Windows is a `.cmd` shim that Node
cannot launch directly without `shell: true`. Same Unix behavior, no
shell-injection surface (argv is a fixed `["agents", "list"]`, not user
input).

Closes #16 (the second of two locations identified in the original
report).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants