bizydraft 0.2.78.dev20251030100819__py3-none-any.whl → 0.2.82.dev20251209023307__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.
- bizydraft/hijack_routes.py +28 -1
- bizydraft/oss_utils.py +14 -3
- bizydraft/patch_handlers.py +105 -5
- bizydraft/static/js/aspectRatio.js +751 -0
- bizydraft/static/js/clipspaceToOss.js +57 -26
- bizydraft/static/js/handleStyle.js +54 -12
- bizydraft/static/js/hookLoad/media.js +415 -0
- bizydraft/static/js/hookLoadMedia.js +92 -285
- bizydraft/static/js/hookLoadModel.js +49 -3
- bizydraft/static/js/imageUpload.js +146 -0
- bizydraft/static/js/limitTimeRange.js +129 -0
- bizydraft/static/js/main.js +4 -1
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/METADATA +1 -1
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/RECORD +16 -13
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/WHEEL +0 -0
- {bizydraft-0.2.78.dev20251030100819.dist-info → bizydraft-0.2.82.dev20251209023307.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { app } from "../../scripts/app.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { hideWidget } from './tool.js';
|
|
3
|
+
import {
|
|
4
|
+
getMediaNodeConfig,
|
|
5
|
+
getMediaInputKeys,
|
|
6
|
+
computeIsMediaNode,
|
|
7
|
+
mediaNodeList,
|
|
8
|
+
fetchMediaConfigWithCache,
|
|
9
|
+
findMediaWidget,
|
|
10
|
+
findMediaWidgets,
|
|
11
|
+
updateWidgetsOptions,
|
|
12
|
+
handleNewUploadedFile,
|
|
13
|
+
extractUrlFromInput,
|
|
14
|
+
initMaps,
|
|
15
|
+
createImageNameWidgetCallbackForMediaWidget,
|
|
16
|
+
createMediaWidgetCallback,
|
|
17
|
+
setupVaWidgets,
|
|
18
|
+
applyWorkflowImageSettings,
|
|
19
|
+
fetchImageList
|
|
20
|
+
} from './hookLoad/media.js';
|
|
6
21
|
|
|
7
22
|
app.registerExtension({
|
|
8
23
|
name: "bizyair.image.to.oss",
|
|
@@ -33,307 +48,99 @@ app.registerExtension({
|
|
|
33
48
|
});
|
|
34
49
|
})
|
|
35
50
|
nodeType.prototype.onNodeCreated = async function() {
|
|
36
|
-
if (await computeIsMediaNode(nodeData.name)) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
let media_widget = null;
|
|
40
|
-
const mediaNodeConfig = await getMediaNodeConfig(nodeData.name);
|
|
41
|
-
const apiInputKeys = getMediaInputKeys(mediaNodeConfig);
|
|
42
|
-
if (apiInputKeys && apiInputKeys.length > 0) {
|
|
43
|
-
for (const key of apiInputKeys) {
|
|
44
|
-
const w = this.widgets.find(x => x.name === key);
|
|
45
|
-
if (w) { media_widget = w; break; }
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (!media_widget) {
|
|
49
|
-
media_widget = this.widgets.find(w => {
|
|
50
|
-
return possibleMediaWidgetNames.includes(w.name);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
// 查找所有name等于接口配置中inputs下的字段的widget(如video、audio等)
|
|
54
|
-
let va_widgets = [];
|
|
55
|
-
if (apiInputKeys && apiInputKeys.length > 0) {
|
|
56
|
-
for (const key of apiInputKeys) {
|
|
57
|
-
const w = this.widgets.find(x => x.name === key);
|
|
58
|
-
if (w) {
|
|
59
|
-
va_widgets.push(w);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 如果API配置没有找到,使用回退逻辑查找常见的媒体widget
|
|
65
|
-
if (va_widgets.length === 0) {
|
|
66
|
-
for (const widgetName of possibleMediaWidgetNames) {
|
|
67
|
-
const w = this.widgets.find(x => x.name === widgetName);
|
|
68
|
-
if (w) {
|
|
69
|
-
va_widgets.push(w);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
let image_name_widget = this.widgets.find(w => w.name === 'image_name');
|
|
74
|
-
let image_list = []
|
|
75
|
-
const getData = async () => {
|
|
76
|
-
const res = await fetch(`${apiHost}/special/community/commit_input_resource?${
|
|
77
|
-
new URLSearchParams({
|
|
78
|
-
ext: computeExt(nodeData.name),
|
|
79
|
-
current: 1,
|
|
80
|
-
page_size: 100
|
|
81
|
-
|
|
82
|
-
}).toString()
|
|
83
|
-
}`, {
|
|
84
|
-
method: 'GET',
|
|
85
|
-
headers: {
|
|
86
|
-
'Content-Type': 'application/json',
|
|
87
|
-
'Authorization': `Bearer ${getCookie('bizy_token')}`
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
const {data} = await res.json()
|
|
91
|
-
const list = (data && data.data && data.data.data && data.data.data.list) || []
|
|
92
|
-
image_list = list.filter(item => item.name).map(item => {
|
|
93
|
-
return {
|
|
94
|
-
url: item.url,
|
|
95
|
-
id: item.id,
|
|
96
|
-
name: item.name
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
// 📊 方案:使用 Map 缓存 URL→Name 映射(O(1) 查找)
|
|
101
|
-
const urlToNameMap = new Map();
|
|
102
|
-
const nameToItemMap = new Map();
|
|
103
|
-
image_list.forEach(item => {
|
|
104
|
-
urlToNameMap.set(item.url, item.name);
|
|
105
|
-
nameToItemMap.set(item.name, item);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// 如果找到va_widgets,处理它们
|
|
109
|
-
if (va_widgets.length > 0) {
|
|
110
|
-
// 标志位:防止批量更新时触发监听
|
|
111
|
-
let isBatchUpdating = false;
|
|
112
|
-
|
|
113
|
-
// 创建image_name_widget来替代显示
|
|
114
|
-
if (!image_name_widget) {
|
|
115
|
-
image_name_widget = this.addWidget("combo", "image_name", "", function(e){
|
|
116
|
-
const item = nameToItemMap.get(e);
|
|
117
|
-
if (item) {
|
|
118
|
-
const image_url = decodeURIComponent(item.url);
|
|
119
|
-
// 批量更新时跳过监听
|
|
120
|
-
isBatchUpdating = true;
|
|
121
|
-
va_widgets.forEach(va_widget => {
|
|
122
|
-
va_widget.value = image_url;
|
|
123
|
-
if (va_widget.callback) {
|
|
124
|
-
va_widget.callback(image_url);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
isBatchUpdating = false;
|
|
128
|
-
}
|
|
129
|
-
}, {
|
|
130
|
-
serialize: true,
|
|
131
|
-
values: image_list.map(item => item.name)
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// 隐藏所有va_widgets 并设置监听
|
|
136
|
-
va_widgets.forEach(va_widget => {
|
|
137
|
-
hideWidget(this, va_widget.name);
|
|
138
|
-
let _value = va_widget.value;
|
|
139
|
-
|
|
140
|
-
// 检查并删除现有的 value 属性描述符(如果存在)
|
|
141
|
-
const existingDescriptor = Object.getOwnPropertyDescriptor(va_widget, 'value');
|
|
142
|
-
if (existingDescriptor && !existingDescriptor.configurable) {
|
|
143
|
-
// 如果不可配置,跳过重新定义
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
51
|
+
if (!(await computeIsMediaNode(nodeData.name))) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
146
54
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
delete va_widget.value;
|
|
150
|
-
}
|
|
55
|
+
const apiHost = 'https://bizyair.cn/api'
|
|
56
|
+
// const apiHost = 'http://localhost:3000/api'
|
|
151
57
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
},
|
|
156
|
-
set: function(newValue) {
|
|
157
|
-
_value = newValue;
|
|
58
|
+
// 获取 API 配置
|
|
59
|
+
const mediaNodeConfig = await getMediaNodeConfig(nodeData.name);
|
|
60
|
+
const apiInputKeys = getMediaInputKeys(mediaNodeConfig);
|
|
158
61
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// 使用 Map 快速查找(O(1))
|
|
164
|
-
const name = urlToNameMap.get(newValue);
|
|
165
|
-
if (name) {
|
|
166
|
-
image_name_widget.value = name;
|
|
167
|
-
} else {
|
|
168
|
-
// 如果没找到,从URL提取文件名
|
|
169
|
-
const fileName = newValue.split('/').pop();
|
|
170
|
-
image_name_widget.value = fileName;
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
enumerable: true,
|
|
174
|
-
configurable: true
|
|
175
|
-
});
|
|
176
|
-
});
|
|
62
|
+
// 查找媒体 widget
|
|
63
|
+
const media_widget = findMediaWidget(this.widgets, apiInputKeys);
|
|
64
|
+
const va_widgets = findMediaWidgets(this.widgets, apiInputKeys);
|
|
65
|
+
let image_name_widget = this.widgets.find(w => w.name === 'image_name');
|
|
177
66
|
|
|
67
|
+
// 获取文件列表
|
|
68
|
+
const image_list = await fetchImageList(apiHost, nodeData.name);
|
|
178
69
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// 保存va_widget的原始callback
|
|
182
|
-
const originalVaCallback = va_widget.callback;
|
|
183
|
-
// 重写va_widget的callback,当被触发时给image_name_widget赋值
|
|
184
|
-
va_widget.callback = function(e) {
|
|
185
|
-
if (image_name_widget) {
|
|
186
|
-
if (typeof e === 'string') {
|
|
187
|
-
// 使用 Map 快速查找(O(1))
|
|
188
|
-
const name = urlToNameMap.get(e);
|
|
189
|
-
if (name) {
|
|
190
|
-
image_name_widget.value = name;
|
|
191
|
-
} else {
|
|
192
|
-
// 如果没找到,从URL提取文件名
|
|
193
|
-
const fileName = e.split('/').pop();
|
|
194
|
-
image_name_widget.value = fileName;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
70
|
+
// 初始化 Map 映射
|
|
71
|
+
const { urlToNameMap, nameToItemMap } = initMaps(image_list);
|
|
198
72
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
};
|
|
204
|
-
});
|
|
205
|
-
}
|
|
73
|
+
// 如果找到va_widgets,处理它们
|
|
74
|
+
if (va_widgets.length > 0) {
|
|
75
|
+
image_name_widget = setupVaWidgets(this, va_widgets, image_list, urlToNameMap, nameToItemMap, image_name_widget, va_widgets[0]);
|
|
76
|
+
}
|
|
206
77
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (media_widget.callback) {
|
|
215
|
-
media_widget.callback(image_url);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}, {
|
|
219
|
-
serialize: true,
|
|
220
|
-
values: image_list.map(item => item.name)
|
|
221
|
-
});
|
|
78
|
+
// 如果va_widgets没有创建image_name_widget,使用原有逻辑创建
|
|
79
|
+
if (!image_name_widget && media_widget) {
|
|
80
|
+
image_name_widget = this.addWidget("combo", "image_name", "",
|
|
81
|
+
createImageNameWidgetCallbackForMediaWidget(nameToItemMap, media_widget),
|
|
82
|
+
{
|
|
83
|
+
serialize: true,
|
|
84
|
+
values: image_list.map(item => item.name)
|
|
222
85
|
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
223
88
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (image_name_widget && actualMediaWidget) {
|
|
228
|
-
const val = urlToNameMap.get(actualMediaWidget.value) || actualMediaWidget.value
|
|
229
|
-
image_name_widget.label = actualMediaWidget.label
|
|
230
|
-
image_name_widget.value = val
|
|
231
|
-
|
|
232
|
-
const currentIndex = this.widgets.indexOf(image_name_widget);
|
|
233
|
-
if (currentIndex > 1) {
|
|
234
|
-
this.widgets.splice(currentIndex, 1);
|
|
235
|
-
this.widgets.splice(1, 0, image_name_widget);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 如果没有进入va_widgets分支,才隐藏media_widget
|
|
239
|
-
if (va_widgets.length === 0) {
|
|
240
|
-
hideWidget(this, media_widget.name)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
actualMediaWidget.options.values = image_list.map(item => item.name);
|
|
244
|
-
|
|
245
|
-
// 对于va_widgets的情况,callback已经在上面重写过了,不需要再次重写
|
|
246
|
-
if (va_widgets.length === 0 && media_widget) {
|
|
247
|
-
const callback = media_widget.callback
|
|
248
|
-
media_widget.callback = function(e) {
|
|
249
|
-
if (typeof e == 'string') {
|
|
250
|
-
// 使用 Map 快速查找(O(1))
|
|
251
|
-
const item = e.includes('http') ?
|
|
252
|
-
(urlToNameMap.has(e) ? {url: e, name: urlToNameMap.get(e)} : null) :
|
|
253
|
-
(nameToItemMap ? nameToItemMap.get(e) : null);
|
|
89
|
+
// 如果进入了va_widgets分支,使用va_widgets中第一个作为media_widget的替代
|
|
90
|
+
const actualMediaWidget = va_widgets.length > 0 ? va_widgets[0] : media_widget;
|
|
254
91
|
|
|
255
|
-
|
|
92
|
+
if (image_name_widget && actualMediaWidget) {
|
|
93
|
+
const val = urlToNameMap.get(actualMediaWidget.value) || actualMediaWidget.value;
|
|
94
|
+
image_name_widget.label = actualMediaWidget.label;
|
|
95
|
+
image_name_widget.value = val;
|
|
256
96
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
} else {
|
|
263
|
-
const item = e[0].split('/')
|
|
264
|
-
const fileName = item[item.length - 1];
|
|
265
|
-
image_name_widget.options.values.pop()
|
|
266
|
-
image_name_widget.options.values.push(fileName)
|
|
267
|
-
image_name_widget.value = fileName
|
|
268
|
-
image_list.push({
|
|
269
|
-
name: fileName,
|
|
270
|
-
url: e[0]
|
|
271
|
-
})
|
|
272
|
-
// 同步更新 Map
|
|
273
|
-
urlToNameMap.set(e[0], fileName);
|
|
274
|
-
if (nameToItemMap) {
|
|
275
|
-
nameToItemMap.set(fileName, {url: e[0], name: fileName});
|
|
276
|
-
}
|
|
277
|
-
if (callback) {
|
|
278
|
-
callback(e)
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return true
|
|
97
|
+
// 调整 widget 顺序
|
|
98
|
+
const currentIndex = this.widgets.indexOf(image_name_widget);
|
|
99
|
+
if (currentIndex > 1) {
|
|
100
|
+
this.widgets.splice(currentIndex, 1);
|
|
101
|
+
this.widgets.splice(1, 0, image_name_widget);
|
|
285
102
|
}
|
|
286
|
-
await getData()
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
async function applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, currentNodeId) {
|
|
290
|
-
if (workflowParams && workflowParams.nodes) {
|
|
291
|
-
// 先获取配置,然后将 mediaNodeList 和配置的 keys 合并
|
|
292
|
-
const config = await fetchMediaConfigWithCache();
|
|
293
|
-
const allMediaNodeTypes = new Set(mediaNodeList);
|
|
294
|
-
if (config) {
|
|
295
|
-
// 将配置中的 keys 添加到集合中
|
|
296
|
-
for (const key of Object.keys(config)) {
|
|
297
|
-
allMediaNodeTypes.add(key);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// 使用同步的 includes 查找匹配的节点(完全避免循环中的异步)
|
|
302
|
-
const imageNode = workflowParams.nodes.find(item =>
|
|
303
|
-
item.id === currentNodeId && allMediaNodeTypes.has(item.type)
|
|
304
|
-
)
|
|
305
103
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
url: imageNode.widgets_values[0]
|
|
311
|
-
})
|
|
312
|
-
media_widget.value = imageNode.widgets_values[0]
|
|
104
|
+
// 如果没有进入va_widgets分支,才隐藏media_widget
|
|
105
|
+
if (va_widgets.length === 0) {
|
|
106
|
+
hideWidget(this, media_widget.name);
|
|
107
|
+
}
|
|
313
108
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
109
|
+
// 更新 widget 选项列表
|
|
110
|
+
updateWidgetsOptions([actualMediaWidget], image_list);
|
|
111
|
+
|
|
112
|
+
// 对于va_widgets的情况,callback已经在上面重写过了,不需要再次重写
|
|
113
|
+
if (va_widgets.length === 0 && media_widget) {
|
|
114
|
+
const callback = media_widget.callback;
|
|
115
|
+
media_widget.callback = createMediaWidgetCallback(
|
|
116
|
+
media_widget,
|
|
117
|
+
urlToNameMap,
|
|
118
|
+
nameToItemMap,
|
|
119
|
+
image_list,
|
|
120
|
+
image_name_widget,
|
|
121
|
+
actualMediaWidget,
|
|
122
|
+
callback
|
|
123
|
+
);
|
|
319
124
|
}
|
|
125
|
+
}
|
|
320
126
|
|
|
321
|
-
|
|
127
|
+
// 应用工作流设置
|
|
128
|
+
const workflowData = window.currentWorkflowData || workflowParams;
|
|
129
|
+
if (workflowData) {
|
|
130
|
+
await applyWorkflowImageSettings(workflowData, image_list, media_widget, image_name_widget, this.id, va_widgets, actualMediaWidget);
|
|
322
131
|
if (window.currentWorkflowData) {
|
|
323
|
-
await applyWorkflowImageSettings(window.currentWorkflowData, image_list, media_widget, image_name_widget, this.id);
|
|
324
|
-
// 清除存储的数据,避免重复处理
|
|
325
132
|
delete window.currentWorkflowData;
|
|
326
|
-
} else {
|
|
327
|
-
// 原有的调用
|
|
328
|
-
await applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, this.id);
|
|
329
133
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
type: 'functionResult',
|
|
333
|
-
method: 'hookLoadImageCompleted',
|
|
334
|
-
params: {}
|
|
335
|
-
}, '*');
|
|
134
|
+
} else {
|
|
135
|
+
await applyWorkflowImageSettings(workflowParams, image_list, media_widget, image_name_widget, this.id, va_widgets, actualMediaWidget);
|
|
336
136
|
}
|
|
137
|
+
|
|
138
|
+
// 发送完成消息
|
|
139
|
+
window.parent.postMessage({
|
|
140
|
+
type: 'functionResult',
|
|
141
|
+
method: 'hookLoadImageCompleted',
|
|
142
|
+
params: {}
|
|
143
|
+
}, '*');
|
|
337
144
|
}
|
|
338
145
|
}
|
|
339
146
|
})
|
|
@@ -29,9 +29,9 @@ app.registerExtension({
|
|
|
29
29
|
|
|
30
30
|
// 异步获取节点配置
|
|
31
31
|
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const nodeConfig = await getNodeConfig(nodeData.name);
|
|
33
|
+
if (nodeConfig) {
|
|
34
|
+
nodeType.prototype.onNodeCreated = function() {
|
|
35
35
|
try {
|
|
36
36
|
const inputs = nodeConfig.config.inputs;
|
|
37
37
|
const inputKeys = Object.keys(inputs);
|
|
@@ -140,5 +140,51 @@ app.registerExtension({
|
|
|
140
140
|
if (nodeConfig) {
|
|
141
141
|
setupNodeMouseBehavior(node, nodeConfig);
|
|
142
142
|
}
|
|
143
|
+
},
|
|
144
|
+
async setup() {
|
|
145
|
+
const app = document.querySelector('#vue-app').__vue_app__
|
|
146
|
+
const pinia = app.config.globalProperties.$pinia
|
|
147
|
+
const settingStore = pinia._s.get('setting')
|
|
148
|
+
await settingStore.set('Comfy.Workflow.ShowMissingModelsWarning', false)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
// 获取 toastStore
|
|
154
|
+
const toastStore = pinia._s.get('toast');
|
|
155
|
+
|
|
156
|
+
// 保存原始的 add 方法
|
|
157
|
+
const originalAdd = toastStore.add;
|
|
158
|
+
|
|
159
|
+
// 重写 add 方法,添加过滤逻辑
|
|
160
|
+
toastStore.add = function(message) {
|
|
161
|
+
const detail = (message.detail || '').toLowerCase();
|
|
162
|
+
const summary = (message.summary || '').toLowerCase();
|
|
163
|
+
const text = `${summary} ${detail}`;
|
|
164
|
+
|
|
165
|
+
// 检查是否包含阻止的关键词
|
|
166
|
+
const blockedKeywords = [
|
|
167
|
+
'missing dependencies',
|
|
168
|
+
'comfyui logs',
|
|
169
|
+
'comfyullogs',
|
|
170
|
+
'refer to the comfyui',
|
|
171
|
+
'you may be missing'
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const shouldBlock = blockedKeywords.some(keyword =>
|
|
175
|
+
text.includes(keyword.toLowerCase())
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// 如果包含阻止的关键词,则不添加 toast
|
|
179
|
+
if (shouldBlock) {
|
|
180
|
+
console.log('Blocked toast:', message);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 否则正常添加
|
|
185
|
+
return originalAdd.call(this, message);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
console.log('Toast blocker activated!');
|
|
143
189
|
}
|
|
144
190
|
})
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { app } from "../../../scripts/app.js";
|
|
2
|
+
|
|
3
|
+
// 简单的链式callback实现
|
|
4
|
+
function chainCallback(originalCallback, newCallback) {
|
|
5
|
+
return function (...args) {
|
|
6
|
+
if (originalCallback) {
|
|
7
|
+
originalCallback.apply(this, args);
|
|
8
|
+
}
|
|
9
|
+
newCallback.apply(this, args);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// 统一的函数:初始化动态输入相关的 widgets
|
|
14
|
+
function initializeDynamicInputs(node) {
|
|
15
|
+
// 只处理目标节点类型
|
|
16
|
+
if (
|
|
17
|
+
node.type !== "BizyAir_Seedream4_5" &&
|
|
18
|
+
node.type !== "BizyAir_NanoBananaPro" &&
|
|
19
|
+
node.type !== "BizyAir_NanoBananaProOfficial"
|
|
20
|
+
) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
node._type = "IMAGE";
|
|
25
|
+
|
|
26
|
+
if (!node.inputs) {
|
|
27
|
+
node.inputs = [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 先检查是否存在 inputcount widget
|
|
31
|
+
let inputCountWidget = node.widgets?.find(
|
|
32
|
+
(w) => w.name === "inputcount"
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// 如果不存在,先创建 inputcount widget(在按钮之前创建,确保显示顺序)
|
|
36
|
+
if (!inputCountWidget) {
|
|
37
|
+
// 计算当前 IMAGE 类型输入数量
|
|
38
|
+
const currentImageInputs = node.inputs.filter(
|
|
39
|
+
(input) => input.type === node._type
|
|
40
|
+
).length;
|
|
41
|
+
// 创建 inputcount widget,默认值为当前输入数量或1
|
|
42
|
+
// 使用 precision: 0 确保是整数类型
|
|
43
|
+
inputCountWidget = node.addWidget(
|
|
44
|
+
"number",
|
|
45
|
+
"inputcount",
|
|
46
|
+
currentImageInputs || 1,
|
|
47
|
+
null,
|
|
48
|
+
{
|
|
49
|
+
min: 1,
|
|
50
|
+
max: 10,
|
|
51
|
+
step: 10, // 旧参数(兼容性),是 step2 的 10 倍
|
|
52
|
+
step2: 1, // 新参数,实际的步长
|
|
53
|
+
precision: 0, // 设置为 0 表示整数
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 检查是否已经存在 "Update inputs" 按钮,避免重复添加
|
|
59
|
+
const updateButton = node.widgets?.find(
|
|
60
|
+
(w) => w.name === "Update inputs"
|
|
61
|
+
);
|
|
62
|
+
if (updateButton) {
|
|
63
|
+
return; // 已经存在,不需要重复添加
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 然后添加 "Update inputs" 按钮(会在 inputcount 之后显示)
|
|
67
|
+
node.addWidget("button", "Update inputs", null, () => {
|
|
68
|
+
if (!node.inputs) {
|
|
69
|
+
node.inputs = [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 查找 inputcount widget
|
|
73
|
+
const inputCountWidget = node.widgets?.find(
|
|
74
|
+
(w) => w.name === "inputcount"
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// 如果仍然找不到,创建它
|
|
78
|
+
if (!inputCountWidget) {
|
|
79
|
+
const currentImageInputs = node.inputs.filter(
|
|
80
|
+
(input) => input.type === node._type
|
|
81
|
+
).length;
|
|
82
|
+
node.addWidget(
|
|
83
|
+
"number",
|
|
84
|
+
"inputcount",
|
|
85
|
+
currentImageInputs || 1,
|
|
86
|
+
null,
|
|
87
|
+
{
|
|
88
|
+
min: 1,
|
|
89
|
+
max: 10,
|
|
90
|
+
step: 10, // 旧参数(兼容性),是 step2 的 10 倍
|
|
91
|
+
step2: 1, // 新参数,实际的步长
|
|
92
|
+
precision: 0, // 设置为 0 表示整数
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const target_number_of_inputs = inputCountWidget.value || 1;
|
|
99
|
+
const num_inputs = node.inputs.filter(
|
|
100
|
+
(input) => input.type === node._type
|
|
101
|
+
).length;
|
|
102
|
+
|
|
103
|
+
if (target_number_of_inputs === num_inputs) {
|
|
104
|
+
return; // already set, do nothing
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (target_number_of_inputs < num_inputs) {
|
|
108
|
+
const inputs_to_remove = num_inputs - target_number_of_inputs;
|
|
109
|
+
for (let i = 0; i < inputs_to_remove; i++) {
|
|
110
|
+
node.removeInput(node.inputs.length - 1);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
for (let i = num_inputs + 1; i <= target_number_of_inputs; ++i) {
|
|
114
|
+
node.addInput(`image_${i}`, node._type, { shape: 7 });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
app.registerExtension({
|
|
121
|
+
name: "bizyair.dynamicImageInputs",
|
|
122
|
+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
123
|
+
// 只处理目标节点类型
|
|
124
|
+
if (
|
|
125
|
+
nodeData.name !== "BizyAir_Seedream4_5" &&
|
|
126
|
+
nodeData.name !== "BizyAir_NanoBananaPro" &&
|
|
127
|
+
nodeData.name !== "BizyAir_NanoBananaProOfficial"
|
|
128
|
+
) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 使用 chainCallback 设置 onConfigure,确保在节点配置完成后也初始化 widgets
|
|
133
|
+
const originalOnConfigure = nodeType.prototype.onConfigure;
|
|
134
|
+
nodeType.prototype.onConfigure = chainCallback(
|
|
135
|
+
originalOnConfigure,
|
|
136
|
+
function (info) {
|
|
137
|
+
// 在 configure 完成后初始化 widgets
|
|
138
|
+
initializeDynamicInputs(this);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
},
|
|
142
|
+
nodeCreated(node) {
|
|
143
|
+
// 在节点创建时也初始化 widgets(用于首次创建节点时)
|
|
144
|
+
initializeDynamicInputs(node);
|
|
145
|
+
},
|
|
146
|
+
});
|