fix: header tab sets card width, edit icons on script header and trigger chip, no pane header shift on open

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Diego Imbert
2026-09-30 19:18:04 +02:00
co-authored by Claude Opus 5.5
parent 5bc516c4fe
commit e751845237
4 changed files with 42 additions and 17 deletions
@@ -1431,16 +1431,26 @@
: `${upstream.trigger.label}${upstream.trigger.draft ? ' · draft' : ''}`,
runState: 'runState' in upstream && !!upstream.runState,
chipIcon: foldedErrors > 0,
chipEdit:
!foldedErrors &&
'trigger' in upstream &&
!!upstream.trigger?.onOpen &&
!upstream.trigger.missing,
header:
'runnableId' in upstream
? { label: upstream.summary || upstream.path, icons: 1 }
? {
label: upstream.summary || upstream.path,
icons: 1,
draft: !!upstream.unsaved
}
: 'scripts' in upstream && upstream.scripts?.length
? upstream.scripts.length === 1
? {
label: upstream.scripts[0].summary || upstream.scripts[0].path,
icons: 1
icons: 1,
draft: !!upstream.scripts[0].unsaved
}
: { label: `${upstream.scripts.length} scripts`, icons: 2 }
: { label: `${upstream.scripts.length} scripts`, icons: 2, chevron: true }
: undefined
})
: undefined
@@ -1124,11 +1124,13 @@
anchored at the right edge of the bar; trash lives at the
far left so destructive ops are visually separated from
navigation/commit. Mirrors the draft Discard placement. -->
{#if !readOnly && !isDraft && isScriptView && script?.hash}
<!-- Rendered while the script loads (disabled) so the bar does not shift. -->
{#if !readOnly && !isDraft && isScriptView}
<Button
variant="subtle"
unifiedSize="sm"
startIcon={{ icon: Trash2 }}
disabled={!script?.hash}
onclick={() => (removeOpen = true)}
iconOnly
title="Archive or delete"
@@ -20,6 +20,7 @@
ShieldAlert,
CheckCircle2,
ChevronDown,
Pencil,
CircleSlash,
Trash2,
XCircle
@@ -352,6 +353,7 @@
{#if producer?.runState}
<RunStateChip runState={producer.runState} class="ml-auto h-4 px-1 rounded" />
{/if}
<Pencil size={10} class={twMerge('shrink-0 opacity-70', !producer?.runState && 'ml-auto')} />
</button>
{:else if coProducers.length > 1}
<!-- svelte-ignore a11y_no_static_element_interactions -->
@@ -412,7 +414,7 @@
{#if trigger?.onOpen}
<button
type="button"
class={chipClass}
class={twMerge(chipClass, 'flex items-center gap-1 min-w-0')}
title={trigger.missing
? `No ${trigger.label} trigger targets ${producer?.path} yet: click to create one`
: `Edit the ${trigger.label} trigger`}
@@ -423,7 +425,8 @@
trigger.onOpen?.()
}}
>
{trigger.label}{trigger.draft ? ' · draft' : ''}
<span class="truncate">{trigger.label}{trigger.draft ? ' · draft' : ''}</span>
{#if !trigger.missing}<Pencil size={9} class="shrink-0 opacity-70" />{/if}
</button>
{:else}
<span class={chipClass}>
@@ -29,28 +29,38 @@ export function assetsOnlyNodeWidth(n: {
chip: string
/** The chip leads with an icon (the folded-errors Fix chip). */
chipIcon?: boolean
/** The chip ends in the edit icon (an editable trigger). */
chipEdit?: boolean
runState: boolean
/** The header's text (a script's summary or path, "N scripts"), with its icon
* count; undefined without a header. */
header?: { label: string; icons: number }
/** The header tab: its text (a script's summary or path, "N scripts"), icon
* count, and whether it ends in the menu chevron (else the edit icon) or has a "draft" mark;
* undefined without a header. */
header?: { label: string; icons: number; chevron?: boolean; draft?: boolean }
}): number {
const icon = 12 + 14 + 10
const text = Math.max(
textWidth(n.kind, 0.7),
textWidth(n.title, 0.75),
textWidth(n.chip, 0.65) + 12 + 2 + (n.chipIcon ? 14 : 0)
textWidth(n.chip, 0.65) + 12 + 2 + (n.chipIcon ? 14 : 0) + (n.chipEdit ? 4 + 9 : 0)
)
const body = Math.ceil(icon + text + 4 + 8 + 2 + 8)
// Header: px-2, icons with gap-1.5, the label, then the run chip (sized for a
// Header tab: mx-2 and a 1px border around px-2, then gap-1.5 between its
// items: the icons, the label, a "draft" mark, and the run chip (sized for a
// two-digit count, so a new run never re-lays the graph) or the menu chevron.
const header = n.header
const h = n.header
const header = h
? Math.ceil(
8 +
n.header.icons * (11 + 6) +
textWidth(n.header.label, 0.65) +
16 +
2 +
16 +
h.icons * (11 + 6) +
textWidth(h.label, 0.65) +
(h.draft ? 6 + textWidth('draft', 0.65) : 0) +
(n.runState ? 6 + 4 + 10 + 2 + textWidth('×99', 0.65) + 4 : 0) +
// The menu chevron, or the edit icon of a single script.
6 +
(n.runState ? 11 + 4 + textWidth('×99', 0.65) + 8 : 11) +
8
(h.chevron ? 11 : 10) +
4
)
: 0
return Math.min(NODE.width, Math.max(MIN_WIDTH, body, header))