Four issues found via live testing of the embedded Chat/AIUI panel
(Archipelago phase 02-07 follow-up), each root-caused rather than
patched over:
1. Loading overlay never dismissed: archyBridge.init() used
window.location.origin (this iframe's OWN origin) as the target for
postMessage calls TO the parent, instead of the parent's actual
origin. Silently correct only when AIUI is served same-origin as
its host (production's /aiui/ proxy) — broken the moment AIUI runs
on a different origin than its embedding page (any dev setup with a
separate AIUI dev server). The 'ready' message, and every
permissions/theme/context/action response after it, was being
dropped by the browser. Fixed by deriving the parent's real origin
from document.referrer (archyBridge.ts).
2. White/black background instead of the branded look: initTheme()
decides light/dark from localStorage or the OS's prefers-color-
scheme, with no awareness of being embedded — App.vue now forces
dark immediately on mount when embedded (before any handshake
completes) and useArchy.ts's theme-update callback now applies
Archy's reported mode too. Separately, body had no background-color
at all, so ChatPage.vue's embedded `background: transparent` fell
through to the browser's white UA default; main.css now paints body
to match the active theme. And ChatPage.vue's embedded branch was
opting out of the same background-image treatment the standalone
dark app uses — it now shares that exact styling instead of a flat
fallback color, matching the standalone look precisely.
3. Dead end when no AI provider credential is available: useAI.ts now
emits a narrow, one-shot needsApiKey signal (401/403, "api key",
"unauthorized", or a proxy-unreachable failure — deliberately not
every transient error) that ChatWindow.vue watches to auto-open
Settings, so the user lands on the fix instead of a silent/dead
chat.
4. claude-proxy.ts's CLI fallback spawned a hardcoded ~/.local/bin/claude
path, breaking with ENOENT on any machine where the CLI lives
elsewhere (e.g. an nvm install). Now resolves via `command -v claude`
first (an optional CLAUDE_BIN env override, then the historical path,
then the bare command name as a last resort so spawn() itself can
still try PATH), and surfaces an actionable in-UI error naming three
ways to fix it when none resolve.
Verified: full send→spawn→response round trip against the local proxy
(both directly and through vite's /api/claude proxy), vue-tsc clean,
vitest 332/335 passing (3 pre-existing unrelated failures, confirmed
present before this commit too), production build clean with both
chatExpanded/mobileChat flags and the new background rule present in
the built assets.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>