bizydraft 0.2.49__py3-none-any.whl → 0.2.87__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bizydraft might be problematic. Click here for more details.

@@ -1,1016 +1,1256 @@
1
1
  import { app } from "../../scripts/app.js";
2
- import { enableAIAppMode, disableAIAppMode, selectInputNode, deselectInputNode, updateInputNodeWidget, getSelectedInputNodes, clearSelectedInputNodes, toggleExportMode } from "./aiAppHandler.js";
2
+ import {
3
+ enableAIAppMode,
4
+ disableAIAppMode,
5
+ selectInputNode,
6
+ deselectInputNode,
7
+ updateInputNodeWidget,
8
+ getSelectedInputNodes,
9
+ clearSelectedInputNodes,
10
+ toggleExportMode,
11
+ } from "./aiAppHandler.js";
3
12
  import { focusNodeOnly } from "./nodeFocusHandler.js";
4
13
 
5
14
  app.registerExtension({
6
- name: "comfy.BizyAir.Socket",
7
-
8
- dispatchCustomEvent(type, detail) {
9
- app.api.dispatchCustomEvent(type, detail);
10
- },
11
-
12
- socket: null,
13
- isConnecting: false,
14
- taskRunning: false,
15
-
16
- // 心跳检测
17
- pingInterval: 5000, // 5秒发送一次心跳
18
- pingTimer: null,
19
- pingTimeout: 3000,
20
- pongReceived: false,
21
- pingTimeoutTimer: null, // ping超时计时器
22
-
23
- /**
24
- * 开始心跳检测
25
- */
26
- startPing() {
27
- this.stopPing();
28
-
29
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
30
- return;
31
- }
32
-
33
- // 立即发送一次ping消息
34
- this.pongReceived = false;
35
- this.socket.send('ping');
15
+ name: "comfy.BizyAir.Socket",
16
+
17
+ dispatchCustomEvent(type, detail) {
18
+ app.api.dispatchCustomEvent(type, detail);
19
+ },
20
+
21
+ socket: null,
22
+ isConnecting: false,
23
+ taskRunning: false,
24
+
25
+ // 心跳检测
26
+ pingInterval: 5000, // 5秒发送一次心跳
27
+ pingTimer: null,
28
+ pingTimeout: 3000,
29
+ pongReceived: false,
30
+ pingTimeoutTimer: null, // ping超时计时器
31
+
32
+ /**
33
+ * 为特定socket开始心跳检测(每个socket独立的心跳)
34
+ */
35
+ startPingForSocket(socket) {
36
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
37
+ return;
38
+ }
36
39
 
40
+ // 在socket对象上存储心跳状态
41
+ socket.pongReceived = false;
42
+ // 立即发送一次ping消息
43
+ socket.send("ping");
44
+
45
+ // 设置ping超时检测
46
+ socket.pingTimeoutTimer = setTimeout(() => {
47
+ if (!socket.pongReceived && socket.readyState === WebSocket.OPEN) {
48
+ console.log("心跳检测超时,关闭此连接");
49
+ this.stopPingForSocket(socket);
50
+ socket.close();
51
+ }
52
+ }, this.pingTimeout);
53
+
54
+ // 设置定时发送ping
55
+ socket.pingTimer = setInterval(() => {
56
+ if (socket.readyState === WebSocket.OPEN) {
57
+ socket.pongReceived = false;
58
+ socket.send("ping");
37
59
  // 设置ping超时检测
38
- this.pingTimeoutTimer = setTimeout(() => {
39
- if (!this.pongReceived) {
40
- this.stopPing();
41
- this.reconnect();
42
- }
60
+ socket.pingTimeoutTimer = setTimeout(() => {
61
+ // 如果没有收到pong响应
62
+ if (!socket.pongReceived) {
63
+ console.log("心跳检测超时,关闭此连接");
64
+ this.stopPingForSocket(socket);
65
+ socket.close();
66
+ }
43
67
  }, this.pingTimeout);
68
+ } else {
69
+ this.stopPingForSocket(socket);
70
+ }
71
+ }, this.pingInterval);
72
+ },
73
+
74
+ /**
75
+ * 停止特定socket的心跳检测
76
+ */
77
+ stopPingForSocket(socket) {
78
+ if (!socket) return;
79
+
80
+ if (socket.pingTimer) {
81
+ clearInterval(socket.pingTimer);
82
+ socket.pingTimer = null;
83
+ }
44
84
 
45
- // 设置定时发送ping
46
- this.pingTimer = setInterval(() => {
47
- if (this.socket && this.socket.readyState === WebSocket.OPEN) {
48
- this.pongReceived = false;
49
- this.socket.send('ping');
50
- // 设置ping超时检测
51
- this.pingTimeoutTimer = setTimeout(() => {
52
- // 如果没有收到pong响应
53
- if (!this.pongReceived) {
54
- console.log('心跳检测超时,重新连接WebSocket');
55
- this.stopPing();
56
- this.reconnect();
57
- }
58
- }, this.pingTimeout);
59
- } else {
60
- this.stopPing();
61
- }
62
- }, this.pingInterval);
63
- },
64
-
65
- /**
66
- * 停止心跳检测
67
- */
68
- stopPing() {
69
- if (this.pingTimer) {
70
- clearInterval(this.pingTimer);
71
- this.pingTimer = null;
72
- }
73
-
74
- if (this.pingTimeoutTimer) {
75
- clearTimeout(this.pingTimeoutTimer);
76
- this.pingTimeoutTimer = null;
77
- }
78
- },
79
-
80
- /**
81
- * 重新连接
82
- */
83
- reconnect() {
84
- if (this.isConnecting) {
85
- return;
86
- }
87
- const url = this.socket ? this.socket.url : app.api.socket.url;
88
- this.closeSocket();
89
- this.createSocket(url);
90
- },
91
-
92
- /**
93
- * 创建新的WebSocket连接
94
- */
95
- createSocket(customUrl) {
96
- // 如果正在连接中,避免重复创建
97
- if (this.isConnecting) {
98
- console.log('WebSocket连接已在创建中,避免重复创建');
99
- return null;
100
- }
85
+ if (socket.pingTimeoutTimer) {
86
+ clearTimeout(socket.pingTimeoutTimer);
87
+ socket.pingTimeoutTimer = null;
88
+ }
89
+ },
101
90
 
102
- // 标记为连接中
103
- this.isConnecting = true;
91
+ /**
92
+ * 开始心跳检测(保留向后兼容)
93
+ */
94
+ startPing() {
95
+ this.stopPing();
104
96
 
105
- // 先关闭现有连接
106
- this.closeSocket();
97
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
98
+ return;
99
+ }
100
+ // 使用新的socket专用心跳方法
101
+ this.startPingForSocket(this.socket);
102
+ },
103
+
104
+ /**
105
+ * 停止心跳检测
106
+ */
107
+ stopPing() {
108
+ if (this.pingTimer) {
109
+ clearInterval(this.pingTimer);
110
+ this.pingTimer = null;
111
+ }
107
112
 
108
- const url = customUrl || app.api.socket.url;
109
- console.log('创建WebSocket连接:', url);
113
+ if (this.pingTimeoutTimer) {
114
+ clearTimeout(this.pingTimeoutTimer);
115
+ this.pingTimeoutTimer = null;
116
+ }
117
+ },
118
+
119
+ /**
120
+ * 重新连接
121
+ */
122
+ reconnect() {
123
+ if (this.isConnecting) {
124
+ return;
125
+ }
126
+ const url = this.socket ? this.socket.url : app.api.socket.url;
127
+ this.closeSocket();
128
+ this.createSocket(url);
129
+ },
130
+
131
+ /**
132
+ * 创建新的WebSocket连接
133
+ */
134
+ createSocket(customUrl) {
135
+ // 如果正在连接中,避免重复创建
136
+ if (this.isConnecting) {
137
+ console.log("WebSocket连接已在创建中,避免重复创建");
138
+ return null;
139
+ }
110
140
 
141
+ // 标记为连接中
142
+ this.isConnecting = true;
143
+
144
+ const url = customUrl || app.api.socket.url;
145
+ console.log("创建WebSocket连接:", url);
146
+
147
+ try {
148
+ const socket = new WebSocket(url);
149
+ const dispatchCustomEvent = this.dispatchCustomEvent;
150
+ const self = this;
151
+
152
+ socket.onopen = function () {
153
+ console.log("WebSocket连接已打开");
154
+ // 清除连接中标志
155
+ self.isConnecting = false;
156
+ // 存储为单例(最新的连接)
157
+ self.socket = socket;
158
+ // 替换app.api.socket
159
+ app.api.socket = socket;
160
+ // 为这个socket启动独立的心跳检测
161
+ self.startPingForSocket(socket);
162
+ };
163
+
164
+ socket.onmessage = function (event) {
111
165
  try {
112
- const socket = new WebSocket(url);
113
- const dispatchCustomEvent = this.dispatchCustomEvent;
114
- const self = this;
115
-
116
- socket.onopen = function() {
117
- console.log('WebSocket连接已打开');
118
- // 清除连接中标志
119
- self.isConnecting = false;
120
- // 存储为单例
121
- self.socket = socket;
122
- // 替换app.api.socket
123
- app.api.socket = socket;
124
- // 开始心跳检测
125
- self.startPing();
126
- };
127
-
128
- socket.onmessage = function (event) {
129
- try {
130
- // 处理心跳响应
131
- if (event.data === 'pong') {
132
- // 标记收到pong响应
133
- self.pongReceived = true;
134
- return;
135
- }
136
- if (event.data instanceof ArrayBuffer) {
137
- const view = new DataView(event.data);
138
- const eventType = view.getUint32(0);
139
-
140
- let imageMime;
141
- switch (eventType) {
142
- case 3:
143
- const decoder = new TextDecoder();
144
- const data = event.data.slice(4);
145
- const nodeIdLength = view.getUint32(4);
146
- dispatchCustomEvent('progress_text', {
147
- nodeId: decoder.decode(data.slice(4, 4 + nodeIdLength)),
148
- text: decoder.decode(data.slice(4 + nodeIdLength))
149
- });
150
- break;
151
- case 1:
152
- const imageType = view.getUint32(4);
153
- const imageData = event.data.slice(8);
154
- switch (imageType) {
155
- case 2:
156
- imageMime = 'image/png';
157
- break;
158
- case 1:
159
- default:
160
- imageMime = 'image/jpeg';
161
- break;
162
- }
163
- const imageBlob = new Blob([imageData], {
164
- type: imageMime
165
- });
166
- dispatchCustomEvent('b_preview', imageBlob);
167
- break;
168
- default:
169
- throw new Error(
170
- `Unknown binary websocket message of type ${eventType}`
171
- );
172
- }
173
- } else {
174
- // 检测[DONE]消息
175
- if (event.data === '[DONE]') {
176
- console.log('收到[DONE]消息,任务已完成,停止心跳并关闭连接');
177
- self.taskRunning = false;
178
- self.stopPing();
179
- self.closeSocket(1000);
180
- return;
181
- }
182
-
183
- const msg = JSON.parse(event.data);
184
- window.parent.postMessage({
185
- type: 'functionResult',
186
- method: 'progress_info_change',
187
- result: msg.progress_info
188
- }, '*');
189
- switch (msg.type) {
190
- case 'load_start':
191
- case 'load_end':
192
- case 'prompt_id':
193
- window.parent.postMessage({
194
- type: 'functionResult',
195
- method: 'preparingStatus',
196
- result: msg
197
- }, '*')
198
- case 'status':
199
- if (msg.data.sid) {
200
- const clientId = msg.data.sid;
201
- window.name = clientId;
202
- sessionStorage.setItem('clientId', clientId);
203
- socket.clientId = clientId;
204
- }
205
- dispatchCustomEvent('status', msg.data.status ?? null);
206
- break;
207
- case 'executing':
208
- dispatchCustomEvent(
209
- 'executing',
210
- msg.data.display_node || msg.data.node
211
- );
212
- break;
213
- case 'execution_success':
214
- case 'execution_cached':
215
- // 检查任务是否完成
216
- if (msg.data.completed) {
217
- self.taskRunning = false;
218
- }
219
- dispatchCustomEvent(msg.type, msg.data);
220
- break;
221
- case 'error':
222
- self.taskRunning = false;
223
- dispatchCustomEvent('execution_error', {
224
- exception_message: msg.data.error_message || 'Unknown error',
225
- traceback: ['Manual error triggered'],
226
- executed: [],
227
- prompt_id: 'manual',
228
- node_id: '0',
229
- node_type: 'RunError',
230
- });
231
- break;
232
- case 'execution_error':
233
- case 'execution_interrupted':
234
- self.taskRunning = false;
235
- dispatchCustomEvent(msg.type, msg.data);
236
- break;
237
- case 'execution_start':
238
- self.taskRunning = true;
239
- dispatchCustomEvent(msg.type, msg.data);
240
- break;
241
- case 'progress':
242
- case 'executed':
243
- case 'graphChanged':
244
- case 'promptQueued':
245
- case 'logs':
246
- case 'b_preview':
247
- dispatchCustomEvent(msg.type, msg.data);
248
- break;
249
- default:
250
- const registeredTypes = socket.registeredTypes || new Set();
251
- const reportedUnknownMessageTypes = socket.reportedUnknownMessageTypes || new Set();
252
-
253
- if (registeredTypes.has(msg.type)) {
254
- app.dispatchEvent(
255
- new CustomEvent(msg.type, { detail: msg.data })
256
- );
257
- } else if (!reportedUnknownMessageTypes.has(msg.type)) {
258
- reportedUnknownMessageTypes.add(msg.type);
259
- console.warn(`Unknown message type ${msg.type}`);
260
- }
261
- }
262
- }
263
- } catch (error) {
264
- console.warn('Unhandled message:', event.data, error);
265
- }
266
- };
267
-
268
- socket.onerror = function(error) {
269
- console.log('WebSocket 错误:', error);
270
- // 清除连接中标志
271
- self.isConnecting = false;
272
- // 停止心跳
273
- self.stopPing();
274
- };
275
-
276
- socket.onclose = function(event) {
277
- console.log('WebSocket 连接已关闭, 状态码:', event.code, event.reason);
278
- // 清除连接中标志
279
- self.isConnecting = false;
280
- // 清理单例引用
281
- if (self.socket === socket) {
282
- self.socket = null;
166
+ // WebSocket URL 中提取 taskId
167
+ let taskIdFromUrl = null;
168
+ try {
169
+ const urlParams = new URLSearchParams(socket.url.split("?")[1]);
170
+ taskIdFromUrl = urlParams.get("taskId");
171
+ if (taskIdFromUrl) {
172
+ taskIdFromUrl = parseInt(taskIdFromUrl, 10);
173
+ }
174
+ console.log("taskIdFromUrl:", taskIdFromUrl);
175
+ } catch (e) {
176
+ console.warn("无法从 WebSocket URL 中提取 taskId:", e);
177
+ }
178
+ // 处理心跳响应
179
+ if (event.data === "pong") {
180
+ // 标记此socket收到pong响应
181
+ socket.pongReceived = true;
182
+ return;
183
+ }
184
+ if (event.data instanceof ArrayBuffer) {
185
+ const view = new DataView(event.data);
186
+ const eventType = view.getUint32(0);
187
+
188
+ let imageMime;
189
+ switch (eventType) {
190
+ case 3:
191
+ const decoder = new TextDecoder();
192
+ const data = event.data.slice(4);
193
+ const nodeIdLength = view.getUint32(4);
194
+ dispatchCustomEvent("progress_text", {
195
+ nodeId: decoder.decode(data.slice(4, 4 + nodeIdLength)),
196
+ text: decoder.decode(data.slice(4 + nodeIdLength)),
197
+ });
198
+ break;
199
+ case 1:
200
+ const imageType = view.getUint32(4);
201
+ const imageData = event.data.slice(8);
202
+ switch (imageType) {
203
+ case 2:
204
+ imageMime = "image/png";
205
+ break;
206
+ case 1:
207
+ default:
208
+ imageMime = "image/jpeg";
209
+ break;
283
210
  }
284
- // 停止心跳
285
- self.stopPing();
286
- };
287
-
288
- socket.registeredTypes = new Set();
289
- socket.reportedUnknownMessageTypes = new Set();
290
-
291
- // 返回创建的socket,但不要立即使用,等待onopen
292
- return socket;
293
- } catch (error) {
294
- console.error('创建WebSocket连接失败:', error);
295
- this.isConnecting = false;
296
- return null;
297
- }
298
- },
299
-
300
- /**
301
- * 获取可用的socket连接,如果不存在则创建
302
- * 返回Promise以确保连接已就绪
303
- */
304
- async getSocketAsync(customUrl) {
305
- return new Promise((resolve, reject) => {
306
- // 如果已有可用连接,直接返回
307
- if (this.socket && this.socket.readyState === WebSocket.OPEN) {
308
- resolve(this.socket);
309
- return;
211
+ const imageBlob = new Blob([imageData], {
212
+ type: imageMime,
213
+ });
214
+ dispatchCustomEvent("b_preview", imageBlob);
215
+ break;
216
+ case 4:
217
+ // PREVIEW_IMAGE_WITH_METADATA
218
+ const decoder4 = new TextDecoder();
219
+ const metadataLength = view.getUint32(4);
220
+ const metadataBytes = event.data.slice(8, 8 + metadataLength);
221
+ const metadata = JSON.parse(decoder4.decode(metadataBytes));
222
+ const imageData4 = event.data.slice(8 + metadataLength);
223
+
224
+ let imageMime4 = metadata.image_type;
225
+
226
+ const imageBlob4 = new Blob([imageData4], {
227
+ type: imageMime4,
228
+ });
229
+
230
+ // Dispatch enhanced preview event with metadata
231
+ dispatchCustomEvent("b_preview_with_metadata", {
232
+ blob: imageBlob4,
233
+ nodeId: metadata.node_id,
234
+ displayNodeId: metadata.display_node_id,
235
+ parentNodeId: metadata.parent_node_id,
236
+ realNodeId: metadata.real_node_id,
237
+ promptId: metadata.prompt_id,
238
+ });
239
+
240
+ // Also dispatch legacy b_preview for backward compatibility
241
+ dispatchCustomEvent("b_preview", imageBlob4);
242
+ break;
243
+ default:
244
+ throw new Error(
245
+ `Unknown binary websocket message of type ${eventType}`
246
+ );
310
247
  }
311
-
312
- // 如果连接正在创建中,等待一段时间后检查
313
- if (this.isConnecting) {
314
- console.log('WebSocket连接创建中,等待...');
315
- const checkInterval = setInterval(() => {
316
- if (this.socket && this.socket.readyState === WebSocket.OPEN) {
317
- clearInterval(checkInterval);
318
- resolve(this.socket);
319
- } else if (!this.isConnecting) {
320
- clearInterval(checkInterval);
321
- reject(new Error('WebSocket连接创建失败'));
322
- }
323
- }, 100); // 每100ms检查一次
324
- return;
248
+ } else {
249
+ // 检测[DONE]消息
250
+ if (event.data === "[DONE]") {
251
+ console.log("收到[DONE]消息,任务已完成,停止心跳并关闭连接");
252
+ self.taskRunning = false;
253
+ self.stopPingForSocket(socket);
254
+ if (socket.readyState === WebSocket.OPEN) {
255
+ socket.close(1000);
256
+ }
257
+ return;
325
258
  }
326
-
327
- // 创建新连接
328
- const socket = this.createSocket(customUrl);
329
- if (!socket) {
330
- reject(new Error('创建WebSocket连接失败'));
331
- return;
259
+ const msg = JSON.parse(event.data);
260
+ // 发送进度信息,添加从 URL 中提取的 taskId
261
+ if (msg.progress_info) {
262
+ const progressData = { ...msg.progress_info };
263
+ if (taskIdFromUrl && !progressData.task_id) {
264
+ progressData.task_id = taskIdFromUrl;
265
+ }
266
+ window.parent.postMessage(
267
+ {
268
+ type: "functionResult",
269
+ method: "progress_info_change",
270
+ result: progressData,
271
+ },
272
+ "*"
273
+ );
332
274
  }
333
275
 
334
- // 监听连接打开事件
335
- socket.addEventListener('open', () => {
336
- resolve(socket);
337
- });
338
-
339
- // 监听错误事件
340
- socket.addEventListener('error', (error) => {
341
- reject(error);
342
- });
343
- });
344
- },
345
-
346
- /**
347
- * 获取可用的socket连接,如果不存在则创建
348
- * 同步版本,可能返回尚未就绪的连接
349
- */
350
- getSocket(customUrl) {
351
- // 如果已有可用连接,直接返回
352
- if (this.socket && this.socket.readyState === WebSocket.OPEN) {
353
- return this.socket;
354
- }
355
-
356
- // 创建新连接
357
- return this.createSocket(customUrl);
358
- },
359
-
360
- /**
361
- * 关闭socket连接
362
- * @param {number} code - 关闭状态码
363
- */
364
- closeSocket(code) {
365
- // 先停止心跳
366
- this.stopPing();
367
-
368
- if (this.socket) {
369
- if (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING) {
370
- console.log('关闭WebSocket连接');
371
- this.socket.close(code);
372
- }
373
- this.socket = null;
374
- }
375
-
376
- // 重置任务状态
377
- this.taskRunning = false;
378
-
379
- return true;
380
- },
381
-
382
- /**
383
- * 更改socket URL并创建新连接
384
- */
385
- changeSocketUrl(newUrl) {
386
- const clientId = sessionStorage.getItem("clientId");
387
- const fullUrl = newUrl + "?clientId=" + clientId + "&a=1";
388
-
389
- return this.createSocket(fullUrl);
390
- },
391
-
392
- /**
393
- * 发送socket消息
394
- * 确保连接已就绪
395
- */
396
- async sendSocketMessage(message) {
397
- try {
398
- const socket = await this.getSocketAsync();
399
- if (socket && socket.readyState === WebSocket.OPEN) {
400
- socket.send(typeof message === 'string' ? message : JSON.stringify(message));
401
- return true;
276
+ switch (msg.type) {
277
+ case "load_start":
278
+ case "load_end":
279
+ case "prompt_id":
280
+ // 发送准备状态信息,添加从 URL 中提取的 taskId
281
+ const preparingData = { ...msg };
282
+ if (taskIdFromUrl) {
283
+ preparingData.task_id = taskIdFromUrl;
284
+ console.log(
285
+ `🔗 [WebSocket] 添加 task_id=${taskIdFromUrl} 到 ${msg.type} 消息`
286
+ );
287
+ }
288
+ window.parent.postMessage(
289
+ {
290
+ type: "functionResult",
291
+ method: "preparingStatus",
292
+ result: preparingData,
293
+ },
294
+ "*"
295
+ );
296
+ break;
297
+ case "status":
298
+ if (msg.data.sid) {
299
+ const clientId = msg.data.sid;
300
+ window.name = clientId; // use window name so it isnt reused when duplicating tabs
301
+ sessionStorage.setItem("clientId", clientId); // store in session storage so duplicate tab can load correct workflow
302
+ }
303
+ dispatchCustomEvent("status", msg.data.status ?? null);
304
+ break;
305
+ case "executing":
306
+ dispatchCustomEvent(
307
+ "executing",
308
+ msg.data.display_node || msg.data.node
309
+ );
310
+ break;
311
+ case "execution_start":
312
+ case "execution_error":
313
+ case "execution_interrupted":
314
+ case "execution_cached":
315
+ case "execution_success":
316
+ case "progress":
317
+ case "progress_state":
318
+ case "executed":
319
+ case "graphChanged":
320
+ case "promptQueued":
321
+ case "logs":
322
+ case "b_preview":
323
+ if (msg.data.balance_not_enough) {
324
+ window.parent.postMessage(
325
+ {
326
+ type: "functionResult",
327
+ method: "balanceNotEnough",
328
+ result: true,
329
+ },
330
+ "*"
331
+ );
332
+ }
333
+ dispatchCustomEvent(msg.type, msg.data);
334
+ break;
335
+ case "feature_flags":
336
+ // Store server feature flags
337
+ this.serverFeatureFlags = msg.data;
338
+ console.log(
339
+ "Server feature flags received:",
340
+ this.serverFeatureFlags
341
+ );
342
+ break;
343
+ default:
344
+ const registeredTypes = socket.registeredTypes || new Set();
345
+ const reportedUnknownMessageTypes =
346
+ socket.reportedUnknownMessageTypes || new Set();
347
+
348
+ if (registeredTypes.has(msg.type)) {
349
+ app.dispatchEvent(
350
+ new CustomEvent(msg.type, { detail: msg.data })
351
+ );
352
+ } else if (!reportedUnknownMessageTypes.has(msg.type)) {
353
+ reportedUnknownMessageTypes.add(msg.type);
354
+ console.warn(`Unknown message type ${msg.type}`);
355
+ }
402
356
  }
403
- return false;
357
+ }
404
358
  } catch (error) {
405
- console.error('发送消息失败:', error);
406
- return false;
359
+ console.warn("Unhandled message:", event.data, error);
407
360
  }
408
- },
409
-
410
- /**
411
- * 发送任务提示
412
- */
413
- async sendPrompt(prompt) {
414
- try {
415
- // 确保有连接
416
- await this.getSocketAsync();
417
- // 发送提示
418
- app.queuePrompt(prompt);
419
- return true;
420
- } catch (error) {
421
- console.error('发送任务提示失败:', error);
422
- return false;
361
+ };
362
+
363
+ socket.onerror = function (error) {
364
+ console.log("WebSocket 错误:", error);
365
+ // 清除连接中标志
366
+ self.isConnecting = false;
367
+ // 停止此socket的心跳检测
368
+ self.stopPingForSocket(socket);
369
+ };
370
+
371
+ socket.onclose = function (event) {
372
+ console.log("WebSocket 连接已关闭, 状态码:", event.code, event.reason);
373
+ // 清除连接中标志
374
+ self.isConnecting = false;
375
+ // 停止此socket的心跳检测
376
+ self.stopPingForSocket(socket);
377
+ // 清理单例引用(如果这是当前活跃的socket)
378
+ if (self.socket === socket) {
379
+ self.socket = null;
423
380
  }
424
- },
425
-
426
- getCookie(name) {
427
- const value = `; ${document.cookie}`;
428
- const parts = value.split(`; ${name}=`);
429
- if (parts.length === 2) return parts.pop().split(';').shift();
430
- },
431
-
432
- async setup() {
433
- const createSocket = this.createSocket.bind(this);
434
- const closeSocket = this.closeSocket.bind(this);
435
-
436
- const customErrorStyles = new Map()
381
+ };
437
382
 
383
+ socket.registeredTypes = new Set();
384
+ socket.reportedUnknownMessageTypes = new Set();
438
385
 
386
+ // 返回创建的socket,但不要立即使用,等待onopen
387
+ return socket;
388
+ } catch (error) {
389
+ console.error("创建WebSocket连接失败:", error);
390
+ this.isConnecting = false;
391
+ return null;
392
+ }
393
+ },
394
+
395
+ /**
396
+ * 获取可用的socket连接,如果不存在则创建
397
+ * 返回Promise以确保连接已就绪
398
+ */
399
+ async getSocketAsync(customUrl) {
400
+ return new Promise((resolve, reject) => {
401
+ // 如果已有可用连接,直接返回
402
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
403
+ resolve(this.socket);
404
+ return;
405
+ }
406
+
407
+ // 如果连接正在创建中,等待一段时间后检查
408
+ if (this.isConnecting) {
409
+ console.log("WebSocket连接创建中,等待...");
410
+ const checkInterval = setInterval(() => {
411
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
412
+ clearInterval(checkInterval);
413
+ resolve(this.socket);
414
+ } else if (!this.isConnecting) {
415
+ clearInterval(checkInterval);
416
+ reject(new Error("WebSocket连接创建失败"));
417
+ }
418
+ }, 100); // 每100ms检查一次
419
+ return;
420
+ }
421
+
422
+ // 创建新连接
423
+ const socket = this.createSocket(customUrl);
424
+ if (!socket) {
425
+ reject(new Error("创建WebSocket连接失败"));
426
+ return;
427
+ }
428
+
429
+ // 监听连接打开事件
430
+ socket.addEventListener("open", () => {
431
+ resolve(socket);
432
+ });
433
+
434
+ // 监听错误事件
435
+ socket.addEventListener("error", (error) => {
436
+ reject(error);
437
+ });
438
+ });
439
+ },
440
+
441
+ /**
442
+ * 获取可用的socket连接,如果不存在则创建
443
+ * 同步版本,可能返回尚未就绪的连接
444
+ */
445
+ getSocket(customUrl) {
446
+ // 如果已有可用连接,直接返回
447
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
448
+ return this.socket;
449
+ }
439
450
 
440
- // 方法映射
441
- const methods = {
442
- customSocket: async function (params) {
443
- const socket = createSocket(params.url);
444
- window.parent.postMessage({
445
- type: 'functionResult',
446
- method: 'customSocket',
447
- result: '自定义socket执行结果'
448
- }, '*');
449
- return socket;
450
- },
451
-
452
- closeSocket: function () {
453
- const result = closeSocket();
454
- window.parent.postMessage({
455
- type: 'functionResult',
456
- method: 'closeSocket',
457
- result: result ? 'Socket连接已关闭' : 'Socket连接关闭失败或已关闭'
458
- }, '*');
459
- return result;
460
- },
461
-
462
- clearCanvas: function () {
463
- app.graph.clear();
464
- window.parent.postMessage({
465
- type: 'functionResult',
466
- method: 'clearCanvas',
467
- result: true
468
- }, '*');
469
- return true;
470
- },
471
- loadWorkflow: function (params) {
472
- app.graph.clear();
473
- document.dispatchEvent(new CustomEvent('workflowLoaded', {
474
- detail: params.json
475
- }));
476
- if (params.json.version) {
477
- app.loadGraphData(params.json);
478
- } else {
479
- app.loadApiJson(params.json, 'bizyair');
480
- }
481
- console.log("-----------loadWorkflow-----------", params.json)
482
- window.parent.postMessage({
483
- type: 'functionResult',
484
- method: 'loadWorkflow',
485
- result: true
486
- }, '*');
487
- return true;
488
- },
489
-
490
- saveWorkflow: async function () {
491
- const graph = await app.graphToPrompt();
492
- window.parent.postMessage({
493
- type: 'functionResult',
494
- method: 'saveWorkflow',
495
- result: graph.workflow
496
- }, '*');
497
- return graph.workflow;
498
- },
499
- getWorkflow: async function () {
500
- const graph = await app.graphToPrompt();
501
- window.parent.postMessage({
502
- type: 'functionResult',
503
- method: 'getWorkflow',
504
- result: graph.workflow
505
- }, '*');
506
- return graph.workflow;
507
- },
508
- getWorkflowNotSave: async function () {
509
- const graph = await app.graphToPrompt();
510
- // 规范化工作流,移除不影响逻辑的视觉字段,避免颜色等样式变化影响校验
511
- const normalizeWorkflow = (workflow) => {
512
- const json = JSON.stringify(workflow, (key, value) => {
513
- if (key === 'color' || key === 'bgcolor' || key === 'extra') return undefined;
514
- return value;
515
- });
516
- return JSON.parse(json);
517
- };
518
- const normalized = normalizeWorkflow(graph.workflow);
519
- window.parent.postMessage({
520
- type: 'functionResult',
521
- method: 'getWorkflowNotSave',
522
- result: normalized
523
- }, '*');
524
- return normalized;
525
- },
526
- // 新增:获取 workflow 和 output
527
- getWorkflowWithOutput: async function () {
528
- const graph = await app.graphToPrompt();
529
- // graph.workflow.nodes.forEach(node => {
530
- // for (const key in graph.output) {
531
- // if (graph.output[key].class_type === node.type) {
532
- // if (!graph.output[key]._meta) {
533
- // graph.output[key]._meta = {};
534
- // }
535
- // graph.output[key]._meta.id = node.id;
536
- // break; // 找到匹配的就跳出循环
537
- // }
538
- // }
539
- // });
540
- for (const key in graph.output) {
541
- graph.output[key]._meta.id = Number(key);
542
- }
543
- window.parent.postMessage({
544
- type: 'functionResult',
545
- method: 'getWorkflowWithOutput',
546
- result: {
547
- workflow: graph.workflow,
548
- output: graph.output
549
- }
550
- }, '*');
551
- return { workflow: graph.workflow, output: graph.output };
552
- },
553
- saveApiJson: async function (params) {
554
- const graph = await app.graphToPrompt();
555
- window.parent.postMessage({
556
- type: 'functionResult',
557
- method: 'saveApiJson',
558
- result: graph.output
559
- }, '*');
560
- return graph.output;
561
- },
562
- getClientId: function () {
563
- const clientId = sessionStorage.getItem("clientId");
564
- window.parent.postMessage({
565
- type: 'functionResult',
566
- method: 'getClientId',
567
- result: clientId
568
- }, '*');
569
- return clientId;
570
- },
571
- runWorkflow: async function () {
572
- try {
573
- // 确保有连接
574
- // await getSocketAsync();
575
- const graph = await app.graphToPrompt();
576
- const clientId = sessionStorage.getItem("clientId");
577
- await app.queuePrompt(graph.output);
578
- const resPrompt = await fetch("api/prompt", {
579
- method: "POST",
580
- body: JSON.stringify({
581
- prompt: graph.output,
582
- clientId,
583
- number: graph.output,
584
- extra_data: {
585
- extra_pnginfo: {
586
- workflow: graph.workflow
587
- }
588
- }
589
- })
590
- });
591
- const resPromptJson = await resPrompt.json();
592
- if (resPromptJson.error) {
593
- this.openCustomError({
594
- nodeId: resPromptJson.node_id,
595
- nodeType: resPromptJson.node_type,
596
- errorMessage: resPromptJson.details,
597
- borderColor: '#FF0000'
598
- })
599
- return
600
- }
601
-
602
- for (const i in resPromptJson.node_errors) {
603
-
604
- if (resPromptJson.node_errors[i].errors) {
605
- const err = resPromptJson.node_errors[i].errors[0]
606
- if (err) {
607
- this.openCustomError({
608
- nodeId: i,
609
- nodeType: err.type,
610
- errorMessage: err.details,
611
- borderColor: '#FF0000'
612
- })
613
- return
614
- }
615
- } else {
616
- console.log(resPromptJson.node_errors[i])
617
- }
618
- }
619
-
620
- if (Object.keys(resPromptJson.node_errors).length) return
621
- // graph.workflow.nodes.forEach(node => {
622
- // for (const key in graph.output) {
623
- // if (graph.output[key].class_type === node.type) {
624
- // if (!graph.output[key]._meta) {
625
- // graph.output[key]._meta = {};
626
- // }
627
- // graph.output[key]._meta.id = node.id;
628
- // console.log(graph.output[key].class_type, node.type, graph.output[key]._meta.id)
629
- // break; // 找到匹配的就跳出循环
630
- // }
631
- // }
632
- // });
633
- for (const key in graph.output) {
634
- graph.output[key]._meta.id = Number(key);
635
- }
636
-
637
- for (let i in graph.output) {
638
- if (graph.output[i].class_type == 'LoadImage') {
639
- graph.output[i].inputs.image = graph.output[i].inputs.image.replace('pasted/http', 'http')
640
- }
641
- }
642
- console.log(graph.output)
643
- window.parent.postMessage({
644
- type: 'functionResult',
645
- method: 'runWorkflow',
646
- result: {
647
- clientId: clientId,
648
- jsonWorkflow: graph.output,
649
- workflow: graph.workflow,
650
- prompt: resPromptJson
651
- }
652
- }, '*');
653
- return true;
654
- } catch (error) {
655
- console.error('运行工作流失败:', error);
656
- window.parent.postMessage({
657
- type: 'functionResult',
658
- method: 'runWorkflow',
659
- error: '运行工作流失败: ' + error.message,
660
- success: false
661
- }, '*');
662
- return false;
663
- }
664
- },
665
- setCookie: function (params) {
666
- const setCookie = (name, value, days) => {
667
- let expires = "";
668
- if (days) {
669
- const date = new Date();
670
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
671
- expires = "; expires=" + date.toUTCString();
672
- }
673
- document.cookie = name + "=" + (value || "") + expires + "; path=/";
674
- };
675
- // console.log("-----------setCookie-----------", params)
676
- // console.log("-----------setCookie-----------", params)
677
- setCookie(params.name, params.value, params.days);
678
-
679
-
680
- return true;
681
- },
682
- fitView: function () {
683
- app.canvas.fitViewToSelectionAnimated()
684
- window.parent.postMessage({
685
- type: 'functionResult',
686
- method: 'fitView',
687
- result: true
688
- }, '*');
689
- return true;
690
- },
691
- clickAssistant: function () {
692
- const assistantBtn = document.querySelector('.btn-assistant');
693
- if (assistantBtn) {
694
- assistantBtn.click();
695
- window.parent.postMessage({
696
- type: 'functionResult',
697
- method: 'clickAssistant',
698
- result: true
699
- }, '*');
700
- return true;
701
- } else {
702
- console.warn('Assistant button not found');
703
- window.parent.postMessage({
704
- type: 'functionResult',
705
- method: 'clickAssistant',
706
- result: false
707
- }, '*');
708
- return false;
709
- }
710
- },
711
- clickCommunity: function () {
712
- const communityBtn = document.querySelector('.btn-community');
713
- if (communityBtn) {
714
- communityBtn.click();
715
- window.parent.postMessage({
716
- type: 'functionResult',
717
- method: 'clickCommunity',
718
- result: true
719
- }, '*');
720
- return true;
721
- } else {
722
- window.parent.postMessage({
723
- type: 'functionResult',
724
- method: 'clickCommunity',
725
- result: false
726
- }, '*');
727
- return false;
728
- }
729
- },
730
- toPublish: async function () {
731
- const graph = await app.graphToPrompt();
732
- window.parent.postMessage({
733
- type: 'functionResult',
734
- method: 'toPublish',
735
- result: graph.workflow
736
- }, '*');
737
- return graph.workflow;
738
- },
739
-
740
- graphToPrompt: async function (params) {
741
- console.log('postEvent.js - graphToPrompt被调用,参数:', params);
742
- const graph = await app.graphToPrompt();
743
- window.parent.postMessage({
744
- type: 'functionResult',
745
- method: 'graphToPrompt',
746
- params: params, // 传递原始参数
747
- result: {
748
- workflow: graph.workflow,
749
- output: graph.output
750
- }
751
- }, '*');
752
- return {
753
- workflow: graph.workflow,
754
- output: graph.output
755
- };
756
- },
757
- loadGraphData: function (params) {
758
- const { json, clear = true, center = false, workflow_name = "" } = params;
759
- if (clear) {
760
- app.graph.clear();
761
- }
762
- app.loadGraphData(json, clear, center, workflow_name);
763
- window.parent.postMessage({
764
- type: 'functionResult',
765
- method: 'loadGraphData',
766
- result: true
767
- }, '*');
768
- return true;
769
- },
451
+ // 创建新连接
452
+ return this.createSocket(customUrl);
453
+ },
454
+
455
+ /**
456
+ * 关闭socket连接
457
+ * @param {number} code - 关闭状态码
458
+ */
459
+ closeSocket(code) {
460
+ // 先停止心跳
461
+ this.stopPing();
462
+
463
+ if (this.socket) {
464
+ if (
465
+ this.socket.readyState === WebSocket.OPEN ||
466
+ this.socket.readyState === WebSocket.CONNECTING
467
+ ) {
468
+ console.log("关闭WebSocket连接");
469
+ this.socket.close(code);
470
+ }
471
+ this.socket = null;
472
+ }
770
473
 
771
- // AI应用相关方法
772
- toggleAIAppMode: function(params) {
773
- const enable = params.enable === true;
774
- const result = enable ? enableAIAppMode() : disableAIAppMode();
775
- window.parent.postMessage({
776
- type: 'functionResult',
777
- method: 'toggleAIAppMode',
778
- result: result
779
- }, '*');
780
- return result;
474
+ // 重置任务状态
475
+ this.taskRunning = false;
476
+
477
+ return true;
478
+ },
479
+
480
+ /**
481
+ * 更改socket URL并创建新连接
482
+ */
483
+ changeSocketUrl(newUrl) {
484
+ const clientId = sessionStorage.getItem("clientId");
485
+ const fullUrl = newUrl + "?clientId=" + clientId + "&a=1";
486
+
487
+ return this.createSocket(fullUrl);
488
+ },
489
+
490
+ /**
491
+ * 发送socket消息
492
+ * 确保连接已就绪
493
+ */
494
+ async sendSocketMessage(message) {
495
+ try {
496
+ const socket = await this.getSocketAsync();
497
+ if (socket && socket.readyState === WebSocket.OPEN) {
498
+ socket.send(
499
+ typeof message === "string" ? message : JSON.stringify(message)
500
+ );
501
+ return true;
502
+ }
503
+ return false;
504
+ } catch (error) {
505
+ console.error("发送消息失败:", error);
506
+ return false;
507
+ }
508
+ },
509
+
510
+ /**
511
+ * 发送任务提示
512
+ */
513
+ async sendPrompt(prompt) {
514
+ try {
515
+ // 确保有连接
516
+ await this.getSocketAsync();
517
+ // 发送提示
518
+ app.queuePrompt(prompt);
519
+ return true;
520
+ } catch (error) {
521
+ console.error("发送任务提示失败:", error);
522
+ return false;
523
+ }
524
+ },
525
+
526
+ getCookie(name) {
527
+ const value = `; ${document.cookie}`;
528
+ const parts = value.split(`; ${name}=`);
529
+ if (parts.length === 2) return parts.pop().split(";").shift();
530
+ },
531
+
532
+ async setup() {
533
+ const createSocket = this.createSocket.bind(this);
534
+ const closeSocket = this.closeSocket.bind(this);
535
+
536
+ const customErrorStyles = new Map();
537
+
538
+ // 用于节流的时间戳
539
+ let lastRunWorkflowTime = 0;
540
+ const THROTTLE_TIME = 2000; // 2秒
541
+
542
+ // 方法映射
543
+ const methods = {
544
+ customSocket: async function (params) {
545
+ const socket = createSocket(params.url);
546
+ window.parent.postMessage(
547
+ {
548
+ type: "functionResult",
549
+ method: "customSocket",
550
+ result: "自定义socket执行结果",
551
+ },
552
+ "*"
553
+ );
554
+ return socket;
555
+ },
556
+
557
+ closeSocket: function () {
558
+ const result = closeSocket();
559
+ window.parent.postMessage(
560
+ {
561
+ type: "functionResult",
562
+ method: "closeSocket",
563
+ result: result ? "Socket连接已关闭" : "Socket连接关闭失败或已关闭",
564
+ },
565
+ "*"
566
+ );
567
+ return result;
568
+ },
569
+
570
+ clearCanvas: function () {
571
+ app.graph.clear();
572
+ window.parent.postMessage(
573
+ {
574
+ type: "functionResult",
575
+ method: "clearCanvas",
576
+ result: true,
577
+ },
578
+ "*"
579
+ );
580
+ return true;
581
+ },
582
+ loadWorkflow: function (params) {
583
+ app.graph.clear();
584
+ document.dispatchEvent(
585
+ new CustomEvent("workflowLoaded", {
586
+ detail: params.json,
587
+ })
588
+ );
589
+ if (params.json.version) {
590
+ app.loadGraphData(params.json);
591
+ } else {
592
+ app.loadApiJson(params.json, "bizyair");
593
+ }
594
+ console.log("-----------loadWorkflow-----------", params.json);
595
+ window.parent.postMessage(
596
+ {
597
+ type: "functionResult",
598
+ method: "loadWorkflow",
599
+ result: true,
600
+ },
601
+ "*"
602
+ );
603
+ return true;
604
+ },
605
+
606
+ saveWorkflow: async function () {
607
+ const graph = await app.graphToPrompt();
608
+ window.parent.postMessage(
609
+ {
610
+ type: "functionResult",
611
+ method: "saveWorkflow",
612
+ result: graph.workflow,
613
+ },
614
+ "*"
615
+ );
616
+ return graph.workflow;
617
+ },
618
+ getWorkflow: async function () {
619
+ const graph = await app.graphToPrompt();
620
+ window.parent.postMessage(
621
+ {
622
+ type: "functionResult",
623
+ method: "getWorkflow",
624
+ result: graph.workflow,
625
+ },
626
+ "*"
627
+ );
628
+ return graph.workflow;
629
+ },
630
+ getWorkflowNotSave: async function () {
631
+ const graph = await app.graphToPrompt();
632
+ // 规范化工作流,移除不影响逻辑的视觉字段,避免颜色等样式变化影响校验
633
+ const normalizeWorkflow = (workflow) => {
634
+ const json = JSON.stringify(workflow, (key, value) => {
635
+ if (key === "color" || key === "bgcolor" || key === "extra")
636
+ return undefined;
637
+ return value;
638
+ });
639
+ return JSON.parse(json);
640
+ };
641
+ const normalized = normalizeWorkflow(graph.workflow);
642
+ window.parent.postMessage(
643
+ {
644
+ type: "functionResult",
645
+ method: "getWorkflowNotSave",
646
+ result: normalized,
647
+ },
648
+ "*"
649
+ );
650
+ return normalized;
651
+ },
652
+ // 新增:获取 workflow 和 output
653
+ getWorkflowWithOutput: async function () {
654
+ const graph = await app.graphToPrompt();
655
+ for (const key in graph.output) {
656
+ graph.output[key]._meta.id = Number(key);
657
+ graph.output[key]._meta.class_type = graph.output[key].class_type;
658
+ }
659
+ window.parent.postMessage(
660
+ {
661
+ type: "functionResult",
662
+ method: "getWorkflowWithOutput",
663
+ result: {
664
+ workflow: graph.workflow,
665
+ output: graph.output,
781
666
  },
782
-
783
- selectInputNode: function(params) {
784
- if (!params.nodeId) return false;
785
- const result = selectInputNode(params.nodeId);
786
- window.parent.postMessage({
787
- type: 'functionResult',
788
- method: 'selectInputNode',
789
- result: result
790
- }, '*');
791
- return result;
667
+ },
668
+ "*"
669
+ );
670
+ return { workflow: graph.workflow, output: graph.output };
671
+ },
672
+ saveApiJson: async function (params) {
673
+ const graph = await app.graphToPrompt();
674
+ window.parent.postMessage(
675
+ {
676
+ type: "functionResult",
677
+ method: "saveApiJson",
678
+ result: graph.output,
679
+ },
680
+ "*"
681
+ );
682
+ return graph.output;
683
+ },
684
+ getClientId: function () {
685
+ const clientId = sessionStorage.getItem("clientId");
686
+ window.parent.postMessage(
687
+ {
688
+ type: "functionResult",
689
+ method: "getClientId",
690
+ result: clientId,
691
+ },
692
+ "*"
693
+ );
694
+ return clientId;
695
+ },
696
+ runWorkflow: async function () {
697
+ try {
698
+ // 确保有连接
699
+ // await getSocketAsync();
700
+
701
+ const graph = await app.graphToPrompt();
702
+ const clientId = sessionStorage.getItem("clientId");
703
+ await app.queuePrompt(graph.output);
704
+ const resPrompt = await fetch("api/prompt", {
705
+ method: "POST",
706
+ body: JSON.stringify({
707
+ prompt: graph.output,
708
+ clientId,
709
+ number: graph.output,
710
+ extra_data: {
711
+ extra_pnginfo: {
712
+ workflow: graph.workflow,
713
+ },
714
+ },
715
+ }),
716
+ });
717
+ const resPromptJson = await resPrompt.json();
718
+ if (resPromptJson.error && resPromptJson.node_id) {
719
+ this.openCustomError({
720
+ nodeId: resPromptJson.node_id,
721
+ nodeType: resPromptJson.node_type,
722
+ errorMessage: resPromptJson.details,
723
+ borderColor: "#FF0000",
724
+ });
725
+ return;
726
+ }
727
+
728
+ for (const i in resPromptJson.node_errors) {
729
+ if (resPromptJson.node_errors[i].errors) {
730
+ const err = resPromptJson.node_errors[i].errors[0];
731
+ if (err) {
732
+ this.openCustomError({
733
+ nodeId: i,
734
+ nodeType: err.type,
735
+ errorMessage: err.details,
736
+ borderColor: "#FF0000",
737
+ });
738
+ return;
739
+ }
740
+ } else {
741
+ console.log(resPromptJson.node_errors[i]);
742
+ }
743
+ }
744
+
745
+ if (Object.keys(resPromptJson.node_errors).length) return;
746
+ for (const key in graph.output) {
747
+ graph.output[key]._meta.id = Number(key);
748
+ graph.output[key]._meta.class_type = graph.output[key].class_type;
749
+ }
750
+ for (let i in graph.output) {
751
+ if (graph.output[i].class_type == "LoadImage") {
752
+ graph.output[i].inputs.image = graph.output[
753
+ i
754
+ ].inputs.image.replace("pasted/http", "http");
755
+ }
756
+ }
757
+ console.log(graph.output);
758
+ window.parent.postMessage(
759
+ {
760
+ type: "functionResult",
761
+ method: "runWorkflow",
762
+ result: {
763
+ clientId: clientId,
764
+ jsonWorkflow: graph.output,
765
+ workflow: graph.workflow,
766
+ prompt: resPromptJson,
767
+ },
792
768
  },
793
-
794
- selectExportNode: function(params) {
795
- if (!params.nodeId) return false;
796
- const result = selectInputNode(params.nodeId);
797
- window.parent.postMessage({
798
- type: 'functionResult',
799
- method: 'selectExportNode',
800
- result: result
801
- }, '*');
802
- return result;
769
+ "*"
770
+ );
771
+ return true;
772
+ } catch (error) {
773
+ console.error("运行工作流失败:", error);
774
+ window.parent.postMessage(
775
+ {
776
+ type: "functionResult",
777
+ method: "runWorkflow",
778
+ error: "运行工作流失败: " + error.message,
779
+ success: false,
803
780
  },
781
+ "*"
782
+ );
783
+ return false;
784
+ }
785
+ },
786
+ setCookie: function (params) {
787
+ const setCookie = (name, value, days) => {
788
+ let expires = "";
789
+ if (days) {
790
+ const date = new Date();
791
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
792
+ expires = "; expires=" + date.toUTCString();
793
+ }
794
+ document.cookie = name + "=" + (value || "") + expires + "; path=/";
795
+ };
796
+ // console.log("-----------setCookie-----------", params)
797
+ // console.log("-----------setCookie-----------", params)
798
+ setCookie(params.name, params.value, params.days);
804
799
 
805
- deselectInputNode: function(params) {
806
- if (!params.nodeId) return false;
807
- const result = deselectInputNode(params.nodeId);
808
- window.parent.postMessage({
809
- type: 'functionResult',
810
- method: 'deselectInputNode',
811
- result: result
812
- }, '*');
813
- return result;
800
+ return true;
801
+ },
802
+ removeCookie: function (params) {
803
+ const expires = new Date(0).toUTCString();
804
+ document.cookie = params.name + "=; expires=" + expires + "; path=/";
805
+ return true;
806
+ },
807
+ fitView: function () {
808
+ app.canvas.fitViewToSelectionAnimated();
809
+ window.parent.postMessage(
810
+ {
811
+ type: "functionResult",
812
+ method: "fitView",
813
+ result: true,
814
+ },
815
+ "*"
816
+ );
817
+ return true;
818
+ },
819
+ clickAssistant: function () {
820
+ const assistantBtn = document.querySelector(".btn-assistant");
821
+ if (assistantBtn) {
822
+ assistantBtn.click();
823
+ window.parent.postMessage(
824
+ {
825
+ type: "functionResult",
826
+ method: "clickAssistant",
827
+ result: true,
814
828
  },
815
-
816
- updateInputNodeWidget: function(params) {
817
- if (!params.nodeId || params.widgetName === undefined) return false;
818
- const result = updateInputNodeWidget(params.nodeId, params.widgetName, params.value);
819
- window.parent.postMessage({
820
- type: 'functionResult',
821
- method: 'updateInputNodeWidget',
822
- result: result
823
- }, '*');
824
- return result;
829
+ "*"
830
+ );
831
+ return true;
832
+ } else {
833
+ console.warn("Assistant button not found");
834
+ window.parent.postMessage(
835
+ {
836
+ type: "functionResult",
837
+ method: "clickAssistant",
838
+ result: false,
825
839
  },
826
-
827
- getInputNodes: function() {
828
- const result = getSelectedInputNodes();
829
- window.parent.postMessage({
830
- type: 'functionResult',
831
- method: 'getInputNodes',
832
- result: result
833
- }, '*');
834
- return result;
840
+ "*"
841
+ );
842
+ return false;
843
+ }
844
+ },
845
+ clickCommunity: function () {
846
+ const communityBtn = document.querySelector(".btn-community");
847
+ if (communityBtn) {
848
+ communityBtn.click();
849
+ window.parent.postMessage(
850
+ {
851
+ type: "functionResult",
852
+ method: "clickCommunity",
853
+ result: true,
835
854
  },
836
-
837
- clearInputNodes: function() {
838
- const result = clearSelectedInputNodes();
839
- window.parent.postMessage({
840
- type: 'functionResult',
841
- method: 'clearInputNodes',
842
- result: result
843
- }, '*');
844
- return result;
855
+ "*"
856
+ );
857
+ return true;
858
+ } else {
859
+ window.parent.postMessage(
860
+ {
861
+ type: "functionResult",
862
+ method: "clickCommunity",
863
+ result: false,
845
864
  },
846
- toggleExportMode: function(params) {
847
- const result = toggleExportMode(params);
848
- window.parent.postMessage({
849
- type: 'functionResult',
850
- method: 'toggleExportMode',
851
- result: result
852
- }, '*');
853
- return result;
865
+ "*"
866
+ );
867
+ return false;
868
+ }
869
+ },
870
+ toPublish: async function () {
871
+ const graph = await app.graphToPrompt();
872
+ window.parent.postMessage(
873
+ {
874
+ type: "functionResult",
875
+ method: "toPublish",
876
+ result: graph.workflow,
877
+ },
878
+ "*"
879
+ );
880
+ return graph.workflow;
881
+ },
882
+
883
+ graphToPrompt: async function (params) {
884
+ console.log("postEvent.js - graphToPrompt被调用,参数:", params);
885
+ const graph = await app.graphToPrompt();
886
+ window.parent.postMessage(
887
+ {
888
+ type: "functionResult",
889
+ method: "graphToPrompt",
890
+ params: params, // 传递原始参数
891
+ result: {
892
+ workflow: graph.workflow,
893
+ output: graph.output,
854
894
  },
855
- openCustomError: function (params) {
856
- const { nodeId, nodeType, errorMessage, borderColor='#FF0000' } = params;
857
- const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId];
858
- function injectErrorDialogStyles() {
859
- const styleId = 'custom-error-dialog-styles';
860
- if (document.getElementById(styleId)) {
861
- return; // 样式已经存在
862
- }
863
-
864
- const style = document.createElement('style');
865
- style.id = styleId;
866
- style.textContent = `
895
+ },
896
+ "*"
897
+ );
898
+ return {
899
+ workflow: graph.workflow,
900
+ output: graph.output,
901
+ };
902
+ },
903
+ loadGraphData: function (params) {
904
+ const {
905
+ json,
906
+ clear = true,
907
+ center = false,
908
+ workflow_name = "",
909
+ } = params;
910
+ if (clear) {
911
+ app.graph.clear();
912
+ }
913
+ app.loadGraphData(json, clear, center, workflow_name);
914
+ window.parent.postMessage(
915
+ {
916
+ type: "functionResult",
917
+ method: "loadGraphData",
918
+ result: true,
919
+ },
920
+ "*"
921
+ );
922
+ return true;
923
+ },
924
+
925
+ // AI应用相关方法
926
+ toggleAIAppMode: function (params) {
927
+ const enable = params.enable === true;
928
+ const result = enable ? enableAIAppMode() : disableAIAppMode();
929
+ window.parent.postMessage(
930
+ {
931
+ type: "functionResult",
932
+ method: "toggleAIAppMode",
933
+ result: result,
934
+ },
935
+ "*"
936
+ );
937
+ return result;
938
+ },
939
+
940
+ selectInputNode: function (params) {
941
+ if (!params.nodeId) return false;
942
+ const result = selectInputNode(params.nodeId);
943
+ window.parent.postMessage(
944
+ {
945
+ type: "functionResult",
946
+ method: "selectInputNode",
947
+ result: result,
948
+ },
949
+ "*"
950
+ );
951
+ return result;
952
+ },
953
+
954
+ selectExportNode: function (params) {
955
+ if (!params.nodeId) return false;
956
+ const result = selectInputNode(params.nodeId);
957
+ window.parent.postMessage(
958
+ {
959
+ type: "functionResult",
960
+ method: "selectExportNode",
961
+ result: result,
962
+ },
963
+ "*"
964
+ );
965
+ return result;
966
+ },
967
+
968
+ deselectInputNode: function (params) {
969
+ if (!params.nodeId) return false;
970
+ const result = deselectInputNode(params.nodeId);
971
+ window.parent.postMessage(
972
+ {
973
+ type: "functionResult",
974
+ method: "deselectInputNode",
975
+ result: result,
976
+ },
977
+ "*"
978
+ );
979
+ return result;
980
+ },
981
+
982
+ updateInputNodeWidget: function (params) {
983
+ if (!params.nodeId || params.widgetName === undefined) return false;
984
+ const result = updateInputNodeWidget(
985
+ params.nodeId,
986
+ params.widgetName,
987
+ params.value
988
+ );
989
+ window.parent.postMessage(
990
+ {
991
+ type: "functionResult",
992
+ method: "updateInputNodeWidget",
993
+ result: result,
994
+ },
995
+ "*"
996
+ );
997
+ return result;
998
+ },
999
+
1000
+ getInputNodes: function () {
1001
+ const result = getSelectedInputNodes();
1002
+ window.parent.postMessage(
1003
+ {
1004
+ type: "functionResult",
1005
+ method: "getInputNodes",
1006
+ result: result,
1007
+ },
1008
+ "*"
1009
+ );
1010
+ return result;
1011
+ },
1012
+
1013
+ clearInputNodes: function () {
1014
+ const result = clearSelectedInputNodes();
1015
+ window.parent.postMessage(
1016
+ {
1017
+ type: "functionResult",
1018
+ method: "clearInputNodes",
1019
+ result: result,
1020
+ },
1021
+ "*"
1022
+ );
1023
+ return result;
1024
+ },
1025
+ toggleExportMode: function (params) {
1026
+ const result = toggleExportMode(params);
1027
+ window.parent.postMessage(
1028
+ {
1029
+ type: "functionResult",
1030
+ method: "toggleExportMode",
1031
+ result: result,
1032
+ },
1033
+ "*"
1034
+ );
1035
+ return result;
1036
+ },
1037
+ openCustomError: function (params) {
1038
+ const {
1039
+ nodeId,
1040
+ nodeType,
1041
+ errorMessage,
1042
+ borderColor = "#FF0000",
1043
+ } = params;
1044
+ const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId];
1045
+ function injectErrorDialogStyles() {
1046
+ const styleId = "custom-error-dialog-styles";
1047
+ if (document.getElementById(styleId)) {
1048
+ return; // 样式已经存在
1049
+ }
1050
+
1051
+ const style = document.createElement("style");
1052
+ style.id = styleId;
1053
+ style.textContent = `
867
1054
  .comfy-error-report .no-results-placeholder p {
868
1055
  text-align: left;
869
1056
  }
870
1057
  `;
871
- document.head.appendChild(style);
872
- };
873
- injectErrorDialogStyles()
874
- function simulateExecutionError(nodeId, nodeType, errorMessage, borderColor) {
875
- // const originalNodeErrorStyle = node.strokeStyles?.['nodeError']
876
- const node = app.graph.getNodeById(nodeId)
877
- if (!node) return
878
- if (!customErrorStyles.has(nodeId)) {
879
- customErrorStyles.set(nodeId, {
880
- originalStyle: node.strokeStyles?.['nodeError'],
881
- customColor: borderColor,
882
- nodeId: nodeId
883
- })
884
- }
885
- node.strokeStyles = node.strokeStyles || {}
886
- node.strokeStyles['nodeError'] = function () {
887
- // if (this.id === nodeId) {
888
- return { color: borderColor, lineWidth: 2 } // 自定义颜色和线宽
889
- // }
890
- }
891
- const mockErrorEvent = {
892
- detail: {
893
- node_id: nodeId,
894
- node_type: nodeType,
895
- exception_message: errorMessage,
896
- exception_type: 'ManualError',
897
- traceback: ['Manual error triggered'],
898
- executed: [],
899
- prompt_id: 'manual',
900
- timestamp: Date.now()
901
- }
902
- }
1058
+ document.head.appendChild(style);
1059
+ }
1060
+ injectErrorDialogStyles();
1061
+ function simulateExecutionError(
1062
+ nodeId,
1063
+ nodeType,
1064
+ errorMessage,
1065
+ borderColor
1066
+ ) {
1067
+ // const originalNodeErrorStyle = node.strokeStyles?.['nodeError']
1068
+ const node = app.graph.getNodeById(nodeId);
1069
+ if (!node) return;
1070
+ if (!customErrorStyles.has(nodeId)) {
1071
+ customErrorStyles.set(nodeId, {
1072
+ originalStyle: node.strokeStyles?.["nodeError"],
1073
+ customColor: borderColor,
1074
+ nodeId: nodeId,
1075
+ });
1076
+ }
1077
+ node.strokeStyles = node.strokeStyles || {};
1078
+ node.strokeStyles["nodeError"] = function () {
1079
+ // if (this.id === nodeId) {
1080
+ return { color: borderColor, lineWidth: 2 }; // 自定义颜色和线宽
1081
+ // }
1082
+ };
1083
+ const mockErrorEvent = {
1084
+ detail: {
1085
+ node_id: nodeId,
1086
+ node_type: nodeType,
1087
+ exception_message: errorMessage,
1088
+ exception_type: "ManualError",
1089
+ traceback: ["Manual error triggered"],
1090
+ executed: [],
1091
+ prompt_id: "manual",
1092
+ timestamp: Date.now(),
1093
+ },
1094
+ };
903
1095
 
904
- // 手动触发事件监听器
905
- app.api.dispatchCustomEvent('execution_error', mockErrorEvent.detail)
906
- }
1096
+ // 手动触发事件监听器
1097
+ app.api.dispatchCustomEvent("execution_error", mockErrorEvent.detail);
1098
+ }
1099
+
1100
+ nodeIds.forEach((id) => {
1101
+ simulateExecutionError(id, nodeType, errorMessage, borderColor);
1102
+ });
907
1103
 
908
- nodeIds.forEach(id => {
909
- simulateExecutionError(id, nodeType, errorMessage, borderColor)
910
- })
1104
+ app.canvas.draw(true, true);
1105
+
1106
+ // 添加发送消息给前端折叠侧边栏的代码
1107
+ window.parent.postMessage(
1108
+ {
1109
+ type: "collapseParamSelector",
1110
+ method: "collapseParamSelector",
1111
+ result: true,
1112
+ },
1113
+ "*"
1114
+ );
1115
+
1116
+ window.parent.postMessage(
1117
+ {
1118
+ type: "functionResult",
1119
+ method: "openCustomError",
1120
+ result: true,
1121
+ },
1122
+ "*"
1123
+ );
1124
+ },
1125
+ clearAllCustomStyles: function () {
1126
+ customErrorStyles.forEach((styleInfo, nodeId) => {
1127
+ const node = app.graph.getNodeById(nodeId);
1128
+ if (!node) return;
1129
+ console.log(node);
1130
+ // 恢复原始样式
1131
+ if (styleInfo.originalStyle) {
1132
+ node.strokeStyles["nodeError"] = styleInfo.originalStyle;
1133
+ } else {
1134
+ delete node.strokeStyles["nodeError"];
1135
+ }
1136
+
1137
+ // 从映射中移除
1138
+ customErrorStyles.delete(nodeId);
1139
+ });
911
1140
 
912
- app.canvas.draw(true, true)
1141
+ // 重绘画布
1142
+ app.canvas.draw(true, true);
1143
+ },
1144
+ focusNodeOnly: function (params) {
1145
+ if (!params.nodeId) return false;
1146
+ const result = focusNodeOnly(params.nodeId);
1147
+ window.parent.postMessage(
1148
+ {
1149
+ type: "functionResult",
1150
+ method: "focusNodeOnly",
1151
+ result: result,
1152
+ },
1153
+ "*"
1154
+ );
1155
+ return result;
1156
+ },
1157
+ };
1158
+
1159
+ methods.deselectExportNode = function (params) {
1160
+ if (params && params.nodeId !== undefined) {
1161
+ if (typeof window.deselectInputNode === "function") {
1162
+ window.deselectInputNode(params.nodeId);
1163
+ }
1164
+ }
1165
+ };
1166
+ methods.clearExportNodes = function () {
1167
+ if (typeof window.clearExportNodes === "function") {
1168
+ window.clearExportNodes();
1169
+ }
1170
+ };
1171
+ // 保存工作流的原始节点颜色信息
1172
+ methods.saveOriginalNodeColors = function (params) {
1173
+ if (typeof window.saveOriginalNodeColors === "function") {
1174
+ window.saveOriginalNodeColors(params.workflowId);
1175
+ }
1176
+ };
1177
+
1178
+ // 监听 Ctrl+Enter 快捷键执行工作流
1179
+ document.addEventListener("keydown", (event) => {
1180
+ if (event.ctrlKey && event.key === "Enter") {
1181
+ event.preventDefault();
1182
+
1183
+ // 节流:检查距离上次执行是否已经过了2秒
1184
+ const now = Date.now();
1185
+ if (now - lastRunWorkflowTime < THROTTLE_TIME) {
1186
+ console.log("触发频率过高,请稍后再试");
1187
+ return;
1188
+ }
913
1189
 
914
- // 添加发送消息给前端折叠侧边栏的代码
915
- window.parent.postMessage({
916
- type: 'collapseParamSelector',
917
- method: 'collapseParamSelector',
918
- result: true
919
- }, '*');
1190
+ // 更新上次执行时间
1191
+ lastRunWorkflowTime = now;
920
1192
 
921
- window.parent.postMessage({
922
- type: 'functionResult',
923
- method: 'openCustomError',
924
- result: true
925
- }, '*');
1193
+ if (methods.runWorkflow) {
1194
+ window.parent.postMessage(
1195
+ {
1196
+ type: "functionResult",
1197
+ method: "ctrlEnter",
1198
+ result: true,
926
1199
  },
927
- clearAllCustomStyles: function () {
928
- customErrorStyles.forEach((styleInfo, nodeId) => {
929
- const node = app.graph.getNodeById(nodeId)
930
- if (!node) return
931
- console.log(node)
932
- // 恢复原始样式
933
- if (styleInfo.originalStyle) {
934
- node.strokeStyles['nodeError'] = styleInfo.originalStyle
935
- } else {
936
- delete node.strokeStyles['nodeError']
937
- }
938
-
939
- // 从映射中移除
940
- customErrorStyles.delete(nodeId)
941
- })
942
-
943
- // 重绘画布
944
- app.canvas.draw(true, true)
1200
+ "*"
1201
+ );
1202
+ methods.runWorkflow();
1203
+ }
1204
+ }
1205
+ });
1206
+
1207
+ window.addEventListener("message", function (event) {
1208
+ if (event.data && event.data.type === "callMethod") {
1209
+ const methodName = event.data.method;
1210
+ const params = event.data.params || {};
1211
+
1212
+ if (methods[methodName]) {
1213
+ methods[methodName](params);
1214
+ } else {
1215
+ console.error("方法不存在:", methodName);
1216
+ window.parent.postMessage(
1217
+ {
1218
+ type: "functionResult",
1219
+ method: methodName,
1220
+ error: `方法 ${methodName} 不存在`,
1221
+ success: false,
945
1222
  },
946
- focusNodeOnly: function(params) {
947
- if (!params.nodeId) return false;
948
- const result = focusNodeOnly(params.nodeId);
949
- window.parent.postMessage({
950
- type: 'functionResult',
951
- method: 'focusNodeOnly',
952
- result: result
953
- }, '*');
954
- return result;
1223
+ "*"
1224
+ );
1225
+ window.parent.postMessage(
1226
+ {
1227
+ type: "functionResult",
1228
+ method: methodName,
1229
+ error: `方法 ${methodName} 不存在`,
1230
+ success: false,
955
1231
  },
956
- };
957
-
958
- methods.deselectExportNode = function(params) {
959
- if (params && params.nodeId !== undefined) {
960
- if (typeof window.deselectInputNode === 'function') {
961
- window.deselectInputNode(params.nodeId);
962
- }
963
- }
964
- };
965
- methods.clearExportNodes = function() {
966
- if (typeof window.clearExportNodes === 'function') {
967
- window.clearExportNodes();
968
- }
969
- };
970
- // 保存工作流的原始节点颜色信息
971
- methods.saveOriginalNodeColors = function(params) {
972
- if (typeof window.saveOriginalNodeColors === 'function') {
973
- window.saveOriginalNodeColors(params.workflowId);
974
- }
975
- };
976
- window.addEventListener('message', function (event) {
977
- if (event.data && event.data.type === 'callMethod') {
978
- const methodName = event.data.method;
979
- const params = event.data.params || {};
980
-
981
-
982
-
983
- if (methods[methodName]) {
984
- methods[methodName](params);
985
- } else {
986
- console.error('方法不存在:', methodName);
987
- window.parent.postMessage({
988
- type: 'functionResult',
989
- method: methodName,
990
- error: `方法 ${methodName} 不存在`,
991
- success: false
992
- }, '*');
993
- window.parent.postMessage({
994
- type: 'functionResult',
995
- method: methodName,
996
- error: `方法 ${methodName} 不存在`,
997
- success: false
998
- }, '*');
999
- }
1000
- }
1001
- });
1002
- window.parent.postMessage({ type: 'iframeReady' }, '*');
1003
- app.api.addEventListener('graphChanged', (e) => {
1004
- console.log('Graph 发生变化,当前 workflow JSON:', e.detail)
1005
- window.parent.postMessage({
1006
- type: 'functionResult',
1007
- method: 'workflowChanged',
1008
- result: e.detail
1009
- }, '*');
1010
-
1011
- document.dispatchEvent(new CustomEvent('workflowLoaded', {
1012
- detail: e.detail
1013
- }));
1232
+ "*"
1233
+ );
1234
+ }
1235
+ }
1236
+ });
1237
+ window.parent.postMessage({ type: "iframeReady" }, "*");
1238
+ app.api.addEventListener("graphChanged", (e) => {
1239
+ console.log("Graph 发生变化,当前 workflow JSON:", e.detail);
1240
+ window.parent.postMessage(
1241
+ {
1242
+ type: "functionResult",
1243
+ method: "workflowChanged",
1244
+ result: e.detail,
1245
+ },
1246
+ "*"
1247
+ );
1248
+
1249
+ document.dispatchEvent(
1250
+ new CustomEvent("workflowLoaded", {
1251
+ detail: e.detail,
1014
1252
  })
1015
- }
1253
+ );
1254
+ });
1255
+ },
1016
1256
  });