dsh-image-gen 0.2.1 → 0.2.3
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.en.md +172 -171
- package/README.md +182 -181
- package/README.zh-CN.md +172 -171
- package/lib/client.js +247 -38
- package/lib/client.js.map +1 -1
- package/lib/index.js +274 -1
- package/lib/types/client/image-ref.d.ts +8 -0
- package/lib/types/client/index.d.ts +22 -2
- package/lib/types/comfyui-workflow.d.ts +8 -0
- package/lib/types/comfyui.d.ts +15 -0
- package/lib/types/config.d.ts +14 -2
- package/lib/types/shared.d.ts +5 -1
- package/package.json +133 -131
- package/skills/install-dsh-image-gen/SKILL.md +2 -2
package/lib/client.js
CHANGED
|
@@ -17,19 +17,58 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
18
18
|
const DEFAULT_SEEDREAM_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3";
|
|
19
19
|
const DEFAULT_DASHSCOPE_ENDPOINT = "https://dashscope.aliyuncs.com/api/v1";
|
|
20
|
+
const DEFAULT_COMFYUI_BASE_URL = "http://127.0.0.1:8188";
|
|
21
|
+
const DEFAULT_COMFYUI_TIMEOUT_MS = 3e5;
|
|
20
22
|
const DEFAULT_MODELS = {
|
|
21
23
|
google: "gemini-3.1-flash-image",
|
|
22
24
|
openai: "gpt-image-2",
|
|
23
25
|
seedream: "doubao-seedream-5-0-260128",
|
|
24
|
-
dashscope: "qwen-image-3.0"
|
|
26
|
+
dashscope: "qwen-image-3.0",
|
|
27
|
+
comfyui: "API workflow"
|
|
25
28
|
};
|
|
26
29
|
const DEFAULT_BASE_URLS = {
|
|
27
30
|
google: DEFAULT_GOOGLE_ENDPOINT,
|
|
28
31
|
openai: DEFAULT_OPENAI_BASE_URL,
|
|
29
32
|
seedream: DEFAULT_SEEDREAM_BASE_URL,
|
|
30
|
-
dashscope: DEFAULT_DASHSCOPE_ENDPOINT
|
|
33
|
+
dashscope: DEFAULT_DASHSCOPE_ENDPOINT,
|
|
34
|
+
comfyui: DEFAULT_COMFYUI_BASE_URL
|
|
31
35
|
};
|
|
32
36
|
//#endregion
|
|
37
|
+
//#region lib/types/comfyui-workflow.js
|
|
38
|
+
/** Pure ComfyUI API-workflow validation and placeholder injection. */
|
|
39
|
+
const COMFYUI_PROMPT_PLACEHOLDER = "{{prompt}}";
|
|
40
|
+
/** Validate imported JSON without exposing workflow graph details to callers. */
|
|
41
|
+
function validateComfyUIWorkflowJson(workflowJson) {
|
|
42
|
+
parseWorkflow(workflowJson);
|
|
43
|
+
}
|
|
44
|
+
function parseWorkflow(workflowJson) {
|
|
45
|
+
if (workflowJson.trim().length === 0) throw new Error("Import a ComfyUI API workflow JSON file in Settings before generating");
|
|
46
|
+
if (new TextEncoder().encode(workflowJson).byteLength > 5242880) throw new Error("ComfyUI workflow file must be no larger than 5 MB");
|
|
47
|
+
let value;
|
|
48
|
+
try {
|
|
49
|
+
value = JSON.parse(workflowJson);
|
|
50
|
+
} catch {
|
|
51
|
+
throw new Error("ComfyUI workflow file is not valid JSON");
|
|
52
|
+
}
|
|
53
|
+
if (!isRecord(value) || Object.keys(value).length === 0) throw new Error("ComfyUI workflow must be a non-empty API-format JSON object");
|
|
54
|
+
if (!Object.values(value).some((node) => {
|
|
55
|
+
const inputs = record(node)?.inputs;
|
|
56
|
+
return inputs !== void 0 && containsPromptPlaceholder(inputs);
|
|
57
|
+
})) throw new Error(`ComfyUI workflow must contain ${COMFYUI_PROMPT_PLACEHOLDER} in a text input`);
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
function containsPromptPlaceholder(value) {
|
|
61
|
+
if (typeof value === "string") return value.includes(COMFYUI_PROMPT_PLACEHOLDER);
|
|
62
|
+
if (Array.isArray(value)) return value.some(containsPromptPlaceholder);
|
|
63
|
+
return isRecord(value) && Object.values(value).some(containsPromptPlaceholder);
|
|
64
|
+
}
|
|
65
|
+
function isRecord(value) {
|
|
66
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
67
|
+
}
|
|
68
|
+
function record(value) {
|
|
69
|
+
return isRecord(value) ? value : void 0;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
33
72
|
//#region lib/types/client/gallery-store.js
|
|
34
73
|
const DB_NAME = "dsh_image_gen_db";
|
|
35
74
|
const DB_VERSION = 2;
|
|
@@ -889,8 +928,20 @@ window.__ModuleLoader__.load({
|
|
|
889
928
|
}
|
|
890
929
|
}
|
|
891
930
|
//#endregion
|
|
931
|
+
//#region lib/types/client/image-ref.js
|
|
932
|
+
/**
|
|
933
|
+
* Extract the verified durable image attachment from a completed tool result block.
|
|
934
|
+
* Supports legacy rc.2 `resultView.content` and modern DSH `block.content`.
|
|
935
|
+
*/
|
|
936
|
+
function imageRef(block) {
|
|
937
|
+
if (!("kind" in block)) return void 0;
|
|
938
|
+
const fromResultView = block.resultView?.card === "generic" ? block.resultView.content?.find((item) => item.type === "image") : void 0;
|
|
939
|
+
if (fromResultView?.type === "image") return fromResultView.attachment;
|
|
940
|
+
const fromBlockContent = Array.isArray(block.content) ? block.content.find((item) => item.type === "image") : void 0;
|
|
941
|
+
if (fromBlockContent?.type === "image") return fromBlockContent.attachment;
|
|
942
|
+
}
|
|
943
|
+
//#endregion
|
|
892
944
|
//#region lib/types/client/index.js
|
|
893
|
-
/** Web settings and generated-image cards contributed by the Bundle. */
|
|
894
945
|
const KEY_REF = {
|
|
895
946
|
google: "GEMINI_API_KEY",
|
|
896
947
|
openai: "OPENAI_API_KEY",
|
|
@@ -906,6 +957,7 @@ window.__ModuleLoader__.load({
|
|
|
906
957
|
providerOpenAI: "OpenAI / 中转站",
|
|
907
958
|
providerSeedream: "字节 Seedream",
|
|
908
959
|
providerDashScope: "阿里 DashScope (通义万相 / Qwen)",
|
|
960
|
+
providerComfyUI: "本地 ComfyUI",
|
|
909
961
|
apiKeyLabel: "{provider} API Key",
|
|
910
962
|
apiKeyPlaceholder: "留空即可保留已配置的 Key",
|
|
911
963
|
apiKeyHint: "安全保存为 {key};页面不会读回明文。",
|
|
@@ -916,7 +968,17 @@ window.__ModuleLoader__.load({
|
|
|
916
968
|
endpointHintOpenAI: "中转站请填其 OpenAI 兼容的 /v1 地址。",
|
|
917
969
|
endpointHintSeedream: "火山方舟兼容的 /api/v3 地址。",
|
|
918
970
|
endpointHintDashScope: "阿里云百炼 DashScope 官方接口地址。",
|
|
971
|
+
endpointHintComfyUI: "正在运行且 DSH Host 可以访问的 ComfyUI 地址,默认使用本机 8188 端口。",
|
|
919
972
|
model: "模型",
|
|
973
|
+
workflow: "API Workflow",
|
|
974
|
+
workflowImport: "导入 JSON 文件",
|
|
975
|
+
workflowReplace: "替换 JSON 文件",
|
|
976
|
+
workflowMissing: "尚未导入工作流",
|
|
977
|
+
workflowImported: "已导入 {name}",
|
|
978
|
+
workflowHint: "请从 ComfyUI 导出 API Format JSON,并在提示词输入位置写入 {{prompt}};可用 {{seed}} 作为随机种子。",
|
|
979
|
+
workflowTooLarge: "工作流文件不能超过 5 MB。",
|
|
980
|
+
timeout: "生成超时(秒)",
|
|
981
|
+
timeoutHint: "包括提交、等待和下载图片;默认 300 秒。",
|
|
920
982
|
saveToWorkspace: "保存到工作区",
|
|
921
983
|
saveToWorkspaceHint: "每次生成后,把图片文件保存到当前会话工作区。",
|
|
922
984
|
folder: "工作区文件夹",
|
|
@@ -946,6 +1008,7 @@ window.__ModuleLoader__.load({
|
|
|
946
1008
|
providerOpenAI: "OpenAI / Relay",
|
|
947
1009
|
providerSeedream: "ByteDance Seedream",
|
|
948
1010
|
providerDashScope: "Aliyun DashScope (Wanx / Qwen)",
|
|
1011
|
+
providerComfyUI: "Local ComfyUI",
|
|
949
1012
|
apiKeyLabel: "{provider} API Key",
|
|
950
1013
|
apiKeyPlaceholder: "Leave empty to keep configured key",
|
|
951
1014
|
apiKeyHint: "Securely saved as {key}; never read back in plaintext.",
|
|
@@ -956,7 +1019,17 @@ window.__ModuleLoader__.load({
|
|
|
956
1019
|
endpointHintOpenAI: "OpenAI-compatible /v1 base URL for relays.",
|
|
957
1020
|
endpointHintSeedream: "Volcengine Ark compatible /api/v3 base URL.",
|
|
958
1021
|
endpointHintDashScope: "Official Aliyun DashScope endpoint.",
|
|
1022
|
+
endpointHintComfyUI: "A running ComfyUI server reachable by the DSH Host; the default points to port 8188 on this computer.",
|
|
959
1023
|
model: "Model",
|
|
1024
|
+
workflow: "API Workflow",
|
|
1025
|
+
workflowImport: "Import JSON file",
|
|
1026
|
+
workflowReplace: "Replace JSON file",
|
|
1027
|
+
workflowMissing: "No workflow imported",
|
|
1028
|
+
workflowImported: "Imported {name}",
|
|
1029
|
+
workflowHint: "Export an API Format JSON from ComfyUI and place {{prompt}} in its prompt input. {{seed}} is available for a random seed.",
|
|
1030
|
+
workflowTooLarge: "Workflow files must be no larger than 5 MB.",
|
|
1031
|
+
timeout: "Generation timeout (seconds)",
|
|
1032
|
+
timeoutHint: "Covers submission, waiting, and image download; defaults to 300 seconds.",
|
|
960
1033
|
saveToWorkspace: "Save to workspace",
|
|
961
1034
|
saveToWorkspaceHint: "Write each generated image as a file into the session workspace.",
|
|
962
1035
|
folder: "Workspace folder",
|
|
@@ -996,6 +1069,12 @@ window.__ModuleLoader__.load({
|
|
|
996
1069
|
.dsh-ig-input{box-sizing:border-box;width:100%;padding:8px 12px;font-size:13px;border:1px solid var(--dsw-alias-border-l2,#d7dbe0);border-radius:8px;background:var(--dsw-alias-bg-layer-3,transparent);color:inherit;outline:none;transition:border-color .15s}
|
|
997
1070
|
.dsh-ig-input:focus{border-color:var(--dsw-alias-brand-primary,#4c78ff)}
|
|
998
1071
|
.dsh-ig-input-group{display:flex;gap:8px;align-items:center}
|
|
1072
|
+
.dsh-ig-file-row{display:flex;align-items:center;gap:10px;min-width:0}
|
|
1073
|
+
.dsh-ig-file-input{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;clip-path:inset(50%)}
|
|
1074
|
+
.dsh-ig-file-button{appearance:none;flex:none;border:1px solid var(--dsw-alias-border-l2,#d7dbe0);border-radius:8px;padding:7px 12px;background:var(--dsw-alias-bg-layer-3,#f9fafb);color:var(--dsw-alias-label-secondary,inherit);font-size:13px;cursor:pointer;transition:background .15s,border-color .15s}
|
|
1075
|
+
.dsh-ig-file-button:hover{background:var(--dsw-alias-bg-layer-2,#edf0f3);border-color:var(--dsw-alias-label-dimmed,#9ca3af)}
|
|
1076
|
+
.dsh-ig-file-button:focus-within{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:2px}
|
|
1077
|
+
.dsh-ig-file-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary,inherit);font-size:12px}
|
|
999
1078
|
.dsh-ig-btn-reset{appearance:none;border:1px solid var(--dsw-alias-border-l2,#d7dbe0);border-radius:8px;padding:7px 12px;background:var(--dsw-alias-bg-layer-3,#f9fafb);color:var(--dsw-alias-label-secondary,inherit);font:inherit;font-size:13px;cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s}
|
|
1000
1079
|
.dsh-ig-btn-reset:hover{background:var(--dsw-alias-bg-layer-2,#edf0f3);border-color:var(--dsw-alias-label-dimmed,#9ca3af)}
|
|
1001
1080
|
.dsh-ig-hint,.dsh-ig-status{margin:0;color:var(--dsw-alias-label-tertiary,#7b818b);font-size:12px;line-height:1.4}
|
|
@@ -1085,7 +1164,6 @@ window.__ModuleLoader__.load({
|
|
|
1085
1164
|
];
|
|
1086
1165
|
/** Mount the settings card, generated-image card, and native conversation gallery view. */
|
|
1087
1166
|
function apply(ctx) {
|
|
1088
|
-
const { api } = ctx.get("connection");
|
|
1089
1167
|
const scope = ctx.settingsScope.bind({ namespace: IMAGE_GENERATION_NAMESPACE });
|
|
1090
1168
|
const locale = ctx.get("locale");
|
|
1091
1169
|
ctx.effect(() => {
|
|
@@ -1098,15 +1176,27 @@ window.__ModuleLoader__.load({
|
|
|
1098
1176
|
};
|
|
1099
1177
|
}, "dsh-image-gen: styles");
|
|
1100
1178
|
const register = ctx.slots.register.bind(ctx.slots);
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1179
|
+
const injectSettingsItem = (owner, credentials) => {
|
|
1180
|
+
const ownerRegister = owner.slots.register.bind(owner.slots);
|
|
1181
|
+
owner.slots.inject("settings.plugin.item", () => ownerRegister({
|
|
1182
|
+
name: "settings.plugin.item",
|
|
1183
|
+
key: IMAGE_GENERATION_NAMESPACE,
|
|
1184
|
+
inject: () => ({
|
|
1185
|
+
scope,
|
|
1186
|
+
credentials,
|
|
1187
|
+
locale
|
|
1188
|
+
})
|
|
1189
|
+
}, ImageGenerationSettingsCard));
|
|
1190
|
+
};
|
|
1191
|
+
const remoteCredentials = asCredentialsRemote(ctx.get("remote.credentials"));
|
|
1192
|
+
const legacyCredentials = credentialsFromLegacyConnection(ctx.get("connection"));
|
|
1193
|
+
if (remoteCredentials !== void 0) injectSettingsItem(ctx, remoteCredentials);
|
|
1194
|
+
else if (legacyCredentials !== void 0) injectSettingsItem(ctx, legacyCredentials);
|
|
1195
|
+
else ctx.inject(["remote.credentials"], (remoteCtx) => {
|
|
1196
|
+
const credentials = asCredentialsRemote(remoteCtx.get("remote.credentials"));
|
|
1197
|
+
if (credentials === void 0) throw new Error("dsh-image-gen: remote.credentials has an incompatible interface");
|
|
1198
|
+
injectSettingsItem(remoteCtx, credentials);
|
|
1199
|
+
});
|
|
1110
1200
|
ctx.slots.inject("tool.call.toolview", () => register({
|
|
1111
1201
|
name: "tool.call.toolview",
|
|
1112
1202
|
key: "generate_image",
|
|
@@ -1127,6 +1217,36 @@ window.__ModuleLoader__.load({
|
|
|
1127
1217
|
inject: () => ({ locale })
|
|
1128
1218
|
}, GalleryViewTab));
|
|
1129
1219
|
}
|
|
1220
|
+
function asCredentialsRemote(value) {
|
|
1221
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
1222
|
+
const candidate = value;
|
|
1223
|
+
return typeof candidate.describe === "function" && typeof candidate.set === "function" ? candidate : void 0;
|
|
1224
|
+
}
|
|
1225
|
+
function credentialsFromLegacyConnection(value) {
|
|
1226
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
1227
|
+
const credentials = value.api?.credentials;
|
|
1228
|
+
if (credentials === void 0 || typeof credentials.describe !== "function" || typeof credentials.set !== "function") return void 0;
|
|
1229
|
+
const legacy = credentials;
|
|
1230
|
+
return {
|
|
1231
|
+
async describe(refs) {
|
|
1232
|
+
const response = await legacy.describe({ refs });
|
|
1233
|
+
return response.result.ok ? {
|
|
1234
|
+
ok: true,
|
|
1235
|
+
value: response.result.value.credentials
|
|
1236
|
+
} : { ok: false };
|
|
1237
|
+
},
|
|
1238
|
+
async set(ref, credentialValue) {
|
|
1239
|
+
const response = await legacy.set({
|
|
1240
|
+
ref,
|
|
1241
|
+
value: credentialValue
|
|
1242
|
+
});
|
|
1243
|
+
return response.result.ok ? { ok: true } : {
|
|
1244
|
+
ok: false,
|
|
1245
|
+
error: response.result.error
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1130
1250
|
/** Edit provider settings and its write-only API credential. */
|
|
1131
1251
|
function ImageGenerationSettingsCard(props) {
|
|
1132
1252
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -1135,6 +1255,9 @@ window.__ModuleLoader__.load({
|
|
|
1135
1255
|
const [provider, setProvider] = (0, react.useState)("google");
|
|
1136
1256
|
const [model, setModel] = (0, react.useState)("");
|
|
1137
1257
|
const [baseURL, setBaseURL] = (0, react.useState)("");
|
|
1258
|
+
const [workflowJson, setWorkflowJson] = (0, react.useState)("");
|
|
1259
|
+
const [workflowName, setWorkflowName] = (0, react.useState)("");
|
|
1260
|
+
const [timeoutSeconds, setTimeoutSeconds] = (0, react.useState)(DEFAULT_COMFYUI_TIMEOUT_MS / 1e3);
|
|
1138
1261
|
const [saveToWorkspace, setSaveToWorkspace] = (0, react.useState)(true);
|
|
1139
1262
|
const [workspaceFolder, setWorkspaceFolder] = (0, react.useState)("dsh-image-gen");
|
|
1140
1263
|
const [key, setKey] = (0, react.useState)("");
|
|
@@ -1158,7 +1281,8 @@ window.__ModuleLoader__.load({
|
|
|
1158
1281
|
google: t("providerGoogle"),
|
|
1159
1282
|
openai: t("providerOpenAI"),
|
|
1160
1283
|
seedream: t("providerSeedream"),
|
|
1161
|
-
dashscope: t("providerDashScope")
|
|
1284
|
+
dashscope: t("providerDashScope"),
|
|
1285
|
+
comfyui: t("providerComfyUI")
|
|
1162
1286
|
};
|
|
1163
1287
|
(0, react.useEffect)(() => {
|
|
1164
1288
|
const value = snapshot.value;
|
|
@@ -1166,13 +1290,21 @@ window.__ModuleLoader__.load({
|
|
|
1166
1290
|
setProvider(next);
|
|
1167
1291
|
setModel(modelOf(next, value));
|
|
1168
1292
|
setBaseURL(baseURLOf(next, value));
|
|
1293
|
+
setWorkflowJson(value?.comfyuiWorkflowJson ?? "");
|
|
1294
|
+
setWorkflowName(value?.comfyuiWorkflowName ?? "");
|
|
1295
|
+
setTimeoutSeconds(Math.max(1, Math.round((value?.comfyuiTimeoutMs ?? 3e5) / 1e3)));
|
|
1169
1296
|
setSaveToWorkspace(value?.saveToWorkspace ?? true);
|
|
1170
1297
|
setWorkspaceFolder(value?.workspaceFolder ?? "dsh-image-gen");
|
|
1171
1298
|
}, [snapshot]);
|
|
1172
1299
|
(0, react.useEffect)(() => {
|
|
1300
|
+
const keyRef = KEY_REF[provider];
|
|
1301
|
+
if (keyRef === void 0) {
|
|
1302
|
+
setConfigured(void 0);
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1173
1305
|
let active = true;
|
|
1174
|
-
props.credentials.describe(
|
|
1175
|
-
if (active) setConfigured(response.
|
|
1306
|
+
props.credentials.describe([keyRef]).then((response) => {
|
|
1307
|
+
if (active) setConfigured(response.ok ? response.value?.[keyRef]?.configured ?? false : void 0);
|
|
1176
1308
|
}).catch(() => {
|
|
1177
1309
|
if (active) setConfigured(void 0);
|
|
1178
1310
|
});
|
|
@@ -1186,16 +1318,23 @@ window.__ModuleLoader__.load({
|
|
|
1186
1318
|
setMessage("");
|
|
1187
1319
|
try {
|
|
1188
1320
|
await props.scope.set("provider", provider);
|
|
1189
|
-
|
|
1190
|
-
|
|
1321
|
+
if (provider === "comfyui") {
|
|
1322
|
+
validateComfyUIWorkflowJson(workflowJson);
|
|
1323
|
+
await props.scope.set("comfyuiBaseURL", baseURL);
|
|
1324
|
+
await props.scope.set("comfyuiWorkflowJson", workflowJson);
|
|
1325
|
+
await props.scope.set("comfyuiWorkflowName", workflowName);
|
|
1326
|
+
await props.scope.set("comfyuiTimeoutMs", Math.max(1, Math.round(timeoutSeconds)) * 1e3);
|
|
1327
|
+
} else {
|
|
1328
|
+
await props.scope.set(provider === "google" ? "googleModel" : provider === "openai" ? "openaiModel" : provider === "seedream" ? "seedreamModel" : "dashscopeModel", model);
|
|
1329
|
+
await props.scope.set(provider === "google" ? "googleEndpoint" : provider === "openai" ? "openaiBaseURL" : provider === "seedream" ? "seedreamBaseURL" : "dashscopeEndpoint", baseURL);
|
|
1330
|
+
}
|
|
1191
1331
|
await props.scope.set("saveToWorkspace", saveToWorkspace);
|
|
1192
1332
|
await props.scope.set("workspaceFolder", workspaceFolder.trim());
|
|
1193
1333
|
if (key.trim().length > 0) {
|
|
1194
|
-
const
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
if (!response.result.ok) throw new Error(response.result.error.message);
|
|
1334
|
+
const keyRef = KEY_REF[provider];
|
|
1335
|
+
if (keyRef === void 0) throw new Error("ComfyUI does not use an API key in this version");
|
|
1336
|
+
const response = await props.credentials.set(keyRef, key.trim());
|
|
1337
|
+
if (!response.ok) throw new Error(response.error?.message ?? "Failed to save API key");
|
|
1199
1338
|
setKey("");
|
|
1200
1339
|
setConfigured(true);
|
|
1201
1340
|
}
|
|
@@ -1207,6 +1346,23 @@ window.__ModuleLoader__.load({
|
|
|
1207
1346
|
}
|
|
1208
1347
|
};
|
|
1209
1348
|
const keyStatus = configured === void 0 ? t("checkingKey") : configured ? t("keyConfigured") : t("keyNotConfigured");
|
|
1349
|
+
const workflowStatus = workflowName.length > 0 ? t("workflowImported", { name: workflowName }) : t("workflowMissing");
|
|
1350
|
+
const importWorkflow = async (event) => {
|
|
1351
|
+
const file = event.target.files?.[0];
|
|
1352
|
+
event.target.value = "";
|
|
1353
|
+
if (file === void 0) return;
|
|
1354
|
+
setMessage("");
|
|
1355
|
+
try {
|
|
1356
|
+
if (file.size > 5242880) throw new Error(t("workflowTooLarge"));
|
|
1357
|
+
const json = await file.text();
|
|
1358
|
+
validateComfyUIWorkflowJson(json);
|
|
1359
|
+
setWorkflowJson(json);
|
|
1360
|
+
setWorkflowName(file.name);
|
|
1361
|
+
setMessage(t("workflowImported", { name: file.name }));
|
|
1362
|
+
} catch (cause) {
|
|
1363
|
+
setMessage(cause instanceof Error ? cause.message : String(cause));
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1210
1366
|
return (0, react_jsx_runtime.jsxs)("li", {
|
|
1211
1367
|
className: `dsh-ig-card ${open ? "dsh-ig-card-open" : ""}`,
|
|
1212
1368
|
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -1279,6 +1435,10 @@ window.__ModuleLoader__.load({
|
|
|
1279
1435
|
(0, react_jsx_runtime.jsx)("option", {
|
|
1280
1436
|
value: "dashscope",
|
|
1281
1437
|
children: t("providerDashScope")
|
|
1438
|
+
}),
|
|
1439
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1440
|
+
value: "comfyui",
|
|
1441
|
+
children: t("providerComfyUI")
|
|
1282
1442
|
})
|
|
1283
1443
|
]
|
|
1284
1444
|
}),
|
|
@@ -1288,7 +1448,7 @@ window.__ModuleLoader__.load({
|
|
|
1288
1448
|
})
|
|
1289
1449
|
]
|
|
1290
1450
|
}),
|
|
1291
|
-
(0, react_jsx_runtime.jsxs)("label", {
|
|
1451
|
+
provider !== "comfyui" ? (0, react_jsx_runtime.jsxs)("label", {
|
|
1292
1452
|
className: "dsh-ig-field",
|
|
1293
1453
|
children: [
|
|
1294
1454
|
(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1307,10 +1467,10 @@ window.__ModuleLoader__.load({
|
|
|
1307
1467
|
}),
|
|
1308
1468
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1309
1469
|
className: "dsh-ig-hint",
|
|
1310
|
-
children: t("apiKeyHint", { key: KEY_REF[provider] })
|
|
1470
|
+
children: t("apiKeyHint", { key: KEY_REF[provider] ?? "" })
|
|
1311
1471
|
})
|
|
1312
1472
|
]
|
|
1313
|
-
}),
|
|
1473
|
+
}) : null,
|
|
1314
1474
|
(0, react_jsx_runtime.jsxs)("label", {
|
|
1315
1475
|
className: "dsh-ig-field",
|
|
1316
1476
|
children: [
|
|
@@ -1340,11 +1500,11 @@ window.__ModuleLoader__.load({
|
|
|
1340
1500
|
}),
|
|
1341
1501
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1342
1502
|
className: "dsh-ig-hint",
|
|
1343
|
-
children: provider === "google" ? t("endpointHintGoogle") : provider === "openai" ? t("endpointHintOpenAI") : provider === "seedream" ? t("endpointHintSeedream") : t("endpointHintDashScope")
|
|
1503
|
+
children: provider === "google" ? t("endpointHintGoogle") : provider === "openai" ? t("endpointHintOpenAI") : provider === "seedream" ? t("endpointHintSeedream") : provider === "dashscope" ? t("endpointHintDashScope") : t("endpointHintComfyUI")
|
|
1344
1504
|
})
|
|
1345
1505
|
]
|
|
1346
1506
|
}),
|
|
1347
|
-
(0, react_jsx_runtime.jsxs)("label", {
|
|
1507
|
+
provider !== "comfyui" ? (0, react_jsx_runtime.jsxs)("label", {
|
|
1348
1508
|
className: "dsh-ig-field",
|
|
1349
1509
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1350
1510
|
className: "dsh-ig-label",
|
|
@@ -1357,7 +1517,61 @@ window.__ModuleLoader__.load({
|
|
|
1357
1517
|
},
|
|
1358
1518
|
required: true
|
|
1359
1519
|
})]
|
|
1360
|
-
}),
|
|
1520
|
+
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
1521
|
+
className: "dsh-ig-field",
|
|
1522
|
+
children: [
|
|
1523
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1524
|
+
className: "dsh-ig-label",
|
|
1525
|
+
children: t("workflow")
|
|
1526
|
+
}),
|
|
1527
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1528
|
+
className: "dsh-ig-file-row",
|
|
1529
|
+
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
1530
|
+
className: "dsh-ig-file-button",
|
|
1531
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
1532
|
+
className: "dsh-ig-file-input",
|
|
1533
|
+
type: "file",
|
|
1534
|
+
accept: ".json,application/json",
|
|
1535
|
+
onChange: (event) => {
|
|
1536
|
+
importWorkflow(event);
|
|
1537
|
+
}
|
|
1538
|
+
}), workflowName.length > 0 ? t("workflowReplace") : t("workflowImport")]
|
|
1539
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1540
|
+
className: "dsh-ig-file-name",
|
|
1541
|
+
title: workflowName,
|
|
1542
|
+
children: workflowName || t("workflowMissing")
|
|
1543
|
+
})]
|
|
1544
|
+
}),
|
|
1545
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1546
|
+
className: "dsh-ig-hint",
|
|
1547
|
+
children: t("workflowHint")
|
|
1548
|
+
})
|
|
1549
|
+
]
|
|
1550
|
+
}), (0, react_jsx_runtime.jsxs)("label", {
|
|
1551
|
+
className: "dsh-ig-field",
|
|
1552
|
+
children: [
|
|
1553
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1554
|
+
className: "dsh-ig-label",
|
|
1555
|
+
children: t("timeout")
|
|
1556
|
+
}),
|
|
1557
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
1558
|
+
className: "dsh-ig-input",
|
|
1559
|
+
type: "number",
|
|
1560
|
+
min: "1",
|
|
1561
|
+
max: "3600",
|
|
1562
|
+
step: "1",
|
|
1563
|
+
value: timeoutSeconds,
|
|
1564
|
+
onChange: (event) => {
|
|
1565
|
+
setTimeoutSeconds(Number(event.target.value));
|
|
1566
|
+
},
|
|
1567
|
+
required: true
|
|
1568
|
+
}),
|
|
1569
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1570
|
+
className: "dsh-ig-hint",
|
|
1571
|
+
children: t("timeoutHint")
|
|
1572
|
+
})
|
|
1573
|
+
]
|
|
1574
|
+
})] }),
|
|
1361
1575
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
1362
1576
|
className: "dsh-ig-field",
|
|
1363
1577
|
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
@@ -1403,11 +1617,11 @@ window.__ModuleLoader__.load({
|
|
|
1403
1617
|
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
1404
1618
|
className: "dsh-ig-status",
|
|
1405
1619
|
role: "status",
|
|
1406
|
-
children: message || keyStatus
|
|
1620
|
+
children: message || (provider === "comfyui" ? workflowStatus : keyStatus)
|
|
1407
1621
|
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1408
1622
|
className: "dsh-ig-save",
|
|
1409
1623
|
type: "submit",
|
|
1410
|
-
disabled: saving || !snapshot.writable,
|
|
1624
|
+
disabled: saving || !snapshot.writable || provider === "comfyui" && workflowJson.length === 0,
|
|
1411
1625
|
children: saving ? t("saving") : t("save")
|
|
1412
1626
|
})]
|
|
1413
1627
|
})
|
|
@@ -1656,18 +1870,13 @@ window.__ModuleLoader__.load({
|
|
|
1656
1870
|
});
|
|
1657
1871
|
}
|
|
1658
1872
|
function modelOf(provider, value) {
|
|
1659
|
-
const stored = provider === "google" ? value?.googleModel : provider === "openai" ? value?.openaiModel : provider === "seedream" ? value?.seedreamModel : value?.dashscopeModel;
|
|
1873
|
+
const stored = provider === "google" ? value?.googleModel : provider === "openai" ? value?.openaiModel : provider === "seedream" ? value?.seedreamModel : provider === "dashscope" ? value?.dashscopeModel : value?.comfyuiWorkflowName;
|
|
1660
1874
|
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_MODELS[provider];
|
|
1661
1875
|
}
|
|
1662
1876
|
function baseURLOf(provider, value) {
|
|
1663
|
-
const stored = provider === "google" ? value?.googleEndpoint : provider === "openai" ? value?.openaiBaseURL : provider === "seedream" ? value?.seedreamBaseURL : value?.dashscopeEndpoint;
|
|
1877
|
+
const stored = provider === "google" ? value?.googleEndpoint : provider === "openai" ? value?.openaiBaseURL : provider === "seedream" ? value?.seedreamBaseURL : provider === "dashscope" ? value?.dashscopeEndpoint : value?.comfyuiBaseURL;
|
|
1664
1878
|
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_BASE_URLS[provider];
|
|
1665
1879
|
}
|
|
1666
|
-
function imageRef(block) {
|
|
1667
|
-
if (!("kind" in block) || block.resultView?.card !== "generic") return void 0;
|
|
1668
|
-
const image = block.resultView.content?.find((item) => item.type === "image");
|
|
1669
|
-
return image?.type === "image" ? image.attachment : void 0;
|
|
1670
|
-
}
|
|
1671
1880
|
/** The workspace file path a completed image call saved, when the result meta carries one. */
|
|
1672
1881
|
function imageSavedTo(block) {
|
|
1673
1882
|
if (!("kind" in block)) return void 0;
|