From a3f8b50bbd0165df5b3625698dd09aae4b53cd66 Mon Sep 17 00:00:00 2001 From: JakeSchroeder Date: Tue, 23 Sep 2025 18:20:48 -0700 Subject: [PATCH] feat: vue node previews with refactored lgnode.vue --- .../vueNodes/components/LGraphNode.vue | 221 +++++------------- .../vueNodes/components/LGraphNodePreview.vue | 69 ++---- .../vueNodes/components/NodeBaseTemplate.vue | 158 +++++++++++++ .../composables/useNodePresentation.ts | 153 ++++++++++++ 4 files changed, 381 insertions(+), 220 deletions(-) create mode 100644 src/renderer/extensions/vueNodes/components/NodeBaseTemplate.vue create mode 100644 src/renderer/extensions/vueNodes/composables/useNodePresentation.ts diff --git a/src/renderer/extensions/vueNodes/components/LGraphNode.vue b/src/renderer/extensions/vueNodes/components/LGraphNode.vue index 08ff30d3b..f7f919259 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNode.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNode.vue @@ -2,137 +2,39 @@
{{ $t('Node Render Error') }}
-
-
- - - -
- -
- - -
+ :node-data="nodeData" + :readonly="readonly" + :container-classes="presentation.containerBaseClasses.value" + :container-style="containerStyle" + :is-collapsed="presentation.isCollapsed.value" + :separator-classes="presentation.separatorClasses" + :progress-classes="presentation.progressClasses" + :progress-bar-classes="presentation.progressBarClasses.value" + :show-progress="presentation.showProgress.value" + :progress-value="progress" + :progress-style="presentation.progressStyle.value" + :progress-bar-style="presentation.progressBarStyle.value" + :has-custom-content="hasCustomContent" + :image-urls="nodeImageUrls" + :show-preview-image="shouldShowPreviewImg" + :preview-image-url="latestPreviewUrl" + :event-handlers="{ + onPointerdown: handlePointerDown, + onPointermove: handlePointerMove, + onPointerup: handlePointerUp, + onWheel: handleWheel + }" + @collapse="handleCollapse" + @update:title="handleHeaderTitleUpdate" + @enter-subgraph="handleEnterSubgraph" + /> diff --git a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue index ce59aabf1..dd060de61 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue @@ -1,46 +1,14 @@ @@ -48,13 +16,12 @@ import { computed } from 'vue' import type { VueNodeData } from '@/composables/graph/useGraphNodeManager' -import NodeContent from '@/renderer/extensions/vueNodes/components/NodeContent.vue' -import NodeHeader from '@/renderer/extensions/vueNodes/components/NodeHeader.vue' -import NodeSlots from '@/renderer/extensions/vueNodes/components/NodeSlots.vue' -import NodeWidgets from '@/renderer/extensions/vueNodes/components/NodeWidgets.vue' +import { useNodePresentation } from '@/renderer/extensions/vueNodes/composables/useNodePresentation' import type { ComfyNodeDef as ComfyNodeDefV2 } from '@/schemas/nodeDef/nodeDefSchemaV2' import { useWidgetStore } from '@/stores/widgetStore' +import NodeBaseTemplate from './NodeBaseTemplate.vue' + const { nodeDef } = defineProps<{ nodeDef: ComfyNodeDefV2 }>() @@ -110,13 +77,9 @@ const nodeData = computed(() => { } }) -const readonly = true - -const hasCustomContent = computed(() => { - return false -}) - -const nodeImageUrls = computed(() => { - return [] +// Use the presentation composable with preview mode +const presentation = useNodePresentation(() => nodeData.value, { + readonly: true, + isPreview: true }) diff --git a/src/renderer/extensions/vueNodes/components/NodeBaseTemplate.vue b/src/renderer/extensions/vueNodes/components/NodeBaseTemplate.vue new file mode 100644 index 000000000..e365d5850 --- /dev/null +++ b/src/renderer/extensions/vueNodes/components/NodeBaseTemplate.vue @@ -0,0 +1,158 @@ + + + diff --git a/src/renderer/extensions/vueNodes/composables/useNodePresentation.ts b/src/renderer/extensions/vueNodes/composables/useNodePresentation.ts new file mode 100644 index 000000000..89440ca8e --- /dev/null +++ b/src/renderer/extensions/vueNodes/composables/useNodePresentation.ts @@ -0,0 +1,153 @@ +import { type ComputedRef, type Ref, computed, unref } from 'vue' + +import type { VueNodeData } from '@/composables/graph/useGraphNodeManager' +import { cn } from '@/utils/tailwindUtil' + +export interface NodePresentationOptions { + readonly?: boolean + isPreview?: boolean + scale?: number + // Interactive node state + isSelected?: ComputedRef + executing?: ComputedRef + progress?: ComputedRef + hasExecutionError?: ComputedRef + hasAnyError?: ComputedRef + bypassed?: ComputedRef + isDragging?: ComputedRef | Ref + shouldHandleNodePointerEvents?: ComputedRef +} + +export interface NodePresentationState { + // Classes + containerBaseClasses: ComputedRef + separatorClasses: string + progressClasses: string + // Computed states + isCollapsed: ComputedRef + showProgress: ComputedRef + progressStyle: ComputedRef<{ width: string } | undefined> + progressBarStyle: ComputedRef<{ width: string } | undefined> + progressBarClasses: ComputedRef + borderClass: ComputedRef + outlineClass: ComputedRef +} + +export function useNodePresentation( + nodeData: () => VueNodeData | undefined, + options: NodePresentationOptions = {} +): NodePresentationState { + const { + isPreview = false, + isSelected, + executing, + progress, + hasAnyError, + bypassed, + isDragging, + shouldHandleNodePointerEvents + } = options + + // Collapsed state + const isCollapsed = computed(() => nodeData()?.flags?.collapsed ?? false) + + // Show progress when executing with defined progress + const showProgress = computed(() => { + if (isPreview) return false + return !!(executing?.value && progress?.value !== undefined) + }) + + // Progress styles + const progressStyle = computed(() => { + if (!showProgress.value || !progress?.value) return undefined + return { width: `${Math.min(progress.value * 100, 100)}%` } + }) + + const progressBarStyle = progressStyle + + // Border class based on state + const borderClass = computed(() => { + if (isPreview) return undefined + if (hasAnyError?.value) { + return 'border-error' + } + if (executing?.value) { + return 'border-blue-500' + } + return undefined + }) + + // Outline class based on selection and state + const outlineClass = computed(() => { + if (isPreview) return undefined + if (!isSelected?.value) { + return undefined + } + if (hasAnyError?.value) { + return 'outline-error' + } + if (executing?.value) { + return 'outline-blue-500' + } + return 'outline-black dark-theme:outline-white' + }) + + // Container base classes (without dynamic state classes) + const containerBaseClasses = computed(() => { + if (isPreview) { + return cn( + 'bg-white dark-theme:bg-charcoal-800', + 'lg-node absolute rounded-2xl', + 'border border-solid border-sand-100 dark-theme:border-charcoal-600', + 'outline-transparent -outline-offset-2 outline-2', + 'pointer-events-none' + ) + } + + return cn( + 'bg-white dark-theme:bg-charcoal-800', + 'lg-node absolute rounded-2xl', + 'border border-solid border-sand-100 dark-theme:border-charcoal-600', + // hover (only when node should handle events) + shouldHandleNodePointerEvents?.value && + 'hover:ring-7 ring-gray-500/50 dark-theme:ring-gray-500/20', + 'outline-transparent -outline-offset-2 outline-2', + borderClass.value, + outlineClass.value, + { + 'animate-pulse': executing?.value, + 'opacity-50 before:rounded-2xl before:pointer-events-none before:absolute before:bg-bypass/60 before:inset-0': + bypassed?.value, + 'will-change-transform': unref(isDragging) + }, + shouldHandleNodePointerEvents?.value + ? 'pointer-events-auto' + : 'pointer-events-none' + ) + }) + + const progressBarClasses = computed(() => { + return cn( + 'absolute inset-x-4 -bottom-[1px] translate-y-1/2 rounded-full', + progressClasses + ) + }) + + // Static classes + const separatorClasses = + 'bg-sand-100 dark-theme:bg-charcoal-600 h-px mx-0 w-full lod-toggle' + const progressClasses = 'h-2 bg-primary-500 transition-all duration-300' + + return { + containerBaseClasses, + separatorClasses, + progressClasses, + isCollapsed, + showProgress, + progressStyle, + progressBarStyle, + progressBarClasses, + borderClass, + outlineClass + } +}