Skip to content

Commit 88deb32

Browse files
AustinMrozgithub-actions[bot]
authored andcommitted
1 parent d64c18b commit 88deb32

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
@@ -79,10 +79,64 @@ export interface GraphNodeManager {
7979
cleanup(): void
8080
}
8181

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

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

192208
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)