mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 00:03:00 +00:00
fix: the header carries what it promised, and nothing it does not
The registry's item described pickers the breadcrumb no longer has: nine fields — the editability flags, the change callbacks, the navigation hook — that nothing read, while the flow editor passed eight of them and quietly lost its inline rename. The type now says what the bar does: a path, and either a summary or the page's own control for one. The flow editor renames through that control, as the detail page already did. `barPlacement` went the same way: no page registered 'inline', so the layout's guard never fired. The sessions page registered an inset of 0 whenever its preview pane never mounted — the "no session selected" and opted-out arms — and 0 reads as "a page owns the right edge", floating the band over the page's first line. It registers an inset only once the pane has a width. The band's sidebar handle docks through `setDetached` like the card's own toggle, so docking still suppresses the rail's entry animation and closes the card; a summary without its breadcrumb no longer leads with a stray separator in an embed; a page's hint sits with its name whichever way the page named itself; and `Menu.closeInstantly` and `EditorHeader`'s `inline`/`hideSummary` go, their callers having been replaced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3f55533558
commit
0f0aaac876
@@ -56,14 +56,9 @@
|
||||
* dropped, leaving only the summary. Used by the condensed session-
|
||||
* preview top bar to save vertical room. */
|
||||
hidePath?: boolean
|
||||
/** Drop the summary, for a host that renders its own after the path. */
|
||||
hideSummary?: boolean
|
||||
/** Workspace whose items the breadcrumb picker lists; defaults to the operating
|
||||
* workspace (see `useOperatingWorkspace`). */
|
||||
workspaceId?: string
|
||||
/** Lay the summary out after the path on one line, for the page header bar, rather than
|
||||
* stacked under it. */
|
||||
inline?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -78,9 +73,7 @@
|
||||
summaryEditable = true,
|
||||
pathEditable = true,
|
||||
hidePath = false,
|
||||
hideSummary = false,
|
||||
workspaceId,
|
||||
inline = false
|
||||
workspaceId
|
||||
}: Props = $props()
|
||||
|
||||
let pathPopoverOpen = $state(false)
|
||||
@@ -133,11 +126,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="max-w-full group px-2 py-0.5 leading-tight {inline
|
||||
? 'flex items-center gap-2 min-w-0'
|
||||
: 'inline-block align-top'}"
|
||||
>
|
||||
<div class="max-w-full group px-2 py-0.5 leading-tight inline-block align-top">
|
||||
<!-- Path row -->
|
||||
{#if !hidePath}
|
||||
<div class="flex items-center max-w-full text-2xs text-secondary font-mono">
|
||||
@@ -247,18 +236,16 @@
|
||||
{/if}
|
||||
|
||||
<!-- Summary -->
|
||||
{#if !hideSummary}
|
||||
<div class="max-w-full {inline ? 'min-w-0' : '-mt-[2px]'}" title={summary}>
|
||||
<EditableInput
|
||||
value={summary ?? ''}
|
||||
placeholder="Add a summary..."
|
||||
editable={summaryEditable}
|
||||
commitOnInput
|
||||
size="sm"
|
||||
onSave={handleSummarySave}
|
||||
textClass="text-xs font-semibold text-emphasis leading-tight"
|
||||
class="max-w-full"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="max-w-full -mt-[2px]" title={summary}>
|
||||
<EditableInput
|
||||
value={summary ?? ''}
|
||||
placeholder="Add a summary..."
|
||||
editable={summaryEditable}
|
||||
commitOnInput
|
||||
size="sm"
|
||||
onSave={handleSummarySave}
|
||||
textClass="text-xs font-semibold text-emphasis leading-tight"
|
||||
class="max-w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
import FlowHistory from './flows/FlowHistory.svelte'
|
||||
import EditorHeader from './EditorHeader.svelte'
|
||||
import AutosaveIndicator from './AutosaveIndicator.svelte'
|
||||
import EditableInput from './common/EditableInput.svelte'
|
||||
import type { FlowBuilderWhitelabelCustomUi } from './custom_ui'
|
||||
import FlowYamlEditor from './flows/header/FlowYamlEditor.svelte'
|
||||
import { type TriggerContext, type ScheduleTrigger } from './triggers'
|
||||
@@ -1544,19 +1545,7 @@
|
||||
<!-- The editor's own top bar is the page header on this route: its breadcrumb and
|
||||
summary are the header's, and its buttons are the header's actions. -->
|
||||
<PageHeaderContent
|
||||
item={{
|
||||
kind: 'flow',
|
||||
path: $pathStore,
|
||||
savedPath: initialPath,
|
||||
summary: flowStore.val.summary,
|
||||
workspaceId: autosaveWorkspace,
|
||||
onBehalfOfEmail: $savedOnBehalfOfEmail,
|
||||
pathEditable: customUi?.topBar?.editablePath != false,
|
||||
summaryEditable: customUi?.topBar?.editableSummary != false,
|
||||
onPathChange: (v) => ($pathStore = v),
|
||||
onSummaryChange: (v) => (flowStore.val.summary = v),
|
||||
onNavigate: (item) => onNavigate?.(item)
|
||||
}}
|
||||
item={{ kind: 'flow', path: $pathStore, summaryContent: flowSummary }}
|
||||
actions={flowHeaderActions}
|
||||
contexts={headerContexts}
|
||||
/>
|
||||
@@ -1635,6 +1624,21 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#snippet flowSummary()}
|
||||
<!-- The summary stays editable where the editor's own bar had it: the breadcrumb
|
||||
beside it is the path, which this editor renames from its settings tab. -->
|
||||
<EditableInput
|
||||
value={flowStore.val.summary ?? ''}
|
||||
placeholder="Add a summary..."
|
||||
editable={customUi?.topBar?.editableSummary != false}
|
||||
commitOnInput
|
||||
size="sm"
|
||||
onSave={(v) => (flowStore.val.summary = v)}
|
||||
textClass="text-xs font-medium text-emphasis leading-tight"
|
||||
class="max-w-full min-w-0"
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
{#snippet flowHeaderActions()}
|
||||
<!-- 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
|
||||
|
||||
@@ -262,6 +262,7 @@ same weight, size and icon size, so the line reads as one control rather than fo
|
||||
<span class="truncate">{level}</span>
|
||||
{/each}
|
||||
</button>
|
||||
{#if hint}{@render hint()}{/if}
|
||||
{#if pathCopied}
|
||||
<span
|
||||
class="absolute left-0 top-full mt-1 z-[6000] rounded-md border bg-surface px-2 py-1 text-2xs text-secondary shadow-md whitespace-nowrap"
|
||||
|
||||
@@ -14,7 +14,15 @@ 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()
|
||||
let {
|
||||
navHidden = false,
|
||||
onDock
|
||||
}: {
|
||||
navHidden?: boolean
|
||||
/** Docks the sidebar. The layout owns the two other things that must happen with it — the
|
||||
* flag that suppresses the rail's entry animation, and closing the card. */
|
||||
onDock?: () => void
|
||||
} = $props()
|
||||
|
||||
const content = $derived(pageHeader.content)
|
||||
const actions = $derived(pageHeader.actions)
|
||||
@@ -38,7 +46,7 @@ The row's height matches the sidebar's own header row, so the two read as one ba
|
||||
<button
|
||||
class="flex items-center p-1.5 rounded hover:bg-surface-hover"
|
||||
aria-label="Show sidebar"
|
||||
onclick={() => (navDetached.val = false)}
|
||||
onclick={() => onDock?.()}
|
||||
>
|
||||
<PanelLeft size={16} class="flex-shrink-0 text-hint" />
|
||||
</button>
|
||||
@@ -61,7 +69,7 @@ The row's height matches the sidebar's own header row, so the two read as one ba
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if item && (item.summaryContent || item.summary)}
|
||||
{#if item && !navHidden && (item.summaryContent || item.summary)}
|
||||
<!-- A dot rather than a slash: the summary names the same item the path just located, it is
|
||||
not another level of it. -->
|
||||
<span class="shrink-0 text-hint/40 text-xs px-0.5" aria-hidden="true">·</span>
|
||||
|
||||
@@ -17,7 +17,6 @@ the header, so nesting is decided by the caller rather than guessed from context
|
||||
actions,
|
||||
actionsOrder,
|
||||
contexts,
|
||||
barPlacement,
|
||||
actingWorkspaceId,
|
||||
barRightInset
|
||||
}: PageHeaderContent = $props()
|
||||
@@ -30,7 +29,6 @@ the header, so nesting is decided by the caller rather than guessed from context
|
||||
actions,
|
||||
actionsOrder,
|
||||
contexts,
|
||||
barPlacement,
|
||||
actingWorkspaceId,
|
||||
barRightInset
|
||||
}))
|
||||
|
||||
@@ -156,10 +156,7 @@
|
||||
{#if canWriteApp && !hideEditBtn}
|
||||
<!-- The page header carries it, with the app's name: a floating button over the app's own
|
||||
canvas lands on whatever the app draws there. -->
|
||||
<PageHeaderContent
|
||||
item={{ kind: 'app', path, raw_app: true, summaryEditable: false, pathEditable: false }}
|
||||
actions={editAction}
|
||||
/>
|
||||
<PageHeaderContent item={{ kind: 'app', path }} actions={editAction} />
|
||||
{/if}
|
||||
|
||||
{#snippet editAction()}
|
||||
|
||||
@@ -202,13 +202,7 @@
|
||||
breadcrumb, the summary keeps its rename-and-labels popover, and everything else rides along
|
||||
as the bar's actions. -->
|
||||
<PageHeaderContent
|
||||
item={{
|
||||
kind: errorHandlerKind,
|
||||
path,
|
||||
summaryContent,
|
||||
pathEditable: false,
|
||||
summaryEditable: false
|
||||
}}
|
||||
item={{ kind: errorHandlerKind, path, summaryContent }}
|
||||
{actions}
|
||||
contexts={headerContexts}
|
||||
/>
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
interface Props {
|
||||
placement?: Placement
|
||||
/** Drops the fly-out, so the panel is out of the DOM the moment it closes. For a menu whose
|
||||
* act tears its own trigger down — picking a workspace — where floating-ui spends the
|
||||
* outro re-placing the panel against a reference on its way out, and it bounces. */
|
||||
closeInstantly?: boolean
|
||||
/** Inline style on the panel. A margin here shifts it after floating-ui has placed it,
|
||||
* which is the only way to anchor it to a box wider than the trigger: melt's menubar
|
||||
* menu ignores `positioning.offset`. */
|
||||
@@ -54,7 +50,6 @@
|
||||
let {
|
||||
placement = 'right-start',
|
||||
contentStyle = '',
|
||||
closeInstantly = false,
|
||||
justifyEnd = false,
|
||||
lightMode = false,
|
||||
maxHeight = 900,
|
||||
@@ -230,7 +225,7 @@
|
||||
data-menu
|
||||
onmouseenter={cancelPendingClose}
|
||||
onmouseleave={scheduleClose}
|
||||
transition:placementFly={{ duration: closeInstantly ? 0 : 100, placement }}
|
||||
transition:placementFly={{ duration: 100, placement }}
|
||||
style={contentStyle}
|
||||
class={twMerge(
|
||||
'z-[6000] border w-56 origin-top-right rounded-md shadow-md focus:outline-none',
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import type { Snippet } from 'svelte'
|
||||
import type { WorkspaceItem, WorkspaceItemKind } from './workspacePicker'
|
||||
import type { WorkspaceItemKind } from './workspacePicker'
|
||||
|
||||
/** An item the page is working on: the breadcrumb walks its path and ends with its summary. */
|
||||
export type PageHeaderItem = {
|
||||
kind: WorkspaceItemKind
|
||||
/** Shown whole, and copied on click. */
|
||||
path: string | undefined
|
||||
/** The item's saved path, so a mid-rename breadcrumb and picker stay coherent. */
|
||||
savedPath?: string
|
||||
summary?: string
|
||||
/** Rendered after the path instead of the editable summary — for a page whose summary is
|
||||
* itself a control, such as a detail page's rename-and-labels popover. */
|
||||
/** Rendered in place of the plain summary, for a page whose summary is itself a control —
|
||||
* a detail page's rename-and-labels popover, an editor's editable title. */
|
||||
summaryContent?: Snippet
|
||||
raw_app?: boolean
|
||||
/** Which workspace the breadcrumb's pickers read (a session editor acts on its fork). */
|
||||
workspaceId?: string
|
||||
pathEditable?: boolean
|
||||
summaryEditable?: boolean
|
||||
onBehalfOfEmail?: string
|
||||
onSummaryChange?: (summary: string) => void
|
||||
onPathChange?: (path: string) => void
|
||||
onNavigate?: (item: WorkspaceItem) => void
|
||||
}
|
||||
|
||||
/** A page with no item of its own, such as a list: the breadcrumb is the section alone. */
|
||||
@@ -50,9 +40,6 @@ export type PageHeaderContent = {
|
||||
* session's side panel. The band stops there instead of running over it, and the band floats
|
||||
* above the page rather than pushing it down, so that column starts at the top. */
|
||||
barRightInset?: number
|
||||
/** 'inline' when the route renders the bar itself — for a layout whose side panel must run to
|
||||
* the top of the viewport beside it, rather than starting under a full-width bar. */
|
||||
barPlacement?: 'layout' | 'inline'
|
||||
/** Contexts those buttons look up. They render under the header, not under the page that
|
||||
* wrote them, so anything the page's tree provides has to travel with them. */
|
||||
contexts?: Map<any, any>
|
||||
@@ -86,7 +73,6 @@ export const pageHeader = {
|
||||
if (c.section !== undefined) merged.section = c.section
|
||||
if (c.hint !== undefined) merged.hint = c.hint
|
||||
if (c.contexts !== undefined) merged.contexts = c.contexts
|
||||
if (c.barPlacement !== undefined) merged.barPlacement = c.barPlacement
|
||||
if (c.actingWorkspaceId !== undefined) merged.actingWorkspaceId = c.actingWorkspaceId
|
||||
if (c.barRightInset !== undefined) merged.barRightInset = c.barRightInset
|
||||
}
|
||||
|
||||
@@ -1541,7 +1541,7 @@
|
||||
<!-- `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)}
|
||||
{#if !devOnly && (!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
|
||||
@@ -1560,7 +1560,7 @@
|
||||
style:right={rightInset != null ? `${rightInset}px` : undefined}
|
||||
style:padding-left={rightInset != null ? undefined : `${leftInset}rem`}
|
||||
>
|
||||
<PageHeaderBar navHidden={menuHidden} />
|
||||
<PageHeaderBar navHidden={menuHidden} onDock={() => setDetached(false)} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $enterpriseLicense && !menuHidden}
|
||||
|
||||
@@ -874,7 +874,7 @@
|
||||
<!-- The session showing fills the band's breadcrumb. The band is the layout's, but it stops
|
||||
where the preview panel starts so that panel can run from the top of the viewport. -->
|
||||
<PageHeaderContent
|
||||
barRightInset={previewCollapsed || fullscreen ? undefined : previewWidth}
|
||||
barRightInset={previewCollapsed || fullscreen || previewWidth === 0 ? undefined : previewWidth}
|
||||
actions={sessionPageActions}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user