diff --git a/frontend/scripts/ui_builder_artifact.json b/frontend/scripts/ui_builder_artifact.json index 5685992677..05a9068963 100644 --- a/frontend/scripts/ui_builder_artifact.json +++ b/frontend/scripts/ui_builder_artifact.json @@ -1,5 +1,5 @@ { "baseUrl": "https://pub-06154ed168a24e73a86ab84db6bf15d8.r2.dev", - "version": "b4f6219", - "sha256": "69575342aab968fc32a89d3410c21bf888b0f00ce5c68fe80936d3adc1359b36" + "version": "00c9834", + "sha256": "5757e5b9cbf79c20d507dc4c588640368e84b873cb48f3806ca8c67fd1aa625f" } diff --git a/frontend/src/lib/components/raw_apps/RawAppEditor.svelte b/frontend/src/lib/components/raw_apps/RawAppEditor.svelte index aa6658b4a9..7249f0ae05 100644 --- a/frontend/src/lib/components/raw_apps/RawAppEditor.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppEditor.svelte @@ -6,6 +6,7 @@ import RawAppEditorHeader from './RawAppEditorHeader.svelte' import RawAppYamlEditor, { type RawAppYamlUpdate } from './RawAppYamlEditor.svelte' import type Drawer from '../common/drawer/Drawer.svelte' + import Alert from '../common/alert/Alert.svelte' import { type Policy, WorkspaceService } from '$lib/gen' import DiffDrawer from '../DiffDrawer.svelte' import { deepEqual } from 'fast-equals' @@ -195,6 +196,9 @@ // them as an overlay inside the preview pane (right side) so they're // visually tied to the build output, not to the source editor. let logs = $state('') + + // Latest UI Builder error; cleared on next successful build. + let buildError = $state(undefined) let logsCollapsed = $state(false) let logsDiv: HTMLDivElement | undefined = $state(undefined) $effect(() => { @@ -229,13 +233,20 @@ ? 'file' : 'runnable' ) + // Tint the Preview tab red so the error is visible when Preview isn't active. + function tintPreviewOnError(t: TabItem): TabItem { + if (t.id !== PREVIEW_TAB_ID || !buildError) return t + const errClass = 'text-red-600 dark:text-red-400' + return { ...t, iconClass: errClass, labelClass: errClass } + } + const tintTabs = (ts: TabItem[]) => ts.map(tintPreviewOnError) // Single mode: both bars mirror the full list (the visible pane carries // every tab). Split mode: left = files/runnables, right = Preview only. const leftPaneTabs = $derived( - splitWithPreview ? tabs.filter((t) => t.id !== PREVIEW_TAB_ID) : tabs + tintTabs(splitWithPreview ? tabs.filter((t) => t.id !== PREVIEW_TAB_ID) : tabs) ) const rightPaneTabs = $derived( - splitWithPreview ? tabs.filter((t) => t.id === PREVIEW_TAB_ID) : tabs + tintTabs(splitWithPreview ? tabs.filter((t) => t.id === PREVIEW_TAB_ID) : tabs) ) // In split mode the right bar always highlights Preview, regardless of the // left pane's active file/runnable. @@ -938,6 +949,12 @@ return } + // `message: undefined` arrives on the next successful build and clears the banner. + if (fromUiBuilder && e.data.type === 'buildError') { + buildError = typeof e.data.message === 'string' ? e.data.message : undefined + return + } + // Inspector events come exclusively from the preview iframe. if (fromPreview && e.data.type === 'inspectorSelect') { inspectorElement = e.data.element as InspectorElementInfo @@ -1557,6 +1574,20 @@ src="/ui_builder/app-preview.html" class="w-full flex-1 block" > + {#if buildError} + + + {/if} {#if logs}