@dsh-plus/image-studio 0.1.14 → 0.1.15

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 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.IconChevronDownOutline14, { className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}` })
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.IconTrashOutline16, { size: 14 }),
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.IconRefreshOutline16, { size: 14 }),
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.IconPlusOutline16, { size: 14 }),
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.IconPlusOutline16, { size: 18 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("upload.pick") })]
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.IconPlusOutline16, { size: 18 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("upload.pick") })]
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.IconLoadingOutline16, {
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.IconCloseOutline16, { size: 16 })
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[], PromptPresetEntry[]>;
29
- paramPresets: z<ParamPresetEntry[], ParamPresetEntry[]>;
30
- providerPresets: z<ProviderPresetEntry[], 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
- }>, Schemastery.ObjectT<{
37
- promptPresets: z<PromptPresetEntry[], PromptPresetEntry[]>;
38
- paramPresets: z<ParamPresetEntry[], ParamPresetEntry[]>;
39
- providerPresets: z<ProviderPresetEntry[], 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
- type ImageStudioConfig = Schemastery.TypeT<typeof Config>;
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
- private settingsRef;
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 { pluginDataPath } from "@dsh-plus/shared";
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: config.maxConcurrent });
1370
+ this.runner = new TaskRunner({ maxConcurrent: this.current().maxConcurrent });
1415
1371
  this.schedulePrune();
1416
- ctx.inject(["settings"], (settingsCtx) => {
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.14",
3
+ "version": "0.1.15",
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.21"
23
+ "@dsh-plus/shared": "0.1.22"
24
24
  },
25
25
  "peerDependencies": {
26
- "@deepseek-ai/cordis": "^4.0.2",
27
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.2",
28
- "@deepseek-ai/dsh-credentials": "^0.1.6-alpha.2",
29
- "@deepseek-ai/dsh-host-webserver": "^0.1.6-alpha.2",
30
- "@deepseek-ai/dsh-settings": "^0.1.6-alpha.2",
31
- "@deepseek-ai/schemastery": "^3.18.2"
26
+ "@deepseek-ai/cordis": "^4.0.4",
27
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.2",
28
+ "@deepseek-ai/dsh-credentials": "^0.1.7-alpha.2",
29
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-alpha.2",
30
+ "@deepseek-ai/dsh-settings": "^0.1.7-alpha.2",
31
+ "@deepseek-ai/schemastery": "^3.18.4"
32
32
  },
33
33
  "devDependencies": {
34
- "@deepseek-ai/cordis": "4.0.2",
35
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.6-alpha.2",
36
- "@deepseek-ai/dsh-credentials": "0.1.6-alpha.2",
37
- "@deepseek-ai/dsh-host-webserver": "0.1.6-alpha.2",
38
- "@deepseek-ai/dsh-session": "0.1.6-alpha.2",
39
- "@deepseek-ai/dsh-settings": "0.1.6-alpha.2",
40
- "@deepseek-ai/schemastery": "3.18.2",
34
+ "@deepseek-ai/cordis": "4.0.4",
35
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-alpha.2",
36
+ "@deepseek-ai/dsh-credentials": "0.1.7-alpha.2",
37
+ "@deepseek-ai/dsh-host-webserver": "0.1.7-alpha.2",
38
+ "@deepseek-ai/dsh-session": "0.1.7-alpha.2",
39
+ "@deepseek-ai/dsh-settings": "0.1.7-alpha.2",
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 { IconDownload, IconRefreshOutline16, IconTrashOutline16 } from '@dsh-plus/shared/client'
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
- <IconTrashOutline16 size={14} /> {t('common.delete')}
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
- <IconRefreshOutline16 size={14} /> {t('common.refresh')}
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
- IconPlusOutline16,
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
- <IconPlusOutline16 size={18} />
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
- <IconPlusOutline16 size={18} />
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, IconCloseOutline16, IconImage, IconSparkles } from '@dsh-plus/shared/client'
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
- <IconCloseOutline16 size={16} />
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 { IconPlusOutline16 } from '@dsh-plus/shared/client'
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
- <IconPlusOutline16 size={14} /> {t('upload.pick')}
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 { IconLoadingOutline16 } from '@dsh-plus/shared/client'
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 ? <IconLoadingOutline16 className="ims-spin" size={14} /> : null}
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.number().min(0).max(16).description('生图任务并发上限(0 = 不限)').default(3),
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
- proxy: z.string().description('上游请求代理(空 = 直连)').default(''),
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
- export type ImageStudioConfig = Schemastery.TypeT<typeof Config>
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 { Config, type ImageStudioConfig } from './config.ts'
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
- export function apply(ctx: Context, config: ImageStudioConfig): void {
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
- * - settings installSection(三组预设 + 并发/超时/代理/画廊上限/上传保留时长);
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
- type ImageStudioConfig,
18
- type ParamPresetEntry,
19
- type PromptPresetEntry,
20
- type ProviderPresetEntry,
21
- SETTINGS_NS,
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
- this.current = () => config
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: config.maxConcurrent })
89
+ this.runner = new TaskRunner<TaskSnapshot>({ maxConcurrent: this.current().maxConcurrent })
100
90
  // 启动回收一次:上次进程遗留的孤儿上传(未被画廊引用且超 TTL)。
101
91
  this.schedulePrune()
102
- ctx.inject(['settings'], (settingsCtx) => {
103
- this.settingsRef = settingsCtx.settings as unknown as SettingsLike
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