dsh-image-gen 0.4.1 → 0.5.1
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 +344 -338
- package/README.md +344 -338
- package/README.zh-CN.md +3 -3
- package/lib/client.js +1553 -379
- package/lib/client.js.map +1 -1
- package/lib/index.js +761 -214
- package/lib/types/client/conversation-image-revisions.d.ts +1 -1
- package/lib/types/client/gallery-view.d.ts +8 -2
- package/lib/types/client/index.d.ts +19 -1
- package/lib/types/client/inspiration-api.d.ts +10 -0
- package/lib/types/client/inspiration-detail.d.ts +14 -0
- package/lib/types/client/provider-pill.d.ts +49 -0
- package/lib/types/config.d.ts +39 -10
- package/lib/types/credentials.d.ts +15 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/inspiration/cache.d.ts +20 -0
- package/lib/types/inspiration/downloader.d.ts +27 -0
- package/lib/types/inspiration/routes.d.ts +13 -0
- package/lib/types/inspiration/sources.d.ts +33 -0
- package/lib/types/inspiration/types.d.ts +65 -0
- package/lib/types/inspiration/updater.d.ts +7 -0
- package/lib/types/openai-compatible.d.ts +1 -1
- package/lib/types/redact.d.ts +2 -0
- package/lib/types/shared.d.ts +37 -2
- package/lib/types/test-route.d.ts +73 -0
- package/package.json +1 -1
- package/skills/install-dsh-image-gen/SKILL.md +22 -22
package/lib/client.js
CHANGED
|
@@ -17,20 +17,52 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
const INSPIRATION_ROUTE = "/plugins/dsh-image-gen/inspiration";
|
|
18
18
|
/** Browser route used for saving generated images to workspace on demand. */
|
|
19
19
|
const SAVE_WORKSPACE_ROUTE = "/plugins/dsh-image-gen/save-workspace";
|
|
20
|
+
/** Browser route the settings card probes provider connectivity through. */
|
|
21
|
+
const TEST_CONNECTION_ROUTE = "/plugins/dsh-image-gen/test";
|
|
20
22
|
/** Namespace persisted through DSH Settings. */
|
|
21
23
|
const IMAGE_GENERATION_NAMESPACE = "image-generation";
|
|
24
|
+
/** Supported providers. */
|
|
25
|
+
const IMAGE_PROVIDERS = [
|
|
26
|
+
"google",
|
|
27
|
+
"openai",
|
|
28
|
+
"openai-compat",
|
|
29
|
+
"seedream",
|
|
30
|
+
"dashscope",
|
|
31
|
+
"xai",
|
|
32
|
+
"zhipu",
|
|
33
|
+
"comfyui"
|
|
34
|
+
];
|
|
22
35
|
/** Providers supported by the first browser workbench release. */
|
|
23
36
|
const CLOUD_IMAGE_PROVIDERS = [
|
|
24
37
|
"google",
|
|
25
38
|
"openai",
|
|
39
|
+
"openai-compat",
|
|
26
40
|
"seedream",
|
|
27
|
-
"dashscope"
|
|
41
|
+
"dashscope",
|
|
42
|
+
"xai",
|
|
43
|
+
"zhipu"
|
|
28
44
|
];
|
|
45
|
+
/** The credential reference each cloud provider's API key is stored under. */
|
|
46
|
+
const CLOUD_CREDENTIAL_REFS = {
|
|
47
|
+
google: "GEMINI_API_KEY",
|
|
48
|
+
openai: "OPENAI_API_KEY",
|
|
49
|
+
"openai-compat": "DSH_IMAGE_GEN_OPENAI_COMPAT_KEY",
|
|
50
|
+
seedream: "ARK_API_KEY",
|
|
51
|
+
dashscope: "DASHSCOPE_API_KEY",
|
|
52
|
+
xai: "XAI_API_KEY",
|
|
53
|
+
zhipu: "ZHIPUAI_API_KEY"
|
|
54
|
+
};
|
|
55
|
+
/** The credential reference storing this provider's API key, when it uses one. */
|
|
56
|
+
function cloudCredentialRef(provider) {
|
|
57
|
+
return CLOUD_IMAGE_PROVIDERS.includes(provider) ? CLOUD_CREDENTIAL_REFS[provider] : void 0;
|
|
58
|
+
}
|
|
29
59
|
/** Default endpoints and base URLs. */
|
|
30
60
|
const DEFAULT_GOOGLE_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/interactions";
|
|
31
61
|
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
32
62
|
const DEFAULT_SEEDREAM_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3";
|
|
33
63
|
const DEFAULT_DASHSCOPE_ENDPOINT = "https://dashscope.aliyuncs.com/api/v1";
|
|
64
|
+
const DEFAULT_XAI_BASE_URL = "https://api.x.ai/v1";
|
|
65
|
+
const DEFAULT_ZHIPU_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
|
34
66
|
const DEFAULT_COMFYUI_BASE_URL = "http://127.0.0.1:8188";
|
|
35
67
|
const DEFAULT_COMFYUI_TIMEOUT_MS = 3e5;
|
|
36
68
|
const DEFAULT_COMFYUI_WORKFLOW_LABEL = "API workflow";
|
|
@@ -39,6 +71,8 @@ window.__ModuleLoader__.load({
|
|
|
39
71
|
const DEFAULT_OPENAI_MODEL = "gpt-image-2";
|
|
40
72
|
const DEFAULT_SEEDREAM_MODEL = "doubao-seedream-5-0-260128";
|
|
41
73
|
const DEFAULT_DASHSCOPE_MODEL = "qwen-image-3.0";
|
|
74
|
+
const DEFAULT_XAI_MODEL = "grok-imagine-image";
|
|
75
|
+
const DEFAULT_ZHIPU_MODEL = "glm-image";
|
|
42
76
|
/** Named workflows, falling back to the legacy single-workflow fields when the list is empty. */
|
|
43
77
|
function resolveComfyUIWorkflows(source) {
|
|
44
78
|
const named = [];
|
|
@@ -85,15 +119,21 @@ window.__ModuleLoader__.load({
|
|
|
85
119
|
const DEFAULT_MODELS = {
|
|
86
120
|
google: DEFAULT_GOOGLE_MODEL,
|
|
87
121
|
openai: DEFAULT_OPENAI_MODEL,
|
|
122
|
+
"openai-compat": "",
|
|
88
123
|
seedream: DEFAULT_SEEDREAM_MODEL,
|
|
89
124
|
dashscope: DEFAULT_DASHSCOPE_MODEL,
|
|
125
|
+
xai: DEFAULT_XAI_MODEL,
|
|
126
|
+
zhipu: DEFAULT_ZHIPU_MODEL,
|
|
90
127
|
comfyui: DEFAULT_COMFYUI_WORKFLOW_LABEL
|
|
91
128
|
};
|
|
92
129
|
const DEFAULT_BASE_URLS = {
|
|
93
130
|
google: DEFAULT_GOOGLE_ENDPOINT,
|
|
94
131
|
openai: DEFAULT_OPENAI_BASE_URL,
|
|
132
|
+
"openai-compat": "",
|
|
95
133
|
seedream: DEFAULT_SEEDREAM_BASE_URL,
|
|
96
134
|
dashscope: DEFAULT_DASHSCOPE_ENDPOINT,
|
|
135
|
+
xai: DEFAULT_XAI_BASE_URL,
|
|
136
|
+
zhipu: DEFAULT_ZHIPU_BASE_URL,
|
|
97
137
|
comfyui: DEFAULT_COMFYUI_BASE_URL
|
|
98
138
|
};
|
|
99
139
|
//#endregion
|
|
@@ -4267,7 +4307,7 @@ window.__ModuleLoader__.load({
|
|
|
4267
4307
|
ratio: "auto",
|
|
4268
4308
|
quality: normalized === "1K" || normalized === "4K" ? normalized : "2K"
|
|
4269
4309
|
};
|
|
4270
|
-
if (provider === "openai") return {
|
|
4310
|
+
if (provider === "openai" || provider === "openai-compat" || provider === "xai" || provider === "zhipu") return {
|
|
4271
4311
|
ratio: ratioFromSize(normalized, "x"),
|
|
4272
4312
|
quality: "standard"
|
|
4273
4313
|
};
|
|
@@ -4309,9 +4349,12 @@ window.__ModuleLoader__.load({
|
|
|
4309
4349
|
tabTasks: "任务",
|
|
4310
4350
|
filterAllProviders: "全部提供商",
|
|
4311
4351
|
filterGoogle: "Google Gemini",
|
|
4312
|
-
filterOpenAI: "OpenAI
|
|
4352
|
+
filterOpenAI: "OpenAI",
|
|
4353
|
+
filterOpenAICompat: "OpenAI 兼容",
|
|
4313
4354
|
filterSeedream: "字节 Seedream",
|
|
4314
4355
|
filterDashScope: "阿里 DashScope",
|
|
4356
|
+
filterXAI: "xAI Grok",
|
|
4357
|
+
filterZhipu: "智谱 GLM",
|
|
4315
4358
|
filterComfyUI: "本地 ComfyUI",
|
|
4316
4359
|
filterAllModels: "全部模型",
|
|
4317
4360
|
filterAllRatios: "全部比例",
|
|
@@ -4391,9 +4434,12 @@ window.__ModuleLoader__.load({
|
|
|
4391
4434
|
tabTasks: "Tasks",
|
|
4392
4435
|
filterAllProviders: "All Providers",
|
|
4393
4436
|
filterGoogle: "Google Gemini",
|
|
4394
|
-
filterOpenAI: "OpenAI
|
|
4437
|
+
filterOpenAI: "OpenAI",
|
|
4438
|
+
filterOpenAICompat: "OpenAI-compatible",
|
|
4395
4439
|
filterSeedream: "ByteDance Seedream",
|
|
4396
4440
|
filterDashScope: "Aliyun DashScope",
|
|
4441
|
+
filterXAI: "xAI Grok",
|
|
4442
|
+
filterZhipu: "Zhipu GLM",
|
|
4397
4443
|
filterComfyUI: "Local ComfyUI",
|
|
4398
4444
|
filterAllModels: "All Models",
|
|
4399
4445
|
filterAllRatios: "All Ratios",
|
|
@@ -5079,6 +5125,10 @@ window.__ModuleLoader__.load({
|
|
|
5079
5125
|
value: "openai",
|
|
5080
5126
|
children: t("filterOpenAI")
|
|
5081
5127
|
}),
|
|
5128
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
5129
|
+
value: "openai-compat",
|
|
5130
|
+
children: t("filterOpenAICompat")
|
|
5131
|
+
}),
|
|
5082
5132
|
(0, react_jsx_runtime.jsx)("option", {
|
|
5083
5133
|
value: "seedream",
|
|
5084
5134
|
children: t("filterSeedream")
|
|
@@ -5087,6 +5137,14 @@ window.__ModuleLoader__.load({
|
|
|
5087
5137
|
value: "dashscope",
|
|
5088
5138
|
children: t("filterDashScope")
|
|
5089
5139
|
}),
|
|
5140
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
5141
|
+
value: "xai",
|
|
5142
|
+
children: t("filterXAI")
|
|
5143
|
+
}),
|
|
5144
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
5145
|
+
value: "zhipu",
|
|
5146
|
+
children: t("filterZhipu")
|
|
5147
|
+
}),
|
|
5090
5148
|
(0, react_jsx_runtime.jsx)("option", {
|
|
5091
5149
|
value: "comfyui",
|
|
5092
5150
|
children: t("filterComfyUI")
|
|
@@ -6137,7 +6195,7 @@ window.__ModuleLoader__.load({
|
|
|
6137
6195
|
function isRevision(value) {
|
|
6138
6196
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
6139
6197
|
const candidate = value;
|
|
6140
|
-
return isAttachment(candidate.attachment) &&
|
|
6198
|
+
return isAttachment(candidate.attachment) && typeof candidate.provider === "string" && CLOUD_IMAGE_PROVIDERS.includes(candidate.provider) && typeof candidate.prompt === "string" && typeof candidate.model === "string" && typeof candidate.output === "string" && typeof candidate.createdAt === "number" && Number.isFinite(candidate.createdAt) && typeof candidate.ratio === "string" && typeof candidate.quality === "string";
|
|
6141
6199
|
}
|
|
6142
6200
|
function isAttachment(value) {
|
|
6143
6201
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
@@ -6145,33 +6203,390 @@ window.__ModuleLoader__.load({
|
|
|
6145
6203
|
return typeof candidate.attachmentId === "string" && (candidate.mediaType === "image/png" || candidate.mediaType === "image/jpeg" || candidate.mediaType === "image/webp" || candidate.mediaType === "image/gif") && typeof candidate.bytes === "number" && typeof candidate.width === "number" && typeof candidate.height === "number";
|
|
6146
6204
|
}
|
|
6147
6205
|
//#endregion
|
|
6148
|
-
//#region lib/types/client/
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6206
|
+
//#region lib/types/client/provider-pill.js
|
|
6207
|
+
/**
|
|
6208
|
+
* Composer tool-row pill: switch the default image provider without leaving
|
|
6209
|
+
* the chat. Registered into the official 'conversation.input.right' slot; the
|
|
6210
|
+
* write path is the same `provider` settings field the settings card uses, so
|
|
6211
|
+
* the two views can never disagree.
|
|
6212
|
+
*/
|
|
6213
|
+
const PILL_DICT = {
|
|
6214
|
+
zh: {
|
|
6215
|
+
pillLabel: "生图",
|
|
6216
|
+
menuTitle: "生图模型",
|
|
6217
|
+
keyConfigured: "Key 已配置",
|
|
6218
|
+
keyMissing: "未配置 Key",
|
|
6219
|
+
noKeyNeeded: "无需 Key",
|
|
6220
|
+
noWorkflow: "未导入工作流",
|
|
6221
|
+
readOnly: "图像设置为只读,无法在此切换",
|
|
6222
|
+
switchFailed: "切换失败"
|
|
6223
|
+
},
|
|
6224
|
+
en: {
|
|
6225
|
+
pillLabel: "Image",
|
|
6226
|
+
menuTitle: "Image model",
|
|
6227
|
+
keyConfigured: "Key configured",
|
|
6228
|
+
keyMissing: "No key",
|
|
6229
|
+
noKeyNeeded: "No key needed",
|
|
6230
|
+
noWorkflow: "No workflow",
|
|
6231
|
+
readOnly: "Image settings are read-only; switch them in the config source",
|
|
6232
|
+
switchFailed: "Switch failed"
|
|
6233
|
+
}
|
|
6234
|
+
};
|
|
6235
|
+
/** Short row labels; the settings card keeps the longer descriptive names. */
|
|
6236
|
+
const PILL_PROVIDER_LABELS = {
|
|
6237
|
+
google: "Gemini",
|
|
6238
|
+
openai: "OpenAI",
|
|
6239
|
+
"openai-compat": "OpenAI 兼容",
|
|
6240
|
+
seedream: "Seedream",
|
|
6241
|
+
dashscope: "DashScope",
|
|
6242
|
+
xai: "Grok",
|
|
6243
|
+
zhipu: "智谱 GLM",
|
|
6244
|
+
comfyui: "ComfyUI"
|
|
6154
6245
|
};
|
|
6246
|
+
/** Whether the user opted into the composer pill; hidden (not merely inert) when off. */
|
|
6247
|
+
function pillVisible(value) {
|
|
6248
|
+
return value?.showProviderPill === true;
|
|
6249
|
+
}
|
|
6250
|
+
/** Model (or ComfyUI workflow) a provider row will use with the persisted settings. */
|
|
6251
|
+
function pillModelOf(provider, value) {
|
|
6252
|
+
if (provider === "comfyui") {
|
|
6253
|
+
const workflow = activeComfyUIWorkflow(value ?? {});
|
|
6254
|
+
return workflow === void 0 ? "" : workflow.name;
|
|
6255
|
+
}
|
|
6256
|
+
const stored = provider === "google" ? value?.googleModel : provider === "openai" ? value?.openaiModel : provider === "openai-compat" ? value?.openaiCompatModel : provider === "seedream" ? value?.seedreamModel : provider === "dashscope" ? value?.dashscopeModel : provider === "xai" ? value?.xaiModel : value?.zhipuModel;
|
|
6257
|
+
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_MODELS[provider];
|
|
6258
|
+
}
|
|
6259
|
+
/** One fixed-position provider menu anchored to the pill button. */
|
|
6260
|
+
function ImageProviderPill(props) {
|
|
6261
|
+
const [snapshot, setSnapshot] = (0, react.useState)(() => props.scope.getSnapshot());
|
|
6262
|
+
const [lang, setLang] = (0, react.useState)(() => props.locale?.getSnapshot?.()?.active?.startsWith("en") ? "en" : "zh");
|
|
6263
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
6264
|
+
const [pending, setPending] = (0, react.useState)(void 0);
|
|
6265
|
+
const [error, setError] = (0, react.useState)("");
|
|
6266
|
+
const [dots, setDots] = (0, react.useState)({
|
|
6267
|
+
google: "unknown",
|
|
6268
|
+
openai: "unknown",
|
|
6269
|
+
"openai-compat": "unknown",
|
|
6270
|
+
seedream: "unknown",
|
|
6271
|
+
dashscope: "unknown",
|
|
6272
|
+
xai: "unknown",
|
|
6273
|
+
zhipu: "unknown"
|
|
6274
|
+
});
|
|
6275
|
+
const [keyTick, setKeyTick] = (0, react.useState)(0);
|
|
6276
|
+
const buttonRef = (0, react.useRef)(null);
|
|
6277
|
+
const [menuStyle, setMenuStyle] = (0, react.useState)(null);
|
|
6278
|
+
(0, react.useEffect)(() => props.scope.subscribe(() => {
|
|
6279
|
+
setSnapshot(props.scope.getSnapshot());
|
|
6280
|
+
}), [props.scope]);
|
|
6281
|
+
(0, react.useEffect)(() => props.locale?.subscribe?.(() => {
|
|
6282
|
+
setLang(props.locale?.getSnapshot?.()?.active?.startsWith("en") ? "en" : "zh");
|
|
6283
|
+
}), [props.locale]);
|
|
6284
|
+
(0, react.useEffect)(() => props.credentialEvents?.listen(() => {
|
|
6285
|
+
setKeyTick((tick) => tick + 1);
|
|
6286
|
+
}), [props.credentialEvents]);
|
|
6287
|
+
(0, react.useEffect)(() => {
|
|
6288
|
+
let active = true;
|
|
6289
|
+
const refs = [];
|
|
6290
|
+
for (const provider of CLOUD_IMAGE_PROVIDERS) {
|
|
6291
|
+
const ref = cloudCredentialRef(provider);
|
|
6292
|
+
if (ref !== void 0) refs.push(ref);
|
|
6293
|
+
}
|
|
6294
|
+
props.credentials.describe(refs).then((response) => {
|
|
6295
|
+
if (!active) return;
|
|
6296
|
+
const next = {};
|
|
6297
|
+
for (const provider of CLOUD_IMAGE_PROVIDERS) {
|
|
6298
|
+
const info = response.ok ? response.value?.[cloudCredentialRef(provider)] : void 0;
|
|
6299
|
+
next[provider] = response.ok ? info?.configured ? "configured" : "missing" : "unknown";
|
|
6300
|
+
}
|
|
6301
|
+
setDots(next);
|
|
6302
|
+
}).catch(() => {
|
|
6303
|
+
if (!active) return;
|
|
6304
|
+
setDots({
|
|
6305
|
+
google: "unknown",
|
|
6306
|
+
openai: "unknown",
|
|
6307
|
+
"openai-compat": "unknown",
|
|
6308
|
+
seedream: "unknown",
|
|
6309
|
+
dashscope: "unknown",
|
|
6310
|
+
xai: "unknown",
|
|
6311
|
+
zhipu: "unknown"
|
|
6312
|
+
});
|
|
6313
|
+
});
|
|
6314
|
+
return () => {
|
|
6315
|
+
active = false;
|
|
6316
|
+
};
|
|
6317
|
+
}, [props.credentials, keyTick]);
|
|
6318
|
+
const t = (keyName) => (lang === "en" ? PILL_DICT.en : PILL_DICT.zh)[keyName];
|
|
6319
|
+
const current = snapshot.value?.provider ?? "google";
|
|
6320
|
+
const writable = snapshot.writable;
|
|
6321
|
+
(0, react.useEffect)(() => {
|
|
6322
|
+
if (!open) {
|
|
6323
|
+
setMenuStyle(null);
|
|
6324
|
+
return;
|
|
6325
|
+
}
|
|
6326
|
+
const rect = buttonRef.current?.getBoundingClientRect();
|
|
6327
|
+
if (rect !== void 0) setMenuStyle({
|
|
6328
|
+
left: rect.left,
|
|
6329
|
+
bottom: window.innerHeight - rect.top + 6
|
|
6330
|
+
});
|
|
6331
|
+
}, [open]);
|
|
6332
|
+
(0, react.useEffect)(() => {
|
|
6333
|
+
if (!open) return;
|
|
6334
|
+
const onPointerDown = (event) => {
|
|
6335
|
+
const target = event.target;
|
|
6336
|
+
if (target instanceof Node && buttonRef.current !== null && !buttonRef.current.contains(target) && !(target instanceof Element && target.closest(".dsh-ig-pill-menu") !== null)) setOpen(false);
|
|
6337
|
+
};
|
|
6338
|
+
const onKeyDown = (event) => {
|
|
6339
|
+
if (event.key === "Escape") setOpen(false);
|
|
6340
|
+
};
|
|
6341
|
+
const onReflow = () => {
|
|
6342
|
+
setOpen(false);
|
|
6343
|
+
};
|
|
6344
|
+
document.addEventListener("mousedown", onPointerDown, true);
|
|
6345
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
6346
|
+
window.addEventListener("resize", onReflow);
|
|
6347
|
+
window.addEventListener("scroll", onReflow, true);
|
|
6348
|
+
return () => {
|
|
6349
|
+
document.removeEventListener("mousedown", onPointerDown, true);
|
|
6350
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
6351
|
+
window.removeEventListener("resize", onReflow);
|
|
6352
|
+
window.removeEventListener("scroll", onReflow, true);
|
|
6353
|
+
};
|
|
6354
|
+
}, [open]);
|
|
6355
|
+
if (!pillVisible(snapshot.value)) return null;
|
|
6356
|
+
/** Same write path as the settings card: one field, one disposer, no implicit side effects. */
|
|
6357
|
+
const select = (provider) => {
|
|
6358
|
+
if (pending !== void 0 || provider === current) {
|
|
6359
|
+
setOpen(false);
|
|
6360
|
+
return;
|
|
6361
|
+
}
|
|
6362
|
+
setError("");
|
|
6363
|
+
setPending(provider);
|
|
6364
|
+
props.scope.set("provider", provider).then(() => {
|
|
6365
|
+
setOpen(false);
|
|
6366
|
+
}).catch((cause) => {
|
|
6367
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
6368
|
+
}).finally(() => {
|
|
6369
|
+
setPending(void 0);
|
|
6370
|
+
});
|
|
6371
|
+
};
|
|
6372
|
+
const dotClassOf = (provider) => {
|
|
6373
|
+
if (provider === "comfyui") return "dsh-ig-pill-dot-neutral";
|
|
6374
|
+
const dot = dots[provider];
|
|
6375
|
+
return dot === "configured" ? "dsh-ig-pill-dot-ok" : dot === "missing" ? "dsh-ig-pill-dot-missing" : "dsh-ig-pill-dot-neutral";
|
|
6376
|
+
};
|
|
6377
|
+
const subtitleOf = (provider) => {
|
|
6378
|
+
if (provider === "comfyui") {
|
|
6379
|
+
const workflow = pillModelOf(provider, snapshot.value);
|
|
6380
|
+
return workflow.length > 0 ? `${t("noKeyNeeded")} · ${workflow}` : `${t("noKeyNeeded")} · ${t("noWorkflow")}`;
|
|
6381
|
+
}
|
|
6382
|
+
const dot = dots[provider];
|
|
6383
|
+
const model = pillModelOf(provider, snapshot.value);
|
|
6384
|
+
if (dot === "configured") return `${t("keyConfigured")} · ${model}`;
|
|
6385
|
+
if (dot === "missing") return `${t("keyMissing")} · ${model}`;
|
|
6386
|
+
return model;
|
|
6387
|
+
};
|
|
6388
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
6389
|
+
className: "dsh-ig-pill-root",
|
|
6390
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
6391
|
+
type: "button",
|
|
6392
|
+
ref: buttonRef,
|
|
6393
|
+
className: `dsh-ig-pill-button${open ? " dsh-ig-pill-button-open" : ""}`,
|
|
6394
|
+
"aria-haspopup": "menu",
|
|
6395
|
+
"aria-expanded": open,
|
|
6396
|
+
title: writable ? t("menuTitle") : t("readOnly"),
|
|
6397
|
+
disabled: !writable,
|
|
6398
|
+
onClick: () => {
|
|
6399
|
+
setOpen((value) => !value);
|
|
6400
|
+
},
|
|
6401
|
+
children: [
|
|
6402
|
+
(0, react_jsx_runtime.jsxs)("svg", {
|
|
6403
|
+
className: "dsh-ig-pill-icon",
|
|
6404
|
+
width: "13",
|
|
6405
|
+
height: "13",
|
|
6406
|
+
viewBox: "0 0 16 16",
|
|
6407
|
+
fill: "none",
|
|
6408
|
+
stroke: "currentColor",
|
|
6409
|
+
strokeWidth: "1.6",
|
|
6410
|
+
strokeLinecap: "round",
|
|
6411
|
+
strokeLinejoin: "round",
|
|
6412
|
+
"aria-hidden": "true",
|
|
6413
|
+
children: [
|
|
6414
|
+
(0, react_jsx_runtime.jsx)("rect", {
|
|
6415
|
+
x: "1.5",
|
|
6416
|
+
y: "2.5",
|
|
6417
|
+
width: "13",
|
|
6418
|
+
height: "11",
|
|
6419
|
+
rx: "2"
|
|
6420
|
+
}),
|
|
6421
|
+
(0, react_jsx_runtime.jsx)("circle", {
|
|
6422
|
+
cx: "5.5",
|
|
6423
|
+
cy: "6.5",
|
|
6424
|
+
r: "1.3"
|
|
6425
|
+
}),
|
|
6426
|
+
(0, react_jsx_runtime.jsx)("path", { d: "M14.5 10.5l-3.2-3.2-6.3 6.2" })
|
|
6427
|
+
]
|
|
6428
|
+
}),
|
|
6429
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
6430
|
+
className: "dsh-ig-pill-provider",
|
|
6431
|
+
children: PILL_PROVIDER_LABELS[current]
|
|
6432
|
+
}),
|
|
6433
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
6434
|
+
className: `dsh-ig-pill-caret${open ? " dsh-ig-pill-caret-open" : ""}`,
|
|
6435
|
+
"aria-hidden": "true",
|
|
6436
|
+
children: (0, react_jsx_runtime.jsx)("svg", {
|
|
6437
|
+
width: "10",
|
|
6438
|
+
height: "10",
|
|
6439
|
+
viewBox: "0 0 16 16",
|
|
6440
|
+
fill: "none",
|
|
6441
|
+
stroke: "currentColor",
|
|
6442
|
+
strokeWidth: "2",
|
|
6443
|
+
strokeLinecap: "round",
|
|
6444
|
+
strokeLinejoin: "round",
|
|
6445
|
+
children: (0, react_jsx_runtime.jsx)("path", { d: "M4 10l4-4 4 4" })
|
|
6446
|
+
})
|
|
6447
|
+
})
|
|
6448
|
+
]
|
|
6449
|
+
}), open && menuStyle !== null ? (0, react_jsx_runtime.jsxs)("div", {
|
|
6450
|
+
className: "dsh-ig-pill-menu",
|
|
6451
|
+
role: "menu",
|
|
6452
|
+
"aria-label": t("menuTitle"),
|
|
6453
|
+
style: menuStyle,
|
|
6454
|
+
children: [
|
|
6455
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
6456
|
+
className: "dsh-ig-pill-menu-title",
|
|
6457
|
+
children: t("menuTitle")
|
|
6458
|
+
}),
|
|
6459
|
+
IMAGE_PROVIDERS.map((provider) => (0, react_jsx_runtime.jsxs)("button", {
|
|
6460
|
+
type: "button",
|
|
6461
|
+
role: "menuitemradio",
|
|
6462
|
+
"aria-checked": provider === current,
|
|
6463
|
+
className: `dsh-ig-pill-option${provider === current ? " dsh-ig-pill-option-active" : ""}`,
|
|
6464
|
+
disabled: pending !== void 0,
|
|
6465
|
+
onClick: () => {
|
|
6466
|
+
select(provider);
|
|
6467
|
+
},
|
|
6468
|
+
children: [
|
|
6469
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
6470
|
+
className: `dsh-ig-pill-dot ${dotClassOf(provider)}`,
|
|
6471
|
+
"aria-hidden": "true"
|
|
6472
|
+
}),
|
|
6473
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
6474
|
+
className: "dsh-ig-pill-option-text",
|
|
6475
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
6476
|
+
className: "dsh-ig-pill-option-name",
|
|
6477
|
+
children: PILL_PROVIDER_LABELS[provider]
|
|
6478
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
6479
|
+
className: "dsh-ig-pill-option-sub",
|
|
6480
|
+
children: subtitleOf(provider)
|
|
6481
|
+
})]
|
|
6482
|
+
}),
|
|
6483
|
+
provider === current ? (0, react_jsx_runtime.jsx)("span", {
|
|
6484
|
+
className: "dsh-ig-pill-check",
|
|
6485
|
+
"aria-hidden": "true",
|
|
6486
|
+
children: (0, react_jsx_runtime.jsx)("svg", {
|
|
6487
|
+
width: "13",
|
|
6488
|
+
height: "13",
|
|
6489
|
+
viewBox: "0 0 16 16",
|
|
6490
|
+
fill: "none",
|
|
6491
|
+
stroke: "currentColor",
|
|
6492
|
+
strokeWidth: "2.2",
|
|
6493
|
+
strokeLinecap: "round",
|
|
6494
|
+
strokeLinejoin: "round",
|
|
6495
|
+
children: (0, react_jsx_runtime.jsx)("path", { d: "M2.5 8.5l3.5 3.5 7-8" })
|
|
6496
|
+
})
|
|
6497
|
+
}) : null
|
|
6498
|
+
]
|
|
6499
|
+
}, provider)),
|
|
6500
|
+
error.length > 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
6501
|
+
className: "dsh-ig-pill-error",
|
|
6502
|
+
role: "alert",
|
|
6503
|
+
children: [
|
|
6504
|
+
t("switchFailed"),
|
|
6505
|
+
":",
|
|
6506
|
+
error
|
|
6507
|
+
]
|
|
6508
|
+
}) : null
|
|
6509
|
+
]
|
|
6510
|
+
}) : null]
|
|
6511
|
+
});
|
|
6512
|
+
}
|
|
6513
|
+
/** Styles for the pill and its fixed-position menu; injected by the client entry. */
|
|
6514
|
+
const PROVIDER_PILL_STYLE = `
|
|
6515
|
+
.dsh-ig-pill-root{position:relative;display:inline-flex;align-items:center;height:26px}
|
|
6516
|
+
.dsh-ig-pill-button{display:inline-flex;align-items:center;gap:5px;height:26px;padding:0 8px 0 7px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:13px;background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:12px;line-height:1;cursor:pointer;transition:border-color .15s,background .15s,color .15s;-webkit-user-select:none;user-select:none}
|
|
6517
|
+
.dsh-ig-pill-button:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed,#9ca3af);color:var(--dsw-alias-label-primary,#111827)}
|
|
6518
|
+
.dsh-ig-pill-button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:1px}
|
|
6519
|
+
.dsh-ig-pill-button:disabled{opacity:.5;cursor:not-allowed}
|
|
6520
|
+
.dsh-ig-pill-button-open{border-color:var(--dsw-alias-brand-primary,#4c78ff);color:var(--dsw-alias-label-primary,#111827)}
|
|
6521
|
+
.dsh-ig-pill-icon{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}
|
|
6522
|
+
.dsh-ig-pill-provider{max-width:96px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6523
|
+
.dsh-ig-pill-caret{display:inline-flex;flex:none;transition:transform .15s}
|
|
6524
|
+
.dsh-ig-pill-caret-open{transform:rotate(180deg)}
|
|
6525
|
+
.dsh-ig-pill-menu{position:fixed;z-index:1000;min-width:264px;max-width:min(320px,calc(100vw - 24px));padding:6px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 12px 32px rgba(0,0,0,.16),0 2px 8px rgba(0,0,0,.08);max-height:min(320px,calc(100vh - 48px));overflow-y:auto}
|
|
6526
|
+
.dsh-ig-pill-menu-title{padding:6px 8px 4px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary,#6b7280)}
|
|
6527
|
+
.dsh-ig-pill-option{display:flex;width:100%;align-items:center;gap:9px;padding:7px 8px;border:0;border-radius:8px;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer}
|
|
6528
|
+
.dsh-ig-pill-option:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,#f3f4f6)}
|
|
6529
|
+
.dsh-ig-pill-option:disabled{opacity:.6;cursor:default}
|
|
6530
|
+
.dsh-ig-pill-option:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:-2px}
|
|
6531
|
+
.dsh-ig-pill-option-active{background:var(--dsw-alias-brand-primary-soft,rgba(76,120,255,.10))}
|
|
6532
|
+
.dsh-ig-pill-dot{flex:none;width:7px;height:7px;border-radius:50%}
|
|
6533
|
+
.dsh-ig-pill-dot-ok{background:#22c55e}
|
|
6534
|
+
.dsh-ig-pill-dot-missing{background:var(--dsw-alias-border-l2,#d1d5db)}
|
|
6535
|
+
.dsh-ig-pill-dot-neutral{background:var(--dsw-alias-brand-primary,#4c78ff)}
|
|
6536
|
+
.dsh-ig-pill-option-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
|
|
6537
|
+
.dsh-ig-pill-option-name{font-size:12.5px;font-weight:500;color:var(--dsw-alias-label-primary,#111827);line-height:1.2}
|
|
6538
|
+
.dsh-ig-pill-option-sub{font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
6539
|
+
.dsh-ig-pill-check{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}
|
|
6540
|
+
.dsh-ig-pill-error{margin-top:4px;padding:6px 8px;border-radius:8px;background:rgba(239,68,68,.08);color:#ef4444;font-size:11.5px;line-height:1.4;word-break:break-all}
|
|
6541
|
+
`;
|
|
6542
|
+
//#endregion
|
|
6543
|
+
//#region lib/types/client/index.js
|
|
6155
6544
|
const DICT = {
|
|
6156
6545
|
zh: {
|
|
6157
6546
|
title: "图像生成",
|
|
6158
|
-
description: "
|
|
6159
|
-
|
|
6547
|
+
description: "配置各 Provider 的 Key 与模型,并选择默认 Provider。",
|
|
6548
|
+
defaultProvider: "默认 Provider",
|
|
6549
|
+
defaultProviderHint: "Agent 生图默认使用;Studio 与工具调用可临时指定其他 Provider。",
|
|
6550
|
+
settingsReadOnly: "设置由配置文件提供,只读;如需修改请编辑对应的配置来源。",
|
|
6160
6551
|
providerGoogle: "Google Gemini",
|
|
6161
|
-
providerOpenAI: "OpenAI
|
|
6552
|
+
providerOpenAI: "OpenAI",
|
|
6553
|
+
providerOpenAICompat: "OpenAI 兼容(中转站)",
|
|
6162
6554
|
providerSeedream: "字节 Seedream",
|
|
6163
6555
|
providerDashScope: "阿里 DashScope (通义万相 / Qwen)",
|
|
6556
|
+
providerXAI: "xAI Grok Imagine",
|
|
6557
|
+
providerZhipu: "智谱 GLM-Image",
|
|
6164
6558
|
providerComfyUI: "本地 ComfyUI",
|
|
6165
6559
|
apiKeyLabel: "{provider} API Key",
|
|
6166
6560
|
apiKeyPlaceholder: "留空即可保留已配置的 Key",
|
|
6167
6561
|
apiKeyHint: "安全保存为 {key};页面不会读回明文。",
|
|
6562
|
+
keyReadOnly: "Key 由 {source} 提供且只读;请在该来源中修改。",
|
|
6563
|
+
credentialsUnavailable: "凭据服务不可用,无法保存 API Key;建议升级 DSH 到稳定版本。",
|
|
6564
|
+
badgeUnavailable: "凭据服务不可用",
|
|
6565
|
+
badgeChecking: "检查中…",
|
|
6566
|
+
badgeConfigured: "Key 已配置",
|
|
6567
|
+
badgeMissing: "Key 未配置",
|
|
6568
|
+
badgeUnknown: "状态未知",
|
|
6569
|
+
comfyuiNoKey: "无需 API Key",
|
|
6570
|
+
testConnection: "测试连接",
|
|
6571
|
+
testing: "正在测试…",
|
|
6572
|
+
testOk: "连接成功",
|
|
6573
|
+
testFailed: "连接失败",
|
|
6574
|
+
testUnauthorized: "Key 无效或无权限",
|
|
6575
|
+
clearKey: "清除 Key",
|
|
6576
|
+
keyCleared: "已清除 Key",
|
|
6577
|
+
clearKeyFailed: "清除 Key 失败",
|
|
6578
|
+
saveKeyFailed: "保存 Key 失败",
|
|
6579
|
+
clearKeyUnsupported: "当前版本 DSH 不支持在此清除 Key,请到凭据管理中删除。",
|
|
6168
6580
|
endpoint: "接口地址",
|
|
6169
6581
|
reset: "重置",
|
|
6170
6582
|
resetTitle: "重置为默认官方地址",
|
|
6171
6583
|
endpointHintGoogle: "Google 官方地址或反代端点(全路径)。",
|
|
6172
|
-
endpointHintOpenAI: "
|
|
6584
|
+
endpointHintOpenAI: "官方 api.openai.com 的 /v1 地址;中转站请使用下方「OpenAI 兼容」行。",
|
|
6585
|
+
endpointHintOpenAICompat: "中转站/自建服务的 OpenAI 兼容 /v1 地址(必填),例如 https://your-relay.example.com/v1。",
|
|
6173
6586
|
endpointHintSeedream: "火山方舟兼容的 /api/v3 地址。",
|
|
6174
6587
|
endpointHintDashScope: "阿里云百炼 DashScope 官方接口地址。",
|
|
6588
|
+
endpointHintXAI: "xAI 官方 api.x.ai 的 /v1 地址。",
|
|
6589
|
+
endpointHintZhipu: "智谱开放平台 open.bigmodel.cn 的 /api/paas/v4 地址。",
|
|
6175
6590
|
endpointHintComfyUI: "正在运行且 DSH Host 可以访问的 ComfyUI 地址,默认使用本机 8188 端口。",
|
|
6176
6591
|
model: "模型",
|
|
6177
6592
|
workflow: "API Workflow 工作流",
|
|
@@ -6188,17 +6603,23 @@ window.__ModuleLoader__.load({
|
|
|
6188
6603
|
workflowDuplicateName: "工作流名称不能重复。",
|
|
6189
6604
|
timeout: "生成超时(秒)",
|
|
6190
6605
|
timeoutHint: "包括提交、等待和下载图片;默认 300 秒。",
|
|
6606
|
+
workspaceSection: "工作区",
|
|
6191
6607
|
saveToWorkspace: "保存到工作区",
|
|
6192
6608
|
saveToWorkspaceHint: "每次生成后,把图片文件保存到当前会话工作区。",
|
|
6193
6609
|
folder: "工作区文件夹",
|
|
6194
6610
|
folderHint: "相对当前会话工作区的子目录;留空表示工作区根目录。",
|
|
6611
|
+
uiSection: "界面",
|
|
6612
|
+
showPill: "在对话输入栏显示生图切换胶囊",
|
|
6613
|
+
showPillHint: "开启后,输入框工具行会显示一枚胶囊,随手切换默认生图 Provider,无需进入设置;默认关闭。",
|
|
6614
|
+
fetchModels: "拉取模型",
|
|
6615
|
+
fetchingModels: "拉取中…",
|
|
6616
|
+
fetchModelsHint: "点击「拉取模型」用当前 Key 获取可用生图模型列表;也可手动输入。",
|
|
6617
|
+
modelsFound: "找到 {n} 个生图模型,点击模型框选择",
|
|
6618
|
+
modelsNone: "未筛出生图模型,可手动输入模型名",
|
|
6195
6619
|
saving: "保存中…",
|
|
6196
6620
|
save: "保存",
|
|
6197
6621
|
saved: "已保存",
|
|
6198
6622
|
savedToPath: "已保存到",
|
|
6199
|
-
checkingKey: "正在检查 API Key…",
|
|
6200
|
-
keyConfigured: "已配置 API Key",
|
|
6201
|
-
keyNotConfigured: "尚未配置 API Key",
|
|
6202
6623
|
generating: "正在生成图片…",
|
|
6203
6624
|
loading: "正在加载图片…",
|
|
6204
6625
|
loadFailed: "图片读取失败 ({status})",
|
|
@@ -6223,23 +6644,49 @@ window.__ModuleLoader__.load({
|
|
|
6223
6644
|
},
|
|
6224
6645
|
en: {
|
|
6225
6646
|
title: "Image Generation",
|
|
6226
|
-
description: "
|
|
6227
|
-
|
|
6647
|
+
description: "Configure each provider key and model, then pick the default provider.",
|
|
6648
|
+
defaultProvider: "Default provider",
|
|
6649
|
+
defaultProviderHint: "Used by the Agent by default; the Studio and tool calls can switch per call.",
|
|
6650
|
+
settingsReadOnly: "Settings come from a profile file and are read-only; edit that source to change them.",
|
|
6228
6651
|
providerGoogle: "Google Gemini",
|
|
6229
|
-
providerOpenAI: "OpenAI
|
|
6652
|
+
providerOpenAI: "OpenAI",
|
|
6653
|
+
providerOpenAICompat: "OpenAI-compatible (relay)",
|
|
6230
6654
|
providerSeedream: "ByteDance Seedream",
|
|
6231
6655
|
providerDashScope: "Aliyun DashScope (Wanx / Qwen)",
|
|
6656
|
+
providerXAI: "xAI Grok Imagine",
|
|
6657
|
+
providerZhipu: "Zhipu GLM-Image",
|
|
6232
6658
|
providerComfyUI: "Local ComfyUI",
|
|
6233
6659
|
apiKeyLabel: "{provider} API Key",
|
|
6234
6660
|
apiKeyPlaceholder: "Leave empty to keep configured key",
|
|
6235
6661
|
apiKeyHint: "Securely saved as {key}; never read back in plaintext.",
|
|
6662
|
+
keyReadOnly: "Key is supplied read-only by {source}; update it there.",
|
|
6663
|
+
credentialsUnavailable: "Credentials service unavailable: API keys cannot be saved; please upgrade DSH to a stable release.",
|
|
6664
|
+
badgeUnavailable: "Credentials unavailable",
|
|
6665
|
+
badgeChecking: "Checking…",
|
|
6666
|
+
badgeConfigured: "Key set",
|
|
6667
|
+
badgeMissing: "Key missing",
|
|
6668
|
+
badgeUnknown: "Unknown",
|
|
6669
|
+
comfyuiNoKey: "No API key needed",
|
|
6670
|
+
testConnection: "Test connection",
|
|
6671
|
+
testing: "Testing…",
|
|
6672
|
+
testOk: "Connection OK",
|
|
6673
|
+
testFailed: "Connection failed",
|
|
6674
|
+
testUnauthorized: "API key rejected",
|
|
6675
|
+
clearKey: "Clear key",
|
|
6676
|
+
keyCleared: "Key cleared",
|
|
6677
|
+
clearKeyFailed: "Failed to clear key",
|
|
6678
|
+
saveKeyFailed: "Failed to save the key",
|
|
6679
|
+
clearKeyUnsupported: "This DSH build cannot clear keys here; remove it from credential management instead.",
|
|
6236
6680
|
endpoint: "Endpoint / Base URL",
|
|
6237
6681
|
reset: "Reset",
|
|
6238
6682
|
resetTitle: "Reset to official default URL",
|
|
6239
6683
|
endpointHintGoogle: "Official Google endpoint or reverse proxy (full path).",
|
|
6240
|
-
endpointHintOpenAI: "
|
|
6684
|
+
endpointHintOpenAI: "Official api.openai.com /v1 base URL; for relays use the \"OpenAI-compatible\" row below.",
|
|
6685
|
+
endpointHintOpenAICompat: "OpenAI-compatible /v1 base URL of your relay or self-hosted service (required), e.g. https://your-relay.example.com/v1.",
|
|
6241
6686
|
endpointHintSeedream: "Volcengine Ark compatible /api/v3 base URL.",
|
|
6242
6687
|
endpointHintDashScope: "Official Aliyun DashScope endpoint.",
|
|
6688
|
+
endpointHintXAI: "Official xAI api.x.ai /v1 base URL.",
|
|
6689
|
+
endpointHintZhipu: "Zhipu open.bigmodel.cn /api/paas/v4 base URL.",
|
|
6243
6690
|
endpointHintComfyUI: "A running ComfyUI server reachable by the DSH Host; the default points to port 8188 on this computer.",
|
|
6244
6691
|
model: "Model",
|
|
6245
6692
|
workflow: "API Workflows",
|
|
@@ -6256,17 +6703,23 @@ window.__ModuleLoader__.load({
|
|
|
6256
6703
|
workflowDuplicateName: "Workflow names must be unique.",
|
|
6257
6704
|
timeout: "Generation timeout (seconds)",
|
|
6258
6705
|
timeoutHint: "Covers submission, waiting, and image download; defaults to 300 seconds.",
|
|
6706
|
+
workspaceSection: "Workspace",
|
|
6259
6707
|
saveToWorkspace: "Save to workspace",
|
|
6260
6708
|
saveToWorkspaceHint: "Write each generated image as a file into the session workspace.",
|
|
6261
6709
|
folder: "Workspace folder",
|
|
6262
6710
|
folderHint: "Subdirectory of the session workspace; empty means the workspace root.",
|
|
6711
|
+
uiSection: "Interface",
|
|
6712
|
+
showPill: "Show the image provider pill in the chat input bar",
|
|
6713
|
+
showPillHint: "Adds a small pill to the composer tool row for switching the default image provider without opening settings; off by default.",
|
|
6714
|
+
fetchModels: "Fetch models",
|
|
6715
|
+
fetchingModels: "Fetching…",
|
|
6716
|
+
fetchModelsHint: "Click \"Fetch models\" to list image-capable models with the stored key; manual input still works.",
|
|
6717
|
+
modelsFound: "{n} image models found; open the model field to pick one",
|
|
6718
|
+
modelsNone: "No image models found; type the model name manually",
|
|
6263
6719
|
saving: "Saving…",
|
|
6264
6720
|
save: "Save",
|
|
6265
6721
|
saved: "Saved",
|
|
6266
6722
|
savedToPath: "Saved to",
|
|
6267
|
-
checkingKey: "Checking API Key…",
|
|
6268
|
-
keyConfigured: "API Key configured",
|
|
6269
|
-
keyNotConfigured: "API Key not configured",
|
|
6270
6723
|
generating: "Generating image…",
|
|
6271
6724
|
loading: "Loading image…",
|
|
6272
6725
|
loadFailed: "Failed to load image ({status})",
|
|
@@ -6322,6 +6775,7 @@ window.__ModuleLoader__.load({
|
|
|
6322
6775
|
.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}
|
|
6323
6776
|
.dsh-ig-btn-reset:hover{background:var(--dsw-alias-bg-layer-2,#edf0f3);border-color:var(--dsw-alias-label-dimmed,#9ca3af)}
|
|
6324
6777
|
.dsh-ig-hint,.dsh-ig-status{margin:0;color:var(--dsw-alias-label-tertiary,#7b818b);font-size:12px;line-height:1.4}
|
|
6778
|
+
.dsh-ig-hint-error{color:var(--dsw-alias-label-error,#d33)}
|
|
6325
6779
|
.dsh-ig-status-error{color:var(--dsw-alias-label-error,#d33);font-weight:500}
|
|
6326
6780
|
.dsh-ig-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#eee)}
|
|
6327
6781
|
.dsh-ig-check-row{display:flex;align-items:center;gap:8px;cursor:pointer}
|
|
@@ -6330,6 +6784,45 @@ window.__ModuleLoader__.load({
|
|
|
6330
6784
|
.dsh-ig-save{appearance:none;border:0;border-radius:8px;padding:6px 16px;background:var(--dsw-alias-label-primary,#111827);color:var(--dsw-alias-bg-layer-3,#fff);font:inherit;font-size:13px;font-weight:500;cursor:pointer;transition:opacity .15s}
|
|
6331
6785
|
.dsh-ig-save:disabled{opacity:.4;cursor:default}
|
|
6332
6786
|
|
|
6787
|
+
/* Provider list: one expandable row per provider, each saving independently. */
|
|
6788
|
+
.dsh-ig-providers{display:grid;gap:10px;margin-top:14px}
|
|
6789
|
+
.dsh-ig-provider-row{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:10px;background:var(--dsw-alias-bg-layer-3,transparent);overflow:hidden;transition:border-color .16s}
|
|
6790
|
+
.dsh-ig-provider-row-open{border-color:var(--dsw-alias-label-dimmed,#9ca3af)}
|
|
6791
|
+
.dsh-ig-provider-head{width:100%;appearance:none;border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;display:flex;align-items:center;gap:10px;padding:11px 12px}
|
|
6792
|
+
.dsh-ig-provider-head:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:-2px}
|
|
6793
|
+
.dsh-ig-provider-name{flex:1;min-width:0;font-size:13.5px;font-weight:550;color:var(--dsw-alias-label-primary,inherit)}
|
|
6794
|
+
.dsh-ig-provider-chevron{flex:none;color:var(--dsw-alias-label-tertiary,#7b818b);transition:transform .16s;display:inline-flex;align-items:center}
|
|
6795
|
+
.dsh-ig-provider-chevron-open{transform:rotate(180deg)}
|
|
6796
|
+
.dsh-ig-provider-body{border-top:1px solid var(--dsw-alias-border-l2,#eee);padding:2px 12px 14px}
|
|
6797
|
+
.dsh-ig-badge{flex:none;display:inline-flex;align-items:center;gap:5px;font-size:11.5px;line-height:1.6;padding:2px 9px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,#d7dbe0);color:var(--dsw-alias-label-secondary,inherit);white-space:nowrap;max-width:60%;overflow:hidden;text-overflow:ellipsis}
|
|
6798
|
+
.dsh-ig-badge-dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
|
|
6799
|
+
.dsh-ig-badge-ok{border-color:rgba(34,197,94,.45);color:#15803d;background:rgba(34,197,94,.08)}
|
|
6800
|
+
.dsh-ig-badge-missing{border-color:rgba(239,68,68,.4);color:#b91c1c;background:rgba(239,68,68,.06)}
|
|
6801
|
+
.dsh-ig-badge-neutral{color:var(--dsw-alias-label-tertiary,#7b818b)}
|
|
6802
|
+
.dsh-ig-badge-checking .dsh-ig-badge-dot{animation:dsh-ig-pulse 1s ease-in-out infinite}
|
|
6803
|
+
@keyframes dsh-ig-pulse{50%{opacity:.25}}
|
|
6804
|
+
|
|
6805
|
+
/* Default provider radio pills. */
|
|
6806
|
+
.dsh-ig-radios{display:flex;flex-wrap:wrap;gap:8px}
|
|
6807
|
+
.dsh-ig-radio{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--dsw-alias-border-l2,#d7dbe0);border-radius:999px;padding:5px 12px;cursor:pointer;font-size:12.5px;color:var(--dsw-alias-label-secondary,inherit);transition:border-color .15s,background .15s}
|
|
6808
|
+
.dsh-ig-radio:hover{border-color:var(--dsw-alias-label-dimmed,#9ca3af)}
|
|
6809
|
+
.dsh-ig-radio-checked{border-color:var(--dsw-alias-brand-primary,#4c78ff);background:rgba(76,120,255,.08);color:var(--dsw-alias-label-primary,inherit)}
|
|
6810
|
+
.dsh-ig-radio input[type=radio]{width:14px;height:14px;accent-color:var(--dsw-alias-brand-primary,#4c78ff);margin:0;cursor:pointer}
|
|
6811
|
+
|
|
6812
|
+
/* Row-level actions: test connection, clear key, save. */
|
|
6813
|
+
.dsh-ig-row-actions{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:14px;flex-wrap:wrap}
|
|
6814
|
+
.dsh-ig-row-buttons{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
6815
|
+
.dsh-ig-btn-secondary{appearance:none;border:1px solid var(--dsw-alias-border-l2,#d7dbe0);border-radius:8px;padding:6px 14px;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,opacity .15s}
|
|
6816
|
+
.dsh-ig-btn-secondary:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,#edf0f3);border-color:var(--dsw-alias-label-dimmed,#9ca3af)}
|
|
6817
|
+
.dsh-ig-btn-secondary:disabled{opacity:.45;cursor:default}
|
|
6818
|
+
.dsh-ig-btn-danger{color:#b91c1c;border-color:rgba(239,68,68,.4)}
|
|
6819
|
+
.dsh-ig-btn-danger:hover:not(:disabled){background:rgba(239,68,68,.08);border-color:rgba(239,68,68,.6)}
|
|
6820
|
+
|
|
6821
|
+
/* Workspace section within the settings card. */
|
|
6822
|
+
.dsh-ig-section{display:grid;gap:6px;margin-top:16px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l2,#eee)}
|
|
6823
|
+
.dsh-ig-section-title{font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--dsw-alias-label-tertiary,#7b818b);text-transform:uppercase}
|
|
6824
|
+
.dsh-ig-status-readonly{color:var(--dsw-alias-label-tertiary,#7b818b);font-style:italic}
|
|
6825
|
+
|
|
6333
6826
|
.dsh-ig-result{display:grid;gap:10px;max-width:520px}
|
|
6334
6827
|
.dsh-ig-promoted-results{display:grid;gap:16px}
|
|
6335
6828
|
.dsh-ig-result-title{font-size:14px;font-weight:600}
|
|
@@ -6551,7 +7044,7 @@ window.__ModuleLoader__.load({
|
|
|
6551
7044
|
ctx.effect(() => {
|
|
6552
7045
|
const style = document.createElement("style");
|
|
6553
7046
|
style.dataset.plugin = "dsh-image-gen";
|
|
6554
|
-
style.textContent = `${STYLE}\n${STUDIO_STYLE}\n${INSPIRATION_STYLE}`;
|
|
7047
|
+
style.textContent = `${STYLE}\n${STUDIO_STYLE}\n${INSPIRATION_STYLE}\n${PROVIDER_PILL_STYLE}`;
|
|
6555
7048
|
document.head.appendChild(style);
|
|
6556
7049
|
return () => {
|
|
6557
7050
|
style.remove();
|
|
@@ -6570,26 +7063,80 @@ window.__ModuleLoader__.load({
|
|
|
6570
7063
|
inject: () => ({ locale })
|
|
6571
7064
|
}, PromotedImageResultNode));
|
|
6572
7065
|
});
|
|
6573
|
-
const
|
|
7066
|
+
const credentialListeners = /* @__PURE__ */ new Set();
|
|
7067
|
+
const notifyCredentialsUpdated = () => {
|
|
7068
|
+
for (const listener of credentialListeners) listener();
|
|
7069
|
+
};
|
|
7070
|
+
ctx.effect(() => {
|
|
7071
|
+
const remote = ctx.get("remote");
|
|
7072
|
+
if (typeof remote?.$on !== "function") return () => {};
|
|
7073
|
+
return remote.$on("credentials/reference-updated", notifyCredentialsUpdated);
|
|
7074
|
+
}, "dsh-image-gen: credential events");
|
|
7075
|
+
const credentialEvents = { listen(callback) {
|
|
7076
|
+
credentialListeners.add(callback);
|
|
7077
|
+
return () => {
|
|
7078
|
+
credentialListeners.delete(callback);
|
|
7079
|
+
};
|
|
7080
|
+
} };
|
|
7081
|
+
const injectSettingsItem = (owner) => {
|
|
6574
7082
|
const ownerRegister = owner.slots.register.bind(owner.slots);
|
|
6575
7083
|
owner.slots.inject("settings.plugin.item", () => ownerRegister({
|
|
6576
7084
|
name: "settings.plugin.item",
|
|
6577
7085
|
key: IMAGE_GENERATION_NAMESPACE,
|
|
6578
7086
|
inject: () => ({
|
|
6579
7087
|
scope,
|
|
6580
|
-
credentials,
|
|
6581
|
-
|
|
7088
|
+
credentials: credentialsProxy,
|
|
7089
|
+
credentialsAvailable,
|
|
7090
|
+
locale,
|
|
7091
|
+
credentialEvents
|
|
6582
7092
|
})
|
|
6583
7093
|
}, ImageGenerationSettingsCard));
|
|
6584
7094
|
};
|
|
6585
|
-
const
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
7095
|
+
const injectComposerPill = (owner) => {
|
|
7096
|
+
const ownerRegister = owner.slots.register.bind(owner.slots);
|
|
7097
|
+
owner.slots.inject("conversation.input.right", () => ownerRegister({
|
|
7098
|
+
name: "conversation.input.right",
|
|
7099
|
+
id: "image-provider",
|
|
7100
|
+
order: 10,
|
|
7101
|
+
inject: () => ({
|
|
7102
|
+
scope,
|
|
7103
|
+
credentials: credentialsProxy,
|
|
7104
|
+
locale,
|
|
7105
|
+
credentialEvents
|
|
7106
|
+
})
|
|
7107
|
+
}, ImageProviderPill));
|
|
7108
|
+
};
|
|
7109
|
+
const credentialsRef = { current: asCredentialsRemote(ctx.get("remote.credentials")) ?? credentialsFromLegacyConnection(ctx.get("connection")) };
|
|
7110
|
+
/** Stable delegating remote so the host-cached inject face never goes stale. */
|
|
7111
|
+
const credentialsProxy = {
|
|
7112
|
+
describe(refs) {
|
|
7113
|
+
const remote = credentialsRef.current;
|
|
7114
|
+
return remote === void 0 ? Promise.resolve({ ok: false }) : remote.describe(refs);
|
|
7115
|
+
},
|
|
7116
|
+
set(ref, value) {
|
|
7117
|
+
const remote = credentialsRef.current;
|
|
7118
|
+
return remote === void 0 ? Promise.resolve({
|
|
7119
|
+
ok: false,
|
|
7120
|
+
error: { message: "credentials service unavailable" }
|
|
7121
|
+
}) : remote.set(ref, value);
|
|
7122
|
+
},
|
|
7123
|
+
get unset() {
|
|
7124
|
+
const remote = credentialsRef.current;
|
|
7125
|
+
return remote?.unset?.bind(remote);
|
|
7126
|
+
}
|
|
7127
|
+
};
|
|
7128
|
+
/** False while the host core exposes no credentials service (preview cores). */
|
|
7129
|
+
const credentialsAvailable = () => credentialsRef.current !== void 0;
|
|
7130
|
+
injectSettingsItem(ctx);
|
|
7131
|
+
injectComposerPill(ctx);
|
|
7132
|
+
ctx.inject(["remote.credentials"], (remoteCtx) => {
|
|
6590
7133
|
const credentials = asCredentialsRemote(remoteCtx.get("remote.credentials"));
|
|
6591
|
-
if (credentials === void 0)
|
|
6592
|
-
|
|
7134
|
+
if (credentials === void 0) {
|
|
7135
|
+
console.warn("dsh-image-gen: remote.credentials resolved with an incompatible interface; key settings stay degraded");
|
|
7136
|
+
return;
|
|
7137
|
+
}
|
|
7138
|
+
credentialsRef.current = credentials;
|
|
7139
|
+
notifyCredentialsUpdated();
|
|
6593
7140
|
});
|
|
6594
7141
|
ctx.slots.inject("tool.call.toolview", () => register({
|
|
6595
7142
|
name: "tool.call.toolview",
|
|
@@ -6653,32 +7200,102 @@ window.__ModuleLoader__.load({
|
|
|
6653
7200
|
}
|
|
6654
7201
|
};
|
|
6655
7202
|
}
|
|
6656
|
-
|
|
7203
|
+
function emptyProviderRow() {
|
|
7204
|
+
return {
|
|
7205
|
+
expanded: false,
|
|
7206
|
+
model: "",
|
|
7207
|
+
baseURL: "",
|
|
7208
|
+
keyInput: "",
|
|
7209
|
+
keyStatus: "checking",
|
|
7210
|
+
keyInfo: void 0,
|
|
7211
|
+
testing: false,
|
|
7212
|
+
testResult: void 0,
|
|
7213
|
+
fetchingModels: false,
|
|
7214
|
+
modelOptions: [],
|
|
7215
|
+
modelFetchMessage: "",
|
|
7216
|
+
modelFetchIsError: false,
|
|
7217
|
+
saving: false,
|
|
7218
|
+
message: "",
|
|
7219
|
+
messageIsError: false,
|
|
7220
|
+
workflows: [],
|
|
7221
|
+
activeWorkflow: "",
|
|
7222
|
+
timeoutSeconds: DEFAULT_COMFYUI_TIMEOUT_MS / 1e3
|
|
7223
|
+
};
|
|
7224
|
+
}
|
|
7225
|
+
/** Settings field each cloud provider persists its model under. */
|
|
7226
|
+
const CLOUD_MODEL_FIELDS = {
|
|
7227
|
+
google: "googleModel",
|
|
7228
|
+
openai: "openaiModel",
|
|
7229
|
+
"openai-compat": "openaiCompatModel",
|
|
7230
|
+
seedream: "seedreamModel",
|
|
7231
|
+
dashscope: "dashscopeModel",
|
|
7232
|
+
xai: "xaiModel",
|
|
7233
|
+
zhipu: "zhipuModel"
|
|
7234
|
+
};
|
|
7235
|
+
/** Settings field each cloud provider persists its endpoint or base URL under. */
|
|
7236
|
+
const CLOUD_URL_FIELDS = {
|
|
7237
|
+
google: "googleEndpoint",
|
|
7238
|
+
openai: "openaiBaseURL",
|
|
7239
|
+
"openai-compat": "openaiCompatBaseURL",
|
|
7240
|
+
seedream: "seedreamBaseURL",
|
|
7241
|
+
dashscope: "dashscopeEndpoint",
|
|
7242
|
+
xai: "xaiBaseURL",
|
|
7243
|
+
zhipu: "zhipuBaseURL"
|
|
7244
|
+
};
|
|
7245
|
+
/** Dictionary key of each cloud provider's endpoint hint. */
|
|
7246
|
+
const CLOUD_HINT_KEYS = {
|
|
7247
|
+
google: "endpointHintGoogle",
|
|
7248
|
+
openai: "endpointHintOpenAI",
|
|
7249
|
+
"openai-compat": "endpointHintOpenAICompat",
|
|
7250
|
+
seedream: "endpointHintSeedream",
|
|
7251
|
+
dashscope: "endpointHintDashScope",
|
|
7252
|
+
xai: "endpointHintXAI",
|
|
7253
|
+
zhipu: "endpointHintZhipu"
|
|
7254
|
+
};
|
|
7255
|
+
/** Config field a cloud provider persists its model under. */
|
|
7256
|
+
function modelFieldOf(provider) {
|
|
7257
|
+
return CLOUD_MODEL_FIELDS[provider];
|
|
7258
|
+
}
|
|
7259
|
+
/** Config field a cloud provider persists its endpoint or base URL under. */
|
|
7260
|
+
function baseURLFieldOf(provider) {
|
|
7261
|
+
return CLOUD_URL_FIELDS[provider];
|
|
7262
|
+
}
|
|
7263
|
+
/** Providers whose settings row offers the "pull models" button. */
|
|
7264
|
+
function modelPullSupported(_provider) {
|
|
7265
|
+
return true;
|
|
7266
|
+
}
|
|
7267
|
+
/** Build one row per provider from persisted settings, including ComfyUI extras. */
|
|
7268
|
+
function rowsFromSettings(value) {
|
|
7269
|
+
const rows = {};
|
|
7270
|
+
for (const provider of IMAGE_PROVIDERS) rows[provider] = {
|
|
7271
|
+
...emptyProviderRow(),
|
|
7272
|
+
model: modelOf(provider, value),
|
|
7273
|
+
baseURL: baseURLOf(provider, value),
|
|
7274
|
+
...provider === "comfyui" ? {
|
|
7275
|
+
workflows: resolveComfyUIWorkflows(value ?? {}),
|
|
7276
|
+
activeWorkflow: activeComfyUIWorkflow(value ?? {})?.name ?? "",
|
|
7277
|
+
timeoutSeconds: Math.max(1, Math.round((value?.comfyuiTimeoutMs ?? 3e5) / 1e3))
|
|
7278
|
+
} : {}
|
|
7279
|
+
};
|
|
7280
|
+
return rows;
|
|
7281
|
+
}
|
|
7282
|
+
/** Edit each provider independently, pick an explicit default, and verify keys inline. */
|
|
6657
7283
|
function ImageGenerationSettingsCard(props) {
|
|
6658
7284
|
const [open, setOpen] = (0, react.useState)(false);
|
|
6659
7285
|
const [snapshot, setSnapshot] = (0, react.useState)(() => props.scope.getSnapshot());
|
|
6660
7286
|
const [lang, setLang] = (0, react.useState)(() => props.locale?.getSnapshot?.()?.active?.startsWith("en") ? "en" : "zh");
|
|
6661
|
-
const [
|
|
6662
|
-
const [
|
|
6663
|
-
const [
|
|
6664
|
-
const [
|
|
6665
|
-
const [
|
|
6666
|
-
const [
|
|
6667
|
-
const [
|
|
6668
|
-
const [
|
|
6669
|
-
const [
|
|
6670
|
-
const [
|
|
6671
|
-
const [
|
|
6672
|
-
const [
|
|
6673
|
-
const [messageIsError, setMessageIsError] = (0, react.useState)(false);
|
|
6674
|
-
const reportMessage = (text) => {
|
|
6675
|
-
setMessage(text);
|
|
6676
|
-
setMessageIsError(false);
|
|
6677
|
-
};
|
|
6678
|
-
const reportError = (text) => {
|
|
6679
|
-
setMessage(text);
|
|
6680
|
-
setMessageIsError(true);
|
|
6681
|
-
};
|
|
7287
|
+
const [defaultProvider, setDefaultProvider] = (0, react.useState)(() => props.scope.getSnapshot().value?.provider ?? "google");
|
|
7288
|
+
const [providerMessage, setProviderMessage] = (0, react.useState)("");
|
|
7289
|
+
const [providerMessageIsError, setProviderMessageIsError] = (0, react.useState)(false);
|
|
7290
|
+
const [saveToWorkspace, setSaveToWorkspace] = (0, react.useState)(() => props.scope.getSnapshot().value?.saveToWorkspace ?? true);
|
|
7291
|
+
const [workspaceFolder, setWorkspaceFolder] = (0, react.useState)(() => props.scope.getSnapshot().value?.workspaceFolder ?? "dsh-image-gen");
|
|
7292
|
+
const [showPill, setShowPill] = (0, react.useState)(() => props.scope.getSnapshot().value?.showProviderPill === true);
|
|
7293
|
+
const [uiMessage, setUiMessage] = (0, react.useState)("");
|
|
7294
|
+
const [uiMessageIsError, setUiMessageIsError] = (0, react.useState)(false);
|
|
7295
|
+
const [workspaceMessage, setWorkspaceMessage] = (0, react.useState)("");
|
|
7296
|
+
const [workspaceMessageIsError, setWorkspaceMessageIsError] = (0, react.useState)(false);
|
|
7297
|
+
const [rows, setRows] = (0, react.useState)(() => rowsFromSettings(props.scope.getSnapshot().value));
|
|
7298
|
+
const [keyTick, setKeyTick] = (0, react.useState)(0);
|
|
6682
7299
|
(0, react.useEffect)(() => props.scope.subscribe(() => {
|
|
6683
7300
|
setSnapshot(props.scope.getSnapshot());
|
|
6684
7301
|
}), [props.scope]);
|
|
@@ -6687,6 +7304,10 @@ window.__ModuleLoader__.load({
|
|
|
6687
7304
|
setLang(props.locale?.getSnapshot?.()?.active?.startsWith("en") ? "en" : "zh");
|
|
6688
7305
|
});
|
|
6689
7306
|
}, [props.locale]);
|
|
7307
|
+
const credentialEvents = props.credentialEvents;
|
|
7308
|
+
(0, react.useEffect)(() => credentialEvents?.listen(() => {
|
|
7309
|
+
setKeyTick((tick) => tick + 1);
|
|
7310
|
+
}), [credentialEvents]);
|
|
6690
7311
|
const t = (keyName, params) => {
|
|
6691
7312
|
let text = (lang === "en" ? DICT.en : DICT.zh)[keyName] || DICT.zh[keyName] || keyName;
|
|
6692
7313
|
if (params) for (const [k, v] of Object.entries(params)) text = text.replace(`{${k}}`, v);
|
|
@@ -6695,46 +7316,122 @@ window.__ModuleLoader__.load({
|
|
|
6695
7316
|
const providerLabels = {
|
|
6696
7317
|
google: t("providerGoogle"),
|
|
6697
7318
|
openai: t("providerOpenAI"),
|
|
7319
|
+
"openai-compat": t("providerOpenAICompat"),
|
|
6698
7320
|
seedream: t("providerSeedream"),
|
|
6699
7321
|
dashscope: t("providerDashScope"),
|
|
7322
|
+
xai: t("providerXAI"),
|
|
7323
|
+
zhipu: t("providerZhipu"),
|
|
6700
7324
|
comfyui: t("providerComfyUI")
|
|
6701
7325
|
};
|
|
6702
7326
|
(0, react.useEffect)(() => {
|
|
6703
7327
|
const value = snapshot.value;
|
|
6704
|
-
|
|
6705
|
-
setProvider(next);
|
|
6706
|
-
setModel(modelOf(next, value));
|
|
6707
|
-
setBaseURL(baseURLOf(next, value));
|
|
6708
|
-
setWorkflows(resolveComfyUIWorkflows(value ?? {}));
|
|
6709
|
-
setActiveWorkflow(activeComfyUIWorkflow(value ?? {})?.name ?? "");
|
|
6710
|
-
setTimeoutSeconds(Math.max(1, Math.round((value?.comfyuiTimeoutMs ?? 3e5) / 1e3)));
|
|
7328
|
+
setDefaultProvider(value?.provider ?? "google");
|
|
6711
7329
|
setSaveToWorkspace(value?.saveToWorkspace ?? true);
|
|
6712
7330
|
setWorkspaceFolder(value?.workspaceFolder ?? "dsh-image-gen");
|
|
7331
|
+
setShowPill(value?.showProviderPill === true);
|
|
7332
|
+
setRows((current) => {
|
|
7333
|
+
const next = {};
|
|
7334
|
+
for (const provider of IMAGE_PROVIDERS) next[provider] = {
|
|
7335
|
+
...current[provider],
|
|
7336
|
+
model: modelOf(provider, value),
|
|
7337
|
+
baseURL: baseURLOf(provider, value),
|
|
7338
|
+
...provider === "comfyui" ? {
|
|
7339
|
+
workflows: resolveComfyUIWorkflows(value ?? {}),
|
|
7340
|
+
activeWorkflow: activeComfyUIWorkflow(value ?? {})?.name ?? "",
|
|
7341
|
+
timeoutSeconds: Math.max(1, Math.round((value?.comfyuiTimeoutMs ?? 3e5) / 1e3))
|
|
7342
|
+
} : {}
|
|
7343
|
+
};
|
|
7344
|
+
return next;
|
|
7345
|
+
});
|
|
6713
7346
|
}, [snapshot]);
|
|
7347
|
+
const credentials = props.credentials;
|
|
7348
|
+
const credentialsAvailable = props.credentialsAvailable;
|
|
6714
7349
|
(0, react.useEffect)(() => {
|
|
6715
|
-
const keyRef = KEY_REF[provider];
|
|
6716
|
-
if (keyRef === void 0) {
|
|
6717
|
-
setConfigured(void 0);
|
|
6718
|
-
return;
|
|
6719
|
-
}
|
|
6720
7350
|
let active = true;
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
7351
|
+
if (!credentialsAvailable()) {
|
|
7352
|
+
setRows((current) => {
|
|
7353
|
+
const next = { ...current };
|
|
7354
|
+
for (const provider of CLOUD_IMAGE_PROVIDERS) next[provider] = {
|
|
7355
|
+
...current[provider],
|
|
7356
|
+
keyStatus: "unavailable",
|
|
7357
|
+
keyInfo: void 0
|
|
7358
|
+
};
|
|
7359
|
+
return next;
|
|
7360
|
+
});
|
|
7361
|
+
return () => {
|
|
7362
|
+
active = false;
|
|
7363
|
+
};
|
|
7364
|
+
}
|
|
7365
|
+
for (const provider of CLOUD_IMAGE_PROVIDERS) {
|
|
7366
|
+
const keyRef = cloudCredentialRef(provider);
|
|
7367
|
+
if (keyRef === void 0) continue;
|
|
7368
|
+
setRows((current) => ({
|
|
7369
|
+
...current,
|
|
7370
|
+
[provider]: {
|
|
7371
|
+
...current[provider],
|
|
7372
|
+
keyStatus: "checking"
|
|
7373
|
+
}
|
|
7374
|
+
}));
|
|
7375
|
+
credentials.describe([keyRef]).then((response) => {
|
|
7376
|
+
if (!active) return;
|
|
7377
|
+
const info = response.ok ? response.value?.[keyRef] : void 0;
|
|
7378
|
+
setRows((current) => ({
|
|
7379
|
+
...current,
|
|
7380
|
+
[provider]: {
|
|
7381
|
+
...current[provider],
|
|
7382
|
+
keyStatus: response.ok ? info?.configured ? "configured" : "missing" : "unknown",
|
|
7383
|
+
keyInfo: info
|
|
7384
|
+
}
|
|
7385
|
+
}));
|
|
7386
|
+
}).catch(() => {
|
|
7387
|
+
if (!active) return;
|
|
7388
|
+
setRows((current) => ({
|
|
7389
|
+
...current,
|
|
7390
|
+
[provider]: {
|
|
7391
|
+
...current[provider],
|
|
7392
|
+
keyStatus: "unknown"
|
|
7393
|
+
}
|
|
7394
|
+
}));
|
|
7395
|
+
});
|
|
7396
|
+
}
|
|
6726
7397
|
return () => {
|
|
6727
7398
|
active = false;
|
|
6728
7399
|
};
|
|
6729
|
-
}, [
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
7400
|
+
}, [
|
|
7401
|
+
credentials,
|
|
7402
|
+
credentialsAvailable,
|
|
7403
|
+
keyTick
|
|
7404
|
+
]);
|
|
7405
|
+
const updateRow = (provider, patch) => {
|
|
7406
|
+
setRows((current) => ({
|
|
7407
|
+
...current,
|
|
7408
|
+
[provider]: {
|
|
7409
|
+
...current[provider],
|
|
7410
|
+
...patch
|
|
7411
|
+
}
|
|
7412
|
+
}));
|
|
7413
|
+
};
|
|
7414
|
+
/** The only action that ever changes the default provider; saving a key never does. */
|
|
7415
|
+
const selectDefaultProvider = (provider) => {
|
|
7416
|
+
setDefaultProvider(provider);
|
|
7417
|
+
setProviderMessage("");
|
|
7418
|
+
setProviderMessageIsError(false);
|
|
7419
|
+
props.scope.set("provider", provider).catch((cause) => {
|
|
7420
|
+
setDefaultProvider(snapshot.value?.provider ?? "google");
|
|
7421
|
+
setProviderMessage(cause instanceof Error ? cause.message : String(cause));
|
|
7422
|
+
setProviderMessageIsError(true);
|
|
7423
|
+
});
|
|
7424
|
+
};
|
|
7425
|
+
const saveProviderRow = async (provider) => {
|
|
7426
|
+
const row = rows[provider];
|
|
7427
|
+
updateRow(provider, {
|
|
7428
|
+
saving: true,
|
|
7429
|
+
message: "",
|
|
7430
|
+
messageIsError: false
|
|
7431
|
+
});
|
|
6734
7432
|
try {
|
|
6735
|
-
await props.scope.set("provider", provider);
|
|
6736
7433
|
if (provider === "comfyui") {
|
|
6737
|
-
const entries = workflows.map((entry) => ({
|
|
7434
|
+
const entries = row.workflows.map((entry) => ({
|
|
6738
7435
|
name: entry.name.trim(),
|
|
6739
7436
|
json: entry.json,
|
|
6740
7437
|
presetPrompt: (entry.presetPrompt ?? "").trim()
|
|
@@ -6744,83 +7441,693 @@ window.__ModuleLoader__.load({
|
|
|
6744
7441
|
validateComfyUIWorkflowJson(entry.json);
|
|
6745
7442
|
}
|
|
6746
7443
|
if (new Set(entries.map((entry) => entry.name)).size !== entries.length) throw new Error(t("workflowDuplicateName"));
|
|
6747
|
-
const activeEntry = entries.find((entry) => entry.name === activeWorkflow) ?? entries[0];
|
|
6748
|
-
await props.scope.set("comfyuiBaseURL", baseURL);
|
|
7444
|
+
const activeEntry = entries.find((entry) => entry.name === row.activeWorkflow) ?? entries[0];
|
|
7445
|
+
await props.scope.set("comfyuiBaseURL", row.baseURL);
|
|
6749
7446
|
await props.scope.set("comfyuiWorkflows", entries);
|
|
6750
7447
|
await props.scope.set("comfyuiActiveWorkflow", activeEntry === void 0 ? "" : activeEntry.name);
|
|
6751
7448
|
await props.scope.set("comfyuiWorkflowJson", activeEntry === void 0 ? "" : activeEntry.json);
|
|
6752
7449
|
await props.scope.set("comfyuiWorkflowName", activeEntry === void 0 ? "" : activeEntry.name);
|
|
6753
|
-
await props.scope.set("comfyuiTimeoutMs", Math.max(1, Math.round(timeoutSeconds)) * 1e3);
|
|
7450
|
+
await props.scope.set("comfyuiTimeoutMs", Math.max(1, Math.round(row.timeoutSeconds)) * 1e3);
|
|
6754
7451
|
} else {
|
|
6755
|
-
await props.scope.set(provider
|
|
6756
|
-
await props.scope.set(provider
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
7452
|
+
await props.scope.set(modelFieldOf(provider), row.model);
|
|
7453
|
+
await props.scope.set(baseURLFieldOf(provider), row.baseURL);
|
|
7454
|
+
if (row.keyInput.trim().length > 0) {
|
|
7455
|
+
const keyRef = cloudCredentialRef(provider);
|
|
7456
|
+
if (keyRef === void 0) throw new Error(t("comfyuiNoKey"));
|
|
7457
|
+
if (!credentialsAvailable()) throw new Error(t("credentialsUnavailable"));
|
|
7458
|
+
const response = await props.credentials.set(keyRef, row.keyInput.trim());
|
|
7459
|
+
if (!response.ok) throw new Error(response.error?.message ?? t("saveKeyFailed"));
|
|
7460
|
+
updateRow(provider, {
|
|
7461
|
+
keyInput: "",
|
|
7462
|
+
keyStatus: "configured"
|
|
7463
|
+
});
|
|
7464
|
+
}
|
|
6767
7465
|
}
|
|
6768
|
-
|
|
7466
|
+
updateRow(provider, {
|
|
7467
|
+
message: t("saved"),
|
|
7468
|
+
messageIsError: false
|
|
7469
|
+
});
|
|
7470
|
+
} catch (cause) {
|
|
7471
|
+
updateRow(provider, {
|
|
7472
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
7473
|
+
messageIsError: true
|
|
7474
|
+
});
|
|
7475
|
+
} finally {
|
|
7476
|
+
updateRow(provider, { saving: false });
|
|
7477
|
+
}
|
|
7478
|
+
};
|
|
7479
|
+
/** Probe through the host route so the browser side never touches credential values. */
|
|
7480
|
+
const testConnection = async (provider) => {
|
|
7481
|
+
updateRow(provider, {
|
|
7482
|
+
testing: true,
|
|
7483
|
+
testResult: void 0,
|
|
7484
|
+
message: "",
|
|
7485
|
+
messageIsError: false
|
|
7486
|
+
});
|
|
7487
|
+
try {
|
|
7488
|
+
const response = await fetch(TEST_CONNECTION_ROUTE, {
|
|
7489
|
+
method: "POST",
|
|
7490
|
+
headers: { "content-type": "application/json" },
|
|
7491
|
+
body: JSON.stringify({ provider })
|
|
7492
|
+
});
|
|
7493
|
+
if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
|
|
7494
|
+
updateRow(provider, { testResult: await response.json() });
|
|
7495
|
+
} catch (cause) {
|
|
7496
|
+
updateRow(provider, { testResult: {
|
|
7497
|
+
ok: false,
|
|
7498
|
+
reason: "error",
|
|
7499
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
7500
|
+
} });
|
|
7501
|
+
} finally {
|
|
7502
|
+
updateRow(provider, { testing: false });
|
|
7503
|
+
}
|
|
7504
|
+
};
|
|
7505
|
+
/** Pull the provider's image-capable model ids through the host route (Google and the OpenAI family). */
|
|
7506
|
+
const fetchProviderModels = async (provider) => {
|
|
7507
|
+
updateRow(provider, {
|
|
7508
|
+
fetchingModels: true,
|
|
7509
|
+
modelFetchMessage: "",
|
|
7510
|
+
modelFetchIsError: false
|
|
7511
|
+
});
|
|
7512
|
+
try {
|
|
7513
|
+
const response = await fetch(TEST_CONNECTION_ROUTE, {
|
|
7514
|
+
method: "POST",
|
|
7515
|
+
headers: { "content-type": "application/json" },
|
|
7516
|
+
body: JSON.stringify({
|
|
7517
|
+
provider,
|
|
7518
|
+
action: "models"
|
|
7519
|
+
})
|
|
7520
|
+
});
|
|
7521
|
+
if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
|
|
7522
|
+
const payload = await response.json();
|
|
7523
|
+
if (payload.ok && Array.isArray(payload.models)) {
|
|
7524
|
+
const models = payload.models.filter((id) => typeof id === "string");
|
|
7525
|
+
updateRow(provider, {
|
|
7526
|
+
modelOptions: models,
|
|
7527
|
+
modelFetchMessage: models.length > 0 ? t("modelsFound", { n: String(models.length) }) : t("modelsNone"),
|
|
7528
|
+
modelFetchIsError: models.length === 0
|
|
7529
|
+
});
|
|
7530
|
+
} else if (payload.reason === "missing-key") updateRow(provider, {
|
|
7531
|
+
modelFetchMessage: t("badgeMissing"),
|
|
7532
|
+
modelFetchIsError: true
|
|
7533
|
+
});
|
|
7534
|
+
else if (payload.reason === "unauthorized") updateRow(provider, {
|
|
7535
|
+
modelFetchMessage: t("testUnauthorized"),
|
|
7536
|
+
modelFetchIsError: true
|
|
7537
|
+
});
|
|
7538
|
+
else updateRow(provider, {
|
|
7539
|
+
modelFetchMessage: payload.message ?? t("testFailed"),
|
|
7540
|
+
modelFetchIsError: true
|
|
7541
|
+
});
|
|
7542
|
+
} catch (cause) {
|
|
7543
|
+
updateRow(provider, {
|
|
7544
|
+
modelFetchMessage: cause instanceof Error ? cause.message : String(cause),
|
|
7545
|
+
modelFetchIsError: true
|
|
7546
|
+
});
|
|
7547
|
+
} finally {
|
|
7548
|
+
updateRow(provider, { fetchingModels: false });
|
|
7549
|
+
}
|
|
7550
|
+
};
|
|
7551
|
+
const clearProviderKey = async (provider) => {
|
|
7552
|
+
const keyRef = cloudCredentialRef(provider);
|
|
7553
|
+
if (keyRef === void 0) return;
|
|
7554
|
+
if (typeof props.credentials.unset !== "function") {
|
|
7555
|
+
updateRow(provider, {
|
|
7556
|
+
message: t("clearKeyUnsupported"),
|
|
7557
|
+
messageIsError: true
|
|
7558
|
+
});
|
|
7559
|
+
return;
|
|
7560
|
+
}
|
|
7561
|
+
updateRow(provider, {
|
|
7562
|
+
saving: true,
|
|
7563
|
+
message: "",
|
|
7564
|
+
messageIsError: false
|
|
7565
|
+
});
|
|
7566
|
+
try {
|
|
7567
|
+
const response = await props.credentials.unset(keyRef);
|
|
7568
|
+
if (!response.ok) throw new Error(response.error?.message ?? t("clearKeyFailed"));
|
|
7569
|
+
updateRow(provider, {
|
|
7570
|
+
keyStatus: "missing",
|
|
7571
|
+
keyInput: "",
|
|
7572
|
+
testResult: void 0,
|
|
7573
|
+
message: t("keyCleared"),
|
|
7574
|
+
messageIsError: false
|
|
7575
|
+
});
|
|
6769
7576
|
} catch (cause) {
|
|
6770
|
-
|
|
7577
|
+
updateRow(provider, {
|
|
7578
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
7579
|
+
messageIsError: true
|
|
7580
|
+
});
|
|
6771
7581
|
} finally {
|
|
6772
|
-
|
|
7582
|
+
updateRow(provider, { saving: false });
|
|
6773
7583
|
}
|
|
6774
7584
|
};
|
|
6775
|
-
const
|
|
6776
|
-
|
|
7585
|
+
const testResultText = (result) => {
|
|
7586
|
+
if (result === void 0) return "";
|
|
7587
|
+
if (result.ok) return t("testOk");
|
|
7588
|
+
if (result.reason === "missing-key") return t("badgeMissing");
|
|
7589
|
+
if (result.reason === "unauthorized") return t("testUnauthorized");
|
|
7590
|
+
return `${t("testFailed")}${result.message !== void 0 && result.message.length > 0 ? `: ${result.message}` : ""}`;
|
|
7591
|
+
};
|
|
7592
|
+
const badgeOf = (provider) => {
|
|
7593
|
+
if (provider === "comfyui") return {
|
|
7594
|
+
text: t("comfyuiNoKey"),
|
|
7595
|
+
className: "dsh-ig-badge dsh-ig-badge-neutral"
|
|
7596
|
+
};
|
|
7597
|
+
const status = rows[provider].keyStatus;
|
|
7598
|
+
if (status === "checking") return {
|
|
7599
|
+
text: t("badgeChecking"),
|
|
7600
|
+
className: "dsh-ig-badge dsh-ig-badge-neutral dsh-ig-badge-checking"
|
|
7601
|
+
};
|
|
7602
|
+
if (status === "configured") return {
|
|
7603
|
+
text: t("badgeConfigured"),
|
|
7604
|
+
className: "dsh-ig-badge dsh-ig-badge-ok"
|
|
7605
|
+
};
|
|
7606
|
+
if (status === "missing") return {
|
|
7607
|
+
text: t("badgeMissing"),
|
|
7608
|
+
className: "dsh-ig-badge dsh-ig-badge-missing"
|
|
7609
|
+
};
|
|
7610
|
+
if (status === "unavailable") return {
|
|
7611
|
+
text: t("badgeUnavailable"),
|
|
7612
|
+
className: "dsh-ig-badge dsh-ig-badge-neutral"
|
|
7613
|
+
};
|
|
7614
|
+
return {
|
|
7615
|
+
text: t("badgeUnknown"),
|
|
7616
|
+
className: "dsh-ig-badge dsh-ig-badge-neutral"
|
|
7617
|
+
};
|
|
7618
|
+
};
|
|
6777
7619
|
const importWorkflow = async (event) => {
|
|
6778
7620
|
const file = event.target.files?.[0];
|
|
6779
7621
|
event.target.value = "";
|
|
6780
7622
|
if (file === void 0) return;
|
|
6781
|
-
|
|
7623
|
+
updateRow("comfyui", {
|
|
7624
|
+
message: "",
|
|
7625
|
+
messageIsError: false
|
|
7626
|
+
});
|
|
6782
7627
|
try {
|
|
6783
7628
|
if (file.size > 5242880) throw new Error(t("workflowTooLarge"));
|
|
6784
7629
|
const json = await file.text();
|
|
6785
7630
|
validateComfyUIWorkflowJson(json);
|
|
6786
|
-
const name = uniqueComfyUIWorkflowName(file.name, workflows.map((entry) => entry.name));
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
7631
|
+
const name = uniqueComfyUIWorkflowName(file.name, rows.comfyui.workflows.map((entry) => entry.name));
|
|
7632
|
+
setRows((current) => ({
|
|
7633
|
+
...current,
|
|
7634
|
+
comfyui: {
|
|
7635
|
+
...current.comfyui,
|
|
7636
|
+
workflows: [...current.comfyui.workflows, {
|
|
7637
|
+
name,
|
|
7638
|
+
json
|
|
7639
|
+
}],
|
|
7640
|
+
activeWorkflow: current.comfyui.activeWorkflow.length > 0 ? current.comfyui.activeWorkflow : name
|
|
7641
|
+
}
|
|
7642
|
+
}));
|
|
7643
|
+
updateRow("comfyui", {
|
|
7644
|
+
message: t("workflowImported", { name }),
|
|
7645
|
+
messageIsError: false
|
|
7646
|
+
});
|
|
6793
7647
|
} catch (cause) {
|
|
6794
|
-
|
|
7648
|
+
updateRow("comfyui", {
|
|
7649
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
7650
|
+
messageIsError: true
|
|
7651
|
+
});
|
|
6795
7652
|
}
|
|
6796
7653
|
};
|
|
6797
7654
|
/** Renaming the active entry keeps the active selection following its new name. */
|
|
6798
7655
|
const renameWorkflow = (index, name) => {
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
7656
|
+
setRows((current) => {
|
|
7657
|
+
const previous = current.comfyui.workflows[index];
|
|
7658
|
+
const workflows = current.comfyui.workflows.map((entry, position) => position === index ? {
|
|
7659
|
+
...entry,
|
|
7660
|
+
name
|
|
7661
|
+
} : entry);
|
|
7662
|
+
const activeWorkflow = previous !== void 0 && previous.name === current.comfyui.activeWorkflow ? name : current.comfyui.activeWorkflow;
|
|
7663
|
+
return {
|
|
7664
|
+
...current,
|
|
7665
|
+
comfyui: {
|
|
7666
|
+
...current.comfyui,
|
|
7667
|
+
workflows,
|
|
7668
|
+
activeWorkflow
|
|
7669
|
+
}
|
|
7670
|
+
};
|
|
7671
|
+
});
|
|
6805
7672
|
};
|
|
6806
7673
|
/** Removing the active entry moves the selection to the first remaining workflow. */
|
|
6807
7674
|
const removeWorkflow = (index) => {
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
7675
|
+
setRows((current) => {
|
|
7676
|
+
const previous = current.comfyui.workflows[index];
|
|
7677
|
+
const workflows = current.comfyui.workflows.filter((_entry, position) => position !== index);
|
|
7678
|
+
const activeWorkflow = previous !== void 0 && previous.name === current.comfyui.activeWorkflow ? workflows[0]?.name ?? "" : current.comfyui.activeWorkflow;
|
|
7679
|
+
return {
|
|
7680
|
+
...current,
|
|
7681
|
+
comfyui: {
|
|
7682
|
+
...current.comfyui,
|
|
7683
|
+
workflows,
|
|
7684
|
+
activeWorkflow
|
|
7685
|
+
}
|
|
7686
|
+
};
|
|
7687
|
+
});
|
|
6812
7688
|
};
|
|
6813
7689
|
/** Editing one entry's preset leaves the rest of the entry untouched. */
|
|
6814
7690
|
const setWorkflowPreset = (index, presetPrompt) => {
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
7691
|
+
setRows((current) => {
|
|
7692
|
+
const workflows = current.comfyui.workflows.map((entry, position) => position === index ? {
|
|
7693
|
+
...entry,
|
|
7694
|
+
presetPrompt
|
|
7695
|
+
} : entry);
|
|
7696
|
+
return {
|
|
7697
|
+
...current,
|
|
7698
|
+
comfyui: {
|
|
7699
|
+
...current.comfyui,
|
|
7700
|
+
workflows
|
|
7701
|
+
}
|
|
7702
|
+
};
|
|
7703
|
+
});
|
|
6819
7704
|
};
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
7705
|
+
/** Workspace checkbox persists immediately, like every other toggle on this card. */
|
|
7706
|
+
const toggleSaveToWorkspace = (next) => {
|
|
7707
|
+
setSaveToWorkspace(next);
|
|
7708
|
+
setWorkspaceMessage("");
|
|
7709
|
+
setWorkspaceMessageIsError(false);
|
|
7710
|
+
props.scope.set("saveToWorkspace", next).catch((cause) => {
|
|
7711
|
+
setSaveToWorkspace(snapshot.value?.saveToWorkspace ?? true);
|
|
7712
|
+
setWorkspaceMessage(cause instanceof Error ? cause.message : String(cause));
|
|
7713
|
+
setWorkspaceMessageIsError(true);
|
|
7714
|
+
});
|
|
7715
|
+
};
|
|
7716
|
+
/** Folder input persists on Enter or blur; no save button needed. */
|
|
7717
|
+
const commitWorkspaceFolder = () => {
|
|
7718
|
+
const next = workspaceFolder.trim();
|
|
7719
|
+
if (next === (snapshot.value?.workspaceFolder ?? "dsh-image-gen")) return;
|
|
7720
|
+
setWorkspaceMessage("");
|
|
7721
|
+
setWorkspaceMessageIsError(false);
|
|
7722
|
+
props.scope.set("workspaceFolder", next).then(() => {
|
|
7723
|
+
setWorkspaceMessage(t("saved"));
|
|
7724
|
+
}).catch((cause) => {
|
|
7725
|
+
setWorkspaceFolder(snapshot.value?.workspaceFolder ?? "dsh-image-gen");
|
|
7726
|
+
setWorkspaceMessage(cause instanceof Error ? cause.message : String(cause));
|
|
7727
|
+
setWorkspaceMessageIsError(true);
|
|
7728
|
+
});
|
|
7729
|
+
};
|
|
7730
|
+
/** The pill toggle saves immediately, like the default-provider radio. */
|
|
7731
|
+
const toggleShowPill = (next) => {
|
|
7732
|
+
setShowPill(next);
|
|
7733
|
+
setUiMessage("");
|
|
7734
|
+
setUiMessageIsError(false);
|
|
7735
|
+
props.scope.set("showProviderPill", next).catch((cause) => {
|
|
7736
|
+
setShowPill(snapshot.value?.showProviderPill === true);
|
|
7737
|
+
setUiMessage(cause instanceof Error ? cause.message : String(cause));
|
|
7738
|
+
setUiMessageIsError(true);
|
|
7739
|
+
});
|
|
7740
|
+
};
|
|
7741
|
+
const renderCloudBody = (provider) => {
|
|
7742
|
+
const row = rows[provider];
|
|
7743
|
+
const keyRef = cloudCredentialRef(provider) ?? "";
|
|
7744
|
+
const keyReadOnly = row.keyInfo?.writable === false;
|
|
7745
|
+
const keyUnavailable = !credentialsAvailable();
|
|
7746
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
7747
|
+
className: "dsh-ig-provider-body",
|
|
7748
|
+
children: (0, react_jsx_runtime.jsxs)("form", {
|
|
7749
|
+
onSubmit: (event) => {
|
|
7750
|
+
event.preventDefault();
|
|
7751
|
+
saveProviderRow(provider);
|
|
7752
|
+
},
|
|
7753
|
+
children: [
|
|
7754
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
7755
|
+
className: "dsh-ig-field",
|
|
7756
|
+
children: [
|
|
7757
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7758
|
+
className: "dsh-ig-label",
|
|
7759
|
+
children: t("apiKeyLabel", { provider: providerLabels[provider] })
|
|
7760
|
+
}),
|
|
7761
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
7762
|
+
className: "dsh-ig-input",
|
|
7763
|
+
type: "password",
|
|
7764
|
+
autoComplete: "off",
|
|
7765
|
+
value: row.keyInput,
|
|
7766
|
+
onChange: (event) => {
|
|
7767
|
+
updateRow(provider, { keyInput: event.target.value });
|
|
7768
|
+
},
|
|
7769
|
+
placeholder: row.keyStatus === "configured" ? t("apiKeyPlaceholder") : "",
|
|
7770
|
+
disabled: !snapshot.writable || keyReadOnly || keyUnavailable
|
|
7771
|
+
}),
|
|
7772
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7773
|
+
className: "dsh-ig-hint",
|
|
7774
|
+
children: keyUnavailable ? t("credentialsUnavailable") : keyReadOnly ? t("keyReadOnly", { source: row.keyInfo?.source ?? "" }) : t("apiKeyHint", { key: keyRef })
|
|
7775
|
+
})
|
|
7776
|
+
]
|
|
7777
|
+
}),
|
|
7778
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
7779
|
+
className: "dsh-ig-field",
|
|
7780
|
+
children: [
|
|
7781
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7782
|
+
className: "dsh-ig-label",
|
|
7783
|
+
children: t("endpoint")
|
|
7784
|
+
}),
|
|
7785
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
7786
|
+
className: "dsh-ig-input-group",
|
|
7787
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
7788
|
+
className: "dsh-ig-input",
|
|
7789
|
+
type: "url",
|
|
7790
|
+
value: row.baseURL,
|
|
7791
|
+
onChange: (event) => {
|
|
7792
|
+
updateRow(provider, { baseURL: event.target.value });
|
|
7793
|
+
},
|
|
7794
|
+
required: true,
|
|
7795
|
+
disabled: !snapshot.writable
|
|
7796
|
+
}), provider !== "openai-compat" ? (0, react_jsx_runtime.jsx)("button", {
|
|
7797
|
+
type: "button",
|
|
7798
|
+
className: "dsh-ig-btn-reset",
|
|
7799
|
+
title: t("resetTitle"),
|
|
7800
|
+
onClick: () => {
|
|
7801
|
+
updateRow(provider, { baseURL: DEFAULT_BASE_URLS[provider] });
|
|
7802
|
+
},
|
|
7803
|
+
disabled: !snapshot.writable,
|
|
7804
|
+
children: t("reset")
|
|
7805
|
+
}) : null]
|
|
7806
|
+
}),
|
|
7807
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7808
|
+
className: "dsh-ig-hint",
|
|
7809
|
+
children: t(CLOUD_HINT_KEYS[provider])
|
|
7810
|
+
})
|
|
7811
|
+
]
|
|
7812
|
+
}),
|
|
7813
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
7814
|
+
className: "dsh-ig-field",
|
|
7815
|
+
children: [
|
|
7816
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7817
|
+
className: "dsh-ig-label",
|
|
7818
|
+
children: t("model")
|
|
7819
|
+
}),
|
|
7820
|
+
modelPullSupported(provider) ? (0, react_jsx_runtime.jsxs)("div", {
|
|
7821
|
+
className: "dsh-ig-input-group",
|
|
7822
|
+
children: [
|
|
7823
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
7824
|
+
className: "dsh-ig-input",
|
|
7825
|
+
value: row.model,
|
|
7826
|
+
onChange: (event) => {
|
|
7827
|
+
updateRow(provider, { model: event.target.value });
|
|
7828
|
+
},
|
|
7829
|
+
list: `dsh-ig-${provider}-model-options`,
|
|
7830
|
+
required: provider !== "openai-compat",
|
|
7831
|
+
disabled: !snapshot.writable
|
|
7832
|
+
}),
|
|
7833
|
+
(0, react_jsx_runtime.jsx)("datalist", {
|
|
7834
|
+
id: `dsh-ig-${provider}-model-options`,
|
|
7835
|
+
children: row.modelOptions.map((id) => (0, react_jsx_runtime.jsx)("option", { value: id }, id))
|
|
7836
|
+
}),
|
|
7837
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
7838
|
+
type: "button",
|
|
7839
|
+
className: "dsh-ig-btn-secondary",
|
|
7840
|
+
disabled: row.fetchingModels || !snapshot.writable,
|
|
7841
|
+
onClick: () => {
|
|
7842
|
+
fetchProviderModels(provider);
|
|
7843
|
+
},
|
|
7844
|
+
children: row.fetchingModels ? t("fetchingModels") : t("fetchModels")
|
|
7845
|
+
})
|
|
7846
|
+
]
|
|
7847
|
+
}) : (0, react_jsx_runtime.jsx)("input", {
|
|
7848
|
+
className: "dsh-ig-input",
|
|
7849
|
+
value: row.model,
|
|
7850
|
+
onChange: (event) => {
|
|
7851
|
+
updateRow(provider, { model: event.target.value });
|
|
7852
|
+
},
|
|
7853
|
+
required: true,
|
|
7854
|
+
disabled: !snapshot.writable
|
|
7855
|
+
}),
|
|
7856
|
+
modelPullSupported(provider) ? row.modelFetchMessage.length > 0 ? (0, react_jsx_runtime.jsx)("span", {
|
|
7857
|
+
className: `dsh-ig-hint${row.modelFetchIsError ? " dsh-ig-hint-error" : ""}`,
|
|
7858
|
+
role: "status",
|
|
7859
|
+
children: row.modelFetchMessage
|
|
7860
|
+
}) : (0, react_jsx_runtime.jsx)("span", {
|
|
7861
|
+
className: "dsh-ig-hint",
|
|
7862
|
+
children: t("fetchModelsHint")
|
|
7863
|
+
}) : null
|
|
7864
|
+
]
|
|
7865
|
+
}),
|
|
7866
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
7867
|
+
className: "dsh-ig-row-actions",
|
|
7868
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
7869
|
+
className: `dsh-ig-status${row.messageIsError ? " dsh-ig-status-error" : ""}`,
|
|
7870
|
+
role: "status",
|
|
7871
|
+
children: row.message || testResultText(row.testResult)
|
|
7872
|
+
}), (0, react_jsx_runtime.jsxs)("span", {
|
|
7873
|
+
className: "dsh-ig-row-buttons",
|
|
7874
|
+
children: [
|
|
7875
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
7876
|
+
type: "button",
|
|
7877
|
+
className: "dsh-ig-btn-secondary",
|
|
7878
|
+
disabled: row.testing,
|
|
7879
|
+
onClick: () => {
|
|
7880
|
+
testConnection(provider);
|
|
7881
|
+
},
|
|
7882
|
+
children: row.testing ? t("testing") : t("testConnection")
|
|
7883
|
+
}),
|
|
7884
|
+
row.keyStatus === "configured" && !keyReadOnly ? (0, react_jsx_runtime.jsx)("button", {
|
|
7885
|
+
type: "button",
|
|
7886
|
+
className: "dsh-ig-btn-secondary dsh-ig-btn-danger",
|
|
7887
|
+
disabled: row.saving,
|
|
7888
|
+
onClick: () => {
|
|
7889
|
+
clearProviderKey(provider);
|
|
7890
|
+
},
|
|
7891
|
+
children: t("clearKey")
|
|
7892
|
+
}) : null,
|
|
7893
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
7894
|
+
className: "dsh-ig-save",
|
|
7895
|
+
type: "submit",
|
|
7896
|
+
disabled: row.saving || !snapshot.writable,
|
|
7897
|
+
children: row.saving ? t("saving") : t("save")
|
|
7898
|
+
})
|
|
7899
|
+
]
|
|
7900
|
+
})]
|
|
7901
|
+
})
|
|
7902
|
+
]
|
|
7903
|
+
})
|
|
7904
|
+
});
|
|
7905
|
+
};
|
|
7906
|
+
const renderComfyUIBody = () => {
|
|
7907
|
+
const row = rows.comfyui;
|
|
7908
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
7909
|
+
className: "dsh-ig-provider-body",
|
|
7910
|
+
children: (0, react_jsx_runtime.jsxs)("form", {
|
|
7911
|
+
onSubmit: (event) => {
|
|
7912
|
+
event.preventDefault();
|
|
7913
|
+
saveProviderRow("comfyui");
|
|
7914
|
+
},
|
|
7915
|
+
children: [
|
|
7916
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
7917
|
+
className: "dsh-ig-field",
|
|
7918
|
+
children: [
|
|
7919
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7920
|
+
className: "dsh-ig-label",
|
|
7921
|
+
children: t("endpoint")
|
|
7922
|
+
}),
|
|
7923
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
7924
|
+
className: "dsh-ig-input-group",
|
|
7925
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
7926
|
+
className: "dsh-ig-input",
|
|
7927
|
+
type: "url",
|
|
7928
|
+
value: row.baseURL,
|
|
7929
|
+
onChange: (event) => {
|
|
7930
|
+
updateRow("comfyui", { baseURL: event.target.value });
|
|
7931
|
+
},
|
|
7932
|
+
required: true,
|
|
7933
|
+
disabled: !snapshot.writable
|
|
7934
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
7935
|
+
type: "button",
|
|
7936
|
+
className: "dsh-ig-btn-reset",
|
|
7937
|
+
title: t("resetTitle"),
|
|
7938
|
+
onClick: () => {
|
|
7939
|
+
updateRow("comfyui", { baseURL: DEFAULT_BASE_URLS.comfyui });
|
|
7940
|
+
},
|
|
7941
|
+
disabled: !snapshot.writable,
|
|
7942
|
+
children: t("reset")
|
|
7943
|
+
})]
|
|
7944
|
+
}),
|
|
7945
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7946
|
+
className: "dsh-ig-hint",
|
|
7947
|
+
children: t("endpointHintComfyUI")
|
|
7948
|
+
})
|
|
7949
|
+
]
|
|
7950
|
+
}),
|
|
7951
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
7952
|
+
className: "dsh-ig-field",
|
|
7953
|
+
children: [
|
|
7954
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
7955
|
+
className: "dsh-ig-label",
|
|
7956
|
+
children: t("workflow")
|
|
7957
|
+
}),
|
|
7958
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
7959
|
+
className: "dsh-ig-file-row",
|
|
7960
|
+
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
7961
|
+
className: "dsh-ig-file-button",
|
|
7962
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
7963
|
+
className: "dsh-ig-file-input",
|
|
7964
|
+
type: "file",
|
|
7965
|
+
accept: ".json,application/json",
|
|
7966
|
+
onChange: (event) => {
|
|
7967
|
+
importWorkflow(event);
|
|
7968
|
+
}
|
|
7969
|
+
}), t("workflowImport")]
|
|
7970
|
+
}), row.workflows.length === 0 ? (0, react_jsx_runtime.jsx)("span", {
|
|
7971
|
+
className: "dsh-ig-file-name",
|
|
7972
|
+
children: t("workflowMissing")
|
|
7973
|
+
}) : null]
|
|
7974
|
+
}),
|
|
7975
|
+
row.workflows.length > 0 ? (0, react_jsx_runtime.jsx)("ul", {
|
|
7976
|
+
className: "dsh-ig-workflow-list",
|
|
7977
|
+
children: row.workflows.map((entry, index) => (0, react_jsx_runtime.jsxs)("li", {
|
|
7978
|
+
className: "dsh-ig-workflow-row",
|
|
7979
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
7980
|
+
className: "dsh-ig-workflow-main",
|
|
7981
|
+
children: [
|
|
7982
|
+
(0, react_jsx_runtime.jsx)("label", {
|
|
7983
|
+
className: "dsh-ig-workflow-active",
|
|
7984
|
+
title: t("workflowActiveTitle"),
|
|
7985
|
+
children: (0, react_jsx_runtime.jsx)("input", {
|
|
7986
|
+
type: "radio",
|
|
7987
|
+
name: "dsh-ig-active-workflow",
|
|
7988
|
+
"aria-label": t("workflowActiveTitle"),
|
|
7989
|
+
checked: entry.name === row.activeWorkflow,
|
|
7990
|
+
onChange: () => {
|
|
7991
|
+
updateRow("comfyui", { activeWorkflow: entry.name });
|
|
7992
|
+
}
|
|
7993
|
+
})
|
|
7994
|
+
}),
|
|
7995
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
7996
|
+
className: "dsh-ig-input dsh-ig-workflow-name",
|
|
7997
|
+
value: entry.name,
|
|
7998
|
+
title: entry.name,
|
|
7999
|
+
onChange: (event) => {
|
|
8000
|
+
renameWorkflow(index, event.target.value);
|
|
8001
|
+
}
|
|
8002
|
+
}),
|
|
8003
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
8004
|
+
type: "button",
|
|
8005
|
+
className: "dsh-ig-btn-reset",
|
|
8006
|
+
onClick: () => {
|
|
8007
|
+
removeWorkflow(index);
|
|
8008
|
+
},
|
|
8009
|
+
children: t("workflowRemove")
|
|
8010
|
+
})
|
|
8011
|
+
]
|
|
8012
|
+
}), (0, react_jsx_runtime.jsx)("input", {
|
|
8013
|
+
className: "dsh-ig-input dsh-ig-workflow-preset",
|
|
8014
|
+
value: entry.presetPrompt ?? "",
|
|
8015
|
+
placeholder: t("workflowPresetPlaceholder"),
|
|
8016
|
+
title: t("workflowPresetTitle"),
|
|
8017
|
+
onChange: (event) => {
|
|
8018
|
+
setWorkflowPreset(index, event.target.value);
|
|
8019
|
+
}
|
|
8020
|
+
})]
|
|
8021
|
+
}, String(index)))
|
|
8022
|
+
}) : null,
|
|
8023
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8024
|
+
className: "dsh-ig-hint",
|
|
8025
|
+
children: t("workflowHint")
|
|
8026
|
+
})
|
|
8027
|
+
]
|
|
8028
|
+
}),
|
|
8029
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
8030
|
+
className: "dsh-ig-field",
|
|
8031
|
+
children: [
|
|
8032
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8033
|
+
className: "dsh-ig-label",
|
|
8034
|
+
children: t("timeout")
|
|
8035
|
+
}),
|
|
8036
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
8037
|
+
className: "dsh-ig-input",
|
|
8038
|
+
type: "number",
|
|
8039
|
+
min: "1",
|
|
8040
|
+
max: "3600",
|
|
8041
|
+
step: "1",
|
|
8042
|
+
value: row.timeoutSeconds,
|
|
8043
|
+
onChange: (event) => {
|
|
8044
|
+
updateRow("comfyui", { timeoutSeconds: Number(event.target.value) });
|
|
8045
|
+
},
|
|
8046
|
+
required: true,
|
|
8047
|
+
disabled: !snapshot.writable
|
|
8048
|
+
}),
|
|
8049
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8050
|
+
className: "dsh-ig-hint",
|
|
8051
|
+
children: t("timeoutHint")
|
|
8052
|
+
})
|
|
8053
|
+
]
|
|
8054
|
+
}),
|
|
8055
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
8056
|
+
className: "dsh-ig-row-actions",
|
|
8057
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
8058
|
+
className: `dsh-ig-status${row.messageIsError ? " dsh-ig-status-error" : ""}`,
|
|
8059
|
+
role: "status",
|
|
8060
|
+
children: row.message || testResultText(row.testResult)
|
|
8061
|
+
}), (0, react_jsx_runtime.jsxs)("span", {
|
|
8062
|
+
className: "dsh-ig-row-buttons",
|
|
8063
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
8064
|
+
type: "button",
|
|
8065
|
+
className: "dsh-ig-btn-secondary",
|
|
8066
|
+
disabled: row.testing,
|
|
8067
|
+
onClick: () => {
|
|
8068
|
+
testConnection("comfyui");
|
|
8069
|
+
},
|
|
8070
|
+
children: row.testing ? t("testing") : t("testConnection")
|
|
8071
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
8072
|
+
className: "dsh-ig-save",
|
|
8073
|
+
type: "submit",
|
|
8074
|
+
disabled: row.saving || !snapshot.writable || row.workflows.length === 0,
|
|
8075
|
+
children: row.saving ? t("saving") : t("save")
|
|
8076
|
+
})]
|
|
8077
|
+
})]
|
|
8078
|
+
})
|
|
8079
|
+
]
|
|
8080
|
+
})
|
|
8081
|
+
});
|
|
8082
|
+
};
|
|
8083
|
+
const renderProviderRow = (provider) => {
|
|
8084
|
+
const row = rows[provider];
|
|
8085
|
+
const badge = badgeOf(provider);
|
|
8086
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
8087
|
+
className: `dsh-ig-provider-row ${row.expanded ? "dsh-ig-provider-row-open" : ""}`,
|
|
8088
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
8089
|
+
type: "button",
|
|
8090
|
+
className: "dsh-ig-provider-head",
|
|
8091
|
+
"aria-expanded": row.expanded,
|
|
8092
|
+
onClick: () => {
|
|
8093
|
+
updateRow(provider, { expanded: !row.expanded });
|
|
8094
|
+
},
|
|
8095
|
+
children: [
|
|
8096
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8097
|
+
className: "dsh-ig-provider-name",
|
|
8098
|
+
children: providerLabels[provider]
|
|
8099
|
+
}),
|
|
8100
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
8101
|
+
className: badge.className,
|
|
8102
|
+
title: badge.text,
|
|
8103
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
8104
|
+
className: "dsh-ig-badge-dot",
|
|
8105
|
+
"aria-hidden": "true"
|
|
8106
|
+
}), badge.text]
|
|
8107
|
+
}),
|
|
8108
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8109
|
+
className: `dsh-ig-provider-chevron ${row.expanded ? "dsh-ig-provider-chevron-open" : ""}`,
|
|
8110
|
+
"aria-hidden": "true",
|
|
8111
|
+
children: (0, react_jsx_runtime.jsx)("svg", {
|
|
8112
|
+
width: "12",
|
|
8113
|
+
height: "12",
|
|
8114
|
+
viewBox: "0 0 16 16",
|
|
8115
|
+
fill: "none",
|
|
8116
|
+
stroke: "currentColor",
|
|
8117
|
+
strokeWidth: "2",
|
|
8118
|
+
strokeLinecap: "round",
|
|
8119
|
+
strokeLinejoin: "round",
|
|
8120
|
+
children: (0, react_jsx_runtime.jsx)("path", { d: "M4 6l4 4 4-4" })
|
|
8121
|
+
})
|
|
8122
|
+
})
|
|
8123
|
+
]
|
|
8124
|
+
}), row.expanded ? provider === "comfyui" ? renderComfyUIBody() : renderCloudBody(provider) : null]
|
|
8125
|
+
}, provider);
|
|
8126
|
+
};
|
|
8127
|
+
return (0, react_jsx_runtime.jsxs)("li", {
|
|
8128
|
+
className: `dsh-ig-card ${open ? "dsh-ig-card-open" : ""}`,
|
|
8129
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
8130
|
+
type: "button",
|
|
6824
8131
|
className: "dsh-ig-head",
|
|
6825
8132
|
"aria-expanded": open,
|
|
6826
8133
|
onClick: () => {
|
|
@@ -6850,280 +8157,147 @@ window.__ModuleLoader__.load({
|
|
|
6850
8157
|
children: (0, react_jsx_runtime.jsx)("path", { d: "M4 6l4 4 4-4" })
|
|
6851
8158
|
})
|
|
6852
8159
|
})]
|
|
6853
|
-
}), open ? (0, react_jsx_runtime.jsxs)("
|
|
8160
|
+
}), open ? (0, react_jsx_runtime.jsxs)("div", {
|
|
6854
8161
|
className: "dsh-ig-body",
|
|
6855
|
-
onSubmit: (event) => {
|
|
6856
|
-
save(event);
|
|
6857
|
-
},
|
|
6858
8162
|
children: [
|
|
6859
|
-
(0, react_jsx_runtime.
|
|
6860
|
-
className: "dsh-ig-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
className: "dsh-ig-label",
|
|
6864
|
-
children: t("provider")
|
|
6865
|
-
}),
|
|
6866
|
-
(0, react_jsx_runtime.jsxs)("select", {
|
|
6867
|
-
className: "dsh-ig-input",
|
|
6868
|
-
value: provider,
|
|
6869
|
-
onChange: (event) => {
|
|
6870
|
-
const next = event.target.value;
|
|
6871
|
-
setProvider(next);
|
|
6872
|
-
setModel(modelOf(next, snapshot.value));
|
|
6873
|
-
setBaseURL(baseURLOf(next, snapshot.value));
|
|
6874
|
-
setKey("");
|
|
6875
|
-
},
|
|
6876
|
-
children: [
|
|
6877
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
6878
|
-
value: "google",
|
|
6879
|
-
children: t("providerGoogle")
|
|
6880
|
-
}),
|
|
6881
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
6882
|
-
value: "openai",
|
|
6883
|
-
children: t("providerOpenAI")
|
|
6884
|
-
}),
|
|
6885
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
6886
|
-
value: "seedream",
|
|
6887
|
-
children: t("providerSeedream")
|
|
6888
|
-
}),
|
|
6889
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
6890
|
-
value: "dashscope",
|
|
6891
|
-
children: t("providerDashScope")
|
|
6892
|
-
}),
|
|
6893
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
6894
|
-
value: "comfyui",
|
|
6895
|
-
children: t("providerComfyUI")
|
|
6896
|
-
})
|
|
6897
|
-
]
|
|
6898
|
-
}),
|
|
6899
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
6900
|
-
className: "dsh-ig-hint",
|
|
6901
|
-
children: providerLabels[provider]
|
|
6902
|
-
})
|
|
6903
|
-
]
|
|
6904
|
-
}),
|
|
6905
|
-
provider !== "comfyui" ? (0, react_jsx_runtime.jsxs)("label", {
|
|
6906
|
-
className: "dsh-ig-field",
|
|
6907
|
-
children: [
|
|
6908
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
6909
|
-
className: "dsh-ig-label",
|
|
6910
|
-
children: t("apiKeyLabel", { provider: providerLabels[provider] })
|
|
6911
|
-
}),
|
|
6912
|
-
(0, react_jsx_runtime.jsx)("input", {
|
|
6913
|
-
className: "dsh-ig-input",
|
|
6914
|
-
type: "password",
|
|
6915
|
-
autoComplete: "off",
|
|
6916
|
-
value: key,
|
|
6917
|
-
onChange: (event) => {
|
|
6918
|
-
setKey(event.target.value);
|
|
6919
|
-
},
|
|
6920
|
-
placeholder: configured ? t("apiKeyPlaceholder") : ""
|
|
6921
|
-
}),
|
|
6922
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
6923
|
-
className: "dsh-ig-hint",
|
|
6924
|
-
children: t("apiKeyHint", { key: KEY_REF[provider] ?? "" })
|
|
6925
|
-
})
|
|
6926
|
-
]
|
|
8163
|
+
!snapshot.writable ? (0, react_jsx_runtime.jsx)("p", {
|
|
8164
|
+
className: "dsh-ig-status dsh-ig-status-readonly",
|
|
8165
|
+
role: "note",
|
|
8166
|
+
children: t("settingsReadOnly")
|
|
6927
8167
|
}) : null,
|
|
6928
|
-
(0, react_jsx_runtime.jsxs)("
|
|
8168
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
6929
8169
|
className: "dsh-ig-field",
|
|
6930
8170
|
children: [
|
|
6931
8171
|
(0, react_jsx_runtime.jsx)("span", {
|
|
6932
8172
|
className: "dsh-ig-label",
|
|
6933
|
-
children: t("
|
|
8173
|
+
children: t("defaultProvider")
|
|
6934
8174
|
}),
|
|
6935
|
-
(0, react_jsx_runtime.
|
|
6936
|
-
className: "dsh-ig-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
},
|
|
6952
|
-
children: t("reset")
|
|
6953
|
-
})]
|
|
8175
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
8176
|
+
className: "dsh-ig-radios",
|
|
8177
|
+
role: "radiogroup",
|
|
8178
|
+
"aria-label": t("defaultProvider"),
|
|
8179
|
+
children: IMAGE_PROVIDERS.map((provider) => (0, react_jsx_runtime.jsxs)("label", {
|
|
8180
|
+
className: `dsh-ig-radio${defaultProvider === provider ? " dsh-ig-radio-checked" : ""}`,
|
|
8181
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
8182
|
+
type: "radio",
|
|
8183
|
+
name: "dsh-ig-default-provider",
|
|
8184
|
+
checked: defaultProvider === provider,
|
|
8185
|
+
onChange: () => {
|
|
8186
|
+
selectDefaultProvider(provider);
|
|
8187
|
+
},
|
|
8188
|
+
disabled: !snapshot.writable
|
|
8189
|
+
}), providerLabels[provider]]
|
|
8190
|
+
}, provider))
|
|
6954
8191
|
}),
|
|
6955
8192
|
(0, react_jsx_runtime.jsx)("span", {
|
|
6956
8193
|
className: "dsh-ig-hint",
|
|
6957
|
-
children:
|
|
6958
|
-
})
|
|
8194
|
+
children: t("defaultProviderHint")
|
|
8195
|
+
}),
|
|
8196
|
+
providerMessage.length > 0 ? (0, react_jsx_runtime.jsx)("span", {
|
|
8197
|
+
className: `dsh-ig-status${providerMessageIsError ? " dsh-ig-status-error" : ""}`,
|
|
8198
|
+
role: "status",
|
|
8199
|
+
children: providerMessage
|
|
8200
|
+
}) : null
|
|
6959
8201
|
]
|
|
6960
8202
|
}),
|
|
6961
|
-
|
|
6962
|
-
className: "dsh-ig-
|
|
6963
|
-
children:
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
className: "dsh-ig-input",
|
|
6968
|
-
value: model,
|
|
6969
|
-
onChange: (event) => {
|
|
6970
|
-
setModel(event.target.value);
|
|
6971
|
-
},
|
|
6972
|
-
required: true
|
|
6973
|
-
})]
|
|
6974
|
-
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
6975
|
-
className: "dsh-ig-field",
|
|
8203
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
8204
|
+
className: "dsh-ig-providers",
|
|
8205
|
+
children: IMAGE_PROVIDERS.map((provider) => renderProviderRow(provider))
|
|
8206
|
+
}),
|
|
8207
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
8208
|
+
className: "dsh-ig-section",
|
|
6976
8209
|
children: [
|
|
6977
8210
|
(0, react_jsx_runtime.jsx)("span", {
|
|
6978
|
-
className: "dsh-ig-
|
|
6979
|
-
children: t("
|
|
8211
|
+
className: "dsh-ig-section-title",
|
|
8212
|
+
children: t("workspaceSection")
|
|
6980
8213
|
}),
|
|
6981
8214
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
6982
|
-
className: "dsh-ig-
|
|
8215
|
+
className: "dsh-ig-field",
|
|
6983
8216
|
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
6984
|
-
className: "dsh-ig-
|
|
8217
|
+
className: "dsh-ig-check-row",
|
|
6985
8218
|
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
accept: ".json,application/json",
|
|
8219
|
+
type: "checkbox",
|
|
8220
|
+
checked: saveToWorkspace,
|
|
6989
8221
|
onChange: (event) => {
|
|
6990
|
-
|
|
6991
|
-
}
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
8222
|
+
toggleSaveToWorkspace(event.target.checked);
|
|
8223
|
+
},
|
|
8224
|
+
disabled: !snapshot.writable
|
|
8225
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
8226
|
+
className: "dsh-ig-label",
|
|
8227
|
+
children: t("saveToWorkspace")
|
|
8228
|
+
})]
|
|
8229
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
8230
|
+
className: "dsh-ig-hint",
|
|
8231
|
+
children: t("saveToWorkspaceHint")
|
|
8232
|
+
})]
|
|
6997
8233
|
}),
|
|
6998
|
-
|
|
6999
|
-
className: "dsh-ig-
|
|
7000
|
-
children:
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
children: (0, react_jsx_runtime.jsx)("input", {
|
|
7009
|
-
type: "radio",
|
|
7010
|
-
name: "dsh-ig-active-workflow",
|
|
7011
|
-
"aria-label": t("workflowActiveTitle"),
|
|
7012
|
-
checked: entry.name === activeWorkflow,
|
|
7013
|
-
onChange: () => {
|
|
7014
|
-
setActiveWorkflow(entry.name);
|
|
7015
|
-
}
|
|
7016
|
-
})
|
|
7017
|
-
}),
|
|
7018
|
-
(0, react_jsx_runtime.jsx)("input", {
|
|
7019
|
-
className: "dsh-ig-input dsh-ig-workflow-name",
|
|
7020
|
-
value: entry.name,
|
|
7021
|
-
title: entry.name,
|
|
7022
|
-
onChange: (event) => {
|
|
7023
|
-
renameWorkflow(index, event.target.value);
|
|
7024
|
-
}
|
|
7025
|
-
}),
|
|
7026
|
-
(0, react_jsx_runtime.jsx)("button", {
|
|
7027
|
-
type: "button",
|
|
7028
|
-
className: "dsh-ig-btn-reset",
|
|
7029
|
-
onClick: () => {
|
|
7030
|
-
removeWorkflow(index);
|
|
7031
|
-
},
|
|
7032
|
-
children: t("workflowRemove")
|
|
7033
|
-
})
|
|
7034
|
-
]
|
|
7035
|
-
}), (0, react_jsx_runtime.jsx)("input", {
|
|
7036
|
-
className: "dsh-ig-input dsh-ig-workflow-preset",
|
|
7037
|
-
value: entry.presetPrompt ?? "",
|
|
7038
|
-
placeholder: t("workflowPresetPlaceholder"),
|
|
7039
|
-
title: t("workflowPresetTitle"),
|
|
8234
|
+
saveToWorkspace ? (0, react_jsx_runtime.jsxs)("label", {
|
|
8235
|
+
className: "dsh-ig-field",
|
|
8236
|
+
children: [
|
|
8237
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8238
|
+
className: "dsh-ig-label",
|
|
8239
|
+
children: t("folder")
|
|
8240
|
+
}),
|
|
8241
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
8242
|
+
className: "dsh-ig-input",
|
|
8243
|
+
value: workspaceFolder,
|
|
7040
8244
|
onChange: (event) => {
|
|
7041
|
-
|
|
7042
|
-
}
|
|
7043
|
-
|
|
7044
|
-
|
|
8245
|
+
setWorkspaceFolder(event.target.value);
|
|
8246
|
+
},
|
|
8247
|
+
onBlur: () => {
|
|
8248
|
+
commitWorkspaceFolder();
|
|
8249
|
+
},
|
|
8250
|
+
onKeyDown: (event) => {
|
|
8251
|
+
if (event.key === "Enter") commitWorkspaceFolder();
|
|
8252
|
+
},
|
|
8253
|
+
placeholder: "dsh-image-gen",
|
|
8254
|
+
disabled: !snapshot.writable
|
|
8255
|
+
}),
|
|
8256
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8257
|
+
className: "dsh-ig-hint",
|
|
8258
|
+
children: t("folderHint")
|
|
8259
|
+
})
|
|
8260
|
+
]
|
|
7045
8261
|
}) : null,
|
|
7046
|
-
(0, react_jsx_runtime.jsx)("
|
|
7047
|
-
className: "dsh-ig-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
}), (0, react_jsx_runtime.jsxs)("label", {
|
|
7052
|
-
className: "dsh-ig-field",
|
|
7053
|
-
children: [
|
|
7054
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
7055
|
-
className: "dsh-ig-label",
|
|
7056
|
-
children: t("timeout")
|
|
7057
|
-
}),
|
|
7058
|
-
(0, react_jsx_runtime.jsx)("input", {
|
|
7059
|
-
className: "dsh-ig-input",
|
|
7060
|
-
type: "number",
|
|
7061
|
-
min: "1",
|
|
7062
|
-
max: "3600",
|
|
7063
|
-
step: "1",
|
|
7064
|
-
value: timeoutSeconds,
|
|
7065
|
-
onChange: (event) => {
|
|
7066
|
-
setTimeoutSeconds(Number(event.target.value));
|
|
7067
|
-
},
|
|
7068
|
-
required: true
|
|
7069
|
-
}),
|
|
7070
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
7071
|
-
className: "dsh-ig-hint",
|
|
7072
|
-
children: t("timeoutHint")
|
|
7073
|
-
})
|
|
8262
|
+
workspaceMessage.length > 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
8263
|
+
className: `dsh-ig-status${workspaceMessageIsError ? " dsh-ig-status-error" : ""}`,
|
|
8264
|
+
role: "status",
|
|
8265
|
+
children: workspaceMessage
|
|
8266
|
+
}) : null
|
|
7074
8267
|
]
|
|
7075
|
-
})] }),
|
|
7076
|
-
(0, react_jsx_runtime.jsxs)("div", {
|
|
7077
|
-
className: "dsh-ig-field",
|
|
7078
|
-
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
7079
|
-
className: "dsh-ig-check-row",
|
|
7080
|
-
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
7081
|
-
type: "checkbox",
|
|
7082
|
-
checked: saveToWorkspace,
|
|
7083
|
-
onChange: (event) => {
|
|
7084
|
-
setSaveToWorkspace(event.target.checked);
|
|
7085
|
-
}
|
|
7086
|
-
}), (0, react_jsx_runtime.jsx)("span", {
|
|
7087
|
-
className: "dsh-ig-label",
|
|
7088
|
-
children: t("saveToWorkspace")
|
|
7089
|
-
})]
|
|
7090
|
-
}), (0, react_jsx_runtime.jsx)("span", {
|
|
7091
|
-
className: "dsh-ig-hint",
|
|
7092
|
-
children: t("saveToWorkspaceHint")
|
|
7093
|
-
})]
|
|
7094
8268
|
}),
|
|
7095
|
-
saveToWorkspace ? (0, react_jsx_runtime.jsxs)("label", {
|
|
7096
|
-
className: "dsh-ig-field",
|
|
7097
|
-
children: [
|
|
7098
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
7099
|
-
className: "dsh-ig-label",
|
|
7100
|
-
children: t("folder")
|
|
7101
|
-
}),
|
|
7102
|
-
(0, react_jsx_runtime.jsx)("input", {
|
|
7103
|
-
className: "dsh-ig-input",
|
|
7104
|
-
value: workspaceFolder,
|
|
7105
|
-
onChange: (event) => {
|
|
7106
|
-
setWorkspaceFolder(event.target.value);
|
|
7107
|
-
},
|
|
7108
|
-
placeholder: "dsh-image-gen"
|
|
7109
|
-
}),
|
|
7110
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
7111
|
-
className: "dsh-ig-hint",
|
|
7112
|
-
children: t("folderHint")
|
|
7113
|
-
})
|
|
7114
|
-
]
|
|
7115
|
-
}) : null,
|
|
7116
8269
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
7117
|
-
className: "dsh-ig-
|
|
7118
|
-
children: [(0, react_jsx_runtime.jsx)("
|
|
7119
|
-
className:
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
8270
|
+
className: "dsh-ig-section",
|
|
8271
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
8272
|
+
className: "dsh-ig-section-title",
|
|
8273
|
+
children: t("uiSection")
|
|
8274
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
8275
|
+
className: "dsh-ig-field",
|
|
8276
|
+
children: [
|
|
8277
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
8278
|
+
className: "dsh-ig-check-row",
|
|
8279
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
8280
|
+
type: "checkbox",
|
|
8281
|
+
checked: showPill,
|
|
8282
|
+
onChange: (event) => {
|
|
8283
|
+
toggleShowPill(event.target.checked);
|
|
8284
|
+
},
|
|
8285
|
+
disabled: !snapshot.writable
|
|
8286
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
8287
|
+
className: "dsh-ig-label",
|
|
8288
|
+
children: t("showPill")
|
|
8289
|
+
})]
|
|
8290
|
+
}),
|
|
8291
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
8292
|
+
className: "dsh-ig-hint",
|
|
8293
|
+
children: t("showPillHint")
|
|
8294
|
+
}),
|
|
8295
|
+
uiMessage.length > 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
8296
|
+
className: `dsh-ig-status${uiMessageIsError ? " dsh-ig-status-error" : ""}`,
|
|
8297
|
+
role: "status",
|
|
8298
|
+
children: uiMessage
|
|
8299
|
+
}) : null
|
|
8300
|
+
]
|
|
7127
8301
|
})]
|
|
7128
8302
|
})
|
|
7129
8303
|
]
|
|
@@ -7674,11 +8848,11 @@ window.__ModuleLoader__.load({
|
|
|
7674
8848
|
};
|
|
7675
8849
|
}
|
|
7676
8850
|
function modelOf(provider, value) {
|
|
7677
|
-
const stored = provider === "
|
|
8851
|
+
const stored = provider === "comfyui" ? activeComfyUIWorkflow(value ?? {})?.name : value?.[CLOUD_MODEL_FIELDS[provider]];
|
|
7678
8852
|
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_MODELS[provider];
|
|
7679
8853
|
}
|
|
7680
8854
|
function baseURLOf(provider, value) {
|
|
7681
|
-
const stored = provider === "
|
|
8855
|
+
const stored = provider === "comfyui" ? value?.comfyuiBaseURL : value?.[CLOUD_URL_FIELDS[provider]];
|
|
7682
8856
|
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_BASE_URLS[provider];
|
|
7683
8857
|
}
|
|
7684
8858
|
//#endregion
|