mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-14 08:02:58 +00:00
fix: an embedded page keeps its name in the band
A page in a session's preview panel loads in a frame that sets `nomenubar`, so the layout renders the band with `navHidden`. That flag hid the whole breadcrumb — which is what carries the page's name — while the actions beside it had no such guard, leaving a row of controls belonging to nothing. Hiding the trail is still right: it leads to the host's workspace and offers a picker that would take the reader out of the session. So the breadcrumb gains a `nameOnly` mode that drops the workspace part and its picker and keeps the page's own name, and the band asks for it instead of rendering nothing. The summary line loses the same guard, so an item in there shows its path and summary. Only the pages whose own title the band took over are affected. Variables, Resources, Schedules and Triggers still draw their own `PageHeader` and render no band when embedded, so nothing is doubled. The bar's left padding is there to sit a button against — the sidebar handle, or the workspace disc, both of which carry their own margin. Embedded there is neither, and the name started as bare text 8px from the edge, so that case takes the same 16px the bar already keeps on its right. Workspace settings drops the copy-id button from the band: the General tab names the workspace ID with a field of its own, which is where someone looks for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
64ada4e26d
commit
fe94f4dc44
@@ -34,7 +34,8 @@ same weight, size and icon size, so the line reads as one control rather than fo
|
||||
section,
|
||||
afterName,
|
||||
actingWorkspaceId,
|
||||
narrow = false
|
||||
narrow = false,
|
||||
nameOnly = false
|
||||
}: {
|
||||
item?: PageHeaderItem
|
||||
section?: PageHeaderSection
|
||||
@@ -45,6 +46,10 @@ same weight, size and icon size, so the line reads as one control rather than fo
|
||||
actingWorkspaceId?: string
|
||||
/** The bar is short of room: the workspace part drops its names and keeps its marks. */
|
||||
narrow?: boolean
|
||||
/** Only the page's own name, with no workspace part and no picker — the band inside a
|
||||
* session's preview frame, where the workspace is the host's and leading the reader out of
|
||||
* it is exactly what that band must not offer. The page still has to say what it is. */
|
||||
nameOnly?: boolean
|
||||
} = $props()
|
||||
|
||||
const scopeId = $derived(actingWorkspaceId ?? $workspaceStore ?? undefined)
|
||||
@@ -185,79 +190,81 @@ same weight, size and icon size, so the line reads as one control rather than fo
|
||||
aria-label="Breadcrumb"
|
||||
class="flex items-center gap-1.5 min-w-0 text-xs font-normal text-primary"
|
||||
>
|
||||
<!-- One part for where the user is: the name of the workspace they are standing in — a fork
|
||||
{#if !nameOnly}
|
||||
<!-- One part for where the user is: the name of the workspace they are standing in — a fork
|
||||
included, in its own colour — leading home, and one chevron whose picker changes it. The
|
||||
picker lists the families and expands one to reach its forks, so a fork needs no part of
|
||||
its own here. -->
|
||||
<div class={narrow ? 'flex items-center shrink-0' : 'flex items-center min-w-0'}>
|
||||
<!-- No hover fill on the name: the chevron beside it is the thing that lights up, and two
|
||||
<div class={narrow ? 'flex items-center shrink-0' : 'flex items-center min-w-0'}>
|
||||
<!-- No hover fill on the name: the chevron beside it is the thing that lights up, and two
|
||||
boxes reacting to one pass of the pointer read as two controls fighting. -->
|
||||
<!-- bind:clientWidth: the menu hangs from the chevron, so it is shifted back by the width of
|
||||
<!-- bind:clientWidth: the menu hangs from the chevron, so it is shifted back by the width of
|
||||
the name to line its left edge up with the disc. -->
|
||||
<div class="flex items-center min-w-0" bind:clientWidth={nameWidth}>
|
||||
<svelte:element
|
||||
this={sessionMode ? 'span' : 'a'}
|
||||
href={sessionMode ? undefined : homeHref}
|
||||
class={scopeChipClass}
|
||||
title={showFork ? `${familyName} / ${scopeName}` : familyName}
|
||||
>
|
||||
{#if narrow}
|
||||
<!-- Short of room, the part keeps what it cannot be read without: the disc in the
|
||||
<div class="flex items-center min-w-0" bind:clientWidth={nameWidth}>
|
||||
<svelte:element
|
||||
this={sessionMode ? 'span' : 'a'}
|
||||
href={sessionMode ? undefined : homeHref}
|
||||
class={scopeChipClass}
|
||||
title={showFork ? `${familyName} / ${scopeName}` : familyName}
|
||||
>
|
||||
{#if narrow}
|
||||
<!-- Short of room, the part keeps what it cannot be read without: the disc in the
|
||||
workspace's own colour, the fork mark, and the environment. The names go — the
|
||||
hover title still carries them, and the picker beside it names them all. -->
|
||||
{@render workspaceDisc()}
|
||||
{#if showFork}
|
||||
<span class={forkChipClass} style={forkAccent}>
|
||||
<GitFork size={ICON} class="flex-shrink-0" />
|
||||
{@render workspaceDisc()}
|
||||
{#if showFork}
|
||||
<span class={forkChipClass} style={forkAccent}>
|
||||
<GitFork size={ICON} class="flex-shrink-0" />
|
||||
{@render envBadgeMark()}
|
||||
</span>
|
||||
{:else}
|
||||
{@render envBadgeMark()}
|
||||
</span>
|
||||
{/if}
|
||||
{:else}
|
||||
{@render envBadgeMark()}
|
||||
{/if}
|
||||
{:else}
|
||||
<BreadcrumbItemContent label={familyName} icon={workspaceDisc} />
|
||||
{#if showFork}
|
||||
<!-- Both names: the fork is where the work happens, the family is which product it
|
||||
<BreadcrumbItemContent label={familyName} icon={workspaceDisc} />
|
||||
{#if showFork}
|
||||
<!-- Both names: the fork is where the work happens, the family is which product it
|
||||
is part of, and either alone leaves the other to be guessed. -->
|
||||
{@render slash()}
|
||||
<span class={forkChipClass} style={forkAccent}>
|
||||
<GitFork size={ICON} class="flex-shrink-0" />
|
||||
<span class="truncate">{scopeName}</span>
|
||||
{@render slash()}
|
||||
<span class={forkChipClass} style={forkAccent}>
|
||||
<GitFork size={ICON} class="flex-shrink-0" />
|
||||
<span class="truncate">{scopeName}</span>
|
||||
{@render envBadgeMark()}
|
||||
</span>
|
||||
{:else}
|
||||
{@render envBadgeMark()}
|
||||
</span>
|
||||
{:else}
|
||||
{@render envBadgeMark()}
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:element>
|
||||
</svelte:element>
|
||||
</div>
|
||||
<Menubar>
|
||||
{#snippet children({ createMenu })}
|
||||
<Menu
|
||||
bind:this={workspaceMenu}
|
||||
{createMenu}
|
||||
usePointerDownOutside
|
||||
placement="bottom-start"
|
||||
contentStyle="margin-left: {-nameWidth}px"
|
||||
>
|
||||
{#snippet triggr({ trigger })}
|
||||
<MeltButton
|
||||
meltElement={trigger}
|
||||
class="flex items-center p-1.5 rounded text-tertiary hover:bg-surface-hover hover:text-primary transition-colors"
|
||||
title="Switch workspace"
|
||||
>
|
||||
<ChevronDown size={ICON} class="flex-shrink-0" />
|
||||
</MeltButton>
|
||||
{/snippet}
|
||||
{#snippet children({ item: menuItem })}
|
||||
<WorkspacePickerBody item={menuItem} closeMenu={() => workspaceMenu?.close()} />
|
||||
{/snippet}
|
||||
</Menu>
|
||||
{/snippet}
|
||||
</Menubar>
|
||||
</div>
|
||||
<Menubar>
|
||||
{#snippet children({ createMenu })}
|
||||
<Menu
|
||||
bind:this={workspaceMenu}
|
||||
{createMenu}
|
||||
usePointerDownOutside
|
||||
placement="bottom-start"
|
||||
contentStyle="margin-left: {-nameWidth}px"
|
||||
>
|
||||
{#snippet triggr({ trigger })}
|
||||
<MeltButton
|
||||
meltElement={trigger}
|
||||
class="flex items-center p-1.5 rounded text-tertiary hover:bg-surface-hover hover:text-primary transition-colors"
|
||||
title="Switch workspace"
|
||||
>
|
||||
<ChevronDown size={ICON} class="flex-shrink-0" />
|
||||
</MeltButton>
|
||||
{/snippet}
|
||||
{#snippet children({ item: menuItem })}
|
||||
<WorkspacePickerBody item={menuItem} closeMenu={() => workspaceMenu?.close()} />
|
||||
{/snippet}
|
||||
</Menu>
|
||||
{/snippet}
|
||||
</Menubar>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if item || section || routePage}
|
||||
{#if !nameOnly && (item || section || routePage)}
|
||||
{@render slash()}
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -87,6 +87,10 @@ The row's height matches the sidebar's own header row, so the two read as one ba
|
||||
class={twMerge(
|
||||
'flex items-center gap-1 shrink-0 min-w-0 bg-surface transition-shadow duration-150',
|
||||
phone ? 'flex-wrap content-center min-h-11 py-1 pl-1 pr-2' : 'h-11 pl-2 pr-4',
|
||||
// The small left padding is there to sit a button against: the sidebar handle, or the
|
||||
// workspace disc, both of which carry their own visual margin. Embedded there is neither,
|
||||
// and the page's name would start as bare text 8px from the edge.
|
||||
navHidden && 'pl-4',
|
||||
showEdge &&
|
||||
'shadow-[inset_0_-1px_0_0_rgb(var(--color-border-light))] dark:shadow-[inset_0_-1px_0_0_#374151] [html.github-dark_&]:shadow-[inset_0_-1px_0_0_rgb(var(--color-border-light))]'
|
||||
)}
|
||||
@@ -121,28 +125,28 @@ The row's height matches the sidebar's own header row, so the two read as one ba
|
||||
On a phone it stops yielding at 10rem, and that floor is what makes the bar wrap: the
|
||||
page's buttons no longer fit beside a trail that wide, so they take the line below
|
||||
instead of squeezing the name down to an ellipsis.
|
||||
An embed (`navHidden`) gets the page's controls without the trail that would offer to
|
||||
navigate the host's workspace. -->
|
||||
{#if !navHidden}
|
||||
<div class={twMerge('flex min-w-0', phone ? 'shrink min-w-[10rem]' : 'shrink-[0.1]')}>
|
||||
<!-- Bridged like the actions below: what a page hangs off its own name renders here, out
|
||||
of the tree that named it, and the pen in there asks that tree who the acting user is
|
||||
before it offers to rename anything. -->
|
||||
{#key content?.contexts}
|
||||
<ContextBridge contexts={content?.contexts}>
|
||||
<NavBreadcrumb
|
||||
{item}
|
||||
{section}
|
||||
{narrow}
|
||||
afterName={content?.afterName}
|
||||
actingWorkspaceId={content?.actingWorkspaceId}
|
||||
/>
|
||||
</ContextBridge>
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
An embed (`navHidden`) keeps the page's own name but drops the workspace part around it:
|
||||
the trail would offer to navigate the host's workspace, while the name is what says which
|
||||
page the controls beside it belong to. -->
|
||||
<div class={twMerge('flex min-w-0', phone ? 'shrink min-w-[10rem]' : 'shrink-[0.1]')}>
|
||||
<!-- Bridged like the actions below: what a page hangs off its own name renders here, out
|
||||
of the tree that named it, and the pen in there asks that tree who the acting user is
|
||||
before it offers to rename anything. -->
|
||||
{#key content?.contexts}
|
||||
<ContextBridge contexts={content?.contexts}>
|
||||
<NavBreadcrumb
|
||||
{item}
|
||||
{section}
|
||||
{narrow}
|
||||
nameOnly={navHidden}
|
||||
afterName={content?.afterName}
|
||||
actingWorkspaceId={content?.actingWorkspaceId}
|
||||
/>
|
||||
</ContextBridge>
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
{#if item && !navHidden && (item.summaryContent || item.summary)}
|
||||
{#if item && (item.summaryContent || item.summary)}
|
||||
<!-- No kind icon: the page below is the item, and saying "this is a flow" above a flow
|
||||
editor tells the reader what they can already see. -->
|
||||
<div class={twMerge('flex items-center gap-1 min-w-0', phone && 'shrink')}>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { page } from '$app/stores'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import CenteredPage from '$lib/components/CenteredPage.svelte'
|
||||
import { Alert, Button, CopyButton, Section, Skeleton, Tab, Tabs } from '$lib/components/common'
|
||||
import { Alert, Button, Section, Skeleton, Tab, Tabs } from '$lib/components/common'
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
|
||||
@@ -2431,9 +2431,6 @@ export async function main(
|
||||
/>
|
||||
|
||||
{#snippet settingsActions()}
|
||||
{#if $workspaceStore}
|
||||
<CopyButton value={$workspaceStore} title={`Copy id: ${$workspaceStore}`} />
|
||||
{/if}
|
||||
{#if $superadmin}
|
||||
<Button variant="default" unifiedSize="sm" on:click={() => goto('#superadmin-settings')}>
|
||||
Instance settings
|
||||
|
||||
Reference in New Issue
Block a user