dsh-comfyui-canvas 0.1.8 → 0.1.10
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/lib/client.js +213 -27
- package/lib/index.js +1835 -1784
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -10,6 +10,31 @@ var STATE_KEY = "__dsh_comfyui_state__";
|
|
|
10
10
|
// 当前生效的 ComfyUI 地址(默认;设置行更改后由 scope 覆盖)。
|
|
11
11
|
var activeBase = DEFAULT_BASE;
|
|
12
12
|
|
|
13
|
+
// 内存回退设置(2026-09-24):核心 #4587 移除客户端 settingsScope 服务后
|
|
14
|
+
// scope 缺失,分屏/rail 状态改用模块级内存态维持交互(页面会话内有效,
|
|
15
|
+
// 不跨重启持久化;host 侧无感知,画布专注标记等 host 联动降级)。
|
|
16
|
+
var MEMORY_STORE = { splitEnabledBySession: {}, railClosedBySession: {}, railWidth: DEFAULT_RAIL };
|
|
17
|
+
var MEMORY_SNAPSHOT = { value: MEMORY_STORE, writable: true };
|
|
18
|
+
var MEMORY_LISTENERS = [];
|
|
19
|
+
function memoryNotify() {
|
|
20
|
+
var copy = MEMORY_LISTENERS.slice();
|
|
21
|
+
for (var i = 0; i < copy.length; i++) { try { copy[i](); } catch (e) { /* listener error ignored */ } }
|
|
22
|
+
}
|
|
23
|
+
function memorySubscribe(cb) {
|
|
24
|
+
MEMORY_LISTENERS.push(cb);
|
|
25
|
+
return function () {
|
|
26
|
+
var idx = MEMORY_LISTENERS.indexOf(cb);
|
|
27
|
+
if (idx >= 0) MEMORY_LISTENERS.splice(idx, 1);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function memorySnapshot() { return MEMORY_SNAPSHOT; }
|
|
31
|
+
function memorySetPath(key, value) {
|
|
32
|
+
MEMORY_STORE[key] = value;
|
|
33
|
+
// 换新引用(useSyncExternalStore 用 Object.is 判定变更),仅真实变更时更新。
|
|
34
|
+
MEMORY_SNAPSHOT = { value: MEMORY_STORE, writable: true };
|
|
35
|
+
memoryNotify();
|
|
36
|
+
}
|
|
37
|
+
|
|
13
38
|
// 画布 iframe 永久挂载在 body 上,从不动它:标签切换只改显隐。
|
|
14
39
|
// 激活时用 fixed 定位对齐视图区(含顶栏),卸载时 visibility:hidden,
|
|
15
40
|
// iframe 文档保持存活,切换标签/插件重载都不会重新加载 ComfyUI。
|
|
@@ -523,9 +548,11 @@ function positionSplit(outer) {
|
|
|
523
548
|
function ComfyUISplitToggle(props) {
|
|
524
549
|
var sessionId = String(props.sessionId ?? "root");
|
|
525
550
|
var scope = props.scope;
|
|
551
|
+
var useScopeStore = scope && typeof scope.subscribe === "function" && typeof scope.getSnapshot === "function";
|
|
526
552
|
var snapshot = React.useSyncExternalStore(
|
|
527
|
-
function (cb) { return scope.subscribe(cb); },
|
|
528
|
-
function () { return scope.getSnapshot(); },
|
|
553
|
+
function (cb) { return useScopeStore ? scope.subscribe(cb) : memorySubscribe(cb); },
|
|
554
|
+
function () { return useScopeStore ? scope.getSnapshot() : memorySnapshot(); },
|
|
555
|
+
function () { return useScopeStore ? scope.getSnapshot() : memorySnapshot(); },
|
|
529
556
|
);
|
|
530
557
|
var value = (snapshot && snapshot.value) || {};
|
|
531
558
|
var map = value.splitEnabledBySession || {};
|
|
@@ -590,7 +617,11 @@ function ComfyUISplitToggle(props) {
|
|
|
590
617
|
drag.lastW = w;
|
|
591
618
|
// 即时生效(CSS 变量驱动挤压样式),并持久化 railWidth。
|
|
592
619
|
applyRailWidth(w);
|
|
593
|
-
|
|
620
|
+
if (scope && typeof scope.set === "function") {
|
|
621
|
+
scope.set("railWidth", w).catch(function () { /* recover handled by scope */ });
|
|
622
|
+
} else {
|
|
623
|
+
memorySetPath("railWidth", w);
|
|
624
|
+
}
|
|
594
625
|
// 拖动中实时重算画布覆盖层与把手位置(rail 左缘随宽度变化),
|
|
595
626
|
// 否则把手会停在起点,脱离 rail 边界。
|
|
596
627
|
var st = canvasState();
|
|
@@ -667,14 +698,22 @@ function ComfyUISplitToggle(props) {
|
|
|
667
698
|
var next = {};
|
|
668
699
|
for (var k in map) next[k] = map[k];
|
|
669
700
|
next[sessionId] = !enabled;
|
|
670
|
-
|
|
701
|
+
if (scope && typeof scope.set === "function") {
|
|
702
|
+
scope.set("splitEnabledBySession", next).catch(function () { /* recover handled by scope */ });
|
|
703
|
+
} else {
|
|
704
|
+
memorySetPath("splitEnabledBySession", next);
|
|
705
|
+
}
|
|
671
706
|
};
|
|
672
707
|
|
|
673
708
|
var toggleRail = function () {
|
|
674
709
|
var next = {};
|
|
675
710
|
for (var k in railClosedMap) next[k] = railClosedMap[k];
|
|
676
711
|
next[sessionId] = !railClosed;
|
|
677
|
-
|
|
712
|
+
if (scope && typeof scope.set === "function") {
|
|
713
|
+
scope.set("railClosedBySession", next).catch(function () { /* recover handled by scope */ });
|
|
714
|
+
} else {
|
|
715
|
+
memorySetPath("railClosedBySession", next);
|
|
716
|
+
}
|
|
678
717
|
};
|
|
679
718
|
|
|
680
719
|
// 顶栏折叠图标(buildCanvas 里 #dsh-comfy-rail-toggle)的点击事件 →
|
|
@@ -773,9 +812,11 @@ function ComfyUISplitToggle(props) {
|
|
|
773
812
|
|
|
774
813
|
function ComfyUISettingsRow(props) {
|
|
775
814
|
var scope = props.scope;
|
|
815
|
+
var useScopeStore = scope && typeof scope.subscribe === "function" && typeof scope.getSnapshot === "function";
|
|
776
816
|
var snapshot = React.useSyncExternalStore(
|
|
777
|
-
function (cb) { return scope.subscribe(cb); },
|
|
778
|
-
function () { return scope.getSnapshot(); },
|
|
817
|
+
function (cb) { return useScopeStore ? scope.subscribe(cb) : memorySubscribe(cb); },
|
|
818
|
+
function () { return useScopeStore ? scope.getSnapshot() : memorySnapshot(); },
|
|
819
|
+
function () { return useScopeStore ? scope.getSnapshot() : memorySnapshot(); },
|
|
779
820
|
);
|
|
780
821
|
var value = (snapshot && snapshot.value) || {};
|
|
781
822
|
var baseUrl = typeof value.baseUrl === "string" && value.baseUrl ? value.baseUrl : DEFAULT_BASE;
|
|
@@ -801,19 +842,32 @@ function ComfyUISettingsRow(props) {
|
|
|
801
842
|
var titleStyle = { fontSize: "13px", fontWeight: "600", color: "var(--dsw-alias-label-primary,#e8e8e8)" };
|
|
802
843
|
|
|
803
844
|
function setField(field, raw) {
|
|
804
|
-
if (!writable
|
|
845
|
+
if (!writable) return;
|
|
805
846
|
var next = raw === "" ? null : raw;
|
|
806
|
-
|
|
847
|
+
if (scope && typeof scope.set === "function") {
|
|
848
|
+
scope.set(field, next).catch(function () { /* recover handled by scope */ });
|
|
849
|
+
} else {
|
|
850
|
+
memorySetPath(field, next);
|
|
851
|
+
}
|
|
807
852
|
}
|
|
808
853
|
|
|
809
854
|
// 插件更新请求:settings 页「检查更新 / 更新」按钮把 updateRequest 写进
|
|
810
855
|
// settings,host watcher 执行 registry 检查或安装,并把结果写回
|
|
811
856
|
// pluginUpdate(status / latestVersion / message / checkedAt / updatedAt)。
|
|
812
857
|
function requestUpdate(action) {
|
|
813
|
-
if (!writable
|
|
814
|
-
|
|
858
|
+
if (!writable) return;
|
|
859
|
+
// 0.1.7 新核心下设置写入通道已迁移(settingsScope 移除):更新请在插件市场
|
|
860
|
+
// 操作。保留 scope 写入以兼容旧核心 host watcher;缺失时给出可见指引。
|
|
861
|
+
if (scope && typeof scope.set === "function") {
|
|
862
|
+
scope.set("updateRequest", { requested: true, action: action }).catch(function () { /* recover handled by scope */ });
|
|
863
|
+
} else {
|
|
864
|
+
setMarketNote("更新请在左侧「插件市场」中操作(DSH 0.1.7 起,本设置页的更新通道已随 settingsScope 一并迁移)。");
|
|
865
|
+
}
|
|
815
866
|
}
|
|
816
867
|
var upStatus = value.pluginUpdate || {};
|
|
868
|
+
var noteState = React.useState("");
|
|
869
|
+
var marketNote = noteState[0];
|
|
870
|
+
var setMarketNote = noteState[1];
|
|
817
871
|
var btnRowStyle = {
|
|
818
872
|
border: "1px solid var(--dsw-alias-border-l2,#3a3a40)",
|
|
819
873
|
borderRadius: "6px", padding: "6px 12px",
|
|
@@ -948,20 +1002,130 @@ function ComfyUISettingsRow(props) {
|
|
|
948
1002
|
background: "var(--dsw-alias-state-business-primary,#3b82f6)", color: "#fff",
|
|
949
1003
|
}),
|
|
950
1004
|
}, upStatus.status === "updating" ? "更新中…" : "更新到 v" + (upStatus.latestVersion || "")) : null,
|
|
1005
|
+
marketNote ? React.createElement("div", {
|
|
1006
|
+
style: { fontSize: "12px", color: "var(--dsw-alias-state-warn-primary,#d29922)", lineHeight: 1.5, whiteSpace: "pre-wrap" },
|
|
1007
|
+
}, marketNote) : null,
|
|
951
1008
|
),
|
|
952
1009
|
);
|
|
953
1010
|
}
|
|
954
1011
|
|
|
955
|
-
|
|
1012
|
+
// ComfyUI 独立标签视图(2026-09-25,替代分屏按钮):conversation.view
|
|
1013
|
+
// 容器内画布 —— iframe + 顶栏(刷新/重启/关闭)+ CanvasStatusOverlay 状态层。
|
|
1014
|
+
// 画布标签的 iframe + 顶栏持久实例(2026-09-25 v2):浏览器里 iframe 一旦脱离 DOM
|
|
1015
|
+
// (removeChild)其浏览上下文即被销毁,重挂必然重载。正确做法(旧 buildCanvas 同款):
|
|
1016
|
+
// 常驻挂到 body、永不 removeChild,激活时 fixed 定位到标签容器并 display:flex,
|
|
1017
|
+
// 离开时仅 display:none —— iframe 文档保活,切回秒开不重载。
|
|
1018
|
+
var TAB_CANVAS = null;
|
|
1019
|
+
|
|
1020
|
+
function CanvasTab(props) {
|
|
1021
|
+
var scope = props.scope;
|
|
1022
|
+
var hostRef = React.useRef(null);
|
|
1023
|
+
React.useEffect(function () {
|
|
1024
|
+
var host = hostRef.current;
|
|
1025
|
+
if (!host) return;
|
|
1026
|
+
var frame, bar, outer;
|
|
1027
|
+
if (!TAB_CANVAS) {
|
|
1028
|
+
outer = document.createElement("div");
|
|
1029
|
+
outer.style.cssText = "position:fixed;display:none;z-index:40;flex-direction:column;overflow:hidden;background:var(--dsw-alias-bg-layer-1,#232324);";
|
|
1030
|
+
bar = document.createElement("div");
|
|
1031
|
+
bar.style.cssText = "flex:none;display:flex;align-items:center;gap:12px;padding:0 12px;height:34px;background:var(--dsw-alias-bg-layer-1,#232324);border-bottom:1px solid var(--dsw-alias-border-l1,rgba(128,128,128,.25));font-size:13px;";
|
|
1032
|
+
var title = document.createElement("strong");
|
|
1033
|
+
title.textContent = "ComfyUI";
|
|
1034
|
+
bar.appendChild(title);
|
|
1035
|
+
var actionBtn = function (label, action) {
|
|
1036
|
+
var btn = document.createElement("button");
|
|
1037
|
+
btn.type = "button";
|
|
1038
|
+
btn.textContent = label;
|
|
1039
|
+
btn.title = label;
|
|
1040
|
+
btn.setAttribute("aria-label", label);
|
|
1041
|
+
btn.style.cssText = "background:transparent;border:none;color:var(--dsw-alias-state-business-primary,#8ab4f8);cursor:pointer;font-size:12px;padding:2px 6px;";
|
|
1042
|
+
btn.addEventListener("click", function () {
|
|
1043
|
+
document.dispatchEvent(new CustomEvent("dsh-comfyui-control", { bubbles: true, detail: { action: action } }));
|
|
1044
|
+
});
|
|
1045
|
+
return btn;
|
|
1046
|
+
};
|
|
1047
|
+
var refreshBtn = document.createElement("button");
|
|
1048
|
+
refreshBtn.type = "button";
|
|
1049
|
+
refreshBtn.textContent = "刷新";
|
|
1050
|
+
refreshBtn.title = "刷新画布";
|
|
1051
|
+
refreshBtn.setAttribute("aria-label", "刷新画布");
|
|
1052
|
+
refreshBtn.style.cssText = "background:transparent;border:none;color:var(--dsw-alias-state-business-primary,#8ab4f8);cursor:pointer;font-size:12px;padding:2px 6px;";
|
|
1053
|
+
refreshBtn.addEventListener("click", function () {
|
|
1054
|
+
var url;
|
|
1055
|
+
try { url = new URL(frame.src || activeBase); } catch (e) { url = null; }
|
|
1056
|
+
if (url) { url.searchParams.set("_dsh_reload", String(Date.now())); frame.src = url.toString(); }
|
|
1057
|
+
else { frame.src = (frame.src || activeBase).split("#")[0] + "?_dsh_reload=" + Date.now(); }
|
|
1058
|
+
});
|
|
1059
|
+
var right = document.createElement("div");
|
|
1060
|
+
right.style.cssText = "margin-left:auto;display:flex;align-items:center;gap:8px;";
|
|
1061
|
+
right.appendChild(refreshBtn);
|
|
1062
|
+
right.appendChild(actionBtn("重启", "restart"));
|
|
1063
|
+
right.appendChild(actionBtn("关闭", "stop"));
|
|
1064
|
+
bar.appendChild(right);
|
|
1065
|
+
frame = document.createElement("iframe");
|
|
1066
|
+
frame.title = "ComfyUI";
|
|
1067
|
+
frame.style.cssText = "flex:1;width:100%;border:none;display:block;background:#fff;";
|
|
1068
|
+
frame.src = activeBase;
|
|
1069
|
+
outer.appendChild(bar);
|
|
1070
|
+
outer.appendChild(frame);
|
|
1071
|
+
document.body.appendChild(outer);
|
|
1072
|
+
TAB_CANVAS = { outer: outer, frame: frame, bar: bar };
|
|
1073
|
+
} else {
|
|
1074
|
+
outer = TAB_CANVAS.outer;
|
|
1075
|
+
frame = TAB_CANVAS.frame;
|
|
1076
|
+
bar = TAB_CANVAS.bar;
|
|
1077
|
+
}
|
|
1078
|
+
var fit = function () {
|
|
1079
|
+
var h = hostRef.current;
|
|
1080
|
+
if (!h) return;
|
|
1081
|
+
var r = h.getBoundingClientRect();
|
|
1082
|
+
outer.style.left = r.left + "px";
|
|
1083
|
+
outer.style.top = r.top + "px";
|
|
1084
|
+
outer.style.width = r.width + "px";
|
|
1085
|
+
outer.style.height = r.height + "px";
|
|
1086
|
+
};
|
|
1087
|
+
fit();
|
|
1088
|
+
outer.style.display = "flex";
|
|
1089
|
+
var sync = function () {
|
|
1090
|
+
// frame.src 常带尾斜杠而 activeBase 不带——裸 !== 会让每次挂载都重置 src
|
|
1091
|
+
// 导致 ComfyUI 重载。归一化后仅当地址真正变化才更新。
|
|
1092
|
+
var want = String(activeBase).replace(/\/+$/, "");
|
|
1093
|
+
var cur = String(frame.src).replace(/\/+$/, "");
|
|
1094
|
+
if (cur !== want) frame.src = activeBase;
|
|
1095
|
+
};
|
|
1096
|
+
var unsub = scope && typeof scope.subscribe === "function" ? scope.subscribe(sync) : memorySubscribe(sync);
|
|
1097
|
+
// 画布标签全屏:隐藏常驻 composer(见 data-dsh-comfyui-fullscreen CSS)。
|
|
1098
|
+
document.documentElement.setAttribute("data-dsh-comfyui-fullscreen", "1");
|
|
1099
|
+
var ro = typeof ResizeObserver === "function" ? new ResizeObserver(fit) : null;
|
|
1100
|
+
if (ro) ro.observe(host);
|
|
1101
|
+
window.addEventListener("resize", fit);
|
|
1102
|
+
return function () {
|
|
1103
|
+
if (typeof unsub === "function") unsub();
|
|
1104
|
+
if (ro) ro.disconnect();
|
|
1105
|
+
window.removeEventListener("resize", fit);
|
|
1106
|
+
document.documentElement.removeAttribute("data-dsh-comfyui-fullscreen");
|
|
1107
|
+
// 仅隐藏,不摘除:iframe 浏览上下文保活,切回秒开。
|
|
1108
|
+
outer.style.display = "none";
|
|
1109
|
+
};
|
|
1110
|
+
}, []);
|
|
1111
|
+
return React.createElement("div", {
|
|
1112
|
+
ref: hostRef,
|
|
1113
|
+
style: { position: "relative", width: "100%", height: "100%", background: "var(--dsw-alias-bg-layer-1,#232324)", overflow: "hidden" },
|
|
1114
|
+
}, React.createElement(CanvasStatusOverlay, { scope: scope, sessionId: props.sessionId || "root" }));
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
var inject = ["slots", "configForms"];
|
|
956
1118
|
|
|
957
1119
|
function apply(ctx) {
|
|
958
|
-
// 2026-09-
|
|
959
|
-
//
|
|
960
|
-
//
|
|
961
|
-
var
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1120
|
+
// 2026-09-25: DSH 0.1.7 新核心 — 配置经 entry Config(profile 支撑)的
|
|
1121
|
+
// ConfigForm 读写(ctx.configForms.get(NAMESPACE),接口与旧 scope 一致);
|
|
1122
|
+
// 缺失时回退到模块级内存存储(会话内有效)。旧 settingsScope 服务已移除。
|
|
1123
|
+
var configForm = null;
|
|
1124
|
+
if (ctx.configForms && typeof ctx.configForms.get === "function") {
|
|
1125
|
+
try { configForm = ctx.configForms.get(NAMESPACE); } catch (e) { configForm = null; }
|
|
1126
|
+
}
|
|
1127
|
+
var scope = configForm && typeof configForm.subscribe === "function"
|
|
1128
|
+
&& typeof configForm.getSnapshot === "function" ? configForm : null;
|
|
965
1129
|
// 画布顶栏「重启 / 关闭 ComfyUI」按钮 → 写 settings.comfyControl,
|
|
966
1130
|
// host watcher 执行 kill / restart(结果由可达性探测反映到状态圆点)。
|
|
967
1131
|
document.addEventListener("dsh-comfyui-control", function (e) {
|
|
@@ -972,7 +1136,7 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
972
1136
|
});
|
|
973
1137
|
// 让画布 iframe 使用设置里的地址(并跟随变更)。
|
|
974
1138
|
var syncConfig = function () {
|
|
975
|
-
var snap = scope ? scope.getSnapshot() :
|
|
1139
|
+
var snap = scope ? scope.getSnapshot() : memorySnapshot();
|
|
976
1140
|
var v = snap && snap.value;
|
|
977
1141
|
var base = v && typeof v.baseUrl === "string" && v.baseUrl
|
|
978
1142
|
? v.baseUrl : DEFAULT_BASE;
|
|
@@ -983,7 +1147,11 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
983
1147
|
var state = canvasState();
|
|
984
1148
|
if (state.canvas) {
|
|
985
1149
|
var frame = state.canvas.querySelector("iframe");
|
|
986
|
-
|
|
1150
|
+
// frame.src 常带尾斜杠而 base 不带——裸 !== 恒真会把分屏 iframe 每次
|
|
1151
|
+
// 重载;归一化后仅地址真正变化才更新(与标签 sync 同款修复)。
|
|
1152
|
+
if (frame && String(frame.src).replace(/\/+$/, "") !== String(base).replace(/\/+$/, "")) {
|
|
1153
|
+
frame.src = base;
|
|
1154
|
+
}
|
|
987
1155
|
var link = state.canvas.querySelector("a");
|
|
988
1156
|
if (link) link.href = base;
|
|
989
1157
|
}
|
|
@@ -991,6 +1159,7 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
991
1159
|
applyRailWidth(v?.railWidth != null ? v.railWidth : DEFAULT_RAIL);
|
|
992
1160
|
};
|
|
993
1161
|
if (scope) scope.subscribe(syncConfig);
|
|
1162
|
+
else memorySubscribe(syncConfig);
|
|
994
1163
|
|
|
995
1164
|
// 全局看门狗:保证分屏标记与真实分屏态一致,杜绝 data-dsh-split-on 残留。
|
|
996
1165
|
// 挤压规则只在 html[data-dsh-split-on="1"] 时生效;一旦残留打上该属性,
|
|
@@ -1004,7 +1173,7 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
1004
1173
|
return tab ? (tab.textContent || "").trim() : "";
|
|
1005
1174
|
};
|
|
1006
1175
|
var enforceSplitMarker = function () {
|
|
1007
|
-
var snap = scope ? scope.getSnapshot() :
|
|
1176
|
+
var snap = scope ? scope.getSnapshot() : memorySnapshot();
|
|
1008
1177
|
var v = snap && snap.value ? snap.value : {};
|
|
1009
1178
|
var map = v.splitEnabledBySession || {};
|
|
1010
1179
|
var anyEnabled = Object.keys(map).some(function (k) { return map[k] === true; });
|
|
@@ -1033,6 +1202,7 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
1033
1202
|
};
|
|
1034
1203
|
var splitWatchdog = null;
|
|
1035
1204
|
if (scope) scope.subscribe(enforceSplitMarker);
|
|
1205
|
+
else memorySubscribe(enforceSplitMarker);
|
|
1036
1206
|
if (typeof MutationObserver === "function") {
|
|
1037
1207
|
splitWatchdog = new MutationObserver(enforceSplitMarker);
|
|
1038
1208
|
splitWatchdog.observe(
|
|
@@ -1057,7 +1227,7 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
1057
1227
|
var onNarrow = function () {
|
|
1058
1228
|
var now = narrowQuery ? narrowQuery.matches : (window.innerWidth < 1200);
|
|
1059
1229
|
if (now && !narrowWas) {
|
|
1060
|
-
var snap = scope ? scope.getSnapshot() :
|
|
1230
|
+
var snap = scope ? scope.getSnapshot() : memorySnapshot();
|
|
1061
1231
|
var v = snap && snap.value ? snap.value : {};
|
|
1062
1232
|
var map = v.splitEnabledBySession || {};
|
|
1063
1233
|
var anyEnabled = Object.keys(map).some(function (k) { return map[k] === true; });
|
|
@@ -1066,6 +1236,8 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
1066
1236
|
for (var k in map) cleared[k] = false;
|
|
1067
1237
|
if (scope && typeof scope.set === "function") {
|
|
1068
1238
|
scope.set("splitEnabledBySession", cleared).catch(function () { /* recover handled by scope */ });
|
|
1239
|
+
} else {
|
|
1240
|
+
memorySetPath("splitEnabledBySession", cleared);
|
|
1069
1241
|
}
|
|
1070
1242
|
// 同步清理 DOM,避免 settings 异步落地前挤压样式/画布残留。
|
|
1071
1243
|
document.documentElement.removeAttribute("data-dsh-split-on");
|
|
@@ -1129,6 +1301,11 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
1129
1301
|
' margin-left: auto !important;',
|
|
1130
1302
|
' overflow: hidden !important;',
|
|
1131
1303
|
'}',
|
|
1304
|
+
// ComfyUI 画布标签全屏:隐藏常驻 composer(ConversationRoot 的
|
|
1305
|
+
// composerSeat 不随视图切换消失,需插件隐藏,否则画布被输入框占底)。
|
|
1306
|
+
'html[data-dsh-comfyui-fullscreen="1"] [data-composer-seat] {',
|
|
1307
|
+
' display: none !important;',
|
|
1308
|
+
'}',
|
|
1132
1309
|
].join("\n");
|
|
1133
1310
|
document.head.appendChild(splitStyle);
|
|
1134
1311
|
}
|
|
@@ -1146,10 +1323,19 @@ var settingsScopeService = typeof ctx.get === "function" ? ctx.get("settingsScop
|
|
|
1146
1323
|
registerSettingsNavIcon(settingsLabel);
|
|
1147
1324
|
}
|
|
1148
1325
|
|
|
1149
|
-
//
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1326
|
+
// ComfyUI 独立标签(第一入口):conversation.view 画布视图(全屏画布)。
|
|
1327
|
+
ctx.slots.inject("conversation.view", function () {
|
|
1328
|
+
return ctx.slots.register({
|
|
1329
|
+
name: "conversation.view",
|
|
1330
|
+
id: "comfyui-canvas-view",
|
|
1331
|
+
order: 20,
|
|
1332
|
+
label: function () { return "ComfyUI"; },
|
|
1333
|
+
inject: function (sessionId) { return { sessionId: String(sessionId), scope: scope }; }
|
|
1334
|
+
}, CanvasTab);
|
|
1335
|
+
});
|
|
1336
|
+
|
|
1337
|
+
// 分屏入口(第二入口,2026-09-25 起与独立标签并存):会话标头右侧 utilities 槽。
|
|
1338
|
+
// 在「对话」标签内开分屏 = 画布左 + 主对话 rail 右(可折叠),与旧版行为一致。
|
|
1153
1339
|
ctx.slots.inject("conversation.session.header.utilities", function () {
|
|
1154
1340
|
return ctx.slots.register({
|
|
1155
1341
|
name: "conversation.session.header.utilities",
|