dsh-comfyui-rp 0.2.2 → 0.2.4
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.
- package/README.md +1 -1
- package/docs/CHANGELOG.md +17 -0
- package/lib/api.js +22 -1
- package/lib/client.js +233 -26
- package/lib/comfy.js +24 -1
- package/lib/jobs.js +6 -0
- package/lib/meta.js +1 -1
- package/lib/store.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ dsh plugin --profile web add git+https://github.com/bmhty123/dsh-comfyui-rp.git
|
|
|
40
40
|
|
|
41
41
|
## 能做什么
|
|
42
42
|
|
|
43
|
-
- **自动出图**:捕获正文 → 解析提示词 → 出图 →
|
|
43
|
+
- **自动出图**:捕获正文 → 解析提示词 → 出图 → 归档,全自动;投递和出图可分开开关。纯黑图会自动换种子重生成,进行中可以随时点「停止自动补图」。
|
|
44
44
|
- **投递**:正文来源和提示词来源分开,可指到另一个会话甚至另一台 DSH;附加消息放正文最上面或最下面,投递成功后自动留空。
|
|
45
45
|
- **看图**:右侧栏面板、全屏、悬浮球、悬浮图片框。翻页、下载、收藏、刷新、删除、圆角;自己拖过 / 缩过之后,切图不再改回初始大小。
|
|
46
46
|
- **跟图**:滚动正文自动切图,连续图按段落比例拆位置,可切光标跟图、手动偏移、清空定位。
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.2.4
|
|
4
|
+
|
|
5
|
+
- **纯黑图误判修复**:判定前先看体积——真·纯黑 PNG 只有 20 多 KB,超过 **120 KB** 的图直接不参与判定。之前有正常图(约 1 MB)在「原地替换后立刻复查」时被判成黑图,导致同一张图被连续替换 3 次。
|
|
6
|
+
- **判黑要两次都黑**:第一次判黑后会隔 0.9 秒、绕过浏览器缓存再验一次,两次都黑才自动替换,避免一次绘制的偶发全黑。
|
|
7
|
+
- **判定可追溯**:每次判定都会把统计(体积、暗像素比例、平均亮度、亮点比例、不透明覆盖)写进 Host 日志,「日志」页能直接看到;出现误判时能一眼定位是哪条规则触发。
|
|
8
|
+
|
|
9
|
+
## 0.2.3
|
|
10
|
+
|
|
11
|
+
- **自动补图可以随时停下**:补图进行中,图片框左下菜单的按钮会变成「终止」(之前因为补图是内部任务,菜单里根本看不到它),点一下清空队列并中止正在跑的那一单;全屏和工作台的「终止」也一样。右侧栏顶部还有一条「停止自动补图」。下一次新任务开始时自动复位。
|
|
12
|
+
- **黑图自动替换开关**:工作流页 →「图片输出相关」→「额外设置」里新增开关,关掉后不再检测纯黑图,待补队列也会清空。
|
|
13
|
+
- **纯黑判定更严,修掉误判**:以前只要求「暗像素 ≥ 99%」就会判黑,夜景、黑底小主体这类图会被误伤。现在必须同时满足:暗像素 ≥ 99.5%、平均亮度 ≤ 8、亮点(亮度 > 110)占比 < 0.1%,并且不透明像素要占到一半以上(透明底的小图不算黑)。
|
|
14
|
+
- **固定种子可手填**:工作流页「每次换随机种子」下面多了一个「固定种子」输入框。
|
|
15
|
+
- 勾着自动时它是灰的,显示**上一轮实际用的种子**(方便看到满意的一张后直接固定下来);
|
|
16
|
+
- 取消勾选后解锁,填的整数会立刻存进工作流,出图时覆盖每一处字面量 seed;取消的那一刻会把上一轮的种子自动带进来。
|
|
17
|
+
- 这些开关改完直接生效,不用再点「保存」。
|
|
18
|
+
|
|
19
|
+
|
|
3
20
|
## 0.2.2
|
|
4
21
|
|
|
5
22
|
- **LoRA 强度可以填负数**:工作台「连续图」的档位、LoRA 页每条的强度都允许 `-5 ~ 5`(反相 LoRA 用)。
|
package/lib/api.js
CHANGED
|
@@ -358,7 +358,7 @@ export function createApi({ store, jobs, hub, getConfig, ctx, sc, routePath, ver
|
|
|
358
358
|
workflows: compact ? [] : store.listWorkflows(),
|
|
359
359
|
activeWorkflowId: cfg.workflowId,
|
|
360
360
|
activeWorkflow: activeWf
|
|
361
|
-
? { id: activeWf.id, name: activeWf.name, size: activeWf.size || null, sizeMode: activeWf.sizeMode || 'manual', randomSeed: activeWf.randomSeed !== false, nodeCount: Object.keys(activeWf.json || {}).length }
|
|
361
|
+
? { id: activeWf.id, name: activeWf.name, size: activeWf.size || null, sizeMode: activeWf.sizeMode || 'manual', randomSeed: activeWf.randomSeed !== false, blackRetry: activeWf.blackRetry !== false, nodeCount: Object.keys(activeWf.json || {}).length }
|
|
362
362
|
: null,
|
|
363
363
|
jobs: jobsList,
|
|
364
364
|
images: imageItems,
|
|
@@ -604,6 +604,9 @@ export function createApi({ store, jobs, hub, getConfig, ctx, sc, routePath, ver
|
|
|
604
604
|
size: wf.size || null,
|
|
605
605
|
sizeMode: wf.sizeMode || 'manual',
|
|
606
606
|
randomSeed: wf.randomSeed !== false,
|
|
607
|
+
seed: wf.seed === null || wf.seed === undefined ? null : Number(wf.seed),
|
|
608
|
+
lastSeed: wf.lastSeed === null || wf.lastSeed === undefined ? null : Number(wf.lastSeed),
|
|
609
|
+
blackRetry: wf.blackRetry !== false,
|
|
607
610
|
pruneOutputs: wf.pruneOutputs !== false,
|
|
608
611
|
onlyFirst: wf.onlyFirst === true,
|
|
609
612
|
loraState: wf.loraState || null,
|
|
@@ -662,12 +665,15 @@ export function createApi({ store, jobs, hub, getConfig, ctx, sc, routePath, ver
|
|
|
662
665
|
const sizeMode = body.sizeMode || wf?.sizeMode || 'manual';
|
|
663
666
|
const loraState = body.loraState !== undefined ? body.loraState : resolveLoraState(wf || {});
|
|
664
667
|
const loraScan = scanLoraStacks(json);
|
|
668
|
+
// 固定种子:干跑也要按工作流页填的那个值算,否则「干跑」和真出一张的结果对不上。
|
|
669
|
+
const seed = body.seed !== undefined ? body.seed : (wf && wf.seed !== undefined ? wf.seed : null);
|
|
665
670
|
const { workflow, report } = applyOverrides(json, {
|
|
666
671
|
positive: String(body.positive ?? ''),
|
|
667
672
|
negative: String(body.negative ?? ''),
|
|
668
673
|
mapping,
|
|
669
674
|
tokenEmpty: 'blank',
|
|
670
675
|
randomSeed: body.randomSeed === true,
|
|
676
|
+
seed,
|
|
671
677
|
loraState,
|
|
672
678
|
size,
|
|
673
679
|
sizeSource: sizeMode,
|
|
@@ -1287,6 +1293,21 @@ export function createApi({ store, jobs, hub, getConfig, ctx, sc, routePath, ver
|
|
|
1287
1293
|
}));
|
|
1288
1294
|
}
|
|
1289
1295
|
|
|
1296
|
+
if (action === 'black-check' && method === 'POST') {
|
|
1297
|
+
const b = body || {};
|
|
1298
|
+
const stats = [
|
|
1299
|
+
b.darkRatio !== undefined ? `暗 ${(Number(b.darkRatio) * 100).toFixed(2)}%` : '',
|
|
1300
|
+
b.meanLum !== undefined ? `平均亮度 ${Number(b.meanLum).toFixed(1)}` : '',
|
|
1301
|
+
b.brightRatio !== undefined ? `亮点 ${(Number(b.brightRatio) * 100).toFixed(2)}%` : '',
|
|
1302
|
+
b.coverage !== undefined ? `不透明 ${Math.round(Number(b.coverage) * 100)}%` : '',
|
|
1303
|
+
].filter(Boolean).join(' · ');
|
|
1304
|
+
const skippedText = b.skipped === 'bytes' ? '跳过(体积太大,不可能是纯黑)' : (b.skipped ? `跳过(${b.skipped})` : '');
|
|
1305
|
+
const head = b.black === true ? '判为纯黑图' : (skippedText || '不是黑图');
|
|
1306
|
+
hub.pushLog(b.black === true ? 'warn' : 'info',
|
|
1307
|
+
`黑图检测:${head} · ${b.fileName || b.imageId || '?'}${b.bytes ? ` · ${b.bytes} B` : ''}${stats ? ` · ${stats}` : ''}`);
|
|
1308
|
+
return sendJson(res, 200, ok({ logged: true }));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1290
1311
|
if (action === 'logs' && method === 'GET') {
|
|
1291
1312
|
return sendJson(res, 200, ok({ logs: hub.logs.slice(-300) }));
|
|
1292
1313
|
}
|
package/lib/client.js
CHANGED
|
@@ -221,6 +221,30 @@ window.__ModuleLoader__.load({
|
|
|
221
221
|
|
|
222
222
|
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, Number(v) || 0)); }
|
|
223
223
|
|
|
224
|
+
/**
|
|
225
|
+
* 自动补图的全局状态:逻辑在 FloatBall 里跑(它一直在,负责检测纯黑图),
|
|
226
|
+
* 但「停止」按钮要能出现在右侧栏面板里,所以两边共享这一份。
|
|
227
|
+
* pending = 队列里的 + 正在跑的;stop = FloatBall 注册过来的停止函数。
|
|
228
|
+
*/
|
|
229
|
+
var BLACK_UI = { pending: 0, stop: null, subs: new Set() };
|
|
230
|
+
function setBlackPendingGlobal(n) {
|
|
231
|
+
var value = Math.max(0, Number(n) || 0);
|
|
232
|
+
if (BLACK_UI.pending === value) return;
|
|
233
|
+
BLACK_UI.pending = value;
|
|
234
|
+
BLACK_UI.subs.forEach(function (fn) { try { fn(value); } catch (e) { /* ignore */ } });
|
|
235
|
+
}
|
|
236
|
+
function useBlackPending() {
|
|
237
|
+
var [pending, setPending] = useState(BLACK_UI.pending);
|
|
238
|
+
useEffect(function () {
|
|
239
|
+
var fn = function (n) { setPending(n); };
|
|
240
|
+
BLACK_UI.subs.add(fn);
|
|
241
|
+
setPending(BLACK_UI.pending);
|
|
242
|
+
return function () { BLACK_UI.subs.delete(fn); };
|
|
243
|
+
}, []);
|
|
244
|
+
return pending;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
224
248
|
function loadTheme() {
|
|
225
249
|
try { return localStorage.getItem(LS_THEME) === "light" ? "light" : "dark"; } catch (e) { return "dark"; }
|
|
226
250
|
}
|
|
@@ -423,6 +447,7 @@ window.__ModuleLoader__.load({
|
|
|
423
447
|
jobs: function (limit) { return req("GET", "jobs?limit=" + (limit || 30)); },
|
|
424
448
|
job: function (id) { return req("GET", "jobs/" + encodeURIComponent(id)); },
|
|
425
449
|
abort: function (id) { return req("POST", "jobs/" + encodeURIComponent(id) + "/abort", {}); },
|
|
450
|
+
blackCheck: function (body) { return req("POST", "black-check", body || {}); },
|
|
426
451
|
delJob: function (id) { return req("DELETE", "jobs/" + encodeURIComponent(id)); },
|
|
427
452
|
clearJobs: function (keepActive) { return req("POST", "jobs/clear", { keepActive: keepActive !== false }); },
|
|
428
453
|
images: function (q) { return req("GET", "images" + (q ? "?" + q : "")); },
|
|
@@ -504,6 +529,8 @@ window.__ModuleLoader__.load({
|
|
|
504
529
|
:root{--ac-bg:#151517;--ac-bg2:#1b1b1d;--ac-card:#1b1b1d;--ac-raised:#2a2a2c;--ac-line:#303033;--ac-line2:#3a3a3d;--ac-text:#ececee;--ac-text2:#c6c6ca;--ac-muted:#a8a8ad;--ac-dim:#8a8a90;--ac-accent:#b3bfd0;--ac-accent2:#dbe1ea;--ac-ok:#6fbf8f;--ac-warn:#d1ac72;--ac-err:#d18a8a;--ac-fs:12.5px;--ac-fs-s:11.5px;--ac-fs-l:11px;}
|
|
505
530
|
.ac-root{--ac-bg:#151517;--ac-bg2:#1b1b1d;--ac-card:#1b1b1d;--ac-raised:#2a2a2c;--ac-line:#303033;--ac-line2:#3a3a3d;--ac-text:#ececee;--ac-text2:#c6c6ca;--ac-muted:#a8a8ad;--ac-dim:#8a8a90;--ac-accent:#b3bfd0;--ac-accent2:#dbe1ea;--ac-ok:#6fbf8f;--ac-warn:#d1ac72;--ac-err:#d18a8a;--ac-fs:12.5px;--ac-fs-s:11.5px;--ac-fs-l:11px;box-sizing:border-box;height:100%;min-height:0;display:flex;flex-direction:column;background:var(--ac-bg);color:var(--ac-text);font-size:var(--ac-fs);line-height:1.5;font-family:var(--ac-font,inherit);color-scheme:dark;}
|
|
506
531
|
.ac-root *{box-sizing:border-box;}
|
|
532
|
+
.ac-blackbar{display:flex;align-items:center;gap:8px;padding:7px 10px;background:rgba(96,72,30,.5);border-bottom:1px solid var(--ac-line);color:var(--ac-text);font-size:var(--ac-fs-s);flex:0 0 auto;}
|
|
533
|
+
.ac-blackbar .ac-blackbar-txt{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
507
534
|
.ac-ballroot,.ac-ballroot *,.ac-overlay,.ac-overlay *{box-sizing:border-box;}
|
|
508
535
|
.ac-root ::selection{background:rgba(130,150,180,.35);}
|
|
509
536
|
.ac-root ::-webkit-scrollbar{width:8px;height:8px;}
|
|
@@ -1758,7 +1785,10 @@ window.__ModuleLoader__.load({
|
|
|
1758
1785
|
var shown = view || img || null;
|
|
1759
1786
|
if (shown && shown.id) lastFrameView = shown;
|
|
1760
1787
|
}, [view && view.id, view && view.url, img && img.id, img && img.url]);
|
|
1761
|
-
|
|
1788
|
+
// 黑图补图是 internal 任务,不在 visibleJobs 里,所以「有没有在跑」要单看这个计数:
|
|
1789
|
+
// 补图进行中也要能把菜单里的「请求生成」换成「终止」。
|
|
1790
|
+
var blackActive = useBlackPending() > 0;
|
|
1791
|
+
var live = blackActive || !!(job && (job.status === "delivering" || job.status === "parsing" || job.status === "running" || job.status === "queued"));
|
|
1762
1792
|
// anchor / turn 也进 key:旧图回填锚点后 id 不变,但跟随逻辑必须重建,
|
|
1763
1793
|
// 否则监听器里还是“没有锚点”的旧 imgs。
|
|
1764
1794
|
var imgKey = imgs.map(function (x) {
|
|
@@ -3693,36 +3723,114 @@ window.__ModuleLoader__.load({
|
|
|
3693
3723
|
});
|
|
3694
3724
|
}, "重新生成");
|
|
3695
3725
|
};
|
|
3696
|
-
|
|
3726
|
+
/**
|
|
3727
|
+
* 纯黑判定(纯函数,便于自检):从缩放后的 RGBA 采样里算,三条同时满足才算黑:
|
|
3728
|
+
* ① 暗像素(亮度 < 14)占比 ≥ 99.5%
|
|
3729
|
+
* ② 平均亮度 ≤ 8
|
|
3730
|
+
* ③ 亮点(亮度 > 110)占比 < 0.1%
|
|
3731
|
+
* 只满足 ① 会误伤夜景 / 黑底小主体——之前就是这么误判的。
|
|
3732
|
+
* 另外要求不透明像素覆盖 ≥ 50%:透明底的小图不是“黑图”,是透明图。
|
|
3733
|
+
*/
|
|
3734
|
+
function judgeNearlyBlack(d) {
|
|
3735
|
+
var pixels = d.length / 4;
|
|
3736
|
+
var opaque = 0;
|
|
3737
|
+
var dark = 0;
|
|
3738
|
+
var bright = 0;
|
|
3739
|
+
var lumSum = 0;
|
|
3740
|
+
for (var i = 0; i < d.length; i += 4) {
|
|
3741
|
+
if (d[i + 3] < 16) continue; // 透明像素不算
|
|
3742
|
+
opaque += 1;
|
|
3743
|
+
var lum = 0.2126 * d[i] + 0.7152 * d[i + 1] + 0.0722 * d[i + 2];
|
|
3744
|
+
lumSum += lum;
|
|
3745
|
+
if (lum < 14) dark += 1;
|
|
3746
|
+
else if (lum > 110) bright += 1;
|
|
3747
|
+
}
|
|
3748
|
+
var darkRatio = opaque ? dark / opaque : 0;
|
|
3749
|
+
var brightRatio = opaque ? bright / opaque : 0;
|
|
3750
|
+
var meanLum = opaque ? lumSum / opaque : 0;
|
|
3751
|
+
var coverage = pixels ? opaque / pixels : 0;
|
|
3752
|
+
var black = opaque >= 8 && coverage >= 0.5
|
|
3753
|
+
&& darkRatio >= 0.995 && meanLum <= 8 && brightRatio < 0.001;
|
|
3754
|
+
return { black: black, darkRatio: darkRatio, brightRatio: brightRatio, meanLum: meanLum, coverage: coverage, opaque: opaque };
|
|
3755
|
+
}
|
|
3756
|
+
// 纯黑图 PNG 压缩后极小(832×1216 的真黑图 ≈ 25 KB),超过这个体积不可能是纯黑。
|
|
3757
|
+
// 这条是硬保险:就算画布绘制 / 解码出了岔子,也不会把一张有内容的图当成黑图。
|
|
3758
|
+
var BLACK_MAX_BYTES = 120000;
|
|
3759
|
+
// 返回 judgeNearlyBlack 的统计对象;bust=true 时加个 nc 参数绕过浏览器缓存(确认二次判定用)。
|
|
3760
|
+
var isNearlyBlack = function (url, bust) {
|
|
3697
3761
|
return new Promise(function (resolve) {
|
|
3698
|
-
if (!url || typeof document === "undefined" || typeof Image === "undefined") return resolve(false);
|
|
3762
|
+
if (!url || typeof document === "undefined" || typeof Image === "undefined") return resolve({ black: false });
|
|
3699
3763
|
var probe = new Image();
|
|
3700
3764
|
probe.onload = function () {
|
|
3701
3765
|
try {
|
|
3702
|
-
|
|
3703
|
-
var
|
|
3766
|
+
if (!probe.naturalWidth || !probe.naturalHeight) return resolve({ black: false });
|
|
3767
|
+
var w = Math.max(1, Math.min(256, probe.naturalWidth));
|
|
3768
|
+
var h = Math.max(1, Math.min(256, probe.naturalHeight));
|
|
3704
3769
|
var canvas = document.createElement("canvas");
|
|
3705
3770
|
canvas.width = w;
|
|
3706
3771
|
canvas.height = h;
|
|
3707
3772
|
var g = canvas.getContext("2d");
|
|
3708
|
-
if (!g) return resolve(false);
|
|
3773
|
+
if (!g) return resolve({ black: false });
|
|
3709
3774
|
g.drawImage(probe, 0, 0, w, h);
|
|
3710
3775
|
var d = g.getImageData(0, 0, w, h).data;
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
for (var i = 0; i < d.length; i += 4) {
|
|
3714
|
-
if (d[i + 3] < 16) continue; // 透明像素不算黑
|
|
3715
|
-
total += 1;
|
|
3716
|
-
if (d[i] < 16 && d[i + 1] < 16 && d[i + 2] < 16) black += 1;
|
|
3717
|
-
}
|
|
3718
|
-
resolve(total > 0 && black / total >= 0.99);
|
|
3719
|
-
} catch (e) { resolve(false); }
|
|
3776
|
+
resolve(judgeNearlyBlack(d));
|
|
3777
|
+
} catch (e) { resolve({ black: false }); }
|
|
3720
3778
|
};
|
|
3721
|
-
probe.onerror = function () { resolve(false); };
|
|
3722
|
-
probe.src = url;
|
|
3779
|
+
probe.onerror = function () { resolve({ black: false }); };
|
|
3780
|
+
probe.src = url + (bust ? (url.indexOf("?") >= 0 ? "&" : "?") + "nc=" + Date.now() : "");
|
|
3781
|
+
});
|
|
3782
|
+
};
|
|
3783
|
+
// 判定入口:先看体积,再验像素;判黑要**两次都黑**才算数(第二次绕过缓存)。
|
|
3784
|
+
var probeBlackImage = function (im) {
|
|
3785
|
+
var url = im && im.url ? String(im.url) : "";
|
|
3786
|
+
var bytes = im && im.bytes ? Number(im.bytes) : 0;
|
|
3787
|
+
if (!url) return Promise.resolve({ black: false, skipped: "no-url" });
|
|
3788
|
+
if (bytes > BLACK_MAX_BYTES) return Promise.resolve({ black: false, skipped: "bytes", bytes: bytes, url: url });
|
|
3789
|
+
return isNearlyBlack(url).then(function (first) {
|
|
3790
|
+
if (!first.black) return { black: false, stats: first, url: url, bytes: bytes };
|
|
3791
|
+
return new Promise(function (resolve) { setTimeout(resolve, 900); })
|
|
3792
|
+
.then(function () { return isNearlyBlack(url, true); })
|
|
3793
|
+
.then(function (second) {
|
|
3794
|
+
return { black: !!second.black, stats: second, first: first, url: url, bytes: bytes, confirmed: true };
|
|
3795
|
+
});
|
|
3723
3796
|
});
|
|
3724
3797
|
};
|
|
3798
|
+
// 把判定结果写进 Host 日志(日志页可见),误判时能直接看到当时的统计。
|
|
3799
|
+
var reportBlackCheck = function (im, verdict) {
|
|
3800
|
+
try {
|
|
3801
|
+
var s = verdict && verdict.stats ? verdict.stats : {};
|
|
3802
|
+
api.blackCheck({
|
|
3803
|
+
imageId: im && im.id ? String(im.id) : "",
|
|
3804
|
+
fileName: im && im.fileName ? String(im.fileName) : "",
|
|
3805
|
+
bytes: verdict && verdict.bytes ? verdict.bytes : (im && im.bytes) || 0,
|
|
3806
|
+
black: !!(verdict && verdict.black),
|
|
3807
|
+
skipped: (verdict && verdict.skipped) || "",
|
|
3808
|
+
darkRatio: s.darkRatio, meanLum: s.meanLum, brightRatio: s.brightRatio, coverage: s.coverage,
|
|
3809
|
+
}).catch(function () { /* 诊断日志失败不影响主流程 */ });
|
|
3810
|
+
} catch (e) { /* ignore */ }
|
|
3811
|
+
};
|
|
3725
3812
|
var blackQueueRef = useRef([]);
|
|
3813
|
+
// 自动补图可以随时叫停:队列清空 + 正在跑的那单 abort,并且这一轮不再自动补。
|
|
3814
|
+
// blackStopRef 在下一轮新任务完成时复位,避免「点过一次停止后就再也不补」。
|
|
3815
|
+
var blackStopRef = useRef(false);
|
|
3816
|
+
var blackJobRef = useRef("");
|
|
3817
|
+
var syncBlackPending = function () {
|
|
3818
|
+
setBlackPendingGlobal(blackQueueRef.current.length + (blackBusyRef.current ? 1 : 0));
|
|
3819
|
+
};
|
|
3820
|
+
var stopBlack = function () {
|
|
3821
|
+
blackStopRef.current = true;
|
|
3822
|
+
blackQueueRef.current = [];
|
|
3823
|
+
var jid = blackJobRef.current;
|
|
3824
|
+
blackJobRef.current = "";
|
|
3825
|
+
syncBlackPending();
|
|
3826
|
+
showToast("已停止自动补图", "ok");
|
|
3827
|
+
if (jid) api.abort(jid).catch(function () { /* 任务可能已经结束了 */ });
|
|
3828
|
+
};
|
|
3829
|
+
// 面板上的「停止自动补图」通过这个入口回调过来。
|
|
3830
|
+
useEffect(function () {
|
|
3831
|
+
BLACK_UI.stop = stopBlack;
|
|
3832
|
+
return function () { if (BLACK_UI.stop === stopBlack) BLACK_UI.stop = null; };
|
|
3833
|
+
}, []);
|
|
3726
3834
|
/** 同一张图(同一套提示词)跨多次自动换种子仍用同一个 key,用来限制最多补几次。 */
|
|
3727
3835
|
var blackSlotKey = function (sourceJob, im) {
|
|
3728
3836
|
var sets = Array.isArray(sourceJob && sourceJob.sets) ? sourceJob.sets : [];
|
|
@@ -3738,18 +3846,24 @@ window.__ModuleLoader__.load({
|
|
|
3738
3846
|
};
|
|
3739
3847
|
var enqueueBlack = function (im, sourceJob, attempt, key, label) {
|
|
3740
3848
|
if (!im || !im.id || !im.url) return;
|
|
3849
|
+
if (blackStopRef.current) return;
|
|
3741
3850
|
blackQueueRef.current.push({
|
|
3742
3851
|
id: String(im.id), jobId: im.jobId || (sourceJob && sourceJob.id) || "",
|
|
3743
|
-
setIndex: im.setIndex, url: im.url,
|
|
3852
|
+
setIndex: im.setIndex, url: im.url, bytes: im.bytes || 0,
|
|
3853
|
+
attempt: attempt || 1, key: key || "", label: label || "",
|
|
3744
3854
|
});
|
|
3855
|
+
syncBlackPending();
|
|
3745
3856
|
};
|
|
3746
3857
|
var drainBlackQueue = function () {
|
|
3747
3858
|
if (blackBusyRef.current) return;
|
|
3748
3859
|
var next = blackQueueRef.current.shift();
|
|
3749
|
-
if (!next) return;
|
|
3860
|
+
if (!next) { syncBlackPending(); return; }
|
|
3861
|
+
if (blackStopRef.current) { blackQueueRef.current = []; syncBlackPending(); return; }
|
|
3750
3862
|
blackBusyRef.current = true;
|
|
3751
|
-
|
|
3752
|
-
|
|
3863
|
+
syncBlackPending();
|
|
3864
|
+
probeBlackImage(next).then(function (verdict) {
|
|
3865
|
+
reportBlackCheck(next, verdict);
|
|
3866
|
+
if (!verdict.black) return null;
|
|
3753
3867
|
if (next.attempt === 1) {
|
|
3754
3868
|
var nk = String(next.jobId || next.id || "");
|
|
3755
3869
|
if (!blackNotifiedRef.current.has(nk)) {
|
|
@@ -3767,17 +3881,32 @@ window.__ModuleLoader__.load({
|
|
|
3767
3881
|
mode: "auto",
|
|
3768
3882
|
sessionId: data && data.activeSessionId || "",
|
|
3769
3883
|
sessionTitle: data && data.activeSessionTitle || "",
|
|
3884
|
+
}).then(function (r) {
|
|
3885
|
+
blackJobRef.current = r && r.job && r.job.id ? String(r.job.id) : "";
|
|
3886
|
+
return r;
|
|
3770
3887
|
});
|
|
3771
3888
|
}).catch(function () { /* 黑图重生成失败不阻塞其它图 */ }).then(function () {
|
|
3772
3889
|
blackBusyRef.current = false;
|
|
3890
|
+
blackJobRef.current = "";
|
|
3891
|
+
syncBlackPending();
|
|
3773
3892
|
drainBlackQueue();
|
|
3774
3893
|
});
|
|
3775
3894
|
};
|
|
3776
3895
|
var checkBlackImages = function (sourceJob) {
|
|
3777
3896
|
if (!sourceJob || sourceJob.status !== "done") return;
|
|
3897
|
+
// 工作流页「图片输出相关 → 黑图自动替换」关掉就整个不检测(连已在队列里的也清掉)。
|
|
3898
|
+
if (data && data.activeWorkflow && data.activeWorkflow.blackRetry === false) {
|
|
3899
|
+
if (blackQueueRef.current.length || blackBusyRef.current) {
|
|
3900
|
+
blackQueueRef.current = [];
|
|
3901
|
+
syncBlackPending();
|
|
3902
|
+
}
|
|
3903
|
+
return;
|
|
3904
|
+
}
|
|
3778
3905
|
// 只处理刚完成的这一轮;翻旧记录 / 重启页面不追溯历史图。
|
|
3779
3906
|
var finished = Date.parse(sourceJob.finishedAt || "");
|
|
3780
3907
|
if (!Number.isFinite(finished) || Date.now() - finished > 120000) return;
|
|
3908
|
+
// 新一轮(非自动补图)开始时解除上一次的「停止」,否则停过一次就再也不会补了。
|
|
3909
|
+
if (sourceJob.regenMode !== "auto") blackStopRef.current = false;
|
|
3781
3910
|
|
|
3782
3911
|
if (sourceJob.regenMode === "auto") {
|
|
3783
3912
|
// 自动补图后继续检查:仍然黑就再换种子,最多 BLACK_MAX_RETRIES 次。
|
|
@@ -3788,8 +3917,9 @@ window.__ModuleLoader__.load({
|
|
|
3788
3917
|
if (!retried || !retried.id || !retried.url) return;
|
|
3789
3918
|
var slotKey = blackSlotKey(sourceJob, retried);
|
|
3790
3919
|
var attempt = Math.max(1, Number(blackRetryRef.current[slotKey]) || 1);
|
|
3791
|
-
|
|
3792
|
-
|
|
3920
|
+
probeBlackImage(retried).then(function (verdict) {
|
|
3921
|
+
reportBlackCheck(retried, verdict);
|
|
3922
|
+
if (!verdict.black) {
|
|
3793
3923
|
delete blackRetryRef.current[slotKey];
|
|
3794
3924
|
notify("黑图已替换为正常图", "ok");
|
|
3795
3925
|
// 替换已经落到图片上、也确认不是黑图了,最后再报出图完成。
|
|
@@ -3821,6 +3951,12 @@ window.__ModuleLoader__.load({
|
|
|
3821
3951
|
drainBlackQueue();
|
|
3822
3952
|
};
|
|
3823
3953
|
var stopJob = function () {
|
|
3954
|
+
// 图片框左下菜单的「终止」就是真正的终止:黑图补图是 internal 任务,
|
|
3955
|
+
// 一起停掉(清队列 + 中止正在跑的那单),否则点了终止它还在后台接着补。
|
|
3956
|
+
if (blackQueueRef.current.length || blackBusyRef.current) {
|
|
3957
|
+
stopBlack();
|
|
3958
|
+
return;
|
|
3959
|
+
}
|
|
3824
3960
|
if (!job || !job.id) return;
|
|
3825
3961
|
act(function () {
|
|
3826
3962
|
return api.abort(job.id).then(function () { showToast("已终止", "ok"); });
|
|
@@ -4547,6 +4683,8 @@ window.__ModuleLoader__.load({
|
|
|
4547
4683
|
});
|
|
4548
4684
|
var [toasts, setToasts] = useState([]);
|
|
4549
4685
|
var [conn, setConn] = useState(true);
|
|
4686
|
+
// 自动补图是 FloatBall 在跑,这里只订阅「还有几个在补」用来显示停止条。
|
|
4687
|
+
var blackPending = useBlackPending();
|
|
4550
4688
|
var [viewer, setViewer] = useState(null);
|
|
4551
4689
|
// 切到右侧栏其它标签时 BridgePanel 会卸载;memory 让回来时还在同一张图。
|
|
4552
4690
|
var bootSession = props && typeof props.sessionId === "string" ? props.sessionId : "";
|
|
@@ -5441,6 +5579,8 @@ window.__ModuleLoader__.load({
|
|
|
5441
5579
|
}).catch(function (e) { showToast("重新生成失败:" + (e.message || e), "err"); });
|
|
5442
5580
|
};
|
|
5443
5581
|
var fullStop = function () {
|
|
5582
|
+
// 全屏里的「终止」也一样:先把自动补图停掉,再中止当前任务。
|
|
5583
|
+
if (BLACK_UI.stop) BLACK_UI.stop();
|
|
5444
5584
|
if (!liveJob || !liveJob.id) return;
|
|
5445
5585
|
Promise.resolve().then(function () { return api.abort(liveJob.id); }).then(function () { showToast("已终止", "ok"); }).catch(function (e) { showToast("终止失败:" + (e.message || e), "err"); });
|
|
5446
5586
|
};
|
|
@@ -5809,6 +5949,11 @@ window.__ModuleLoader__.load({
|
|
|
5809
5949
|
};
|
|
5810
5950
|
|
|
5811
5951
|
return h("div", { className: cls("ac-root", fullImage && "fullimg", theme === "light" && "light") },
|
|
5952
|
+
blackPending > 0 ? h("div", { className: "ac-blackbar" },
|
|
5953
|
+
h("span", { className: "ac-blackbar-txt" },
|
|
5954
|
+
"检测到纯黑图,正在自动重新生成" + (blackPending > 1 ? "(队列 " + blackPending + " 个)" : "")),
|
|
5955
|
+
h(Btn, { small: true, variant: "danger", onClick: function () { if (BLACK_UI.stop) BLACK_UI.stop(); } }, "停止自动补图"),
|
|
5956
|
+
) : null,
|
|
5812
5957
|
fullImage
|
|
5813
5958
|
? h("div", {
|
|
5814
5959
|
className: cls("ac-fullview", square && "square"),
|
|
@@ -6843,7 +6988,7 @@ window.__ModuleLoader__.load({
|
|
|
6843
6988
|
h("div", { className: "ac-wfbar" },
|
|
6844
6989
|
h("span", { className: "ac-wfname", title: wf ? wf.name : "未选择" }, "工作流:" + (wf ? wf.name : "未选择")),
|
|
6845
6990
|
h("span", { style: { flex: 1 } }),
|
|
6846
|
-
job && job.cancellable ? h(Btn, { small: true, variant: "danger", onClick: function () { api.abort(job.id).then(function () { props.toast("已终止", "ok"); }).catch(function (e) { props.toast("终止失败:" + (e.message || e), "err"); }); } }, "终止") : null,
|
|
6991
|
+
job && job.cancellable ? h(Btn, { small: true, variant: "danger", onClick: function () { if (BLACK_UI.stop) BLACK_UI.stop(); api.abort(job.id).then(function () { props.toast("已终止", "ok"); }).catch(function (e) { props.toast("终止失败:" + (e.message || e), "err"); }); } }, "终止") : null,
|
|
6847
6992
|
h(Btn, {
|
|
6848
6993
|
variant: "primary", className: "ac-genbtn",
|
|
6849
6994
|
disabled: busy || !sets.length || !sets.some(function (s) { return s.positive; }) || !!(job && job.cancellable),
|
|
@@ -7363,6 +7508,21 @@ window.__ModuleLoader__.load({
|
|
|
7363
7508
|
var [dry, setDry] = useState(props.initialDry || null);
|
|
7364
7509
|
var [jsonOpen, setJsonOpen] = useState(props.initialJsonOpen === true);
|
|
7365
7510
|
var fileRef = useRef(null);
|
|
7511
|
+
// 「固定种子」文本框:null = 不在编辑(显示自动 / 已保存的值),字符串 = 正在输入的原文。
|
|
7512
|
+
var [seedDraft, setSeedDraft] = useState(null);
|
|
7513
|
+
var seedTimer = useRef(null);
|
|
7514
|
+
useEffect(function () {
|
|
7515
|
+
return function () { if (seedTimer.current) clearTimeout(seedTimer.current); };
|
|
7516
|
+
}, []);
|
|
7517
|
+
// 种子这类开关不走「保存」按钮也会立刻落库,否则改完忘了保存,出图还是老种子。
|
|
7518
|
+
var persistWf = function (p) {
|
|
7519
|
+
patch(p);
|
|
7520
|
+
if (!wf || !wf.id) return;
|
|
7521
|
+
if (seedTimer.current) clearTimeout(seedTimer.current);
|
|
7522
|
+
seedTimer.current = setTimeout(function () {
|
|
7523
|
+
api.updateWorkflow(wf.id, p).catch(function () { /* 落库失败不阻塞界面 */ });
|
|
7524
|
+
}, 400);
|
|
7525
|
+
};
|
|
7366
7526
|
|
|
7367
7527
|
var loadWf = function (wid) {
|
|
7368
7528
|
if (!wid) { setWf(null); setScan(null); setJsonText(""); return Promise.resolve(null); }
|
|
@@ -7425,6 +7585,7 @@ window.__ModuleLoader__.load({
|
|
|
7425
7585
|
size: wf ? wf.size : null,
|
|
7426
7586
|
sizeMode: wf ? wf.sizeMode : "manual",
|
|
7427
7587
|
randomSeed: wf ? wf.randomSeed : true,
|
|
7588
|
+
seed: wf && wf.seed !== undefined && wf.seed !== null && Number.isFinite(Number(wf.seed)) ? Math.round(Number(wf.seed)) : null,
|
|
7428
7589
|
pruneOutputs: wf ? wf.pruneOutputs : true,
|
|
7429
7590
|
onlyFirst: true,
|
|
7430
7591
|
};
|
|
@@ -7455,7 +7616,7 @@ window.__ModuleLoader__.load({
|
|
|
7455
7616
|
doSave(undefined, name);
|
|
7456
7617
|
};
|
|
7457
7618
|
var create = function () {
|
|
7458
|
-
setWf({ id: "", name: "新工作流", json: {}, mapping: {}, mappingSeeded: false, mappingVersion: 0, outputNodes: [], outputSelectionVersion: 2, size: null, sizeMode: "manual", randomSeed: true, pruneOutputs: true, onlyFirst: false, loraState: null });
|
|
7619
|
+
setWf({ id: "", name: "新工作流", json: {}, mapping: {}, mappingSeeded: false, mappingVersion: 0, outputNodes: [], outputSelectionVersion: 2, size: null, sizeMode: "manual", randomSeed: true, seed: null, lastSeed: null, pruneOutputs: true, onlyFirst: false, loraState: null });
|
|
7459
7620
|
setId("");
|
|
7460
7621
|
setJsonText("{}");
|
|
7461
7622
|
setScan(null);
|
|
@@ -7479,6 +7640,32 @@ window.__ModuleLoader__.load({
|
|
|
7479
7640
|
var patch = function (p) {
|
|
7480
7641
|
setWf(function (old) { return old ? Object.assign({}, old, p) : old; });
|
|
7481
7642
|
};
|
|
7643
|
+
// 固定种子:自动模式只读,显示上一轮实际用的种子;取消自动后可以手填,
|
|
7644
|
+
// 填的数字会随保存写进工作流,出图时覆盖每一处字面量 seed。
|
|
7645
|
+
var seedAuto = !wf || wf.randomSeed !== false;
|
|
7646
|
+
var seedDisplay = seedAuto
|
|
7647
|
+
? (wf && wf.lastSeed !== null && wf.lastSeed !== undefined ? String(wf.lastSeed) : "")
|
|
7648
|
+
: (wf && wf.seed !== null && wf.seed !== undefined ? String(wf.seed) : "");
|
|
7649
|
+
var editSeed = function (value) {
|
|
7650
|
+
setSeedDraft(value);
|
|
7651
|
+
var n = Number(value);
|
|
7652
|
+
if (String(value).trim() !== "" && isFinite(n) && n >= 0) patch({ seed: Math.round(n) });
|
|
7653
|
+
};
|
|
7654
|
+
var commitSeed = function (value) {
|
|
7655
|
+
setSeedDraft(null);
|
|
7656
|
+
var raw = String(value === undefined || value === null ? "" : value).trim();
|
|
7657
|
+
if (raw === "") { persistWf({ seed: null }); return; }
|
|
7658
|
+
var n = Number(raw);
|
|
7659
|
+
if (!isFinite(n)) { persistWf({ seed: null }); return; }
|
|
7660
|
+
persistWf({ seed: Math.max(0, Math.min(9007199254740991, Math.round(n))) });
|
|
7661
|
+
};
|
|
7662
|
+
var toggleRandomSeed = function (on) {
|
|
7663
|
+
if (on) { persistWf({ randomSeed: true }); return; }
|
|
7664
|
+
// 取消自动时顺手把「上一轮用的种子」带过来,省得再手敲一遍。
|
|
7665
|
+
var carry = wf.seed !== null && wf.seed !== undefined ? wf.seed
|
|
7666
|
+
: (wf.lastSeed !== null && wf.lastSeed !== undefined ? wf.lastSeed : null);
|
|
7667
|
+
persistWf({ randomSeed: false, seed: carry });
|
|
7668
|
+
};
|
|
7482
7669
|
var mapping = (wf && wf.mapping) || {};
|
|
7483
7670
|
var setMapping = function (nodeId, role) {
|
|
7484
7671
|
var next = Object.assign({}, mapping);
|
|
@@ -7787,11 +7974,31 @@ window.__ModuleLoader__.load({
|
|
|
7787
7974
|
h("div", { className: "ac-subtitle", style: { marginTop: 12 } }, "额外设置"),
|
|
7788
7975
|
h("div", { className: "ac-form" },
|
|
7789
7976
|
h("div", { className: "ac-row" },
|
|
7790
|
-
h(Switch, { checked: wf.randomSeed !== false, onChange: function (e) {
|
|
7977
|
+
h(Switch, { checked: wf.randomSeed !== false, onChange: function (e) { toggleRandomSeed(e.target.checked); } }, "每次换随机种子"),
|
|
7978
|
+
),
|
|
7979
|
+
h("div", { className: "ac-row" },
|
|
7980
|
+
h(Field, { label: "固定种子" }, h(TextInput, {
|
|
7981
|
+
value: seedDraft !== null ? seedDraft : seedDisplay,
|
|
7982
|
+
disabled: seedAuto,
|
|
7983
|
+
placeholder: seedAuto ? "自动(显示上一轮用的种子)" : "填一个整数,每轮都用它",
|
|
7984
|
+
title: seedAuto
|
|
7985
|
+
? "自动换种子时只读:显示上一轮实际用的种子;取消上面勾选即可编辑"
|
|
7986
|
+
: "取消自动后,这里填的数字会写进工作流,每轮都用它",
|
|
7987
|
+
onChange: function (e) { editSeed(e.target.value); },
|
|
7988
|
+
onBlur: function (e) { commitSeed(e.target.value); },
|
|
7989
|
+
onKeyDown: function (e) { if (e.key === "Enter") e.currentTarget.blur(); },
|
|
7990
|
+
})),
|
|
7991
|
+
h("span", { className: "ac-hint" }, seedAuto ? "自动:每轮随机" : "固定:每轮都用这个种子"),
|
|
7791
7992
|
),
|
|
7792
7993
|
h("div", { className: "ac-row" },
|
|
7793
7994
|
h(Switch, { checked: wf.pruneOutputs !== false, onChange: function (e) { patch({ pruneOutputs: e.target.checked }); } }, "提交前裁剪输出节点(只提交上面勾选的)"),
|
|
7794
7995
|
),
|
|
7996
|
+
h("div", { className: "ac-row" },
|
|
7997
|
+
h(Switch, { checked: wf.blackRetry !== false, onChange: function (e) { persistWf({ blackRetry: e.target.checked }); } }, "黑图自动替换"),
|
|
7998
|
+
h("span", { className: "ac-hint" }, wf.blackRetry !== false
|
|
7999
|
+
? "出图后检测到纯黑图就自动换种子重生成;进行中可在图片框菜单点「终止」"
|
|
8000
|
+
: "已关闭:纯黑图不再自动替换"),
|
|
8001
|
+
),
|
|
7795
8002
|
),
|
|
7796
8003
|
),
|
|
7797
8004
|
),
|
package/lib/comfy.js
CHANGED
|
@@ -631,7 +631,7 @@ function countOccurrences(hay, needle) {
|
|
|
631
631
|
*/
|
|
632
632
|
export function applyOverrides(
|
|
633
633
|
workflow,
|
|
634
|
-
{ positive = '', negative = '', mapping = {}, tokenEmpty = 'blank', randomSeed = true, loraState = null, size = null, sizeSource = '' } = {},
|
|
634
|
+
{ positive = '', negative = '', mapping = {}, tokenEmpty = 'blank', randomSeed = true, seed = null, loraState = null, size = null, sizeSource = '' } = {},
|
|
635
635
|
) {
|
|
636
636
|
let wf = structuredClone(workflow);
|
|
637
637
|
const report = {
|
|
@@ -978,6 +978,29 @@ export function applyOverrides(
|
|
|
978
978
|
});
|
|
979
979
|
}
|
|
980
980
|
}
|
|
981
|
+
} else if (seed !== null && seed !== undefined && Number.isFinite(Number(seed))) {
|
|
982
|
+
// 固定种子:把每一处字面量 seed 都写成用户填的这个值(工作台「固定种子」文本框)。
|
|
983
|
+
// 只动和上面同一类节点,连线出来的种子不碰。
|
|
984
|
+
const fixed = Math.round(Number(seed));
|
|
985
|
+
for (const [nodeId, node] of Object.entries(wf)) {
|
|
986
|
+
if (!node || typeof node !== 'object' || !node.inputs) continue;
|
|
987
|
+
if (!SEED_NODE_RE.test(String(node.class_type || ''))) continue;
|
|
988
|
+
for (const key of Object.keys(node.inputs)) {
|
|
989
|
+
if (key !== 'seed' && key !== 'noise_seed' && key !== 'rand_seed') continue;
|
|
990
|
+
const before = node.inputs[key];
|
|
991
|
+
if (typeof before !== 'number' || !Number.isFinite(before)) continue;
|
|
992
|
+
node.inputs[key] = fixed;
|
|
993
|
+
report.seeds.push({
|
|
994
|
+
nodeId,
|
|
995
|
+
classType: node.class_type,
|
|
996
|
+
title: node?._meta?.title || node?.title || '',
|
|
997
|
+
inputKey: key,
|
|
998
|
+
before,
|
|
999
|
+
after: fixed,
|
|
1000
|
+
fixed: true,
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
981
1004
|
}
|
|
982
1005
|
|
|
983
1006
|
/* ── 第 4 步:LoRA 开关 ──
|
package/lib/jobs.js
CHANGED
|
@@ -578,6 +578,7 @@ export function createJobsEngine({ ctx, sc, store, hub, getConfig, getActiveSess
|
|
|
578
578
|
mapping: wf.mapping || {},
|
|
579
579
|
tokenEmpty: 'blank',
|
|
580
580
|
randomSeed: job?.forceRandomSeed === true || wf.randomSeed !== false,
|
|
581
|
+
seed: wf.seed === null || wf.seed === undefined ? null : Number(wf.seed),
|
|
581
582
|
loraState: set.continuousShot
|
|
582
583
|
? withLoraStrength(wf, resolveLoraState(wf), set.continuousShot)
|
|
583
584
|
: resolveLoraState(wf),
|
|
@@ -743,6 +744,11 @@ export function createJobsEngine({ ctx, sc, store, hub, getConfig, getActiveSess
|
|
|
743
744
|
const wfN = prepared.workflow;
|
|
744
745
|
if (index === 0) job.overrideReport = prepared.report;
|
|
745
746
|
const usedSeeds = seedsOfWorkflow(wfN);
|
|
747
|
+
// 把这一轮实际用的种子记到工作流上:工作流页「固定种子」那一栏在自动模式下
|
|
748
|
+
// 显示的就是它,用户看到满意的一张可以直接取消自动、把这颗种子固定下来。
|
|
749
|
+
if (index === 0 && wf.id && usedSeeds.length) {
|
|
750
|
+
try { store.patchWorkflow(wf.id, { lastSeed: usedSeeds[0].value }); } catch { /* 记录失败不影响出图 */ }
|
|
751
|
+
}
|
|
746
752
|
if (runSets.length > 1) log('info', `第 ${index + 1}/${runSets.length} 套:排队提交`, { jobId: job.id });
|
|
747
753
|
const { promptId } = await queuePrompt(cfg.comfy.baseUrl, wfN, CLIENT_ID);
|
|
748
754
|
job.comfyPromptId = promptId;
|
package/lib/meta.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const PACKAGE_NAME = 'dsh-comfyui-rp';
|
|
8
8
|
export const PLUGIN_NAME = 'dsh-comfyui-rp';
|
|
9
|
-
export const VERSION = '0.2.
|
|
9
|
+
export const VERSION = '0.2.4';
|
|
10
10
|
|
|
11
11
|
/** 对外路由前缀。不能带尾斜杠(dsh-host-webserver 的 prefix 匹配规则)。 */
|
|
12
12
|
export const ROUTE_PATH = '/__airp_comfy';
|
package/lib/store.js
CHANGED
|
@@ -483,6 +483,9 @@ export function createStore(dataDir) {
|
|
|
483
483
|
size: null,
|
|
484
484
|
sizeMode: 'manual',
|
|
485
485
|
randomSeed: true,
|
|
486
|
+
seed: null,
|
|
487
|
+
lastSeed: null,
|
|
488
|
+
blackRetry: true,
|
|
486
489
|
pruneOutputs: true,
|
|
487
490
|
onlyFirst: false,
|
|
488
491
|
loraState: null,
|
|
@@ -553,6 +556,9 @@ export function createStore(dataDir) {
|
|
|
553
556
|
size: null,
|
|
554
557
|
sizeMode: 'manual',
|
|
555
558
|
randomSeed: true,
|
|
559
|
+
seed: null,
|
|
560
|
+
lastSeed: null,
|
|
561
|
+
blackRetry: true,
|
|
556
562
|
pruneOutputs: true,
|
|
557
563
|
onlyFirst: false,
|
|
558
564
|
loraState: null,
|
|
@@ -577,7 +583,7 @@ export function createStore(dataDir) {
|
|
|
577
583
|
const w = api.getWorkflow(id);
|
|
578
584
|
if (!w) return null;
|
|
579
585
|
const allow = ['name', 'json', 'mapping', 'mappingSeeded', 'mappingVersion', 'outputNodes', 'outputSelectionVersion', 'tokenEmpty', 'size', 'sizeMode',
|
|
580
|
-
'randomSeed', 'pruneOutputs', 'onlyFirst', 'loraState'];
|
|
586
|
+
'randomSeed', 'seed', 'lastSeed', 'pruneOutputs', 'onlyFirst', 'loraState', 'blackRetry'];
|
|
581
587
|
for (const k of allow) {
|
|
582
588
|
if (patch && Object.prototype.hasOwnProperty.call(patch, k)) w[k] = k === 'tokenEmpty' ? 'blank' : patch[k];
|
|
583
589
|
}
|
package/package.json
CHANGED