From 8d901de4bd678c1cde38fb160ee0f886aea39e4f Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Wed, 30 Sep 2026 18:07:11 +0200 Subject: [PATCH] fix: remove-from-View opens its dialog, error chip fits, no link to an undrawn asset Co-Authored-By: Claude Opus 5.5 (1M context) --- .../assets/AssetGraph/AssetGraphCanvas.svelte | 23 +++++++++++-------- .../assets/AssetGraph/assetNodeWidth.ts | 4 +++- .../(logged)/pipeline/[folder]/+page.svelte | 18 ++++++++++++--- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/frontend/src/lib/components/assets/AssetGraph/AssetGraphCanvas.svelte b/frontend/src/lib/components/assets/AssetGraph/AssetGraphCanvas.svelte index 569592a31d..e9eec04bb0 100644 --- a/frontend/src/lib/components/assets/AssetGraph/AssetGraphCanvas.svelte +++ b/frontend/src/lib/components/assets/AssetGraph/AssetGraphCanvas.svelte @@ -1193,6 +1193,15 @@ onDeleteAssetUpstream && u && !u.multiple && r ? upstreamDeletion(u, r, v.upstream) : undefined + // The scripts and triggers folded into this node, whose errors it reports. + const scriptIds = u ? (u.multiple ? u.runnableIds : [u.runnableId]) : [] + const foldedIds = [ + ...scriptIds, + ...m.edges + .filter((e) => e.kind === 'trigger-native' && scriptIds.includes(e.target)) + .map((e) => e.source) + ] + const foldedErrors = foldedIds.filter((id) => nodeFixes?.has(id)).length const asset = { kind: data.asset_kind, path: data.path } const scriptCount = u?.multiple ? u.runnableIds.length : 1 // Chips this estimate does not model keep the regular width. @@ -1203,13 +1212,15 @@ title: data.noAsset ? `${scriptCount} script${scriptCount === 1 ? '' : 's'}` : formatShortAssetPath(asset), - chip: - 'none' in upstream + chip: foldedErrors + ? `${foldedErrors} errors · Fix` + : 'none' in upstream ? 'External source' : 'multiple' in upstream ? 'Multiple upstream nodes' : `${upstream.trigger.label}${upstream.trigger.draft ? ' · draft' : ''}`, runState: 'runState' in upstream && !!upstream.runState, + chipIcon: foldedErrors > 0, scriptChips: 'runnableId' in upstream ? 1 @@ -1218,14 +1229,6 @@ : 0 }) : undefined - // The scripts and triggers folded into this node, whose errors it reports. - const scriptIds = u ? (u.multiple ? u.runnableIds : [u.runnableId]) : [] - const foldedIds = [ - ...scriptIds, - ...m.edges - .filter((e) => e.kind === 'trigger-native' && scriptIds.includes(e.target)) - .map((e) => e.source) - ] return { ...data, upstream, diff --git a/frontend/src/lib/components/assets/AssetGraph/assetNodeWidth.ts b/frontend/src/lib/components/assets/AssetGraph/assetNodeWidth.ts index 66efcbb1d4..d2eef08933 100644 --- a/frontend/src/lib/components/assets/AssetGraph/assetNodeWidth.ts +++ b/frontend/src/lib/components/assets/AssetGraph/assetNodeWidth.ts @@ -27,6 +27,8 @@ export function assetsOnlyNodeWidth(n: { kind: string title: string chip: string + /** The chip leads with an icon (the folded-errors Fix chip). */ + chipIcon?: boolean runState: boolean /** Producing scripts: one chip for one, two icons and a "+N" for more. */ scriptChips: number @@ -35,7 +37,7 @@ export function assetsOnlyNodeWidth(n: { const text = Math.max( textWidth(n.kind, 0.7), textWidth(n.title, 0.75), - textWidth(n.chip, 0.65) + 12 + 2 + textWidth(n.chip, 0.65) + 12 + 2 + (n.chipIcon ? 14 : 0) ) // The run chip is sized for a two-digit count, so a new run never re-lays the graph. const scripts = diff --git a/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte b/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte index ad6a34719b..9c31e4cc39 100644 --- a/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/pipeline/[folder]/+page.svelte @@ -1387,7 +1387,12 @@ fixes.set(`script:${path}`, { explainer: [ 'Runs after each write to ', - assetLink(asset), + // Not drawn when only this subscription names it: nothing to select. + shownGraph.edges.some( + (e) => e.asset_kind === asset.kind && e.asset_path === asset.path + ) + ? assetLink(asset) + : asset.path, ', but nothing writes it, so it never runs.' ], actions: [ @@ -1465,8 +1470,15 @@ detail: unsaved ? 'Drops the draft.' : 'Opens its archive or delete confirmation.', - run: () => { - if (mode !== 'edit') setMode('edit') + run: async () => { + if (mode !== 'edit') await setMode('edit') + // The pane takes the remove request only once it is open on the + // script: a request made as it mounts is its starting point. + if (!unsaved) { + pe.activeDraftPath = undefined + pe.selection = { kind: 'runnable', runnable_kind: 'script', path } + await tick() + } handleRunnableMenuRemove({ runnable_kind: 'script', path, unsaved }) } }