dsh-image-gen 0.9.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -75,7 +75,7 @@ window.__ModuleLoader__.load({
75
75
  //#region lib/types/watermark.js
76
76
  /** Model names alone do not establish the protocol of an unknown relay. */
77
77
  function officialContract(config, mode) {
78
- const model = (mode === "generate" ? config.openaiCompatExtraBody : config.openaiCompatEditExtra)?.model ?? config.openaiCompatModel;
78
+ const model = (mode === "generate" ? config.openaiCompatExtraBody : config.openaiCompatEditExtra)?.model ?? (mode === "edit" ? config.openaiCompatEditModel?.trim() || config.openaiCompatModel : config.openaiCompatModel);
79
79
  if (typeof model !== "string") return void 0;
80
80
  try {
81
81
  const url = new URL(config.openaiCompatBaseURL ?? "");
@@ -87,6 +87,10 @@ window.__ModuleLoader__.load({
87
87
  }
88
88
  /** Known official edit contracts take precedence over relay-specific format settings. */
89
89
  function compatEditFormat(config) {
90
+ try {
91
+ const url = new URL(config.openaiCompatBaseURL ?? "");
92
+ if (url.protocol === "https:" && !url.port && !url.search && !url.hash && (url.hostname === "api.siliconflow.cn" || url.hostname === "api.siliconflow.com") && url.pathname.replace(/\/+$/, "") === "/v1") return "siliconflowJson";
93
+ } catch {}
90
94
  const contract = officialContract(config, "edit");
91
95
  if (contract === "sensenova") return "jsonImageUrlArray";
92
96
  if (contract === "qwen") return "qwenJson";
@@ -181,6 +185,10 @@ window.__ModuleLoader__.load({
181
185
  "off",
182
186
  "on"
183
187
  ];
188
+ /** A model picker/request must use the same operation-specific default. */
189
+ function studioModelForMode(profile, mode) {
190
+ return mode === "edit" ? profile.editModel ?? profile.model : profile.model;
191
+ }
184
192
  function studioWatermarkCapability(profile, mode) {
185
193
  return profile?.watermarkCapabilities?.[mode] ?? (mode === "generate" && profile?.watermarkOptions?.length ? { status: "supported" } : {
186
194
  status: "unknown",
@@ -337,7 +345,7 @@ window.__ModuleLoader__.load({
337
345
  }
338
346
  if (!isRecord(value) || Object.keys(value).length === 0) throw new Error("ComfyUI workflow must be a non-empty API-format JSON object");
339
347
  if (!Object.values(value).some((node) => {
340
- const inputs = record$1(node)?.inputs;
348
+ const inputs = record$2(node)?.inputs;
341
349
  return inputs !== void 0 && containsPromptPlaceholder(inputs);
342
350
  })) throw new Error(`ComfyUI workflow must contain ${COMFYUI_PROMPT_PLACEHOLDER} (or ${LEGACY_PROMPT_PLACEHOLDER}) in a text input`);
343
351
  return value;
@@ -346,7 +354,7 @@ window.__ModuleLoader__.load({
346
354
  function countImagePlaceholders(workflow) {
347
355
  let count = 0;
348
356
  for (const node of Object.values(workflow)) {
349
- const inputs = record$1(record$1(node)?.inputs);
357
+ const inputs = record$2(record$2(node)?.inputs);
350
358
  if (inputs !== void 0 && isImagePlaceholder(inputs[IMAGE_INPUT_KEY])) count += 1;
351
359
  }
352
360
  return count;
@@ -362,7 +370,7 @@ window.__ModuleLoader__.load({
362
370
  function isRecord(value) {
363
371
  return typeof value === "object" && value !== null && !Array.isArray(value);
364
372
  }
365
- function record$1(value) {
373
+ function record$2(value) {
366
374
  return isRecord(value) ? value : void 0;
367
375
  }
368
376
  //#endregion
@@ -116729,8 +116737,27 @@ img.ProseMirror-separator {
116729
116737
  //#region lib/types/client/tl/tl-canvas-bridge.js
116730
116738
  const pending$1 = /* @__PURE__ */ new Map();
116731
116739
  const consumers = /* @__PURE__ */ new Set();
116740
+ const availabilityListeners = /* @__PURE__ */ new Set();
116732
116741
  let delivering = false;
116733
116742
  let generation = 0;
116743
+ /** History actions must not silently queue into a canvas that is not open. */
116744
+ function hasTlLandingConsumer() {
116745
+ return consumers.size > 0;
116746
+ }
116747
+ function subscribeTlLandingAvailability(listener) {
116748
+ availabilityListeners.add(listener);
116749
+ return () => {
116750
+ availabilityListeners.delete(listener);
116751
+ };
116752
+ }
116753
+ function sendTlLandingToOpenCanvas(item) {
116754
+ if (!hasTlLandingConsumer()) return false;
116755
+ pushTlLandings([{
116756
+ ...item,
116757
+ onlyWhileCanvasOpen: true
116758
+ }]);
116759
+ return true;
116760
+ }
116734
116761
  /** Invalidates async landings that started before the user cleared the canvas. */
116735
116762
  function getTlLandingGeneration() {
116736
116763
  return generation;
@@ -116750,12 +116777,18 @@ img.ProseMirror-separator {
116750
116777
  }
116751
116778
  /** Register a ready editor. False means it unmounted before committing. */
116752
116779
  function registerTlLandingConsumer(consumer) {
116780
+ const wasAvailable = hasTlLandingConsumer();
116753
116781
  consumers.add(consumer);
116782
+ if (!wasAvailable) for (const listener of availabilityListeners) listener();
116754
116783
  deliver();
116784
+ let registered = true;
116755
116785
  return () => {
116786
+ if (!registered) return;
116787
+ registered = false;
116756
116788
  consumers.delete(consumer);
116757
116789
  if (consumers.size === 0) {
116758
- for (const [id, item] of pending$1) if (item.fromConversation) pending$1.delete(id);
116790
+ for (const [id, item] of pending$1) if (item.fromConversation || item.onlyWhileCanvasOpen) pending$1.delete(id);
116791
+ for (const listener of availabilityListeners) listener();
116759
116792
  }
116760
116793
  };
116761
116794
  }
@@ -116782,6 +116815,84 @@ img.ProseMirror-separator {
116782
116815
  }
116783
116816
  }
116784
116817
  //#endregion
116818
+ //#region lib/types/client/tl/gallery-image-transfer.js
116819
+ const GALLERY_IMAGE_MIME = "application/x-dsh-image-gen-gallery-image";
116820
+ const EXTENSIONS = /* @__PURE__ */ new Map([
116821
+ ["image/png", "png"],
116822
+ ["image/jpeg", "jpg"],
116823
+ ["image/webp", "webp"],
116824
+ ["image/gif", "gif"]
116825
+ ]);
116826
+ function galleryLanding(item) {
116827
+ return {
116828
+ galleryId: item.id,
116829
+ attachment: item.attachment,
116830
+ prompt: item.prompt.slice(0, 120),
116831
+ provider: item.provider,
116832
+ model: item.model
116833
+ };
116834
+ }
116835
+ /** A real file also works when the host drops custom MIME data between containers. */
116836
+ function startGalleryImageDrag(event, item, blob) {
116837
+ const transfer = event.dataTransfer;
116838
+ const mediaType = blob?.type || item.attachment.mediaType;
116839
+ const extension = EXTENSIONS.get(mediaType);
116840
+ if (!transfer || !blob?.size || !extension) {
116841
+ event.preventDefault();
116842
+ return false;
116843
+ }
116844
+ try {
116845
+ const name = item.id.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 100) || "image";
116846
+ const file = new File([blob], `${name}.${extension}`, { type: mediaType });
116847
+ transfer.items.clear();
116848
+ if (transfer.items.add(file) === null) throw new Error("Drag file unavailable");
116849
+ transfer.effectAllowed = "copy";
116850
+ try {
116851
+ transfer.setData(GALLERY_IMAGE_MIME, JSON.stringify(galleryLanding(item)));
116852
+ } catch {}
116853
+ return true;
116854
+ } catch {
116855
+ event.preventDefault();
116856
+ return false;
116857
+ }
116858
+ }
116859
+ function readGalleryImageDrop(transfer) {
116860
+ try {
116861
+ const raw = transfer.getData(GALLERY_IMAGE_MIME);
116862
+ if (!raw || raw.length > 64e3) return void 0;
116863
+ const value = JSON.parse(raw);
116864
+ if (!record$1(value) || !text(value.galleryId) || !record$1(value.attachment)) return void 0;
116865
+ const a = value.attachment;
116866
+ if (!text(a.attachmentId) || typeof a.mediaType !== "string" || !EXTENSIONS.has(a.mediaType) || !dimension(a.bytes) || !dimension(a.width) || !dimension(a.height) || a.name !== void 0 && (typeof a.name !== "string" || a.name.length > 1024)) return void 0;
116867
+ if (typeof value.prompt !== "string" || value.prompt.length > 120 || typeof value.provider !== "string" || !text(value.provider) || typeof value.model !== "string" || value.model.length > 1024) return void 0;
116868
+ return {
116869
+ galleryId: value.galleryId,
116870
+ attachment: {
116871
+ attachmentId: a.attachmentId,
116872
+ mediaType: a.mediaType,
116873
+ bytes: a.bytes,
116874
+ width: a.width,
116875
+ height: a.height,
116876
+ ...typeof a.name === "string" ? { name: a.name } : {}
116877
+ },
116878
+ prompt: value.prompt,
116879
+ provider: value.provider,
116880
+ model: value.model
116881
+ };
116882
+ } catch {
116883
+ return;
116884
+ }
116885
+ }
116886
+ function record$1(value) {
116887
+ return typeof value === "object" && value !== null && !Array.isArray(value);
116888
+ }
116889
+ function text(value) {
116890
+ return typeof value === "string" && value.length > 0 && value.length <= 1024;
116891
+ }
116892
+ function dimension(value) {
116893
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
116894
+ }
116895
+ //#endregion
116785
116896
  //#region lib/types/client/tl/studio-tl-canvas.js
116786
116897
  /**
116787
116898
  * Infinite canvas surface for the Studio workbench, backed by tldraw.
@@ -116850,7 +116961,7 @@ img.ProseMirror-separator {
116850
116961
  const collapsed = prompt.replace(/\s+/g, " ").trim();
116851
116962
  return collapsed.length === 0 ? void 0 : collapsed.slice(0, 120);
116852
116963
  }
116853
- async function landTlItems(editor, items, isActive) {
116964
+ async function landTlItems(editor, items, isActive, onUnreadable) {
116854
116965
  const landedIds = landedIdsOf(editor);
116855
116966
  const pending = items.filter((item) => !landedIds.has(item.galleryId));
116856
116967
  if (pending.length === 0) return true;
@@ -116879,6 +116990,7 @@ img.ProseMirror-separator {
116879
116990
  });
116880
116991
  } catch (error) {
116881
116992
  console.warn("[dsh-image-gen] canvas landing skipped (attachment unreadable):", item.galleryId, error);
116993
+ if (isActive()) onUnreadable?.();
116882
116994
  continue;
116883
116995
  }
116884
116996
  const size = displaySizeOf(item.attachment);
@@ -116985,9 +117097,12 @@ img.ProseMirror-separator {
116985
117097
  phase: "idle",
116986
117098
  count: 0
116987
117099
  });
117100
+ const [landingError, setLandingError] = (0, react.useState)(false);
116988
117101
  const retrySync = (0, react.useRef)(() => {});
117102
+ const editorRef = (0, react.useRef)(null);
116989
117103
  const onMount = (0, react.useCallback)((editor) => {
116990
117104
  let active = true;
117105
+ editorRef.current = editor;
116991
117106
  editor.run(() => editor.selectNone());
116992
117107
  mountedEditors.add(editor);
116993
117108
  applyBrandTheme(editor);
@@ -116996,11 +117111,13 @@ img.ProseMirror-separator {
116996
117111
  console.info(`[dsh-image-gen] tldraw mounted (instance ${editor.id})`);
116997
117112
  const stopLanding = registerTlLandingConsumer((items) => {
116998
117113
  const generation = getTlLandingGeneration();
116999
- return landTlItems(editor, items, () => active && generation === getTlLandingGeneration());
117114
+ setLandingError(false);
117115
+ return landTlItems(editor, items, () => active && generation === getTlLandingGeneration(), () => setLandingError(true));
117000
117116
  });
117001
117117
  let stopSync = startCanvasSync(editor, setSyncStatus);
117002
117118
  retrySync.current = () => stopSync.retry();
117003
117119
  const clear = () => {
117120
+ setLandingError(false);
117004
117121
  stopSync();
117005
117122
  editor.complete();
117006
117123
  editor.run(() => {
@@ -117024,6 +117141,7 @@ img.ProseMirror-separator {
117024
117141
  mountedCanvases.add(clear);
117025
117142
  return () => {
117026
117143
  active = false;
117144
+ if (editorRef.current === editor) editorRef.current = null;
117027
117145
  mountedEditors.delete(editor);
117028
117146
  mountedCanvases.delete(clear);
117029
117147
  stopLanding();
@@ -117035,17 +117153,30 @@ img.ProseMirror-separator {
117035
117153
  const statusText = lang === "en" ? syncStatus.phase === "ready" ? `Selection ready · ${syncStatus.count} shapes` : syncStatus.phase === "preparing" ? `Preparing selection · ${syncStatus.count} shapes…` : "Selection sync failed" : syncStatus.phase === "ready" ? `选区已就绪 · ${syncStatus.count} 个元素` : syncStatus.phase === "preparing" ? `正在准备选区 · ${syncStatus.count} 个元素…` : "选区同步失败";
117036
117154
  return (0, react_jsx_runtime.jsxs)("div", {
117037
117155
  className: "dsh-ig-tl-canvas",
117156
+ onDropCapture: (event) => {
117157
+ const item = readGalleryImageDrop(event.dataTransfer);
117158
+ const editor = editorRef.current;
117159
+ if (!item || !editor) return;
117160
+ event.preventDefault();
117161
+ event.stopPropagation();
117162
+ setLandingError(false);
117163
+ const generation = getTlLandingGeneration();
117164
+ landTlItems(editor, [item], () => editorRef.current === editor && generation === getTlLandingGeneration(), () => setLandingError(true)).catch((error) => {
117165
+ if (editorRef.current === editor) setLandingError(true);
117166
+ console.warn("[dsh-image-gen] history image drop failed:", error);
117167
+ });
117168
+ },
117038
117169
  children: [(0, react_jsx_runtime.jsx)(Tldraw, {
117039
117170
  persistenceKey: "dsh-image-gen-workbench-v1",
117040
117171
  sessionId,
117041
117172
  components: TL_COMPONENTS,
117042
117173
  onMount
117043
- }), syncStatus.phase !== "idle" && (0, react_jsx_runtime.jsxs)("div", {
117174
+ }), (landingError || syncStatus.phase !== "idle") && (0, react_jsx_runtime.jsxs)("div", {
117044
117175
  className: "dsh-ig-canvas-sync-status",
117045
- "data-phase": syncStatus.phase,
117176
+ "data-phase": landingError ? "error" : syncStatus.phase,
117046
117177
  role: "status",
117047
117178
  "aria-live": "polite",
117048
- children: [(0, react_jsx_runtime.jsxs)("span", { children: [statusText, syncStatus.phase === "error" ? `:${canvasSyncError(syncStatus.error, lang)}` : ""] }), syncStatus.phase === "error" && (0, react_jsx_runtime.jsx)("button", {
117179
+ children: [(0, react_jsx_runtime.jsx)("span", { children: landingError ? lang === "en" ? "Could not read the original image. Reload it and try again." : "无法读取原图,请重新加载图片后重试。" : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [statusText, syncStatus.phase === "error" ? `:${canvasSyncError(syncStatus.error, lang)}` : ""] }) }), !landingError && syncStatus.phase === "error" && (0, react_jsx_runtime.jsx)("button", {
117049
117180
  type: "button",
117050
117181
  onClick: () => retrySync.current(),
117051
117182
  children: lang === "en" ? "Retry" : "重试"
@@ -117062,6 +117193,26 @@ img.ProseMirror-separator {
117062
117193
  return en ? "Check the connection or re-import an unavailable image, then retry." : "请检查连接;若原图已失效,请重新导入后重试。";
117063
117194
  }
117064
117195
  //#endregion
117196
+ //#region lib/types/client/tl/gallery-canvas-button.js
117197
+ /** An explicit alternative to native dragging, including a visible unavailable state. */
117198
+ const GalleryCanvasButton = ({ item, lang, disabled, className, onToast }) => {
117199
+ const available = (0, react.useSyncExternalStore)(subscribeTlLandingAvailability, hasTlLandingConsumer, () => false);
117200
+ const open = lang === "en" ? "Open the infinite canvas first" : "请先打开无限画布";
117201
+ const title = !available ? open : disabled ? lang === "en" ? "Wait for the image to load" : "请等待图片加载完成" : lang === "en" ? "Send to canvas" : "发送到画布";
117202
+ return (0, react_jsx_runtime.jsx)("button", {
117203
+ type: "button",
117204
+ className,
117205
+ title,
117206
+ "aria-label": title,
117207
+ disabled: !available || disabled,
117208
+ onClick: (event) => {
117209
+ event.stopPropagation();
117210
+ onToast(sendTlLandingToOpenCanvas(galleryLanding(item)) ? lang === "en" ? "Sent to canvas" : "已发送到画布" : open);
117211
+ },
117212
+ children: (0, react_jsx_runtime.jsx)(Frame, { size: 14 })
117213
+ });
117214
+ };
117215
+ //#endregion
117065
117216
  //#region lib/types/client/studio-view.js
117066
117217
  /** Below this workbench width the recent-generations rail folds automatically. */
117067
117218
  const WORKBENCH_NARROW_WIDTH = 560;
@@ -117619,6 +117770,7 @@ img.ProseMirror-separator {
117619
117770
  };
117620
117771
  }, []);
117621
117772
  const activeProfile = (0, react.useMemo)(() => config?.providers.find((item) => item.provider === provider), [config, provider]);
117773
+ const requestModel = mode === "edit" && activeProfile?.editModel ? studioModelForMode(activeProfile, mode) : model;
117622
117774
  const configuredCount = config?.providers.filter((item) => item.configured).length ?? 0;
117623
117775
  const apiConfiguredCount = config?.providers.filter((item) => item.configured && !isSubscriptionProvider(item.provider)).length ?? 0;
117624
117776
  const subscriptionSignedInCount = config?.providers.filter((item) => item.configured && isSubscriptionProvider(item.provider)).length ?? 0;
@@ -117962,7 +118114,7 @@ img.ProseMirror-separator {
117962
118114
  body: JSON.stringify({
117963
118115
  mode,
117964
118116
  provider: target.profile.provider,
117965
- model: target.profile.model,
118117
+ model: studioModelForMode(target.profile, mode),
117966
118118
  prompt: prompt.trim(),
117967
118119
  ratio: target.ratio,
117968
118120
  quality: target.quality,
@@ -118031,7 +118183,7 @@ img.ProseMirror-separator {
118031
118183
  body: JSON.stringify({
118032
118184
  mode,
118033
118185
  provider,
118034
- model,
118186
+ model: requestModel,
118035
118187
  prompt: prompt.trim(),
118036
118188
  ratio,
118037
118189
  quality: selectedQuality,
@@ -118351,6 +118503,8 @@ img.ProseMirror-separator {
118351
118503
  }) : displayItems.map((item) => (0, react_jsx_runtime.jsx)(RecentItem, {
118352
118504
  item,
118353
118505
  active: selected?.id === item.id,
118506
+ lang,
118507
+ onToast: flash,
118354
118508
  onClick: () => selectItem(item)
118355
118509
  }, item.id)), items.length > visibleLimit && (0, react_jsx_runtime.jsx)("button", {
118356
118510
  type: "button",
@@ -118376,6 +118530,8 @@ img.ProseMirror-separator {
118376
118530
  children: list.map((item) => (0, react_jsx_runtime.jsx)(RecentItem, {
118377
118531
  item,
118378
118532
  active: false,
118533
+ lang,
118534
+ onToast: flash,
118379
118535
  onClick: () => void applyFavoriteImage(item)
118380
118536
  }, item.id))
118381
118537
  });
@@ -118612,7 +118768,7 @@ img.ProseMirror-separator {
118612
118768
  className: p.configured ? "is-configured" : "is-missing",
118613
118769
  children: [(0, react_jsx_runtime.jsxs)("div", {
118614
118770
  className: "dsh-ig-status-item-name",
118615
- children: [(0, react_jsx_runtime.jsx)("strong", { children: p.label }), (0, react_jsx_runtime.jsx)("small", { children: p.model })]
118771
+ children: [(0, react_jsx_runtime.jsx)("strong", { children: p.label }), (0, react_jsx_runtime.jsx)("small", { children: studioModelForMode(p, mode) })]
118616
118772
  }), (0, react_jsx_runtime.jsx)("span", {
118617
118773
  className: "dsh-ig-status-item-tag",
118618
118774
  children: isSubscriptionProvider(p.provider) ? p.configured ? t("subSignedIn") : t("subSignedOut") : p.configured ? t("configured") : t("unconfigured")
@@ -118736,7 +118892,7 @@ img.ProseMirror-separator {
118736
118892
  children: (0, react_jsx_runtime.jsx)(Sparkles, { size: 28 })
118737
118893
  }),
118738
118894
  (0, react_jsx_runtime.jsx)("strong", { children: comparisonEnabled ? t("comparing", { count: String(comparisonTargets.length) }) : count > 1 ? t("generatingCount", { count: String(count) }) : t("generating") }),
118739
- (0, react_jsx_runtime.jsx)("span", { children: comparisonEnabled ? comparisonTargets.map((target) => target.profile.label).join(" · ") : `${activeProfile?.label ?? ""} · ${model}` })
118895
+ (0, react_jsx_runtime.jsx)("span", { children: comparisonEnabled ? comparisonTargets.map((target) => target.profile.label).join(" · ") : `${activeProfile?.label ?? ""} · ${requestModel}` })
118740
118896
  ]
118741
118897
  }) : currentBatch && currentBatch.length > 1 ? (0, react_jsx_runtime.jsx)("div", {
118742
118898
  className: "dsh-ig-canvas-cluster",
@@ -119050,8 +119206,8 @@ img.ProseMirror-separator {
119050
119206
  (0, react_jsx_runtime.jsxs)("span", {
119051
119207
  className: "dsh-ig-model-copy",
119052
119208
  children: [(0, react_jsx_runtime.jsx)("strong", { children: item.label }), (0, react_jsx_runtime.jsx)("small", {
119053
- title: item.model,
119054
- children: item.model
119209
+ title: studioModelForMode(item, mode),
119210
+ children: studioModelForMode(item, mode)
119055
119211
  })]
119056
119212
  }),
119057
119213
  target && (0, react_jsx_runtime.jsxs)("span", {
@@ -119120,11 +119276,11 @@ img.ProseMirror-separator {
119120
119276
  }))
119121
119277
  }), (0, react_jsx_runtime.jsx)(FieldSelect, {
119122
119278
  label: t("model"),
119123
- value: model,
119279
+ value: requestModel,
119124
119280
  onChange: setModel,
119125
119281
  options: activeProfile === void 0 ? [] : [{
119126
- value: activeProfile.model,
119127
- label: activeProfile.model
119282
+ value: studioModelForMode(activeProfile, mode),
119283
+ label: studioModelForMode(activeProfile, mode)
119128
119284
  }]
119129
119285
  })]
119130
119286
  }),
@@ -119602,23 +119758,37 @@ img.ProseMirror-separator {
119602
119758
  label: opt.value === "__default__" ? lang === "en" ? "Provider default (omit)" : "渠道默认(不发送)" : LOCALIZED_QUALITIES[opt.value]?.[lang] ?? opt.label
119603
119759
  }));
119604
119760
  }
119605
- const RecentItem = ({ item, active, onClick }) => {
119761
+ const RecentItem = ({ item, active, lang, onToast, onClick }) => {
119606
119762
  const [setRef, inView] = useInView();
119607
119763
  const image = useAttachmentImage(item.attachment, inView);
119608
- return (0, react_jsx_runtime.jsx)("button", {
119609
- ref: setRef,
119610
- type: "button",
119764
+ return (0, react_jsx_runtime.jsxs)("div", {
119611
119765
  className: `dsh-ig-recent-item ${active ? "is-active" : ""}`,
119612
- onClick,
119613
- title: item.prompt,
119614
- children: (0, react_jsx_runtime.jsx)("div", {
119615
- className: "dsh-ig-recent-thumb",
119616
- children: image.url !== null ? (0, react_jsx_runtime.jsx)("img", {
119617
- src: image.url,
119618
- alt: "",
119619
- loading: "lazy"
119620
- }) : (0, react_jsx_runtime.jsx)(ImagePlus, { size: 18 })
119621
- })
119766
+ children: [(0, react_jsx_runtime.jsx)("button", {
119767
+ ref: setRef,
119768
+ type: "button",
119769
+ className: "dsh-ig-recent-select",
119770
+ onClick,
119771
+ title: item.prompt,
119772
+ draggable: Boolean(image.blob?.size),
119773
+ onDragStart: (event) => {
119774
+ startGalleryImageDrag(event, item, image.blob);
119775
+ },
119776
+ children: (0, react_jsx_runtime.jsx)("div", {
119777
+ className: "dsh-ig-recent-thumb",
119778
+ children: image.url !== null ? (0, react_jsx_runtime.jsx)("img", {
119779
+ src: image.url,
119780
+ alt: "",
119781
+ loading: "lazy",
119782
+ draggable: false
119783
+ }) : (0, react_jsx_runtime.jsx)(ImagePlus, { size: 18 })
119784
+ })
119785
+ }), (0, react_jsx_runtime.jsx)(GalleryCanvasButton, {
119786
+ item,
119787
+ lang,
119788
+ disabled: !image.blob?.size,
119789
+ className: "dsh-ig-recent-send",
119790
+ onToast
119791
+ })]
119622
119792
  });
119623
119793
  };
119624
119794
  const FavoriteFolderBar = ({ folders, active, onSelect, onDelete, deleteLabel }) => {
@@ -123457,6 +123627,7 @@ img.ProseMirror-separator {
123457
123627
  }) : (0, react_jsx_runtime.jsx)("div", {
123458
123628
  className: "dsh-ig-gallery-grid",
123459
123629
  children: filteredItems.map((item, idx) => (0, react_jsx_runtime.jsx)(GalleryCard, {
123630
+ lang,
123460
123631
  item,
123461
123632
  index: idx,
123462
123633
  isManageMode,
@@ -123841,7 +124012,7 @@ img.ProseMirror-separator {
123841
124012
  ]
123842
124013
  });
123843
124014
  };
123844
- const GalleryCard = ({ item, index: _index, isManageMode, isSelected, t, onClick, onToggleSelect, onRequestDelete, onPreview: _onPreview, onBlobLoaded, onToast }) => {
124015
+ const GalleryCard = ({ lang, item, index: _index, isManageMode, isSelected, t, onClick, onToggleSelect, onRequestDelete, onPreview: _onPreview, onBlobLoaded, onToast }) => {
123845
124016
  const [url, setUrl] = (0, react.useState)();
123846
124017
  const [blob, setBlob] = (0, react.useState)();
123847
124018
  const [loading, setLoading] = (0, react.useState)(true);
@@ -123931,11 +124102,23 @@ img.ProseMirror-separator {
123931
124102
  className: "dsh-ig-gallery-card-img",
123932
124103
  src: url,
123933
124104
  alt: item.prompt,
123934
- loading: "lazy"
124105
+ loading: "lazy",
124106
+ draggable: !isManageMode && Boolean(blob?.size),
124107
+ onDragStart: (event) => {
124108
+ if (isManageMode) event.preventDefault();
124109
+ else startGalleryImageDrag(event, item, blob);
124110
+ }
123935
124111
  }),
123936
124112
  (0, react_jsx_runtime.jsxs)("div", {
123937
124113
  className: "dsh-ig-card-toolbar",
123938
124114
  children: [
124115
+ !isManageMode && (0, react_jsx_runtime.jsx)(GalleryCanvasButton, {
124116
+ item,
124117
+ lang,
124118
+ disabled: !blob?.size,
124119
+ className: "dsh-ig-tool-btn",
124120
+ onToast
124121
+ }),
123939
124122
  (0, react_jsx_runtime.jsx)("button", {
123940
124123
  type: "button",
123941
124124
  className: "dsh-ig-tool-btn",
@@ -124229,12 +124412,48 @@ img.ProseMirror-separator {
124229
124412
  }
124230
124413
  //#endregion
124231
124414
  //#region lib/types/client/tl/conversation-landings.js
124415
+ /** Keep one landing subscription while the optional UI service loads or unloads. */
124416
+ function createCanvasSessionSelection() {
124417
+ let current;
124418
+ const listeners = /* @__PURE__ */ new Set();
124419
+ const notify = () => {
124420
+ for (const listener of listeners) listener();
124421
+ };
124422
+ return {
124423
+ getSnapshot: () => current?.getSnapshot(),
124424
+ subscribe: (listener) => {
124425
+ listeners.add(listener);
124426
+ return () => {
124427
+ listeners.delete(listener);
124428
+ };
124429
+ },
124430
+ bind(source) {
124431
+ current = source;
124432
+ const stop = source.subscribe(notify);
124433
+ notify();
124434
+ return () => {
124435
+ stop();
124436
+ if (current !== source) return;
124437
+ current = void 0;
124438
+ notify();
124439
+ };
124440
+ }
124441
+ };
124442
+ }
124443
+ function currentSessionId(sessions, selection) {
124444
+ const selected = selection?.getSnapshot();
124445
+ if (selected !== void 0) return selected.key || void 0;
124446
+ const snapshot = sessions.list.getSnapshot();
124447
+ if (snapshot.current) return snapshot.current;
124448
+ const mainViews = Object.values(snapshot.byId ?? {}).filter((row) => (row.retainedBy?.mainView ?? 0) > 0);
124449
+ return mainViews.length === 1 ? mainViews[0].id : void 0;
124450
+ }
124232
124451
  /** Subscribe to live additions, never to card rendering or history replay. */
124233
- function startConversationLandings(sessions) {
124452
+ function startConversationLandings(sessions, selection) {
124234
124453
  let source;
124235
124454
  let stopEvents;
124236
124455
  const followCurrent = () => {
124237
- const id = sessions.list.getSnapshot().current;
124456
+ const id = currentSessionId(sessions, selection);
124238
124457
  const next = id === void 0 ? void 0 : sessions.binding(id)?.eventSource;
124239
124458
  if (next === source) return;
124240
124459
  stopEvents?.();
@@ -124263,15 +124482,20 @@ img.ProseMirror-separator {
124263
124482
  });
124264
124483
  };
124265
124484
  const stopList = sessions.list.subscribe(followCurrent);
124485
+ const stopSelection = selection?.subscribe(followCurrent);
124266
124486
  followCurrent();
124267
124487
  return () => {
124268
124488
  stopList();
124489
+ stopSelection?.();
124269
124490
  stopEvents?.();
124270
124491
  };
124271
124492
  }
124272
124493
  //#endregion
124273
124494
  //#region lib/types/client/studio-style.js
124274
124495
  const STUDIO_STYLE = `
124496
+ .dsh-ig-recent-select{display:block;width:100%;height:100%;padding:0;border:0;border-radius:inherit;background:transparent;cursor:pointer}
124497
+ .dsh-ig-recent-send{position:absolute;right:3px;bottom:3px;display:flex;align-items:center;justify-content:center;width:25px;height:25px;padding:0;border:1px solid var(--ig-line);border-radius:5px;background:var(--ig-bg-panel);cursor:pointer}
124498
+ .dsh-ig-recent-send:disabled{opacity:.45;cursor:not-allowed}
124275
124499
  .dsh-ig-gallery-page-body.is-workbench{flex:1 1 0;height:100%;min-height:0;padding:0;overflow:hidden;display:flex;flex-direction:column}
124276
124500
  .dsh-ig-workbench{--ig-blue:var(--dsw-alias-brand-primary-new-colorprimary-new-color,#2f64f5);--ig-blue-line:color-mix(in srgb,var(--ig-blue) 45%,transparent);--ig-line:var(--dsw-alias-border-l2,color-mix(in srgb,currentColor 11%,transparent));--ig-soft:color-mix(in srgb,currentColor 4%,transparent);--ig-fg:var(--dsw-alias-label-primary,var(--foreground,#182033));--ig-fg-2:var(--dsw-alias-label-secondary,#5a6478);--ig-fg-3:var(--dsw-alias-label-tertiary,#717b90);--ig-fg-cap:var(--dsw-alias-label-caption,#929bad);--ig-bg:var(--dsw-alias-bg-layer-1,var(--background,#fff));--ig-bg-panel:var(--dsw-alias-bg-layer-2,color-mix(in srgb,var(--background,#fff) 96%,#f5f7fb));--ig-bg-thumb:var(--dsw-alias-bg-skeleton,#eef1f7);--ig-danger:#b63737;--ig-warn:#8b671c;position:relative;display:flex;flex-direction:column;min-height:0;height:100%;overflow:hidden;padding:10px 12px 12px;color:var(--ig-fg);box-sizing:border-box;background:radial-gradient(circle at 60% -20%,rgba(63,104,246,.07),transparent 30%)}
124277
124501
  body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a}
@@ -125221,7 +125445,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
125221
125445
  `;
125222
125446
  //#endregion
125223
125447
  //#region lib/types/client/index.js
125224
- console.info(`[dsh-image-gen] canvas bundle 2026-10-15T09:51:10.196Z`);
125448
+ console.info(`[dsh-image-gen] canvas bundle 2026-10-26T00:24:12.240Z`);
125225
125449
  /** Right-sidebar tab-type registration id (DSH 0.1.5 `sidebarRightTabs`). */
125226
125450
  const SIDEBAR_STUDIO_TAB_ID = "dsh-image-gen/studio";
125227
125451
  /** The tab kind: a page type opened by kind, recognizing no resource address. */
@@ -125296,14 +125520,23 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
125296
125520
  reset: "重置",
125297
125521
  resetTitle: "重置为默认官方地址",
125298
125522
  endpointHintGoogle: "Google 官方地址或反代端点(全路径)。",
125523
+ googleProtocol: "请求协议",
125524
+ googleInteractions: "Interactions(默认)",
125525
+ googleNative: "原生 generateContent",
125526
+ saveGoogleProtocolFirst: "请求协议尚未保存,请先点击保存,再拉取模型或测试连接。",
125527
+ endpointHintGoogleNative: "填写 API 基础地址(如 https://example.com/v1beta);也兼容 Interactions 或 generateContent 完整地址。",
125299
125528
  endpointHintOpenAI: "官方 api.openai.com 的 /v1 地址;中转站请使用下方「OpenAI 兼容」行。",
125300
125529
  endpointHintOpenAICompat: "中转站/自建服务的 OpenAI 兼容 /v1 地址(必填),例如 https://your-relay.example.com/v1。",
125301
125530
  editFormat: "图生图请求形态",
125531
+ editModel: "图生图模型(可选)",
125532
+ editModelHint: "留空沿用上方模型。硅基流动可分别填写 Qwen/Qwen-Image(文生图)和 Qwen/Qwen-Image-Edit(图生图)。",
125533
+ editFormatSiliconFlow: "硅基流动 JSON(image 字符串)",
125534
+ editFormatSiliconFlowHint: "编辑尺寸由参考图决定,不发送尺寸和质量字段。Qwen-Image-Edit 支持一张图,Edit-2509 最多三张;参考图字段由插件填入。",
125302
125535
  editFormatMultipart: "标准 multipart(OpenAI 官方)",
125303
125536
  editFormatAutomatic: "自动:官方接口 JSON 格式",
125304
125537
  editFormatJsonImageUrlArray: "JSON images 数组(商汤等中转)",
125305
125538
  editFormatFormReferenceImages: "multipart + reference_images JSON 数组(base64)",
125306
- editFormatHint: "商汤、通义已确认的官方接口自动使用对应 JSON 格式;其他中转按接口文档选择。文生图不受影响。",
125539
+ editFormatHint: "商汤、通义、硅基流动已确认的官方接口自动使用对应 JSON 格式;其他中转按接口文档选择。文生图不受影响。",
125307
125540
  editExtra: "附加 JSON 字段",
125308
125541
  editExtraPlaceholder: "{\"watermark\": false, \"prompt_extend\": true}",
125309
125542
  editExtraHint: "仅 JSON 形态生效;会合并到请求体末尾,可覆盖默认字段。留空表示不附加。",
@@ -125461,14 +125694,23 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
125461
125694
  reset: "Reset",
125462
125695
  resetTitle: "Reset to official default URL",
125463
125696
  endpointHintGoogle: "Official Google endpoint or reverse proxy (full path).",
125697
+ googleProtocol: "Request protocol",
125698
+ googleInteractions: "Interactions (default)",
125699
+ googleNative: "Native generateContent",
125700
+ saveGoogleProtocolFirst: "Save the request protocol before fetching models or testing the connection.",
125701
+ endpointHintGoogleNative: "API base URL (e.g. https://example.com/v1beta). Full Interactions or generateContent URLs also work.",
125464
125702
  endpointHintOpenAI: "Official api.openai.com /v1 base URL; for relays use the \"OpenAI-compatible\" row below.",
125465
125703
  endpointHintOpenAICompat: "OpenAI-compatible /v1 base URL of your relay or self-hosted service (required), e.g. https://your-relay.example.com/v1.",
125466
125704
  editFormat: "Edit request format",
125705
+ editModel: "Editing model (optional)",
125706
+ editModelHint: "Leave empty to reuse the model above. SiliconFlow can use Qwen/Qwen-Image for generation and Qwen/Qwen-Image-Edit for editing.",
125707
+ editFormatSiliconFlow: "SiliconFlow JSON (image string)",
125708
+ editFormatSiliconFlowHint: "Edit dimensions follow the reference; size and quality fields are omitted. Qwen-Image-Edit supports one reference, Edit-2509 up to three. The plugin fills the image fields.",
125467
125709
  editFormatMultipart: "Standard multipart (official OpenAI)",
125468
125710
  editFormatAutomatic: "Automatic: official JSON contract",
125469
125711
  editFormatJsonImageUrlArray: "JSON images array (SenseNova etc.)",
125470
125712
  editFormatFormReferenceImages: "Multipart with a reference_images JSON array (base64)",
125471
- editFormatHint: "Recognized official SenseNova and Qwen endpoints automatically use their JSON edit contracts. Choose other relay formats according to their documentation. Text-to-image is unaffected.",
125713
+ editFormatHint: "Recognized official SenseNova, Qwen and SiliconFlow endpoints automatically use their JSON edit contracts. Choose other relay formats according to their documentation. Text-to-image is unaffected.",
125472
125714
  editExtra: "Extra JSON fields",
125473
125715
  editExtraPlaceholder: "{\"watermark\": false, \"prompt_extend\": true}",
125474
125716
  editExtraHint: "Only used with the JSON format; merged into the request body last and may override defaults. Leave empty for none.",
@@ -125881,7 +126123,13 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
125881
126123
  ctx.inject(["sessions"], (owner) => {
125882
126124
  const sessions = owner.get("sessions");
125883
126125
  if (typeof sessions?.binding !== "function" || typeof sessions.list?.subscribe !== "function") return;
125884
- owner.effect(() => startConversationLandings(sessions), "dsh-image-gen: live canvas images");
126126
+ const selection = createCanvasSessionSelection();
126127
+ owner.effect(() => startConversationLandings(sessions, selection), "dsh-image-gen: live canvas images");
126128
+ owner.inject(["uiSession"], (uiOwner) => {
126129
+ const current = uiOwner.get("uiSession")?.adapter?.current;
126130
+ if (typeof current?.getSnapshot !== "function" || typeof current.subscribe !== "function") return;
126131
+ uiOwner.effect(() => selection.bind(current), "dsh-image-gen: current canvas session");
126132
+ });
125885
126133
  });
125886
126134
  ctx.effect(() => {
125887
126135
  const style = document.createElement("style");
@@ -126097,7 +126345,9 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126097
126345
  return {
126098
126346
  expanded: false,
126099
126347
  model: "",
126348
+ editModel: "",
126100
126349
  baseURL: "",
126350
+ googleNative: false,
126101
126351
  keyInput: "",
126102
126352
  keyStatus: "checking",
126103
126353
  keyInfo: void 0,
@@ -126178,7 +126428,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126178
126428
  }
126179
126429
  /** Persisted or selected edit-format value → row state; unknown values fall back to standard multipart. */
126180
126430
  function parseEditFormat(value) {
126181
- return value === "jsonImageUrlArray" || value === "formReferenceImages" ? value : "multipart";
126431
+ return value === "jsonImageUrlArray" || value === "formReferenceImages" || value === "siliconflowJson" ? value : "multipart";
126182
126432
  }
126183
126433
  /** Build one row per provider from persisted settings, including ComfyUI extras. */
126184
126434
  function rowsFromSettings(value) {
@@ -126187,6 +126437,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126187
126437
  ...emptyProviderRow(),
126188
126438
  model: modelOf(provider, value),
126189
126439
  baseURL: baseURLOf(provider, value),
126440
+ ...provider === "google" ? { googleNative: value?.googleNative === true } : {},
126190
126441
  ...provider === "openai" ? { sizeTableText: editExtraTextOf(value?.openaiSizes) } : {},
126191
126442
  ...provider === "comfyui" ? {
126192
126443
  workflows: resolveComfyUIWorkflows(value ?? {}),
@@ -126194,6 +126445,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126194
126445
  timeoutSeconds: Math.max(1, Math.round((value?.comfyuiTimeoutMs ?? 3e5) / 1e3))
126195
126446
  } : {},
126196
126447
  ...provider === "openai-compat" ? {
126448
+ editModel: value?.openaiCompatEditModel ?? "",
126197
126449
  editFormat: parseEditFormat(value?.openaiCompatEditFormat),
126198
126450
  editExtraText: editExtraTextOf(value?.openaiCompatEditExtra),
126199
126451
  sizeTableText: editExtraTextOf(value?.openaiCompatSizes),
@@ -126446,6 +126698,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126446
126698
  for (const provider of IMAGE_PROVIDERS) next[provider] = {
126447
126699
  ...current[provider],
126448
126700
  model: modelOf(provider, value),
126701
+ ...provider === "openai-compat" ? { editModel: value?.openaiCompatEditModel ?? "" } : {},
126449
126702
  baseURL: baseURLOf(provider, value),
126450
126703
  ...provider === "comfyui" ? {
126451
126704
  workflows: resolveComfyUIWorkflows(value ?? {}),
@@ -126573,8 +126826,10 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126573
126826
  if (provider === "openai") for (const tiers of Object.values(sizeTable)) for (const size of Object.values(tiers)) validateOfficialSize(row.model, size);
126574
126827
  await saveSetting(modelFieldOf(provider), row.model);
126575
126828
  await saveSetting(baseURLFieldOf(provider), row.baseURL);
126829
+ if (provider === "google") await saveSetting("googleNative", row.googleNative);
126576
126830
  if (provider === "openai") await saveSetting("openaiSizes", sizeTable);
126577
126831
  if (provider === "openai-compat") {
126832
+ await saveSetting("openaiCompatEditModel", row.editModel.trim());
126578
126833
  await saveSetting("openaiCompatEditFormat", row.editFormat);
126579
126834
  await saveSetting("openaiCompatEditExtra", editExtra);
126580
126835
  await saveSetting("openaiCompatSizes", sizeTable);
@@ -126624,6 +126879,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126624
126879
  const address = row.baseURL.trim();
126625
126880
  if (address.length === 0) return t("endpointRequired");
126626
126881
  if (address !== baseURLOf(provider, props.scope.getSnapshot().value).trim()) return t("saveEndpointFirst");
126882
+ if (provider === "google" && row.googleNative !== (props.scope.getSnapshot().value?.googleNative === true)) return t("saveGoogleProtocolFirst");
126627
126883
  };
126628
126884
  /** Localize the host's missing-address error while preserving upstream details. */
126629
126885
  const providerErrorMessage = (message) => message === "Base URL is not configured" ? t("savedEndpointMissing") : message ?? t("testFailed");
@@ -126925,7 +127181,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126925
127181
  const row = rows[provider];
126926
127182
  const officialEditFormat = provider === "openai-compat" ? compatEditFormat({
126927
127183
  openaiCompatBaseURL: row.baseURL,
126928
- openaiCompatModel: row.model
127184
+ openaiCompatModel: row.editModel.trim() || row.model
126929
127185
  }) : "multipart";
126930
127186
  const automaticEditFormat = officialEditFormat !== "multipart";
126931
127187
  const editFormat = automaticEditFormat ? officialEditFormat : row.editFormat;
@@ -126995,10 +127251,31 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
126995
127251
  }),
126996
127252
  (0, react_jsx_runtime.jsx)("span", {
126997
127253
  className: "dsh-ig-hint",
126998
- children: t(CLOUD_HINT_KEYS[provider])
127254
+ children: t(provider === "google" && row.googleNative ? "endpointHintGoogleNative" : CLOUD_HINT_KEYS[provider])
126999
127255
  })
127000
127256
  ]
127001
127257
  }),
127258
+ provider === "google" ? (0, react_jsx_runtime.jsxs)("label", {
127259
+ className: "dsh-ig-field",
127260
+ children: [(0, react_jsx_runtime.jsx)("span", {
127261
+ className: "dsh-ig-label",
127262
+ children: t("googleProtocol")
127263
+ }), (0, react_jsx_runtime.jsxs)("select", {
127264
+ className: "dsh-ig-input",
127265
+ value: row.googleNative ? "native" : "interactions",
127266
+ onChange: (event) => {
127267
+ updateRow(provider, { googleNative: event.target.value === "native" });
127268
+ },
127269
+ disabled: !settingsWritable,
127270
+ children: [(0, react_jsx_runtime.jsx)("option", {
127271
+ value: "interactions",
127272
+ children: t("googleInteractions")
127273
+ }), (0, react_jsx_runtime.jsx)("option", {
127274
+ value: "native",
127275
+ children: t("googleNative")
127276
+ })]
127277
+ })]
127278
+ }) : null,
127002
127279
  (0, react_jsx_runtime.jsxs)("label", {
127003
127280
  className: "dsh-ig-field",
127004
127281
  children: [
@@ -127052,6 +127329,28 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
127052
127329
  }) : null
127053
127330
  ]
127054
127331
  }),
127332
+ provider === "openai-compat" ? (0, react_jsx_runtime.jsxs)("label", {
127333
+ className: "dsh-ig-field",
127334
+ children: [
127335
+ (0, react_jsx_runtime.jsx)("span", {
127336
+ className: "dsh-ig-label",
127337
+ children: t("editModel")
127338
+ }),
127339
+ (0, react_jsx_runtime.jsx)("input", {
127340
+ className: "dsh-ig-input",
127341
+ value: row.editModel,
127342
+ onChange: (event) => {
127343
+ updateRow(provider, { editModel: event.target.value });
127344
+ },
127345
+ list: "dsh-ig-openai-compat-model-options",
127346
+ disabled: !settingsWritable
127347
+ }),
127348
+ (0, react_jsx_runtime.jsx)("span", {
127349
+ className: "dsh-ig-hint",
127350
+ children: t("editModelHint")
127351
+ })
127352
+ ]
127353
+ }) : null,
127055
127354
  provider === "openai-compat" ? (0, react_jsx_runtime.jsxs)("label", {
127056
127355
  className: "dsh-ig-field",
127057
127356
  children: [
@@ -127082,12 +127381,16 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
127082
127381
  (0, react_jsx_runtime.jsx)("option", {
127083
127382
  value: "formReferenceImages",
127084
127383
  children: t("editFormatFormReferenceImages")
127384
+ }),
127385
+ (0, react_jsx_runtime.jsx)("option", {
127386
+ value: "siliconflowJson",
127387
+ children: t("editFormatSiliconFlow")
127085
127388
  })
127086
127389
  ]
127087
127390
  }),
127088
127391
  (0, react_jsx_runtime.jsx)("span", {
127089
127392
  className: "dsh-ig-hint",
127090
- children: t("editFormatHint")
127393
+ children: t(editFormat === "siliconflowJson" ? "editFormatSiliconFlowHint" : "editFormatHint")
127091
127394
  })
127092
127395
  ]
127093
127396
  }) : null,
@@ -127260,7 +127563,7 @@ body[data-ds-dark-theme] .dsh-ig-workbench{--ig-danger:#f08c8c;--ig-warn:#e5b76a
127260
127563
  ]
127261
127564
  })
127262
127565
  ] }) : null,
127263
- provider === "openai-compat" && (editFormat === "jsonImageUrlArray" || editFormat === "qwenJson") ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [editFormat === "jsonImageUrlArray" ? (0, react_jsx_runtime.jsxs)("label", {
127566
+ provider === "openai-compat" && (editFormat === "jsonImageUrlArray" || editFormat === "qwenJson" || editFormat === "siliconflowJson") ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [editFormat === "jsonImageUrlArray" ? (0, react_jsx_runtime.jsxs)("label", {
127264
127567
  className: "dsh-ig-field",
127265
127568
  children: [(0, react_jsx_runtime.jsx)("span", {
127266
127569
  className: "dsh-ig-label",