* feat(frontend): restore raw app 'open preview in separate window'
Re-adds the detached preview window dropped when preview hosting moved to
the host (ui-builder f52d8e5b). Live-synced preview + dark mode, and wires
the runnable bridge to the detached window so backend calls resolve there.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(frontend): repaint detached raw app preview after refresh
The detached preview tab is a blank app-preview.html shell fed by the
editor over postMessage. A one-shot opener load listener can't survive the
tab refreshing itself, so a manual reload left it blank. It now posts
'appPreviewReady' on every (re)load and the editor re-sends the build; the
orphaned window is also closed when the editor unmounts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(frontend): keep load-based feed for detached preview initial open
Relying solely on the appPreviewReady handshake left the detached window
blank on first open against app-preview.html artifacts that predate the
handshake (the pinned UI Builder tarball). Restore the one-shot load feed so
initial open works regardless of the served shell; the handshake still
covers manual refresh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(frontend): re-sync dark mode when refocusing detached preview
The focus-reuse path replayed the build but not the theme, so re-opening an
existing detached window after a dark-mode toggle kept the stale theme until
the next build. Extract a feedExternalPreview() helper (theme + build) used by
the open, focus-reuse, load and handshake paths.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(frontend): bump ui_builder artifact to 062d11c (preview handshake)
Pins the UI Builder artifact built from windmill-code-ui-builder#14, which
adds the appPreviewReady handshake, detached-preview favicon and title.
Activates refresh-repaint + favicon for the detached raw app preview.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(frontend): serve ui_builder static bundle in dev, proxy :4000 only as fallback
The postinstall downloads the pinned UI Builder artifact into static/ui_builder,
which SvelteKit already serves at /ui_builder. Skip the :4000 proxy when that
bundle is present so dev uses it directly (matching prod / the backend's
static-vs-:4000 fallback); no separate UI Builder dev server needed. Delete
static/ui_builder to develop the builder against :4000.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(frontend): harden detached preview origin + scope window name
Addresses cubic review on #9765:
- P1: only honor appPreviewReady from a same-origin sender and post the build
with targetOrigin=location.origin, so user app code that navigates the
detached window cross-origin can't trigger/receive a build (app source).
- P2: scope the detached window name per app path so two open editors don't
collide over one OS-level preview window.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: add get_app_runtime_logs tool to global chat
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: only handle raw app backend messages from the runner's own iframe
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: add list_app_runs tool to global chat for raw app backend runs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor: simplify raw app chat tool results
* nits
* nits
* chore: bump ui builder artifact
* fix: resolve pending runtime log requests on cleanup
* fix: harden raw app runtime log requests
* nits
* fix: show raw app tool results in status
* fix: cap raw app runtime log results
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(raw_apps): surface UI Builder build errors over the preview pane
Companion to the matching change in the UI Builder repo (see linked PR),
which stops rendering the build-error overlay over the VS Code editor
iframe and instead emits a `buildError` postMessage on every build
(message: undefined on success to clear).
Listen for that message on the existing window message handler (already
source-gated by the UI Builder iframe), store it in a `buildError`
$state, and surface it in two places:
* A red banner over the preview iframe, sibling to the existing logs
overlay (`top-12 left-2 right-2 z-20` so it clears the tab bar) —
failures appear right where the user looks for the rendered output.
* The Preview tab's icon and label tint red
(`text-red-600 dark:text-red-400`, matching the existing error
convention in raw_apps) — important in single-tab mode where the
preview pane is collapsed to 0px and the banner would be hidden.
Done by mapping `leftPaneTabs` / `rightPaneTabs` through a small
`tintPreviewOnError` helper so the source-of-truth `tabs` array is
untouched (DnD, ordering, fallback selection keep using the original
previewTab object).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(raw_apps): use Alert component for the build-error banner
Replace the hand-rolled red div with the shared `Alert` component
(`type="error"`, `title="Build failed"`). The error text stays in a
`<pre>` child so multi-line bundler output keeps its formatting, with
`max-h-60` so a long error never takes over the whole preview pane.
The absolute-positioned wrapper (`top-12 left-2 right-2 z-20`) and the
`role="alert"` move to that wrapper so the Alert component itself stays
unstyled at the call site.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(raw_apps): solid bg-surface backing behind build-error Alert
The Alert's error background is semi-transparent in dark mode
(`bg-red-900/40` in `common/alert/model.ts`), so the preview iframe
shows through when the banner is laid over it. Add a `::before`
pseudo on the Alert root with `bg-surface` (matched `rounded-md`,
`-z-10` so it sits behind the red bg) to give it a solid plate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(raw_apps): isolate banner stacking context, DRY tab tint chain
Two small follow-ups from review:
* Add `isolate` to the build-error banner wrapper so the `before:-z-10`
pseudo's stacking context is pinned locally — it works today because
`position: absolute` + `z-20` creates one, but `isolate` makes the
dependency self-documenting and survives a future refactor that
removes the explicit `z-20`.
* Extract `tintTabs = (ts) => ts.map(tintPreviewOnError)` so the two
`$derived` blocks for leftPaneTabs / rightPaneTabs read identically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(raw_apps): trim build-error overlay comments
Per review feedback. Keep only the load-bearing facts (bg-surface backs
the Alert's translucent red, isolate pins the pseudo stacking, the
`message: undefined` clear convention) and drop the prose context that
duplicated what the code already shows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(raw_apps): bump bundled ui_builder to 00c9834
Brings in the postMessage emission from
windmill-labs/windmill-code-ui-builder#9 (merged) so this PR's host
listener actually receives `buildError` events. SHA verified against
the R2 artifact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(raw_apps): custom tab system for source / runnable / preview
Replaces the fixed split-pane layout with a tab bar inside the editor
area. Each frontend file is a tab, each selected runnable is a tab,
and the Preview is pinned to the right (non-closable). Tabs are an
alternative discoverability surface to the sidebar — both stay
functional, but tabs make navigation viable on small screens with
the sidebar collapsed.
A "Split with Preview" toggle in the tab bar's trailing slot pairs
the active tab with the preview side-by-side for wide-screen
multitasking. The toggle hides when Preview is already the active
tab.
The UI Builder, runnable editor, and preview iframe all stay mounted
across tab switches (toggled via `display`) — no bundler restarts, no
preview state loss, no editor remounts.
- New common/tabs/DraggableTabs.svelte: reusable tab strip with
drag-reorder (@windmill-labs/svelte-dnd-action), pinned-left/right
slots excluded from the drag zone, hover-revealed X close, middle-
click close, keyboard navigation (arrows / Enter / Backspace),
and a `trailing` snippet for inline toolbar add-ons.
- raw_apps/RawAppEditor.svelte:
- Tab state (`tabs`, `activeTabId`, `splitWithPreview`) lives in
Windmill. Persisted in localStorage keyed by workspace + app path.
- Sidebar file clicks (`handleSelectFile`) and runnable selection
(`selectedRunnable` via `bind:`) are mirrored into tabs via an
effect — the sidebar interaction is otherwise untouched.
- Listener augmented: `setActiveDocument` backfills tabs for files
VS Code opens by itself; `setFiles` / `runnables` updates drop
stale tabs.
- Bundler / inspector / rebuild toolbar moves into the tab bar's
trailing slot — always visible regardless of active tab.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(raw_apps): modern tab styling + resizable split-with-preview
Two polish passes on the new tab system:
DraggableTabs styling:
- Remove the bottom border on the tab strip + the accent-coloured
border-b-2 on the active tab. The active tab now shares the
surface background with the content area below it, so the
boundary visually "disappears" — modern IDE-style tabs.
- Inactive tabs sit on the darker surface-secondary tab strip and
get a subtle right separator so they don't blur into each other.
Split-with-Preview is now a real resizable Splitpanes:
- The content area is rendered as a Splitpanes (always), with the
source/runnable slot on the left and the preview iframe on the
right. The user can drag the divider to adjust the ratio when
the "Split with Preview" toggle is on.
- Iframes never remount across single↔split toggles — pane sizes
are driven reactively from (activeTabKind, splitWithPreview),
not by adding/removing the Splitpanes itself.
- The user's preferred split ratio is remembered while they're
dragging and reapplied next time split is enabled.
- The inner splitter is CSS-hidden in single mode so the toggle
button stays the single canonical way to flip layouts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(raw_apps): split mode moves preview tab into the right pane
Cleaner mental model for split-with-preview. Instead of "split the
active tab + always keep the Preview tab around", the Split toggle
now physically moves the Preview tab out of the bar and into a
permanent right pane. When the user toggles split off, the Preview
tab reappears in the bar like any other tab.
- New `displayedTabs` derived: filters out the Preview tab when
splitWithPreview is on, so the user sees only file/runnable tabs
in the bar and a dedicated preview pane on the right.
- `toggleSplit` redirects the active tab to the most recent
file/runnable when the user toggles split on with Preview active,
so they don't end up staring at an empty left pane.
- Split toggle is now always visible — the user can flip both ways.
The button label flips between "Pin preview to the right" and
"Move preview back into a tab" to reflect what's about to happen.
- reorderTabs preserves the Preview tab in the underlying `tabs`
array even though it's filtered out of the drag set in split mode.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(raw_apps): VS Code-style "Preview" header on the right pane
In split mode, the right pane now shows a small "Preview" tab-styled
header anchored at its top-left — making the layout read like a real
VS Code editor split, where each group has its own tab bar.
- Header appears only when `splitWithPreview && activeTabKind !== 'preview'`
(i.e. when the right pane is meaningfully separate from the left's
content). In single mode with preview active, the right pane is the
only thing visible and the main tab bar already labels it.
- The header uses the same styling as an active tab: `bg-surface`
on a `bg-surface-secondary` strip, h-8, text-xs, no border.
- An X button next to the label toggles split off — equivalent to
closing the editor in VS Code's split view (preview goes back to
living as a tab in the main bar).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(raw_apps): VS Code-style symmetric tab bars per pane
Restructure the editor area so each pane is a self-contained "editor
group" with its own tab bar at the top. The Splitpanes is now the
topmost element — the divider runs floor-to-ceiling, splitting both
the tab bars and the content.
Layout (left pane = source / runnable, right pane = preview):
- Left pane top: DraggableTabs (file/runnable tabs, Preview tab when
split is off) + Split-toggle in the trailing slot.
- Right pane top: a custom preview header — "Preview" label styled
like an active tab on the left + the preview-affecting toolbar
(bundler, inspector, rebuild) on the right.
- Each pane independently sized via Splitpanes; iframes + the
runnable panel stay mounted and toggled via `display` so state
survives every transition.
Trade-off: in single-mode with Preview active (paneA=0), the left
tab bar is hidden along with the left pane. To switch back to a
file tab the user uses the sidebar — which is exactly the
discoverability surface tabs were meant to complement, not replace.
Button placement by semantic ownership:
- Layout control (Split toggle) — left side, with the editor.
- Preview-affecting controls (bundler, inspector, rebuild) — right
side, with the preview. No close-X on the right; the Split toggle
on the left is the canonical way to flip layouts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(raw_apps): keep tab bar visible when Preview is active in single mode
The "VS Code-style" restructure put the tab bar inside the left
Pane. When activeTabKind became 'preview' in single mode, the left
pane collapsed to width 0 and the entire tab bar disappeared with
it — leaving the user with no way to switch back to a file tab
except via the sidebar.
Move the main tab bar back above the inner Splitpanes (full width,
always visible). The preview pseudo-header stays inside the right
pane, carrying the bundler / inspector / rebuild toolbar. The
splitter only goes through the content area below the tab bar,
which is acceptable given how much friction the disappearing-tabs
edge case caused.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(raw_apps): per-pane tab bars with mirrored single-mode lists
Replace the single tab bar above the inner Splitpanes with one
DraggableTabs per pane. Splitter now goes floor-to-ceiling through
tabs AND content in split mode.
In single mode both bars mirror the full tab list, so the visible
pane always carries every tab — fixes the bug where activating
Preview hid the tab strip. Clicking Preview while in split mode is
a no-op (Preview is permanently visible in the right pane).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(raw_apps): polish tab strip and sync editor font to text-xs
* feat(raw_apps): move logs overlay onto the preview pane
* refactor(splitpanes): extract pixel-aware minSize helper
* fix(raw_apps): tab hydration loads correct file; closeTab in split mode
* fix(raw_apps): lazy-mount UI Builder iframe + add dev:ui-builder script
* feat(raw_apps): default split view, blue preview tab, fix dnd ghosting
* fix(raw_apps): remove 1px splitter sliver beside preview in single view
* fix(raw_apps): tab scrollbar on hover, fix thumb height + resize staleness
* refactor(raw_apps): don't persist tab/split layout in localStorage
* refactor(raw_apps): derive pane sizes + binding setter instead of effects
* style(raw_apps): trim verbose comments
* feat(raw_apps): accept appendLogs delta from the UI Builder iframe
* fix(raw_apps): exit inspect mode on Escape
* fix(raw_apps): Escape clears lingering inspector selection after pick
* style(raw_apps): accent-selected styling for active tab, bg-surface strip
* fix(raw_apps): address PR review nits (drop debug log, timer/reorder/pane-setter, dev script restore)
* fix(raw_apps): clear inspector overlay on the preview iframe, not the source
* style(raw_apps): neutral tab look (surface-tertiary/text-emphasis selected, text-hint idle)
* chore(raw_apps): bump bundled ui_builder to 61b6fdd
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add workspace-shared ui/ folder reusable across raw apps
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: add shared ui/ drawer in raw app editor sidebar
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: forward workspace shared ui/ to raw app editor iframe
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* all
* all
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix raw app header overflow
* update ui-builder hash
* Make monaco default size match brand guidelines
* nit
* Move run button to test panel
* wip improve history
* add current checkout point
* fix logic to switch wetween history state
* improve history visualisation
* improve animations
* nit
* remove test page
* fix timing issue when selecty history entries
* update ui_builder hash
* remove dev file
* nit
* revert setting editor font to 13.5 px
* update ui builder
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>