feat: label the pipeline + buttons: add data source, add downstream step on hover

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Diego Imbert
2026-09-30 12:28:17 +02:00
co-authored by Claude Opus 5.5
parent 21690a2ed8
commit 69cea7f3d9
3 changed files with 21 additions and 34 deletions
@@ -16,6 +16,7 @@
import type { ScriptLang } from '$lib/gen'
import type { NativeTriggerKind } from './types'
import { PIPELINE_LANGUAGES } from './pipelineLanguages'
import { NODE } from '$lib/components/graph/util'
import type { PipelineOutputKind } from './pipelineTemplates'
// Each left-column kind is just "pipeline script triggered by <trigger
@@ -58,6 +59,9 @@
}
</script>
<!-- The layout gives this node a full node-width slot; centering the pill in
it lines it up with the nodes below whatever its label's width. -->
<div class="flex justify-center" style="width: {NODE.width}px;">
<PipelineInsertMenu
kinds={[
{
@@ -143,13 +147,14 @@
{#snippet trigger()}
<!-- Quiet insert affordance, mirroring the flow editor's inline +
buttons (bg-surface + gray border + secondary text) — a filled
accent circle outweighed every real node on the canvas. -->
accent pill would outweigh every real node on the canvas. -->
<button
type="button"
class="w-8 h-8 rounded-full flex items-center justify-center bg-surface border border-gray-400 dark:border-gray-600 text-secondary shadow-sm hover:bg-surface-hover transition-colors cursor-pointer"
title="Add to pipeline"
class="h-8 px-3 rounded-full flex items-center gap-1.5 whitespace-nowrap bg-surface border border-gray-400 dark:border-gray-600 text-xs font-normal text-secondary shadow-sm hover:bg-surface-hover transition-colors cursor-pointer"
>
<Plus size={16} />
<Plus size={16} class="shrink-0" />
Add data source
</button>
{/snippet}
</PipelineInsertMenu>
</div>
@@ -31,12 +31,6 @@
import type { AssetKind } from '$lib/gen'
import { NODE } from '$lib/components/graph/util'
// Width of the + node's rendered DOM element. Sugiyama allocates a full
// NODE.width slot for every node, so the small round button ends up
// left-aligned in its slot. We compensate by shifting the + node right
// by half the difference so its visual center matches the slot center.
const ADD_NODE_WIDTH = 40
interface Props {
graph: AssetGraphResponse
selection?: AssetGraphSelection | undefined
@@ -967,9 +961,6 @@
const xCenter = paneWidth / 2 - bboxWidth / 2
return model.nodes.map<Node>((n) => {
const p = layoutPositions.get(n.id) ?? { x: 0, y: 0 }
// Compensate for the + node being narrower than its layout slot
// so it visually centers over the node(s) below.
const xShift = n.id === ADD_NODE_ID ? (NODE.width - ADD_NODE_WIDTH) / 2 : 0
// Activity-panel emphasis (purely visual rings, kept off `selected`
// which swaps the details pane). Hover wins over pin so the cursor
// always tracks.
@@ -1001,7 +992,7 @@
return {
id: n.id,
type: n.type,
position: { x: p.x + xCenter + xShift, y: p.y + 40 },
position: { x: p.x + xCenter, y: p.y + 40 },
data: n.data,
class: boundClass ?? dbtClass ?? runClass ?? assetClass,
selected: n.id === selectedId,
@@ -425,30 +425,21 @@
onPick={handlePick}
>
{#snippet trigger()}
<!--
Sizing notes for the round + button:
- w-6/h-6 (24px) chosen so that with border-2 (2px each
side) the inner area is exactly 20px — divisible by
the 16px icon to leave a 2px gap on every side. At
20px / 12px before, the gap was 4px-each but the
odd rounding interacted badly with svelte-flow's
fractional zoom transforms and the icon drifted
half a pixel off-center on certain zoom levels.
- `grid place-items-center` instead of flex centering:
flex's baseline alignment introduces a sub-pixel
nudge on small elements that grid avoids.
- `leading-none` strips the default line-height
contribution that the SVG inherits via the parent's
text rendering, otherwise the icon is shifted
downward by ~0.5px at fractional zooms.
-->
<!-- A circle at rest; hovering widens it into a labelled pill. The
label animates its max-width (width itself can't transition to
auto) and never wraps, so the pill grows on one line. -->
<button
type="button"
onclick={(e) => e.stopPropagation()}
class="bg-surface border border-gray-400 dark:border-gray-600 text-secondary hover:bg-surface-hover rounded-full w-6 h-6 grid place-items-center shadow-sm leading-none"
title="Add downstream pipeline script"
class="group/add h-6 min-w-6 rounded-full flex items-center justify-center bg-surface border border-gray-400 dark:border-gray-600 text-secondary hover:text-primary hover:border-gray-500 shadow-sm leading-none overflow-hidden transition-[padding] duration-200 ease-out hover:pl-1"
aria-label="Add downstream step"
>
<Plus size={16} strokeWidth={2.5} />
<Plus size={16} strokeWidth={2.5} class="shrink-0" />
<span
class="max-w-0 opacity-0 whitespace-nowrap text-2xs font-normal transition-[max-width,opacity,margin] duration-200 ease-out group-hover/add:max-w-32 group-hover/add:opacity-100 group-hover/add:ml-1 group-hover/add:mr-2"
>
Add downstream step
</span>
</button>
{/snippet}
</PipelineInsertMenu>