From 2b9a3972369a0d368db18af19169971204d9e70d Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Tue, 1 Jul 2025 14:11:58 +0200 Subject: [PATCH] Asset nodes --- .../flows/map/FlowModuleSchemaItem.svelte | 7 +- .../lib/components/graph/FlowGraphV2.svelte | 70 +++++++++++++++++-- .../components/graph/graphBuilder.svelte.ts | 9 +++ .../graph/renderers/nodes/AssetNode.svelte | 36 ++++++++++ 4 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 frontend/src/lib/components/graph/renderers/nodes/AssetNode.svelte diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte index 0bd3c10066..cf454c0da7 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte @@ -143,7 +143,6 @@ let flowStateStore = $derived(flowEditorContext?.flowStateStore) let assets = $derived(id ? $flowStateStore?.[id]?.assetsCache : undefined) - let containsSelectedAsset = $derived(assets?.some((a) => assetEq(a, selectedAssetStore?.val))) let stepHistoryLoader = getStepHistoryLoaderContext() @@ -276,8 +275,7 @@ class={classNames( 'w-full module flex rounded-sm cursor-pointer max-w-full ', 'flex relative', - deletable ? aiModuleActionToBgColor(action) : '', - containsSelectedAsset ? '!bg-surface-hover' : '' + deletable ? aiModuleActionToBgColor(action) : '' )} style="width: 275px; height: 34px; background-color: {hover && bgHoverColor ? bgHoverColor @@ -292,8 +290,7 @@
diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index b1e090cfb8..3b437644ed 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -17,6 +17,7 @@ import { graphBuilder, isTriggerStep, + type AssetN, type InlineScript, type InsertKind, type NodeLayout, @@ -50,11 +51,18 @@ import SubflowBound from './renderers/nodes/SubflowBound.svelte' import { deepEqual } from 'fast-equals' import ViewportResizer from './ViewportResizer.svelte' + import type { FlowEditorContext } from '../flows/types' + import AssetNode from './renderers/nodes/AssetNode.svelte' + import { formatAsset } from '../assets/lib' let useDataflow: Writable = writable(false) const triggerContext = getContext('TriggerContext') + const flowEditorContextOpt: FlowEditorContext | undefined = + getContext('FlowEditorContext') + const flowStateStoreOpt = flowEditorContextOpt.flowStateStore + let fullWidth = 0 let width = $state(0) @@ -220,9 +228,11 @@ .decross(nodes.length > 20 ? decrossTwoLayer() : decrossOpt()) .coord(coordCenter()) .nodeSize((d) => { + const id: string | undefined = d?.data?.['id'] ?? '' + const assetOffset = $flowStateStoreOpt?.[id]?.assetsCache?.length ? 100 : 0 return [ - (nodeWidths[d?.data?.['id'] ?? ''] ?? 1) * (NODE.width + NODE.gap.horizontal * 1), - NODE.height + NODE.gap.vertical + (nodeWidths[id] ?? 1) * (NODE.width + NODE.gap.horizontal * 1), + NODE.height + NODE.gap.vertical + assetOffset ] as readonly [number, number] }) boxSize = layout(dag as any) @@ -256,6 +266,55 @@ return newNodes } + function computeAssetNodes(nodes: Node[]) { + const ASSET_X_GAP = 20 + const ASSET_WIDTH = 180 + const ASSET_Y_OFFSET = 55 + return nodes.flatMap((node) => { + const assets = $flowStateStoreOpt?.[node.id]?.assetsCache + const assetNodes = assets?.map( + (asset, assetIdx) => + ({ + id: `${node.id}-asset-${formatAsset(asset)}`, + type: 'asset', + data: { asset }, + position: { + x: + (ASSET_WIDTH + ASSET_X_GAP) * (assetIdx - assets.length / 2) + + (NODE.width + ASSET_X_GAP) / 2, + y: ASSET_Y_OFFSET + }, + parentId: node.id, + width: ASSET_WIDTH + }) satisfies Node & AssetN + ) + return assetNodes ?? [] + }) + } + + function computeAssetEdges(edges: Edge[], assetNodes: (Node & AssetN)[]): Edge[] { + return assetNodes.map( + (n) => + ({ + id: `${n.id}-edge`, + source: n.parentId ?? '', + target: n.id, + type: 'empty', + data: { + insertable: false, + sourceId: n.id, + targetId: n.parentId, + moving: moving, + eventHandlers: eventHandler, + index: 0, + enableTrigger: false, + disableAi: disableAi, + disableMoveIds: [] + } + }) satisfies Edge + ) + } + let eventHandler = { deleteBranch: (detail, label) => { $selectedId = label @@ -344,7 +403,9 @@ let newGraph = graph nodes = layoutNodes(newGraph.nodes) - edges = newGraph.edges + const assetNodes = computeAssetNodes(nodes) + nodes = [...nodes, ...assetNodes] + edges = [...newGraph.edges, ...computeAssetEdges(newGraph.edges, assetNodes)] await tick() height = Math.max(...nodes.map((n) => n.position.y + NODE.height + 100), minHeight) } @@ -363,7 +424,8 @@ branchOneEnd: BranchOneEndNode, subflowBound: SubflowBound, noBranch: NoBranchNode, - trigger: TriggersNode + trigger: TriggersNode, + asset: AssetNode } as any const edgeTypes = { diff --git a/frontend/src/lib/components/graph/graphBuilder.svelte.ts b/frontend/src/lib/components/graph/graphBuilder.svelte.ts index 4a1032ff01..5145537361 100644 --- a/frontend/src/lib/components/graph/graphBuilder.svelte.ts +++ b/frontend/src/lib/components/graph/graphBuilder.svelte.ts @@ -4,6 +4,7 @@ import { getDependeeAndDependentComponents } from '../flows/flowExplorer' import { dfsByModule } from '../flows/previousResults' import { defaultIfEmptyString } from '$lib/utils' import type { GraphModuleState } from './model' +import type { Asset } from '../assets/lib' export type InsertKind = | 'script' @@ -90,6 +91,7 @@ export type FlowNode = | SubflowBoundN | NoBranchN | TriggerN + | AssetN export type InputN = { type: 'input2' @@ -256,6 +258,13 @@ export type TriggerN = { } } +export type AssetN = { + type: 'asset' + data: { + asset: Asset + } +} + // input2: InputNode, // module: ModuleNode, // branchAllStart: BranchAllStart, diff --git a/frontend/src/lib/components/graph/renderers/nodes/AssetNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/AssetNode.svelte new file mode 100644 index 0000000000..bea1ca4289 --- /dev/null +++ b/frontend/src/lib/components/graph/renderers/nodes/AssetNode.svelte @@ -0,0 +1,36 @@ + + + + {#snippet children({ darkMode })} + +
(selectedAssetStore.val = data.asset)} + onmouseleave={() => (selectedAssetStore.val = undefined)} + > + + + {formatAsset(data.asset)} + +
+ {/snippet} +