Skip to content

Commit bb51a5a

Browse files
authored
1 parent 674d884 commit bb51a5a

File tree

8 files changed

+276
-42
lines changed

8 files changed

+276
-42
lines changed

src/composables/graph/useGraphNodeManager.ts

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,64 @@ export interface GraphNodeManager {
7878
cleanup(): void
7979
}
8080

81+
export function safeWidgetMapper(
82+
node: LGraphNode,
83+
slotMetadata: Map<string, WidgetSlotMetadata>
84+
): (widget: IBaseWidget) => SafeWidgetData {
85+
const nodeDefStore = useNodeDefStore()
86+
return function (widget) {
87+
try {
88+
// TODO: Use widget.getReactiveData() once TypeScript types are updated
89+
let value = widget.value
90+
91+
// For combo widgets, if value is undefined, use the first option as default
92+
if (
93+
value === undefined &&
94+
widget.type === 'combo' &&
95+
widget.options?.values &&
96+
Array.isArray(widget.options.values) &&
97+
widget.options.values.length > 0
98+
) {
99+
value = widget.options.values[0]
100+
}
101+
const spec = nodeDefStore.getInputSpecForWidget(node, widget.name)
102+
const slotInfo = slotMetadata.get(widget.name)
103+
104+
return {
105+
name: widget.name,
106+
type: widget.type,
107+
value: value,
108+
label: widget.label,
109+
options: widget.options ? { ...widget.options } : undefined,
110+
callback: widget.callback,
111+
spec,
112+
slotMetadata: slotInfo,
113+
isDOMWidget: isDOMWidget(widget)
114+
}
115+
} catch (error) {
116+
return {
117+
name: widget.name || 'unknown',
118+
type: widget.type || 'text',
119+
value: undefined
120+
}
121+
}
122+
}
123+
}
124+
125+
export function isValidWidgetValue(value: unknown): value is WidgetValue {
126+
return (
127+
value === null ||
128+
value === undefined ||
129+
typeof value === 'string' ||
130+
typeof value === 'number' ||
131+
typeof value === 'boolean' ||
132+
typeof value === 'object'
133+
)
134+
}
135+
81136
export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
82137
// Get layout mutations composable
83138
const { createNode, deleteNode, setSource } = useLayoutMutations()
84-
const nodeDefStore = useNodeDefStore()
85139
// Safe reactive data extracted from LiteGraph nodes
86140
const vueNodeData = reactive(new Map<string, VueNodeData>())
87141

@@ -147,45 +201,7 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
147201
linked: input.link != null
148202
})
149203
})
150-
return (
151-
node.widgets?.map((widget) => {
152-
try {
153-
// TODO: Use widget.getReactiveData() once TypeScript types are updated
154-
let value = widget.value
155-
156-
// For combo widgets, if value is undefined, use the first option as default
157-
if (
158-
value === undefined &&
159-
widget.type === 'combo' &&
160-
widget.options?.values &&
161-
Array.isArray(widget.options.values) &&
162-
widget.options.values.length > 0
163-
) {
164-
value = widget.options.values[0]
165-
}
166-
const spec = nodeDefStore.getInputSpecForWidget(node, widget.name)
167-
const slotInfo = slotMetadata.get(widget.name)
168-
169-
return {
170-
name: widget.name,
171-
type: widget.type,
172-
value: value,
173-
label: widget.label,
174-
options: widget.options ? { ...widget.options } : undefined,
175-
callback: widget.callback,
176-
spec,
177-
slotMetadata: slotInfo,
178-
isDOMWidget: isDOMWidget(widget)
179-
}
180-
} catch (error) {
181-
return {
182-
name: widget.name || 'unknown',
183-
type: widget.type || 'text',
184-
value: undefined
185-
}
186-
}
187-
}) ?? []
188-
)
204+
return node.widgets?.map(safeWidgetMapper(node, slotMetadata)) ?? []
189205
})
190206

191207
const nodeType =

src/composables/useCoreCommands.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,12 @@ export function useCoreCommands(): ComfyCommand[] {
12191219
await settingStore.set('Comfy.Assets.UseAssetAPI', !current)
12201220
await useWorkflowService().reloadCurrentWorkflow() // ensure changes take effect immediately
12211221
}
1222+
},
1223+
{
1224+
id: 'Comfy.ToggleLinear',
1225+
icon: 'pi pi-database',
1226+
label: 'toggle linear mode',
1227+
function: () => (canvasStore.linearMode = !canvasStore.linearMode)
12221228
}
12231229
]
12241230

src/platform/workflow/management/stores/workflowStore.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
ComfyWorkflowJSON,
1414
NodeId
1515
} from '@/platform/workflow/validation/schemas/workflowSchema'
16+
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
1617
import { useWorkflowThumbnail } from '@/renderer/core/thumbnail/useWorkflowThumbnail'
1718
import { api } from '@/scripts/api'
1819
import { app as comfyApp } from '@/scripts/app'
@@ -329,6 +330,7 @@ export const useWorkflowStore = defineStore('workflow', () => {
329330
tabActivationHistory.value.shift()
330331
}
331332

333+
useCanvasStore().linearMode = !!loadedWorkflow.activeState.extra?.linearMode
332334
return loadedWorkflow
333335
}
334336

