@dsh-plus/image-studio 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +8 -8
- package/lib/index.d.ts +30 -25
- package/lib/index.js +48 -59
- package/package.json +15 -15
- package/src/client/panel/gallery.tsx +7 -3
- package/src/client/panel/generator.tsx +3 -3
- package/src/client/panel/panel.tsx +2 -2
- package/src/client/panel/picker.tsx +2 -2
- package/src/client/panel/tasks.tsx +2 -2
- package/src/config.ts +24 -9
- package/src/index.ts +4 -3
- package/src/service.ts +15 -36
package/lib/client.js
CHANGED
|
@@ -307,7 +307,7 @@ window.__ModuleLoader__.load({
|
|
|
307
307
|
className: `${p}-pending`,
|
|
308
308
|
children: props.dirtyLabel
|
|
309
309
|
}) : null,
|
|
310
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
310
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}` })
|
|
311
311
|
]
|
|
312
312
|
}), props.open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
313
313
|
className: `${p}-body`,
|
|
@@ -2036,7 +2036,7 @@ ${extra}
|
|
|
2036
2036
|
className: "ims-btn ims-btnGhost",
|
|
2037
2037
|
onClick: () => setConfirming(true),
|
|
2038
2038
|
children: [
|
|
2039
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
2039
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 14 }),
|
|
2040
2040
|
" ",
|
|
2041
2041
|
t("common.delete")
|
|
2042
2042
|
]
|
|
@@ -2064,7 +2064,7 @@ ${extra}
|
|
|
2064
2064
|
className: "ims-btn ims-btnGhost ims-btnSmall",
|
|
2065
2065
|
onClick: onRefresh,
|
|
2066
2066
|
children: [
|
|
2067
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
2067
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, { size: 14 }),
|
|
2068
2068
|
" ",
|
|
2069
2069
|
t("common.refresh")
|
|
2070
2070
|
]
|
|
@@ -2564,7 +2564,7 @@ ${extra}
|
|
|
2564
2564
|
onUploaded: selectUploaded,
|
|
2565
2565
|
className: "ims-btn ims-btnPrimary ims-btnSmall",
|
|
2566
2566
|
children: [
|
|
2567
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
2567
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineMedium, { size: 14 }),
|
|
2568
2568
|
" ",
|
|
2569
2569
|
t("upload.pick")
|
|
2570
2570
|
]
|
|
@@ -3017,7 +3017,7 @@ ${extra}
|
|
|
3017
3017
|
kind: "upload",
|
|
3018
3018
|
id: entry.id
|
|
3019
3019
|
}))].slice(0, 16)),
|
|
3020
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
3020
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineMedium, { size: 18 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("upload.pick") })]
|
|
3021
3021
|
})
|
|
3022
3022
|
]
|
|
3023
3023
|
}),
|
|
@@ -3058,7 +3058,7 @@ ${extra}
|
|
|
3058
3058
|
accept: "image/png",
|
|
3059
3059
|
className: "ims-thumbAdd",
|
|
3060
3060
|
onUploaded: (entries) => setMask(uploadRef(entries)),
|
|
3061
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
3061
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineMedium, { size: 18 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("upload.pick") })]
|
|
3062
3062
|
})
|
|
3063
3063
|
]
|
|
3064
3064
|
}),
|
|
@@ -3210,7 +3210,7 @@ ${extra}
|
|
|
3210
3210
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3211
3211
|
className: `ims-task ims-task-${task.state}`,
|
|
3212
3212
|
children: [
|
|
3213
|
-
active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
3213
|
+
active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutlineRegular, {
|
|
3214
3214
|
className: "ims-spin",
|
|
3215
3215
|
size: 14
|
|
3216
3216
|
}) : null,
|
|
@@ -3365,7 +3365,7 @@ ${extra}
|
|
|
3365
3365
|
className: "ims-iconBtn",
|
|
3366
3366
|
"aria-label": t("common.close"),
|
|
3367
3367
|
onClick: () => studio.setOpen(false),
|
|
3368
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.
|
|
3368
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutlineMedium, { size: 16 })
|
|
3369
3369
|
})]
|
|
3370
3370
|
}),
|
|
3371
3371
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import z from "@deepseek-ai/schemastery";
|
|
2
1
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
2
|
+
import { UnwrapVolatile } from "@dsh-plus/shared";
|
|
3
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
4
|
//#region src/config.d.ts
|
|
5
|
+
/** settings 命名空间(字面量即合法命名空间,编译期校验)。 */
|
|
6
|
+
declare const SETTINGS_NS = "dsh-plus-image-studio";
|
|
4
7
|
/** 提示词预设(解析后视图)。 */
|
|
5
8
|
interface PromptPresetEntry {
|
|
6
9
|
id: string;
|
|
@@ -24,26 +27,29 @@ interface ProviderPresetEntry {
|
|
|
24
27
|
credentialRef: string;
|
|
25
28
|
extraHeaders: Record<string, string>;
|
|
26
29
|
}
|
|
27
|
-
declare const Config: z<Schemastery.ObjectS<{
|
|
28
|
-
promptPresets: z<PromptPresetEntry[]
|
|
29
|
-
paramPresets: z<ParamPresetEntry[]
|
|
30
|
-
providerPresets: z<ProviderPresetEntry[]
|
|
31
|
-
maxConcurrent: z<number, number>;
|
|
32
|
-
requestTimeoutMs: z<number, number>;
|
|
33
|
-
proxy: z<string, string>;
|
|
34
|
-
galleryMaxItems: z<number, number>;
|
|
35
|
-
uploadTtlHours: z<number, number>;
|
|
36
|
-
}
|
|
37
|
-
promptPresets: z<PromptPresetEntry[]
|
|
38
|
-
paramPresets: z<ParamPresetEntry[]
|
|
39
|
-
providerPresets: z<ProviderPresetEntry[]
|
|
40
|
-
maxConcurrent: z<number, number>;
|
|
41
|
-
requestTimeoutMs: z<number, number>;
|
|
42
|
-
proxy: z<string, string>;
|
|
43
|
-
galleryMaxItems: z<number, number>;
|
|
44
|
-
uploadTtlHours: z<number, number>;
|
|
45
|
-
}
|
|
46
|
-
|
|
30
|
+
declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
31
|
+
promptPresets: z<NoInfer<PromptPresetEntry[]>, NoInfer<PromptPresetEntry[]>, "volatile-defined">;
|
|
32
|
+
paramPresets: z<NoInfer<ParamPresetEntry[]>, NoInfer<ParamPresetEntry[]>, "volatile-defined">;
|
|
33
|
+
providerPresets: z<NoInfer<ProviderPresetEntry[]>, NoInfer<ProviderPresetEntry[]>, "volatile-defined">;
|
|
34
|
+
maxConcurrent: z<number, number, "volatile-defined">;
|
|
35
|
+
requestTimeoutMs: z<number, number, "volatile-defined">;
|
|
36
|
+
proxy: z<string, string, "volatile-defined">;
|
|
37
|
+
galleryMaxItems: z<number, number, "volatile-defined">;
|
|
38
|
+
uploadTtlHours: z<number, number, "volatile-defined">;
|
|
39
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
40
|
+
promptPresets: z<NoInfer<PromptPresetEntry[]>, NoInfer<PromptPresetEntry[]>, "volatile-defined">;
|
|
41
|
+
paramPresets: z<NoInfer<ParamPresetEntry[]>, NoInfer<ParamPresetEntry[]>, "volatile-defined">;
|
|
42
|
+
providerPresets: z<NoInfer<ProviderPresetEntry[]>, NoInfer<ProviderPresetEntry[]>, "volatile-defined">;
|
|
43
|
+
maxConcurrent: z<number, number, "volatile-defined">;
|
|
44
|
+
requestTimeoutMs: z<number, number, "volatile-defined">;
|
|
45
|
+
proxy: z<string, string, "volatile-defined">;
|
|
46
|
+
galleryMaxItems: z<number, number, "volatile-defined">;
|
|
47
|
+
uploadTtlHours: z<number, number, "volatile-defined">;
|
|
48
|
+
}>>, "plain">;
|
|
49
|
+
/** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
|
|
50
|
+
type ImageStudioConfigFields = Schemastery.TypeT<typeof Config>;
|
|
51
|
+
/** 平面配置形态(消费面的读取形态,由活动引用解包得到)。 */
|
|
52
|
+
type ImageStudioConfig = UnwrapVolatile<ImageStudioConfigFields>;
|
|
47
53
|
//#endregion
|
|
48
54
|
//#region src/provider/types.d.ts
|
|
49
55
|
/**
|
|
@@ -204,8 +210,7 @@ declare class ImageStudioService extends Service {
|
|
|
204
210
|
private readonly log;
|
|
205
211
|
/** 进行中的孤儿回收(串行化:索引重写不能并发)。 */
|
|
206
212
|
private pruning;
|
|
207
|
-
|
|
208
|
-
constructor(ctx: Context, config: ImageStudioConfig);
|
|
213
|
+
constructor(ctx: Context, config: ImageStudioConfig | ImageStudioConfigFields);
|
|
209
214
|
/** 配置热更:并发上限即时生效(进行中任务不受影响)。 */
|
|
210
215
|
private applyConfig;
|
|
211
216
|
private promptPresets;
|
|
@@ -480,6 +485,6 @@ declare module '@deepseek-ai/cordis' {
|
|
|
480
485
|
imageStudio: ImageStudioService;
|
|
481
486
|
}
|
|
482
487
|
}
|
|
483
|
-
declare function apply(ctx: Context, config: ImageStudioConfig): void;
|
|
488
|
+
declare function apply(ctx: Context, config: ImageStudioConfig | ImageStudioConfigFields): void;
|
|
484
489
|
//#endregion
|
|
485
|
-
export { Config, type CredentialStatusWire, type ErrorCode, type GalleryItem, type GalleryListWire, type GenerateRequest, IMAGE_SIZE_RULE, ImageStudioError, ImageStudioService, MAX_SOURCE_IMAGES, PARAM_CATALOG, type ParamEntry, type ParamSpec, type ParamSpecMap, ParamValidationError, type PresetsWire, type ProvidersWire, SIZE_PRESETS, type SourceRef, type TaskWire, UPLOAD_MAX_BYTES, type UploadEntry, apply, checkImageSize, credentialRefNameOf, detectImageMime, extOfMime, inject, isImageId, isValidPresetId, mimeOfExt, name, normalizeParamSpecs, paramEntryOf, paramKeysFor, statusOfCode, validateParamSpecs };
|
|
490
|
+
export { Config, type CredentialStatusWire, type ErrorCode, type GalleryItem, type GalleryListWire, type GenerateRequest, IMAGE_SIZE_RULE, ImageStudioError, ImageStudioService, MAX_SOURCE_IMAGES, PARAM_CATALOG, type ParamEntry, type ParamSpec, type ParamSpecMap, ParamValidationError, type PresetsWire, type ProvidersWire, SETTINGS_NS, SIZE_PRESETS, type SourceRef, type TaskWire, UPLOAD_MAX_BYTES, type UploadEntry, apply, checkImageSize, credentialRefNameOf, detectImageMime, extOfMime, inject, isImageId, isValidPresetId, mimeOfExt, name, normalizeParamSpecs, paramEntryOf, paramKeysFor, statusOfCode, validateParamSpecs };
|
package/lib/index.js
CHANGED
|
@@ -1,56 +1,13 @@
|
|
|
1
|
-
import z from "@deepseek-ai/schemastery";
|
|
2
1
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
2
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
3
|
+
import { pluginDataPath, unwrapVolatile } from "@dsh-plus/shared";
|
|
4
4
|
import { request } from "node:http";
|
|
5
5
|
import { request as request$1 } from "node:https";
|
|
6
6
|
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { appendFile, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
-
import
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region src/config.ts
|
|
13
|
-
/**
|
|
14
|
-
* 配置单一事实源:cordis 行级 Config(组合默认)与 settings namespace
|
|
15
|
-
* (用户层,$DSH_HOME/settings.yaml 热生效)共用同一 schemastery schema。
|
|
16
|
-
* 三组纯用户配置:提示词预设 / 参数预设 / 提供商预设(API key 不在此,
|
|
17
|
-
* 走 credentials,引用名由 preset id 派生)。
|
|
18
|
-
* @module image-studio/config
|
|
19
|
-
*/
|
|
20
|
-
/** settings 命名空间(字面量即合法命名空间,编译期校验)。 */
|
|
21
|
-
const SETTINGS_NS = "dsh-plus-image-studio";
|
|
22
|
-
const PromptPreset = z.object({
|
|
23
|
-
id: z.string().required().description("预设 id(稳定标识,删除级联引用)"),
|
|
24
|
-
name: z.string().required().description("预设名称"),
|
|
25
|
-
text: z.string().required().description("提示词模板")
|
|
26
|
-
});
|
|
27
|
-
const ParamPreset = z.object({
|
|
28
|
-
id: z.string().required().description("预设 id"),
|
|
29
|
-
name: z.string().required().description("预设名称"),
|
|
30
|
-
endpoint: z.union([z.const("generation"), z.const("edit")]).required().description("适用端点:generation | edit"),
|
|
31
|
-
/** 参数表结构由 params/spec 校验,settings 层存原始 JSON。 */
|
|
32
|
-
paramSpecs: z.any().required().description("参数开关表(ParamSpecMap JSON)")
|
|
33
|
-
});
|
|
34
|
-
const ProviderPreset = z.object({
|
|
35
|
-
id: z.string().required().description("预设 id"),
|
|
36
|
-
name: z.string().required().description("预设名称"),
|
|
37
|
-
protocol: z.string().required().description("协议 id(如 openai-images)"),
|
|
38
|
-
baseUrl: z.string().required().description("API 基础地址(含 /v1)"),
|
|
39
|
-
model: z.string().required().description("模型 id"),
|
|
40
|
-
credentialRef: z.string().required().description("凭据引用名(IMAGE_STUDIO_PRESET_*)"),
|
|
41
|
-
extraHeaders: z.dict(z.string()).description("附加请求头(部分中转需要)").default({})
|
|
42
|
-
});
|
|
43
|
-
const Config = z.object({
|
|
44
|
-
promptPresets: z.array(PromptPreset).description("提示词预设").default([]),
|
|
45
|
-
paramPresets: z.array(ParamPreset).description("参数预设").default([]),
|
|
46
|
-
providerPresets: z.array(ProviderPreset).description("提供商预设").default([]),
|
|
47
|
-
maxConcurrent: z.number().min(0).max(16).description("生图任务并发上限(0 = 不限)").default(3),
|
|
48
|
-
requestTimeoutMs: z.number().min(1e4).max(6e5).description("单请求超时(毫秒;生图长耗时,默认 5 分钟)").default(3e5),
|
|
49
|
-
proxy: z.string().description("上游请求代理(空 = 直连)").default(""),
|
|
50
|
-
galleryMaxItems: z.number().min(0).max(1e4).description("画廊保留上限(条,0 = 不清理;超限按最旧删除)").default(500),
|
|
51
|
-
uploadTtlHours: z.number().min(1).max(720).description("上传原图保留时长(小时;未被画廊条目引用的超期即回收)").default(24)
|
|
52
|
-
});
|
|
53
|
-
//#endregion
|
|
10
|
+
import z from "@deepseek-ai/schemastery";
|
|
54
11
|
//#region src/errors.ts
|
|
55
12
|
var ImageStudioError = class extends Error {
|
|
56
13
|
code;
|
|
@@ -1398,7 +1355,6 @@ async function pruneUploads(options) {
|
|
|
1398
1355
|
}
|
|
1399
1356
|
//#endregion
|
|
1400
1357
|
//#region src/service.ts
|
|
1401
|
-
/** 静态 schema 引用(installSection 用;避免循环 import config → service)。 */
|
|
1402
1358
|
var ImageStudioService = class extends Service {
|
|
1403
1359
|
static inject = ["credentials"];
|
|
1404
1360
|
current;
|
|
@@ -1408,21 +1364,12 @@ var ImageStudioService = class extends Service {
|
|
|
1408
1364
|
pruning;
|
|
1409
1365
|
constructor(ctx, config) {
|
|
1410
1366
|
super(ctx, "imageStudio");
|
|
1411
|
-
this.current = () => config;
|
|
1367
|
+
this.current = () => unwrapVolatile(config);
|
|
1412
1368
|
this.log = (message) => ctx.logger("image-studio").warn(message);
|
|
1413
1369
|
this.pruning = null;
|
|
1414
|
-
this.runner = new TaskRunner({ maxConcurrent:
|
|
1370
|
+
this.runner = new TaskRunner({ maxConcurrent: this.current().maxConcurrent });
|
|
1415
1371
|
this.schedulePrune();
|
|
1416
|
-
ctx.
|
|
1417
|
-
this.settingsRef = settingsCtx.settings;
|
|
1418
|
-
settingsCtx.settings.installSection(ctx, SETTINGS_NS, Config, config, {
|
|
1419
|
-
setSource: (source) => {
|
|
1420
|
-
this.current = source;
|
|
1421
|
-
this.applyConfig();
|
|
1422
|
-
},
|
|
1423
|
-
onChange: () => {}
|
|
1424
|
-
});
|
|
1425
|
-
});
|
|
1372
|
+
ctx.events.on("loader/volatile-update", () => this.applyConfig());
|
|
1426
1373
|
ctx.inject(["webServer"], (webCtx) => {
|
|
1427
1374
|
registerImageStudioApi(webCtx, this);
|
|
1428
1375
|
});
|
|
@@ -1788,6 +1735,48 @@ function stripHostParams(params) {
|
|
|
1788
1735
|
return rest;
|
|
1789
1736
|
}
|
|
1790
1737
|
//#endregion
|
|
1738
|
+
//#region src/config.ts
|
|
1739
|
+
/**
|
|
1740
|
+
* 配置单一事实源:cordis 行级 Config(组合默认)与 settings namespace
|
|
1741
|
+
* (用户层,$DSH_HOME/settings.yaml 热生效)共用同一 schemastery schema。
|
|
1742
|
+
* 三组纯用户配置:提示词预设 / 参数预设 / 提供商预设(API key 不在此,
|
|
1743
|
+
* 走 credentials,引用名由 preset id 派生)。
|
|
1744
|
+
* @module image-studio/config
|
|
1745
|
+
*/
|
|
1746
|
+
/** settings 命名空间(字面量即合法命名空间,编译期校验)。 */
|
|
1747
|
+
const SETTINGS_NS = "dsh-plus-image-studio";
|
|
1748
|
+
const PromptPreset = z.object({
|
|
1749
|
+
id: z.string().required().description("预设 id(稳定标识,删除级联引用)"),
|
|
1750
|
+
name: z.string().required().description("预设名称"),
|
|
1751
|
+
text: z.string().required().description("提示词模板")
|
|
1752
|
+
});
|
|
1753
|
+
const ParamPreset = z.object({
|
|
1754
|
+
id: z.string().required().description("预设 id"),
|
|
1755
|
+
name: z.string().required().description("预设名称"),
|
|
1756
|
+
endpoint: z.union([z.const("generation"), z.const("edit")]).required().description("适用端点:generation | edit"),
|
|
1757
|
+
/** 参数表结构由 params/spec 校验,settings 层存原始 JSON。 */
|
|
1758
|
+
paramSpecs: z.any().required().description("参数开关表(ParamSpecMap JSON)")
|
|
1759
|
+
});
|
|
1760
|
+
const ProviderPreset = z.object({
|
|
1761
|
+
id: z.string().required().description("预设 id"),
|
|
1762
|
+
name: z.string().required().description("预设名称"),
|
|
1763
|
+
protocol: z.string().required().description("协议 id(如 openai-images)"),
|
|
1764
|
+
baseUrl: z.string().required().description("API 基础地址(含 /v1)"),
|
|
1765
|
+
model: z.string().required().description("模型 id"),
|
|
1766
|
+
credentialRef: z.string().required().description("凭据引用名(IMAGE_STUDIO_PRESET_*)"),
|
|
1767
|
+
extraHeaders: z.dict(z.string()).description("附加请求头(部分中转需要)").default({})
|
|
1768
|
+
});
|
|
1769
|
+
const Config = z.object({
|
|
1770
|
+
promptPresets: z.array(PromptPreset).description("提示词预设").default([]).volatile(),
|
|
1771
|
+
paramPresets: z.array(ParamPreset).description("参数预设").default([]).volatile(),
|
|
1772
|
+
providerPresets: z.array(ProviderPreset).description("提供商预设").default([]).volatile(),
|
|
1773
|
+
maxConcurrent: z.number().min(0).max(16).description("生图任务并发上限(0 = 不限)").default(3).volatile(),
|
|
1774
|
+
requestTimeoutMs: z.number().min(1e4).max(6e5).description("单请求超时(毫秒;生图长耗时,默认 5 分钟)").default(3e5).volatile(),
|
|
1775
|
+
proxy: z.string().description("上游请求代理(空 = 直连)").default("").volatile(),
|
|
1776
|
+
galleryMaxItems: z.number().min(0).max(1e4).description("画廊保留上限(条,0 = 不清理;超限按最旧删除)").default(500).volatile(),
|
|
1777
|
+
uploadTtlHours: z.number().min(1).max(720).description("上传原图保留时长(小时;未被画廊条目引用的超期即回收)").default(24).volatile()
|
|
1778
|
+
});
|
|
1779
|
+
//#endregion
|
|
1791
1780
|
//#region src/index.ts
|
|
1792
1781
|
const name = "dsh-plus-image-studio";
|
|
1793
1782
|
const inject = ["credentials"];
|
|
@@ -1795,4 +1784,4 @@ function apply(ctx, config) {
|
|
|
1795
1784
|
ctx.plugin(ImageStudioService, config);
|
|
1796
1785
|
}
|
|
1797
1786
|
//#endregion
|
|
1798
|
-
export { Config, IMAGE_SIZE_RULE, ImageStudioError, ImageStudioService, MAX_SOURCE_IMAGES, PARAM_CATALOG, ParamValidationError, SIZE_PRESETS, UPLOAD_MAX_BYTES, apply, checkImageSize, credentialRefNameOf, detectImageMime, extOfMime, inject, isImageId, isValidPresetId, mimeOfExt, name, normalizeParamSpecs, paramEntryOf, paramKeysFor, statusOfCode, validateParamSpecs };
|
|
1787
|
+
export { Config, IMAGE_SIZE_RULE, ImageStudioError, ImageStudioService, MAX_SOURCE_IMAGES, PARAM_CATALOG, ParamValidationError, SETTINGS_NS, SIZE_PRESETS, UPLOAD_MAX_BYTES, apply, checkImageSize, credentialRefNameOf, detectImageMime, extOfMime, inject, isImageId, isValidPresetId, mimeOfExt, name, normalizeParamSpecs, paramEntryOf, paramKeysFor, statusOfCode, validateParamSpecs };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/image-studio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "dsh-plus service+ui plugin: 图像工作室——OpenAI Images 协议文生图/图生图,全参数开关化,双预设体系,并发生图任务与可二次编辑画廊",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -20,24 +20,24 @@
|
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"https-proxy-agent": "^9.1.0",
|
|
23
|
-
"@dsh-plus/shared": "0.1.
|
|
23
|
+
"@dsh-plus/shared": "0.1.23"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
27
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
28
|
-
"@deepseek-ai/dsh-credentials": "^0.1.
|
|
29
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.
|
|
30
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
31
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
26
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
27
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-rc.1",
|
|
28
|
+
"@deepseek-ai/dsh-credentials": "^0.1.7-rc.1",
|
|
29
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.1",
|
|
30
|
+
"@deepseek-ai/dsh-settings": "^0.1.7-rc.1",
|
|
31
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@deepseek-ai/cordis": "4.0.
|
|
35
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.1.
|
|
36
|
-
"@deepseek-ai/dsh-credentials": "0.1.
|
|
37
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.
|
|
38
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
39
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
40
|
-
"@deepseek-ai/schemastery": "3.18.
|
|
34
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
35
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
|
|
36
|
+
"@deepseek-ai/dsh-credentials": "0.1.7-rc.1",
|
|
37
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.7-rc.1",
|
|
38
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.1",
|
|
39
|
+
"@deepseek-ai/dsh-settings": "0.1.7-rc.1",
|
|
40
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
41
41
|
"@types/node": "26.4.0",
|
|
42
42
|
"@types/react": "~18.3.31",
|
|
43
43
|
"react": "^18.2.0"
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* @module image-studio/client/panel/gallery
|
|
5
5
|
*/
|
|
6
6
|
import { Modal } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
IconDownload,
|
|
9
|
+
IconRefreshOutlineRegular,
|
|
10
|
+
IconTrashOutlineRegular,
|
|
11
|
+
} from '@dsh-plus/shared/client'
|
|
8
12
|
import { type ReactElement, useState } from 'react'
|
|
9
13
|
import type { GalleryItem } from '../../gallery/store.ts'
|
|
10
14
|
import { deleteGalleryItem, imageUrl, uploadImageUrl } from '../api.ts'
|
|
@@ -156,7 +160,7 @@ function GalleryDetail(props: {
|
|
|
156
160
|
className="ims-btn ims-btnGhost"
|
|
157
161
|
onClick={() => setConfirming(true)}
|
|
158
162
|
>
|
|
159
|
-
<
|
|
163
|
+
<IconTrashOutlineRegular size={14} /> {t('common.delete')}
|
|
160
164
|
</button>
|
|
161
165
|
)}
|
|
162
166
|
</div>
|
|
@@ -173,7 +177,7 @@ export function GalleryView(props: GalleryViewProps): ReactElement {
|
|
|
173
177
|
<div className="ims-galleryBar">
|
|
174
178
|
<span className="ims-hint">{t('gallery.total').replace('{n}', String(items.length))}</span>
|
|
175
179
|
<button type="button" className="ims-btn ims-btnGhost ims-btnSmall" onClick={onRefresh}>
|
|
176
|
-
<
|
|
180
|
+
<IconRefreshOutlineRegular size={14} /> {t('common.refresh')}
|
|
177
181
|
</button>
|
|
178
182
|
</div>
|
|
179
183
|
{failed ? (
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import {
|
|
12
12
|
IconLayers,
|
|
13
|
-
|
|
13
|
+
IconPlusOutlineMedium,
|
|
14
14
|
type NamespaceSettingsApi,
|
|
15
15
|
type Scope,
|
|
16
16
|
} from '@dsh-plus/shared/client'
|
|
@@ -440,7 +440,7 @@ export function GeneratorForm(props: GeneratorProps): ReactElement {
|
|
|
440
440
|
)
|
|
441
441
|
}
|
|
442
442
|
>
|
|
443
|
-
<
|
|
443
|
+
<IconPlusOutlineMedium size={18} />
|
|
444
444
|
<span>{t('upload.pick')}</span>
|
|
445
445
|
</UploadButton>
|
|
446
446
|
</div>
|
|
@@ -473,7 +473,7 @@ export function GeneratorForm(props: GeneratorProps): ReactElement {
|
|
|
473
473
|
className="ims-thumbAdd"
|
|
474
474
|
onUploaded={(entries) => setMask(uploadRef(entries))}
|
|
475
475
|
>
|
|
476
|
-
<
|
|
476
|
+
<IconPlusOutlineMedium size={18} />
|
|
477
477
|
<span>{t('upload.pick')}</span>
|
|
478
478
|
</UploadButton>
|
|
479
479
|
</div>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Modal } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
11
11
|
import type { NamespaceSettingsApi, Scope } from '@dsh-plus/shared/client'
|
|
12
|
-
import { IconBrush,
|
|
12
|
+
import { IconBrush, IconCloseOutlineMedium, IconImage, IconSparkles } from '@dsh-plus/shared/client'
|
|
13
13
|
import {
|
|
14
14
|
type ReactElement,
|
|
15
15
|
useCallback,
|
|
@@ -147,7 +147,7 @@ export function StudioPanel(props: StudioPanelProps): ReactElement | null {
|
|
|
147
147
|
aria-label={t('common.close')}
|
|
148
148
|
onClick={() => studio.setOpen(false)}
|
|
149
149
|
>
|
|
150
|
-
<
|
|
150
|
+
<IconCloseOutlineMedium size={16} />
|
|
151
151
|
</button>
|
|
152
152
|
</header>
|
|
153
153
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module image-studio/client/panel/picker
|
|
6
6
|
*/
|
|
7
7
|
import { Modal } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
8
|
-
import {
|
|
8
|
+
import { IconPlusOutlineMedium } from '@dsh-plus/shared/client'
|
|
9
9
|
import { type ReactElement, useState } from 'react'
|
|
10
10
|
import type { SourceRef, UploadEntry } from '../../dto.ts'
|
|
11
11
|
import type { GalleryItem } from '../../gallery/store.ts'
|
|
@@ -128,7 +128,7 @@ export function ImagePicker(props: ImagePickerProps): ReactElement {
|
|
|
128
128
|
onUploaded={selectUploaded}
|
|
129
129
|
className="ims-btn ims-btnPrimary ims-btnSmall"
|
|
130
130
|
>
|
|
131
|
-
<
|
|
131
|
+
<IconPlusOutlineMedium size={14} /> {t('upload.pick')}
|
|
132
132
|
</UploadButton>
|
|
133
133
|
<button type="button" className="ims-btn ims-btnGhost" onClick={onClose}>
|
|
134
134
|
{t('common.close')}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 轮询由面板根组件负责,本组件纯渲染。
|
|
5
5
|
* @module image-studio/client/panel/tasks
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { IconLoadingOutlineRegular } from '@dsh-plus/shared/client'
|
|
8
8
|
import type { ReactElement } from 'react'
|
|
9
9
|
import type { TaskWire } from '../../dto.ts'
|
|
10
10
|
import type { Translate } from '../i18n.ts'
|
|
@@ -33,7 +33,7 @@ export function TasksBar(props: TasksBarProps): ReactElement | null {
|
|
|
33
33
|
const active = task.state === 'queued' || task.state === 'running'
|
|
34
34
|
return (
|
|
35
35
|
<div key={task.id} className={`ims-task ims-task-${task.state}`}>
|
|
36
|
-
{active ? <
|
|
36
|
+
{active ? <IconLoadingOutlineRegular className="ims-spin" size={14} /> : null}
|
|
37
37
|
<span className={`ims-taskState ims-taskState-${task.state}`}>
|
|
38
38
|
{t(STATE_KEYS[task.state])}
|
|
39
39
|
</span>
|
package/src/config.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @module image-studio/config
|
|
7
7
|
*/
|
|
8
8
|
import z from '@deepseek-ai/schemastery'
|
|
9
|
+
import type { UnwrapVolatile } from '@dsh-plus/shared'
|
|
9
10
|
|
|
10
11
|
import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
|
|
11
12
|
|
|
@@ -71,30 +72,44 @@ const ProviderPreset: z<ProviderPresetEntry, ProviderPresetEntry> = z.object({
|
|
|
71
72
|
extraHeaders: z.dict(z.string()).description('附加请求头(部分中转需要)').default({}),
|
|
72
73
|
})
|
|
73
74
|
|
|
75
|
+
// 0.1.7:全字段 `.volatile()`——条目进入 settings describe 视图(卡片可读写),
|
|
76
|
+
// loader 原位提交活动引用,current() 现取即热。
|
|
74
77
|
export const Config = z.object({
|
|
75
|
-
promptPresets: z.array(PromptPreset).description('提示词预设').default([]),
|
|
76
|
-
paramPresets: z.array(ParamPreset).description('参数预设').default([]),
|
|
77
|
-
providerPresets: z.array(ProviderPreset).description('提供商预设').default([]),
|
|
78
|
-
maxConcurrent: z
|
|
78
|
+
promptPresets: z.array(PromptPreset).description('提示词预设').default([]).volatile(),
|
|
79
|
+
paramPresets: z.array(ParamPreset).description('参数预设').default([]).volatile(),
|
|
80
|
+
providerPresets: z.array(ProviderPreset).description('提供商预设').default([]).volatile(),
|
|
81
|
+
maxConcurrent: z
|
|
82
|
+
.number()
|
|
83
|
+
.min(0)
|
|
84
|
+
.max(16)
|
|
85
|
+
.description('生图任务并发上限(0 = 不限)')
|
|
86
|
+
.default(3)
|
|
87
|
+
.volatile(),
|
|
79
88
|
requestTimeoutMs: z
|
|
80
89
|
.number()
|
|
81
90
|
.min(10_000)
|
|
82
91
|
.max(600_000)
|
|
83
92
|
.description('单请求超时(毫秒;生图长耗时,默认 5 分钟)')
|
|
84
|
-
.default(300_000)
|
|
85
|
-
|
|
93
|
+
.default(300_000)
|
|
94
|
+
.volatile(),
|
|
95
|
+
proxy: z.string().description('上游请求代理(空 = 直连)').default('').volatile(),
|
|
86
96
|
galleryMaxItems: z
|
|
87
97
|
.number()
|
|
88
98
|
.min(0)
|
|
89
99
|
.max(10_000)
|
|
90
100
|
.description('画廊保留上限(条,0 = 不清理;超限按最旧删除)')
|
|
91
|
-
.default(500)
|
|
101
|
+
.default(500)
|
|
102
|
+
.volatile(),
|
|
92
103
|
uploadTtlHours: z
|
|
93
104
|
.number()
|
|
94
105
|
.min(1)
|
|
95
106
|
.max(720)
|
|
96
107
|
.description('上传原图保留时长(小时;未被画廊条目引用的超期即回收)')
|
|
97
|
-
.default(24)
|
|
108
|
+
.default(24)
|
|
109
|
+
.volatile(),
|
|
98
110
|
})
|
|
99
111
|
|
|
100
|
-
|
|
112
|
+
/** 活动字段形态(0.1.7 loader 解析产物:volatile 字段为活动引用)。 */
|
|
113
|
+
export type ImageStudioConfigFields = Schemastery.TypeT<typeof Config>
|
|
114
|
+
/** 平面配置形态(消费面的读取形态,由活动引用解包得到)。 */
|
|
115
|
+
export type ImageStudioConfig = UnwrapVolatile<ImageStudioConfigFields>
|
package/src/index.ts
CHANGED
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis'
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import type { ImageStudioConfig, ImageStudioConfigFields } from './config.ts'
|
|
13
13
|
import { ImageStudioService } from './service.ts'
|
|
14
14
|
|
|
15
15
|
export const name = 'dsh-plus-image-studio'
|
|
16
16
|
|
|
17
17
|
export const inject = ['credentials'] as const
|
|
18
18
|
|
|
19
|
+
export { Config, SETTINGS_NS } from './config.ts'
|
|
19
20
|
export { credentialRefNameOf, isValidPresetId } from './credentials.ts'
|
|
20
21
|
export type {
|
|
21
22
|
CredentialStatusWire,
|
|
@@ -38,7 +39,6 @@ export { checkImageSize, IMAGE_SIZE_RULE, SIZE_PRESETS } from './params/size.ts'
|
|
|
38
39
|
export type { ParamSpec, ParamSpecMap } from './params/spec.ts'
|
|
39
40
|
export { normalizeParamSpecs, ParamValidationError, validateParamSpecs } from './params/spec.ts'
|
|
40
41
|
export { ImageStudioService } from './service.ts'
|
|
41
|
-
export { Config }
|
|
42
42
|
|
|
43
43
|
declare module '@deepseek-ai/cordis' {
|
|
44
44
|
interface Context {
|
|
@@ -46,6 +46,7 @@ declare module '@deepseek-ai/cordis' {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
// config 运行期为 loader 解析的 volatile 活动字段(测试可直接传平面值)。
|
|
50
|
+
export function apply(ctx: Context, config: ImageStudioConfig | ImageStudioConfigFields): void {
|
|
50
51
|
ctx.plugin(ImageStudioService, config)
|
|
51
52
|
}
|
package/src/service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* image-studio 服务主体:
|
|
3
|
-
* -
|
|
3
|
+
* - 配置:0.1.7 volatile 活动引用(三组预设 + 并发/超时/代理/画廊上限/
|
|
4
|
+
* 上传保留时长;loader 原位提交热生效);
|
|
4
5
|
* - TaskRunner 并发生图:提交即排队,成功自动入画廊(元数据 + 图片落盘);
|
|
5
6
|
* - 提供商预设按 id 即时 resolve credentials(不缓存);
|
|
6
7
|
* - 上传暂存区:本地文件上传的源图/遮罩,孤儿按 TTL 回收;
|
|
@@ -12,13 +13,14 @@ import type { Context as ContextT } from '@deepseek-ai/cordis'
|
|
|
12
13
|
import { Service } from '@deepseek-ai/cordis'
|
|
13
14
|
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
|
14
15
|
import type {} from '@deepseek-ai/dsh-settings'
|
|
16
|
+
import { unwrapVolatile } from '@dsh-plus/shared'
|
|
15
17
|
import { registerImageStudioApi } from './api.ts'
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
import type {
|
|
19
|
+
ImageStudioConfig,
|
|
20
|
+
ImageStudioConfigFields,
|
|
21
|
+
ParamPresetEntry,
|
|
22
|
+
PromptPresetEntry,
|
|
23
|
+
ProviderPresetEntry,
|
|
22
24
|
} from './config.ts'
|
|
23
25
|
import { credentialRefNameOf, isValidPresetId } from './credentials.ts'
|
|
24
26
|
import type { GenerateRequest, SourceRef, TaskWire } from './dto.ts'
|
|
@@ -47,16 +49,6 @@ import {
|
|
|
47
49
|
saveUpload as saveUploadEntry,
|
|
48
50
|
} from './uploads/store.ts'
|
|
49
51
|
|
|
50
|
-
interface SettingsLike {
|
|
51
|
-
installSection(
|
|
52
|
-
ownerCtx: ContextT,
|
|
53
|
-
ns: string,
|
|
54
|
-
schema: unknown,
|
|
55
|
-
base: unknown,
|
|
56
|
-
hooks: { setSource(source: () => unknown): void; onChange(): void },
|
|
57
|
-
): void
|
|
58
|
-
}
|
|
59
|
-
|
|
60
52
|
/** credentials seam 最小面(真实服务经 inject 探测获取)。 */
|
|
61
53
|
interface CredentialsFace {
|
|
62
54
|
resolve(ref: unknown): Promise<{ value: string; source: string } | undefined>
|
|
@@ -87,28 +79,18 @@ export class ImageStudioService extends Service {
|
|
|
87
79
|
private readonly log: (message: string) => void
|
|
88
80
|
/** 进行中的孤儿回收(串行化:索引重写不能并发)。 */
|
|
89
81
|
private pruning: Promise<void> | null
|
|
90
|
-
// settings 服务窄面(settingsCtx.inject 回调内落存)。declare:不参与类字段
|
|
91
|
-
// 初始化 emit,保持实例运行期形状与既有产物一致(不因声明而多出 undefined 字段)。
|
|
92
|
-
private declare settingsRef: SettingsLike | undefined
|
|
93
82
|
|
|
94
|
-
constructor(ctx: ContextT, config: ImageStudioConfig) {
|
|
83
|
+
constructor(ctx: ContextT, config: ImageStudioConfig | ImageStudioConfigFields) {
|
|
95
84
|
super(ctx, 'imageStudio')
|
|
96
|
-
|
|
85
|
+
// 0.1.7 替代 installSection/setSource:活动引用原位提交,现取即热。
|
|
86
|
+
this.current = () => unwrapVolatile(config)
|
|
97
87
|
this.log = (message) => ctx.logger('image-studio').warn(message)
|
|
98
88
|
this.pruning = null
|
|
99
|
-
this.runner = new TaskRunner<TaskSnapshot>({ maxConcurrent:
|
|
89
|
+
this.runner = new TaskRunner<TaskSnapshot>({ maxConcurrent: this.current().maxConcurrent })
|
|
100
90
|
// 启动回收一次:上次进程遗留的孤儿上传(未被画廊引用且超 TTL)。
|
|
101
91
|
this.schedulePrune()
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
settingsCtx.settings.installSection(ctx, SETTINGS_NS, configSchemaRef, config, {
|
|
105
|
-
setSource: (source) => {
|
|
106
|
-
this.current = source as () => ImageStudioConfig
|
|
107
|
-
this.applyConfig()
|
|
108
|
-
},
|
|
109
|
-
onChange: () => {},
|
|
110
|
-
})
|
|
111
|
-
})
|
|
92
|
+
// volatile 提交事件(替代原 setSource→applyConfig):并发上限热更。
|
|
93
|
+
ctx.events.on('loader/volatile-update', () => this.applyConfig())
|
|
112
94
|
ctx.inject(['webServer'], (webCtx) => {
|
|
113
95
|
registerImageStudioApi(webCtx as ContextT, this)
|
|
114
96
|
})
|
|
@@ -585,9 +567,6 @@ export class ImageStudioService extends Service {
|
|
|
585
567
|
}
|
|
586
568
|
}
|
|
587
569
|
|
|
588
|
-
/** 静态 schema 引用(installSection 用;避免循环 import config → service)。 */
|
|
589
|
-
import { Config as configSchemaRef } from './config.ts'
|
|
590
|
-
|
|
591
570
|
/** 剔除 host 承载参数(model 不进协议参数表——openai-images 请求体单独拼)。 */
|
|
592
571
|
function stripHostParams(params: Record<string, unknown>): Record<string, unknown> {
|
|
593
572
|
const { model: _model, ...rest } = params
|