fix: name the open workspace settings tab in the page header breadcrumb (#11617)

* feat: name the open workspace settings tab in the page header breadcrumb

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: let the page header breadcrumb shrink when nothing else in the bar can

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: keep the breadcrumb's slower shrink when a summary shares the bar

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guilhem
2026-10-08 17:31:35 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent d685332771
commit e233908c1e
4 changed files with 37 additions and 4 deletions
@@ -396,6 +396,15 @@ not this component) — so the lead can run the whole line.
onPick={() => {}}
/>
{:else if section}
{#if section.parent}
<!-- Gives width back with the workspace part, before the section's own name does. -->
<span
class={twMerge(SEGMENT, LEAD, 'shrink-[999] hover:bg-transparent hover:text-secondary')}
>
<span class="truncate">{section.parent}</span>
</span>
{@render slash()}
{/if}
<!-- A section's own widget lays out as a row: its title, and whatever belongs with it. -->
<span
class={twMerge(
@@ -32,6 +32,7 @@ The row's height matches the sidebar's own header row, so the two read as one ba
const item = $derived(content?.item)
const section = $derived(content?.section)
const hasSummary = $derived(!!item && !!(item.summaryContent || item.summary))
// Below this the trail holds a tighter cap on the path it draws. The names in it truncate on
// their own at any width (see NavBreadcrumb), so this is not a breakpoint the look changes at —
@@ -135,8 +136,18 @@ The row's height matches the sidebar's own header row, so the two read as one ba
where the user is.
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="flex shrink-[0.3] min-w-[5rem]">
page the controls beside it belong to.
The 0.3 only holds against a summary or an actions box that shrinks too: alone, a factor
sum below 1 makes flex hand out only that share of the overflow, and the bar overflows
instead. -->
<div
class={twMerge(
'flex shrink-[0.3] min-w-[5rem]',
!hasSummary &&
(actions.length === 0 || (phone && !content?.actionsFlexible && !content?.actionsFill)) &&
'shrink'
)}
>
<!-- Bridged like the actions below: what a page hangs off its own name renders here, out of
the tree that named it, and the rename editor in there asks that tree who the acting user
is before it offers to rename anything. -->
@@ -161,7 +172,7 @@ The row's height matches the sidebar's own header row, so the two read as one ba
</svelte:boundary>
</div>
{#if item && (item.summaryContent || item.summary)}
{#if item && hasSummary}
<!-- 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')}>
@@ -24,6 +24,9 @@ export type PageHeaderItem = {
/** A page with no item of its own, such as a list: the breadcrumb is the section alone. */
export type PageHeaderSection = {
label: string
/** The page the section belongs to, when the section is one pane of it — a settings page's
* active tab. Drawn as a lead segment before `label`. */
parent?: string
/** Rendered in place of `label` when the name is itself a control, such as an editable title. */
content?: Snippet
/** Scopes the segment's picker, when the section maps to a kind of item. */
@@ -1457,6 +1457,12 @@
}
]
)
// The label of the entry the sidebar highlights, so the breadcrumb and the sidebar name the
// same pane: both read `tab`, which resolves the URL's aliases (`teams`, `deploy_to`, …).
const tabLabel = $derived(
navigationGroups.flatMap((g) => g.items).find((i) => i.id === tab)?.label
)
</script>
<CenteredPage wrapperClasses="pb-0 h-full" handleOverflow={false} class="flex flex-col h-full">
@@ -1464,7 +1470,11 @@
<!-- No title of its own: the band already says which workspace's settings these are, and the
two controls that sat beside the title are the page's actions. The panes below scroll
inside themselves, so the bar keeps its edge at rest rather than only under a scroll. -->
<PageHeaderContent actions={settingsActions} separator="always" />
<PageHeaderContent
section={tabLabel ? { parent: 'Workspace settings', label: tabLabel } : undefined}
actions={settingsActions}
separator="always"
/>
<div class="flex grow min-h-0 pb-4">
<!-- Sidebar Navigation -->