From 34e0ca6f605e5919b4a3c56cc18512a3f5d5c2bd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 05:33:11 +0000 Subject: [PATCH 1/4] fix(ui): AI build page preview header reads the build card's label and live state; progress shows labels Claude-Session: https://claude.ai/code/session_01ADHkmQw9ePD2KCzPfJGa6p Co-authored-by: Claude --- .../app-shell/src/console/ai/AiChatPage.tsx | 22 +++++++++ .../app-shell/src/console/ai/LiveCanvas.tsx | 48 ++++++++++++++----- ui/packages/i18n/src/locales/ar.ts | 3 +- ui/packages/i18n/src/locales/de.ts | 3 +- ui/packages/i18n/src/locales/en.ts | 3 +- ui/packages/i18n/src/locales/es.ts | 3 +- ui/packages/i18n/src/locales/fr.ts | 3 +- ui/packages/i18n/src/locales/ja.ts | 3 +- ui/packages/i18n/src/locales/ko.ts | 3 +- ui/packages/i18n/src/locales/pt.ts | 3 +- ui/packages/i18n/src/locales/ru.ts | 3 +- ui/packages/i18n/src/locales/zh.ts | 3 +- .../plugin-chatbot/src/ChatbotEnhanced.tsx | 44 +++++++++++++++-- ui/packages/plugin-chatbot/src/mapMessages.ts | 15 +++++- 14 files changed, 133 insertions(+), 26 deletions(-) diff --git a/ui/packages/app-shell/src/console/ai/AiChatPage.tsx b/ui/packages/app-shell/src/console/ai/AiChatPage.tsx index a0d8fd74206..829d1e06215 100644 --- a/ui/packages/app-shell/src/console/ai/AiChatPage.tsx +++ b/ui/packages/app-shell/src/console/ai/AiChatPage.tsx @@ -1831,9 +1831,26 @@ export function ChatPane({ : prev ?? { name: appName, materialized: true }, ); }, []); + // The chat's build card's own answer per built app (its label and whether it + // says the app is live), keyed by the app's machine name. The canvas header + // reads it instead of deriving a publish state of its own, so the two can + // never disagree, and it follows the card when the build is published. + const [buildResults, setBuildResults] = useState>({}); + const handleBuildResult = useCallback( + ({ appName, label, live }: { appName: string; label?: string; live: boolean }) => { + setBuildResults((prev) => { + const cur = prev[appName]; + if (cur && cur.label === label && cur.live === live) return prev; + return { ...prev, [appName]: { ...(label ? { label } : {}), live } }; + }); + }, + [], + ); + const canvasBuild = canvasApp ? buildResults[canvasApp.name] : undefined; // A different conversation is a different build session — close the pane. useEffect(() => { setCanvasApp(null); + setBuildResults({}); }, [conversationId]); const activeAgentLabel = useMemo(() => { @@ -2895,6 +2912,7 @@ export function ChatPane({ // ADR-0045: build materialized → canvas leaves the draft overlay for // the real (unlisted) app; the reload shows live seed rows. onBuildMaterialized={handleBuildMaterialized} + onBuildResult={handleBuildResult} // objectui#11666 — mirrored onto the assistant bus (effect above). onPlanApprovalPendingChange={setPlanApprovalPending} previewDraftLabel={t('console.ai.previewDraft', { defaultValue: 'Preview' })} @@ -2931,6 +2949,8 @@ export function ChatPane({ `), which is * globally unique, unlike the display name (two AI apps can both be `library`). @@ -73,12 +84,22 @@ function canvasSrc(appName: string, materialized: boolean, path?: string): strin return path ? `${spaBase()}${path}` : `${base}?preview=draft`; } -export function LiveCanvas({ appName, appSegment, materialized = false, path, refreshKey, onClose }: LiveCanvasProps) { +export function LiveCanvas({ + appName, + appLabel, + live = false, + appSegment, + materialized = false, + path, + refreshKey, + onClose, +}: LiveCanvasProps) { const { t } = useObjectTranslation(); const navigate = useNavigate(); const iframeRef = useRef(null); // Route on the package-id segment (unique), display by name (friendly). const routeSeg = appSegment && appSegment.length ? appSegment : appName; + const displayName = appLabel || appName; // Materialized world swap: changing src on the SAME iframe element // navigates it in place (no white-flash remount). useEffect(() => { @@ -108,15 +129,20 @@ export function LiveCanvas({ appName, appSegment, materialized = false, path, re
- {materialized - ? t('console.ai.liveCanvasUnlisted', { - app: appName, - defaultValue: 'Live app — {{app}} (unlisted until published)', + {live + ? t('console.ai.liveCanvasLive', { + app: displayName, + defaultValue: 'Live app — {{app}}', }) - : t('console.ai.liveCanvas', { - app: appName, - defaultValue: 'Live preview — {{app}} (draft)', - })} + : materialized + ? t('console.ai.liveCanvasUnlisted', { + app: displayName, + defaultValue: 'Live app — {{app}} (unlisted until published)', + }) + : t('console.ai.liveCanvas', { + app: displayName, + defaultValue: 'Live preview — {{app}} (draft)', + })} {/* Bridge to the design surface — this pane is the RUNNING app, so the builder needs an explicit way to jump into the Studio designer to @@ -149,7 +175,7 @@ export function LiveCanvas({ appName, appSegment, materialized = false, path, re