From 1d2e032224acfbb2d45ebfae5fd128fc743e5671 Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Thu, 1 Oct 2026 00:27:09 -0700 Subject: [PATCH] feat: the raw app editor fills the band like the other three MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was the one editor route still rendering its own bar under the band, so it showed two header rows and the band named the app "Apps raw" — `navPageFor`'s first-segment fallback, because nothing on the route registered an item. Its top bar now becomes the page header the way the script, flow and low-code app editors' do: the path and summary are the breadcrumb's, the file-sidebar toggle, autosave cloud, presence mark, menu, Diff, Jobs, export, AI and Deploy are the header's actions, and the path is a plain label since this editor renames from its deploy drawer. The actions render under the band, outside the subtree `RawAppEditor` declares the operating workspace in, and `OpenInSessionButton` asks that context who the acting user is — so the workspace travels with them. `operatingWorkspaceContextEntry()` hands it over without exporting the key. Co-Authored-By: Claude Opus 5 (1M context) --- .../components/operatingWorkspace.svelte.ts | 8 + .../components/raw_apps/RawAppEditor.svelte | 4 + .../raw_apps/RawAppEditorHeader.svelte | 332 +++++++++++------- .../apps_raw/edit/[...path]/+page.svelte | 1 + 4 files changed, 218 insertions(+), 127 deletions(-) diff --git a/frontend/src/lib/components/operatingWorkspace.svelte.ts b/frontend/src/lib/components/operatingWorkspace.svelte.ts index 93a7f2c0de..70f1c63c1c 100644 --- a/frontend/src/lib/components/operatingWorkspace.svelte.ts +++ b/frontend/src/lib/components/operatingWorkspace.svelte.ts @@ -22,6 +22,14 @@ export function setOperatingWorkspace(resolve: () => string | undefined): void { setContext(KEY, outer ? () => resolve() ?? outer() : resolve) } +/** This subtree's operating workspace as an entry for a `ContextBridge` contexts map. A snippet + * handed to the page header's band renders outside the subtree that declared the workspace, so + * without this it would quietly act on the navigation workspace instead. Reads context: call + * during component initialisation. */ +export function operatingWorkspaceContextEntry(): [any, any] { + return [KEY, getContext(KEY)] +} + /** The workspace this component acts on, as a store. Reads context: call during component * initialisation. Falls back to the navigation workspace where no host set one, or where the * host's resolves to nothing. */ diff --git a/frontend/src/lib/components/raw_apps/RawAppEditor.svelte b/frontend/src/lib/components/raw_apps/RawAppEditor.svelte index fd0bd23c53..c7c2e8699e 100644 --- a/frontend/src/lib/components/raw_apps/RawAppEditor.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppEditor.svelte @@ -187,6 +187,8 @@ // EditorHeader's path/breadcrumb row dropped (summary only). Used by the // session preview to save vertical room. condensedHeader?: boolean + /** True for the route's own editor: its top bar becomes the page header. */ + ownsPageHeader?: boolean } let { @@ -226,6 +228,7 @@ onRestore, onSavedNewAppPath, condensedHeader = false, + ownsPageHeader = false, version = undefined, onTakeLatest = undefined, draftBaseVersion = undefined @@ -2454,6 +2457,7 @@ sidebarCollapsed={sidebarCollapsed.val} onToggleSidebar={() => (sidebarCollapsed.val = !sidebarCollapsed.val)} {condensedHeader} + {ownsPageHeader} /> void liveEditorDraftStoragePath?: string /** Indicator-only overrides for the sessions preview: the AutosaveIndicator @@ -223,6 +229,7 @@ sidebarCollapsed = false, onToggleSidebar = undefined, condensedHeader = false, + ownsPageHeader = false, onNavigate = undefined, liveEditorDraftStoragePath = undefined, autosaveWorkspace = undefined, @@ -308,11 +315,29 @@ // Top-bar responsive collapse — container width, not viewport. let topbarWidth = $state(0) - const compactTopbar = $derived(topbarWidth > 0 && topbarWidth < 720) + // In the page header the buttons share the row with the breadcrumb, so they collapse sooner: + // the trail and the summary take ~430px of the same row, and the whole group — the menu, Diff, + // Jobs, Export, AI and Deploy — is ~560px. + const compactBelow = $derived(ownsPageHeader ? 1350 : 720) + const compactTopbar = $derived.by(() => { + const w = ownsPageHeader ? pageHeader.barWidth : topbarWidth + return w > 0 && w < compactBelow + }) + // A phone's bar holds the trail and Deploy. The file sidebar's toggle, Jobs and the export + // join the menu that compact already shows. + const phoneTopbar = $derived( + ownsPageHeader && pageHeader.barWidth > 0 && pageHeader.barWidth < PHONE_BAR + ) // Top-bar button size + bar height. Condensed (session preview) uses the // smallest well-supported unified size (`sm`) so the bar is thinner. - const headerBtnSize = $derived(condensedHeader ? 'sm' : 'md') + // In the page header the buttons ride a 44px band, the same size the condensed session-pane + // header uses. + const headerBtnSize = $derived(condensedHeader || ownsPageHeader ? 'sm' : 'md') + + // The actions render under the band, outside the subtree RawAppEditor declared the operating + // workspace in, so it has to travel with them: OpenInSessionButton asks who the acting user is. + const headerContexts = new Map([operatingWorkspaceContextEntry()]) function closeSaveDrawer() { saveDrawerOpen = false @@ -903,121 +928,174 @@ errorByComponent={{}} /> -
- + +{:else} +
+ -
- {#if onToggleSidebar} -
+{/if} + +{#snippet sidebarToggle()} + {#if onToggleSidebar} + - {/snippet} - +{/snippet} - + (summary = v.trim())} + textClass="text-xs font-medium text-emphasis leading-tight" + class="max-w-full min-w-0" + /> +{/snippet} + +{#snippet rawAppHeaderActions()} + + {@render sidebarToggle()} + {@render autosaveIndicator()} + {@render awarenessMark()} + {@render rawAppActions()} +{/snippet} + +{#snippet rawAppActions()} + + {#snippet buttonReplacement()} + + {/snippet} + + + -
+ -
+ Diff + +
-
- -
+
({jobs?.length > 99 ? '99+' : (jobs?.length ?? 0)})
+
+ + + + + + {#if !phoneTopbar} {#snippet fallback()} - - + } + ] + : []) + ] + : undefined} + > + Deploy + +{/snippet} diff --git a/frontend/src/routes/(root)/(logged)/apps_raw/edit/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/apps_raw/edit/[...path]/+page.svelte index 399e28f9dc..6a33c177c6 100644 --- a/frontend/src/routes/(root)/(logged)/apps_raw/edit/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/apps_raw/edit/[...path]/+page.svelte @@ -606,6 +606,7 @@
{ draftSync.remove() goto(`/apps_raw/edit/${savedPath}`)