mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 08:02:47 +00:00
fix: keep a page's own controls when the workspace navigation is hidden
`menuHidden` means "this is an embed, don't show the workspace's navigation" — but Runs, the detail pages and the flow editor now keep their controls in the band and nowhere else, so hiding it took the filters, the timeframe and the page's buttons with it. A session's preview iframe opens exactly that way. The band now renders for an embed too, without its breadcrumb, so the page keeps its controls and the embed still gains no way to navigate the host's workspace. The flow editor's autosave indicator moves into the header's actions with the rest of its bar: saving state, the autosave toggle and other people's drafts were left behind in the branch the full-page editor no longer takes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
283cf48ccf
commit
3f55533558
@@ -1561,115 +1561,129 @@
|
||||
contexts={headerContexts}
|
||||
/>
|
||||
{:else}
|
||||
<!-- Nav between steps-->
|
||||
<div
|
||||
bind:clientWidth={topbarWidth}
|
||||
class="justify-between flex flex-row items-center pl-2 pr-4 space-x-4 scrollbar-hidden overflow-x-auto h-full relative {condensedHeader
|
||||
? 'max-h-9'
|
||||
: 'max-h-12'}"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2 min-w-0">
|
||||
{#if customUi?.topBar?.path != false}
|
||||
<div class="min-w-0 overflow-hidden">
|
||||
<EditorHeader
|
||||
bind:summary={flowStore.val.summary}
|
||||
bind:path={$pathStore}
|
||||
savedPath={initialPath}
|
||||
onBehalfOfEmail={$savedOnBehalfOfEmail}
|
||||
summaryEditable={customUi?.topBar?.editableSummary != false}
|
||||
pathEditable={customUi?.topBar?.editablePath != false}
|
||||
hidePath={condensedHeader}
|
||||
workspaceId={autosaveWorkspace}
|
||||
onNavigate={(item) => onNavigate?.(item)}
|
||||
<!-- Nav between steps-->
|
||||
<div
|
||||
bind:clientWidth={topbarWidth}
|
||||
class="justify-between flex flex-row items-center pl-2 pr-4 space-x-4 scrollbar-hidden overflow-x-auto h-full relative {condensedHeader
|
||||
? 'max-h-9'
|
||||
: 'max-h-12'}"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2 min-w-0">
|
||||
{#if customUi?.topBar?.path != false}
|
||||
<div class="min-w-0 overflow-hidden">
|
||||
<EditorHeader
|
||||
bind:summary={flowStore.val.summary}
|
||||
bind:path={$pathStore}
|
||||
savedPath={initialPath}
|
||||
onBehalfOfEmail={$savedOnBehalfOfEmail}
|
||||
summaryEditable={customUi?.topBar?.editableSummary != false}
|
||||
pathEditable={customUi?.topBar?.editablePath != false}
|
||||
hidePath={condensedHeader}
|
||||
workspaceId={autosaveWorkspace}
|
||||
onNavigate={(item) => onNavigate?.(item)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if opWorkspace && indicatorPath !== undefined}
|
||||
<AutosaveIndicator
|
||||
workspace={opWorkspace}
|
||||
itemKind="flow"
|
||||
path={indicatorPath}
|
||||
draftOnly={newFlow}
|
||||
{onResetToDeployed}
|
||||
{loadedFromDraft}
|
||||
{othersDraftsCount}
|
||||
{onOpenOthersDrafts}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if opWorkspace && indicatorPath !== undefined}
|
||||
<AutosaveIndicator
|
||||
workspace={opWorkspace}
|
||||
itemKind="flow"
|
||||
path={indicatorPath}
|
||||
draftOnly={newFlow}
|
||||
{onResetToDeployed}
|
||||
{loadedFromDraft}
|
||||
{othersDraftsCount}
|
||||
{onOpenOthersDrafts}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-row gap-2 items-center shrink-0">
|
||||
{#if $enterpriseLicense && !newFlow && !inSessionPane}
|
||||
<Awareness />
|
||||
{/if}
|
||||
<Dropdown items={getMoreItems} size={headerBtnSize} fixedHeight={!condensedHeader} />
|
||||
{#if diffEnabled && !diffInMenu}
|
||||
<!-- A disabled <button> fires no pointer events, so a title/tooltip on
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-row gap-2 items-center shrink-0">
|
||||
{#if $enterpriseLicense && !newFlow && !inSessionPane}
|
||||
<Awareness />
|
||||
{/if}
|
||||
<Dropdown items={getMoreItems} size={headerBtnSize} fixedHeight={!condensedHeader} />
|
||||
{#if diffEnabled && !diffInMenu}
|
||||
<!-- A disabled <button> fires no pointer events, so a title/tooltip on
|
||||
it never shows on hover. pointer-events-none on the button lets the
|
||||
hover reach this titled wrapper instead. -->
|
||||
<div title={diffTitle} class={diffDisabled ? 'flex cursor-not-allowed' : 'flex'}>
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize={headerBtnSize}
|
||||
on:click={() => openDiffDrawer()}
|
||||
disabled={diffDisabled}
|
||||
btnClasses={diffDisabled ? 'pointer-events-none' : undefined}
|
||||
title={diffTitle}
|
||||
startIcon={{ icon: DiffIcon }}
|
||||
>
|
||||
Diff
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !compactTopbar}
|
||||
{@render previewButtons()}
|
||||
{/if}
|
||||
<div title={diffTitle} class={diffDisabled ? 'flex cursor-not-allowed' : 'flex'}>
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize={headerBtnSize}
|
||||
on:click={() => openDiffDrawer()}
|
||||
disabled={diffDisabled}
|
||||
btnClasses={diffDisabled ? 'pointer-events-none' : undefined}
|
||||
title={diffTitle}
|
||||
startIcon={{ icon: DiffIcon }}
|
||||
>
|
||||
Diff
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !compactTopbar}
|
||||
{@render previewButtons()}
|
||||
{/if}
|
||||
|
||||
<DeployButton
|
||||
on:save={async ({ detail }) => await handleSaveFlow(detail)}
|
||||
{loading}
|
||||
{loadingSave}
|
||||
unifiedSize={headerBtnSize}
|
||||
{dropdownItems}
|
||||
/>
|
||||
<DeployButton
|
||||
on:save={async ({ detail }) => await handleSaveFlow(detail)}
|
||||
{loading}
|
||||
{loadingSave}
|
||||
unifiedSize={headerBtnSize}
|
||||
{dropdownItems}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#snippet flowHeaderActions()}
|
||||
|
||||
{#if $enterpriseLicense && !newFlow && !inSessionPane}
|
||||
<Awareness />
|
||||
{/if}
|
||||
<Dropdown items={getMoreItems} size={headerBtnSize} fixedHeight={!condensedHeader} />
|
||||
{#if diffEnabled && !diffInMenu}
|
||||
<!-- A disabled <button> fires no pointer events, so a title/tooltip on
|
||||
<!-- Saving state, the autosave toggle and other people's drafts: the editor's own
|
||||
bar carries them beside the path, so the header has to carry them when it owns
|
||||
the bar — without them a full-page editor saves with no word either way. -->
|
||||
{#if opWorkspace && indicatorPath !== undefined}
|
||||
<AutosaveIndicator
|
||||
workspace={opWorkspace}
|
||||
itemKind="flow"
|
||||
path={indicatorPath}
|
||||
draftOnly={newFlow}
|
||||
{onResetToDeployed}
|
||||
{loadedFromDraft}
|
||||
{othersDraftsCount}
|
||||
{onOpenOthersDrafts}
|
||||
/>
|
||||
{/if}
|
||||
{#if $enterpriseLicense && !newFlow && !inSessionPane}
|
||||
<Awareness />
|
||||
{/if}
|
||||
<Dropdown items={getMoreItems} size={headerBtnSize} fixedHeight={!condensedHeader} />
|
||||
{#if diffEnabled && !diffInMenu}
|
||||
<!-- A disabled <button> fires no pointer events, so a title/tooltip on
|
||||
it never shows on hover. pointer-events-none on the button lets the
|
||||
hover reach this titled wrapper instead. -->
|
||||
<div title={diffTitle} class={diffDisabled ? 'flex cursor-not-allowed' : 'flex'}>
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize={headerBtnSize}
|
||||
on:click={() => openDiffDrawer()}
|
||||
disabled={diffDisabled}
|
||||
btnClasses={diffDisabled ? 'pointer-events-none' : undefined}
|
||||
title={diffTitle}
|
||||
startIcon={{ icon: DiffIcon }}
|
||||
>
|
||||
Diff
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !compactTopbar}
|
||||
{@render previewButtons()}
|
||||
{/if}
|
||||
<div title={diffTitle} class={diffDisabled ? 'flex cursor-not-allowed' : 'flex'}>
|
||||
<Button
|
||||
variant="default"
|
||||
unifiedSize={headerBtnSize}
|
||||
on:click={() => openDiffDrawer()}
|
||||
disabled={diffDisabled}
|
||||
btnClasses={diffDisabled ? 'pointer-events-none' : undefined}
|
||||
title={diffTitle}
|
||||
startIcon={{ icon: DiffIcon }}
|
||||
>
|
||||
Diff
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if !compactTopbar}
|
||||
{@render previewButtons()}
|
||||
{/if}
|
||||
|
||||
<DeployButton
|
||||
on:save={async ({ detail }) => await handleSaveFlow(detail)}
|
||||
{loading}
|
||||
{loadingSave}
|
||||
unifiedSize={headerBtnSize}
|
||||
{dropdownItems}
|
||||
/>
|
||||
<DeployButton
|
||||
on:save={async ({ detail }) => await handleSaveFlow(detail)}
|
||||
{loading}
|
||||
{loadingSave}
|
||||
unifiedSize={headerBtnSize}
|
||||
{dropdownItems}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
<!-- Rendered either inline in the top bar (wide) or as a graph overlay
|
||||
|
||||
@@ -14,6 +14,8 @@ The row's height matches the sidebar's own header row, so the two read as one ba
|
||||
import { pageHeader } from './pageHeaderRegistry.svelte'
|
||||
import ContextBridge from './ContextBridge.svelte'
|
||||
|
||||
let { navHidden = false }: { navHidden?: boolean } = $props()
|
||||
|
||||
const content = $derived(pageHeader.content)
|
||||
const actions = $derived(pageHeader.actions)
|
||||
|
||||
@@ -22,7 +24,7 @@ The row's height matches the sidebar's own header row, so the two read as one ba
|
||||
</script>
|
||||
|
||||
<div data-page-header class="flex items-center gap-1 h-11 pl-2 pr-4 shrink-0 min-w-0 bg-surface">
|
||||
{#if navDetached.val}
|
||||
{#if navDetached.val && !navHidden}
|
||||
<!-- The only way back to a hidden sidebar: hovering slides the card in for a look, a click
|
||||
puts it back for good. Docked, the sidebar speaks for itself and nothing leads the bar. -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
@@ -45,15 +47,19 @@ The row's height matches the sidebar's own header row, so the two read as one ba
|
||||
|
||||
<!-- The breadcrumb yields width grudgingly (shrink-[0.1]): when a page fills the bar with
|
||||
controls, they are what should narrow, not the name of where the user is. It still gives
|
||||
way rather than pushing them off the bar once there is nothing left to take. -->
|
||||
<div class="flex min-w-0 shrink-[0.1]">
|
||||
<NavBreadcrumb
|
||||
{item}
|
||||
{section}
|
||||
hint={content?.hint}
|
||||
actingWorkspaceId={content?.actingWorkspaceId}
|
||||
/>
|
||||
</div>
|
||||
way rather than pushing them off the bar once there is nothing left to take.
|
||||
An embed (`navHidden`) gets the page's controls without the trail that would offer to
|
||||
navigate the host's workspace. -->
|
||||
{#if !navHidden}
|
||||
<div class="flex min-w-0 shrink-[0.1]">
|
||||
<NavBreadcrumb
|
||||
{item}
|
||||
{section}
|
||||
hint={content?.hint}
|
||||
actingWorkspaceId={content?.actingWorkspaceId}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if item && (item.summaryContent || item.summary)}
|
||||
<!-- A dot rather than a slash: the summary names the same item the path just located, it is
|
||||
|
||||
@@ -1538,7 +1538,10 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-col h-full w-full relative">
|
||||
{#if !menuHidden && !devOnly && pageHeader.content?.barPlacement !== 'inline'}
|
||||
<!-- `menuHidden` hides the workspace navigation, not the page: an embedded Runs or detail
|
||||
page keeps its own controls, which now live in this band and nowhere else. The band
|
||||
renders without its breadcrumb there, so the embed gains no workspace nav. -->
|
||||
{#if !devOnly && pageHeader.content?.barPlacement !== 'inline' && (!menuHidden || pageHeader.actions.length > 0)}
|
||||
{@const rightInset = pageHeader.content?.barRightInset}
|
||||
{@const leftInset = useDrawer ? 0 : railWidth}
|
||||
<!-- One element for both placements, styled rather than branched: a page registers its
|
||||
@@ -1557,7 +1560,7 @@
|
||||
style:right={rightInset != null ? `${rightInset}px` : undefined}
|
||||
style:padding-left={rightInset != null ? undefined : `${leftInset}rem`}
|
||||
>
|
||||
<PageHeaderBar />
|
||||
<PageHeaderBar navHidden={menuHidden} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $enterpriseLicense && !menuHidden}
|
||||
|
||||
Reference in New Issue
Block a user