src/renderer/core/canvas/canvasStore.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export const useCanvasStore = defineStore('canvas', () => {
4040
// Reactive scale percentage that syncs with app.canvas.ds.scale
4141
const appScalePercentage = ref(100)
4242

43+
const linearMode = ref(false)
44+
4345
// Set up scale synchronization when canvas is available
4446
let originalOnChanged: ((scale: number, offset: Point) => void) | undefined =
4547
undefined
@@ -138,6 +140,7 @@ export const useCanvasStore = defineStore('canvas', () => {
138140
groupSelected,
139141
rerouteSelected,
140142
appScalePercentage,
143+
linearMode,
141144
updateSelectedItems,
142145
getCanvas,
143146
setAppZoomFromPercentage,

src/stores/imagePreviewStore.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
4040
const { nodeIdToNodeLocatorId, nodeToNodeLocatorId } = useWorkflowStore()
4141
const { executionIdToNodeLocatorId } = useExecutionStore()
4242
const scheduledRevoke: Record<NodeLocatorId, { stop: () => void }> = {}
43+
const latestOutput = ref<string[]>([])
4344

4445
function scheduleRevoke(locator: NodeLocatorId, cb: () => void) {
4546
scheduledRevoke[locator]?.stop()
@@ -146,6 +147,13 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
146147
}
147148
}
148149

150+
//TODO:Preview params and deduplication
151+
latestOutput.value =
152+
(outputs as ExecutedWsMessage['output'])?.images?.map((image) => {
153+
const imgUrlPart = new URLSearchParams(image)
154+
const rand = app.getRandParam()
155+
return api.apiURL(`/view?${imgUrlPart}${rand}`)
156+
}) ?? []
149157
app.nodeOutputs[nodeLocatorId] = outputs
150158
nodeOutputs.value[nodeLocatorId] = outputs
151159
}
@@ -213,6 +221,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
213221
scheduledRevoke[nodeLocatorId].stop()
214222
delete scheduledRevoke[nodeLocatorId]
215223
}
224+
latestOutput.value = previewImages
216225
app.nodePreviewImages[nodeLocatorId] = previewImages
217226
nodePreviewImages.value[nodeLocatorId] = previewImages
218227
}
@@ -381,6 +390,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => {
381390

382391
// State
383392
nodeOutputs,
384-
nodePreviewImages
393+
nodePreviewImages,
394+
latestOutput
385395
}
386396
})

src/views/GraphView.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
<div id="comfyui-body-left" class="comfyui-body-left" />
66
<div id="comfyui-body-right" class="comfyui-body-right" />
77
<div
8+
v-show="!linearMode"
89
id="graph-canvas-container"
910
ref="graphCanvasContainerRef"
1011
class="graph-canvas-container"
1112
>
1213
<GraphCanvas @ready="onGraphReady" />
1314
</div>
15+
<LinearView v-if="linearMode" />
1416
</div>
1517

1618
<GlobalToast />
@@ -22,6 +24,7 @@
2224

2325
<script setup lang="ts">
2426
import { useEventListener } from '@vueuse/core'
27+
import { storeToRefs } from 'pinia'
2528
import type { ToastMessageOptions } from 'primevue/toast'
2629
import { useToast } from 'primevue/usetoast'
2730
import {
@@ -53,6 +56,7 @@ import { useSettingStore } from '@/platform/settings/settingStore'
5356
import { useTelemetry } from '@/platform/telemetry'
5457
import { useFrontendVersionMismatchWarning } from '@/platform/updates/common/useFrontendVersionMismatchWarning'
5558
import { useVersionCompatibilityStore } from '@/platform/updates/common/versionCompatibilityStore'
59+
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
5660
import type { StatusWsMessageStatus } from '@/schemas/apiSchema'
5761
import { api } from '@/scripts/api'
5862
import { app } from '@/scripts/app'
@@ -75,6 +79,7 @@ import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
7579
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
7680
import { useWorkspaceStore } from '@/stores/workspaceStore'
7781
import { electronAPI, isElectron } from '@/utils/envUtil'
82+
import LinearView from '@/views/LinearView.vue'
7883
7984
setupAutoQueueHandler()
8085
useProgressFavicon()
@@ -89,6 +94,7 @@ const queueStore = useQueueStore()
8994
const assetsStore = useAssetsStore()
9095
const versionCompatibilityStore = useVersionCompatibilityStore()
9196
const graphCanvasContainerRef = ref<HTMLDivElement | null>(null)
97+
const { linearMode } = storeToRefs(useCanvasStore())
9298
9399
const telemetry = useTelemetry()
94100
const firebaseAuthStore = useFirebaseAuthStore()

0 commit comments

Comments
 (0)