dsh-image-gen 0.6.11 → 0.7.0

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
@@ -116227,6 +116227,7 @@ img.ProseMirror-separator {
116227
116227
  const GRID_GAP = 40;
116228
116228
  const GRID_COLS = 3;
116229
116229
  const mountedCanvases = /* @__PURE__ */ new Set();
116230
+ const mountedEditors = /* @__PURE__ */ new Set();
116230
116231
  /** Reset canvas-owned data only. Gallery records and host attachments are untouched. */
116231
116232
  function clearStudioTlCanvases() {
116232
116233
  if (mountedCanvases.size === 0) return false;
@@ -116236,6 +116237,17 @@ img.ProseMirror-separator {
116236
116237
  return true;
116237
116238
  }
116238
116239
  /**
116240
+ * Cancel in-editor selection on every mounted canvas, keeping shapes and sync
116241
+ * untouched. Selection is interaction state tied to the surface: leaving the
116242
+ * studio page cancels it so a stale selection can neither resurface on return
116243
+ * nor linger in the host mirror.
116244
+ */
116245
+ function deselectStudioTlCanvases() {
116246
+ if (mountedEditors.size === 0) return false;
116247
+ for (const editor of mountedEditors) editor.run(() => editor.selectNone());
116248
+ return true;
116249
+ }
116250
+ /**
116239
116251
  * Dock the stock toolbar vertically along the left edge (Figma-style) instead
116240
116252
  * of tldraw's signature bottom-center pill. It is still the same
116241
116253
  * DefaultToolbar with the same tools and overflow handling — only the
@@ -116410,6 +116422,8 @@ img.ProseMirror-separator {
116410
116422
  const retrySync = (0, react.useRef)(() => {});
116411
116423
  const onMount = (0, react.useCallback)((editor) => {
116412
116424
  let active = true;
116425
+ editor.run(() => editor.selectNone());
116426
+ mountedEditors.add(editor);
116413
116427
  applyBrandTheme(editor);
116414
116428
  editor.updateInstanceState({ isGridMode: true });
116415
116429
  console.info(`[dsh-image-gen] tldraw mounted (instance ${editor.id})`);
@@ -116443,6 +116457,7 @@ img.ProseMirror-separator {
116443
116457
  mountedCanvases.add(clear);
116444
116458
  return () => {
116445
116459
  active = false;
116460
+ mountedEditors.delete(editor);
116446
116461
  mountedCanvases.delete(clear);
116447
116462
  stopLanding();
116448
116463
  stopSync();
@@ -119969,7 +119984,7 @@ img.ProseMirror-separator {
119969
119984
  return "1:1";
119970
119985
  }
119971
119986
  const GalleryViewTab = (props) => {
119972
- const { locale, sessionId, useSessions, useWorkspaces, credentialEvents, inSidebar, defaultTab, initialCanvasSurface } = props;
119987
+ const { locale, sessionId, useSessions, useWorkspaces, credentialEvents, inSidebar, defaultTab, initialCanvasSurface, useTabInfo } = props;
119973
119988
  const [activeTab, setActiveTab] = (0, react.useState)(defaultTab ?? "gallery");
119974
119989
  const [studioDraft, setStudioDraft] = (0, react.useState)(void 0);
119975
119990
  const [items, setItems] = (0, react.useState)([]);
@@ -119992,6 +120007,10 @@ img.ProseMirror-separator {
119992
120007
  localStorage.setItem("dsh-ig-only-current-workspace", String(onlyCurrentWorkspace));
119993
120008
  } catch {}
119994
120009
  }, [onlyCurrentWorkspace]);
120010
+ const tabVisible = useTabInfo?.().tab?.visible;
120011
+ (0, react.useEffect)(() => {
120012
+ if (tabVisible === false) deselectStudioTlCanvases();
120013
+ }, [tabVisible]);
119995
120014
  (0, react.useEffect)(() => {
119996
120015
  let mounted = true;
119997
120016
  fetch(STUDIO_ROUTE).then((res) => res.ok ? res.json() : null).then((data) => {
@@ -122038,6 +122057,12 @@ img.ProseMirror-separator {
122038
122057
  return typeof candidate.attachmentId === "string" && (candidate.mediaType === "image/png" || candidate.mediaType === "image/jpeg" || candidate.mediaType === "image/webp" || candidate.mediaType === "image/gif") && typeof candidate.bytes === "number" && typeof candidate.width === "number" && typeof candidate.height === "number";
122039
122058
  }
122040
122059
  //#endregion
122060
+ //#region lib/types/client/settings-write.js
122061
+ /** DSH 0.1.7 resolves refused writes to false; older settings scopes resolve to void. */
122062
+ async function writeSetting(scope, field, value, rejectedMessage) {
122063
+ if (await scope.set(field, value) === false) throw new Error(rejectedMessage);
122064
+ }
122065
+ //#endregion
122041
122066
  //#region lib/types/client/provider-pill.js
122042
122067
  /**
122043
122068
  * Composer tool-row pill: switch the default image provider without leaving
@@ -122058,7 +122083,8 @@ img.ProseMirror-separator {
122058
122083
  subSignedOut: "未登录",
122059
122084
  subStatusUnknown: "状态未知",
122060
122085
  readOnly: "图像设置为只读,无法在此切换",
122061
- switchFailed: "切换失败"
122086
+ switchFailed: "切换失败",
122087
+ writeRejected: "设置未保存,请重试"
122062
122088
  },
122063
122089
  en: {
122064
122090
  pillLabel: "Image",
@@ -122072,7 +122098,8 @@ img.ProseMirror-separator {
122072
122098
  subSignedOut: "Signed out",
122073
122099
  subStatusUnknown: "Unknown",
122074
122100
  readOnly: "Image settings are read-only; switch them in the config source",
122075
- switchFailed: "Switch failed"
122101
+ switchFailed: "Switch failed",
122102
+ writeRejected: "Setting was not saved. Please try again"
122076
122103
  }
122077
122104
  };
122078
122105
  /** Provider row names, per locale. Brand words stay untranslated; only the
@@ -122280,7 +122307,7 @@ img.ProseMirror-separator {
122280
122307
  }
122281
122308
  setError("");
122282
122309
  setPending(provider);
122283
- props.scope.set("provider", provider).then(() => {
122310
+ writeSetting(props.scope, "provider", provider, t("writeRejected")).then(() => {
122284
122311
  setOpen(false);
122285
122312
  }).catch((cause) => {
122286
122313
  setError(cause instanceof Error ? cause.message : String(cause));
@@ -122473,7 +122500,7 @@ img.ProseMirror-separator {
122473
122500
  `;
122474
122501
  //#endregion
122475
122502
  //#region lib/types/client/index.js
122476
- console.info(`[dsh-image-gen] canvas bundle 2026-09-23T16:16:55.812Z`);
122503
+ console.info(`[dsh-image-gen] canvas bundle 2026-09-26T07:20:53.102Z`);
122477
122504
  /** Right-sidebar tab-type registration id (DSH 0.1.5 `sidebarRightTabs`). */
122478
122505
  const SIDEBAR_STUDIO_TAB_ID = "dsh-image-gen/studio";
122479
122506
  /** The tab kind: a page type opened by kind, recognizing no resource address. */
@@ -122598,6 +122625,7 @@ img.ProseMirror-separator {
122598
122625
  saving: "保存中…",
122599
122626
  save: "保存",
122600
122627
  saved: "已保存",
122628
+ settingsWriteRejected: "有一项设置未能保存,请检查后重试。",
122601
122629
  savedToPath: "已保存到",
122602
122630
  generating: "正在生成图片…",
122603
122631
  loading: "正在加载图片…",
@@ -122741,6 +122769,7 @@ img.ProseMirror-separator {
122741
122769
  saving: "Saving…",
122742
122770
  save: "Save",
122743
122771
  saved: "Saved",
122772
+ settingsWriteRejected: "A setting could not be saved. Please check and try again.",
122744
122773
  savedToPath: "Saved to",
122745
122774
  generating: "Generating image…",
122746
122775
  loading: "Loading image…",
@@ -123057,19 +123086,40 @@ img.ProseMirror-separator {
123057
123086
  :has(.dsh-ig-gallery-page:not(.dsh-ig-page-in-sidebar)) [class*="widthHandle"],
123058
123087
  .root:has(.dsh-ig-gallery-page:not(.dsh-ig-page-in-sidebar)) [class*="widthHandle"]{display:none!important}
123059
123088
  `;
123060
- /** Required browser services. */
123089
+ /**
123090
+ * Required browser services. The settings form reader is deliberately absent:
123091
+ * DSH 0.1.7 serves it as `configForms` (keyed by the Loader entry id) while
123092
+ * 0.1.5/0.1.6 expose `settingsScope` (keyed by the legacy namespace), so it is
123093
+ * probed at runtime in `apply`. A static inject of either name would park this
123094
+ * fiber forever on hosts from the other generation — the issue #55 failure.
123095
+ */
123061
123096
  const inject = [
123062
123097
  "slots",
123063
123098
  "connection",
123064
123099
  "remote",
123065
- "settingsScope",
123066
123100
  "locale"
123067
123101
  ];
123102
+ /** Host with neither settings generation: reads empty, writes refuse, never throws. */
123103
+ function degradedScope() {
123104
+ return {
123105
+ getSnapshot: () => ({
123106
+ value: void 0,
123107
+ writable: false
123108
+ }),
123109
+ subscribe: () => () => {},
123110
+ set: async () => false
123111
+ };
123112
+ }
123068
123113
  /** Mount the settings card, generated-image card, and native conversation gallery view. */
123069
123114
  function apply(ctx) {
123070
- const scope = ctx.settingsScope.bind({ namespace: IMAGE_GENERATION_NAMESPACE });
123071
- const chatScope = ctx.settingsScope.bind({ namespace: "ui-chat" });
123072
- const isCompactTranscript = () => chatScope.getSnapshot().value?.transcriptView !== "normal";
123115
+ const configForms = ctx.get("configForms");
123116
+ const settingsScopeApi = configForms === void 0 ? ctx.get("settingsScope") : void 0;
123117
+ const scope = configForms?.get("image-gen") ?? settingsScopeApi?.bind({ namespace: "image-generation" }) ?? degradedScope();
123118
+ const chatScope = configForms?.get("ui-chat") ?? settingsScopeApi?.bind({ namespace: "ui-chat" }) ?? degradedScope();
123119
+ const isCompactTranscript = () => {
123120
+ const mode = chatScope.getSnapshot().value?.transcriptView;
123121
+ return configForms === void 0 ? mode !== "normal" : mode !== "standard" && mode !== "detailed" && mode !== "verbose";
123122
+ };
123073
123123
  const locale = ctx.get("locale");
123074
123124
  const promotion = { enabled: false };
123075
123125
  ctx.inject(["sessions"], (owner) => {
@@ -123562,6 +123612,7 @@ img.ProseMirror-separator {
123562
123612
  if (params) for (const [k, v] of Object.entries(params)) text = text.replace(`{${k}}`, v);
123563
123613
  return text;
123564
123614
  };
123615
+ const saveSetting = (field, value) => writeSetting(props.scope, field, value, t("settingsWriteRejected"));
123565
123616
  const providerLabels = {
123566
123617
  google: t("providerGoogle"),
123567
123618
  openai: t("providerOpenAI"),
@@ -123673,7 +123724,7 @@ img.ProseMirror-separator {
123673
123724
  setDefaultProvider(provider);
123674
123725
  setProviderMessage("");
123675
123726
  setProviderMessageIsError(false);
123676
- props.scope.set("provider", provider).catch((cause) => {
123727
+ saveSetting("provider", provider).catch((cause) => {
123677
123728
  setDefaultProvider(snapshot.value?.provider ?? "google");
123678
123729
  setProviderMessage(cause instanceof Error ? cause.message : String(cause));
123679
123730
  setProviderMessageIsError(true);
@@ -123699,12 +123750,12 @@ img.ProseMirror-separator {
123699
123750
  }
123700
123751
  if (new Set(entries.map((entry) => entry.name)).size !== entries.length) throw new Error(t("workflowDuplicateName"));
123701
123752
  const activeEntry = entries.find((entry) => entry.name === row.activeWorkflow) ?? entries[0];
123702
- await props.scope.set("comfyuiBaseURL", row.baseURL);
123703
- await props.scope.set("comfyuiWorkflows", entries);
123704
- await props.scope.set("comfyuiActiveWorkflow", activeEntry === void 0 ? "" : activeEntry.name);
123705
- await props.scope.set("comfyuiWorkflowJson", activeEntry === void 0 ? "" : activeEntry.json);
123706
- await props.scope.set("comfyuiWorkflowName", activeEntry === void 0 ? "" : activeEntry.name);
123707
- await props.scope.set("comfyuiTimeoutMs", Math.max(1, Math.round(row.timeoutSeconds)) * 1e3);
123753
+ await saveSetting("comfyuiBaseURL", row.baseURL);
123754
+ await saveSetting("comfyuiWorkflows", entries);
123755
+ await saveSetting("comfyuiActiveWorkflow", activeEntry === void 0 ? "" : activeEntry.name);
123756
+ await saveSetting("comfyuiWorkflowJson", activeEntry === void 0 ? "" : activeEntry.json);
123757
+ await saveSetting("comfyuiWorkflowName", activeEntry === void 0 ? "" : activeEntry.name);
123758
+ await saveSetting("comfyuiTimeoutMs", Math.max(1, Math.round(row.timeoutSeconds)) * 1e3);
123708
123759
  } else if (isSubscriptionProvider(provider)) await Promise.resolve();
123709
123760
  else {
123710
123761
  let editExtra = {};
@@ -123721,16 +123772,16 @@ img.ProseMirror-separator {
123721
123772
  editExtra = parsed;
123722
123773
  }
123723
123774
  }
123724
- await props.scope.set(modelFieldOf(provider), row.model);
123725
- await props.scope.set(baseURLFieldOf(provider), row.baseURL);
123775
+ await saveSetting(modelFieldOf(provider), row.model);
123776
+ await saveSetting(baseURLFieldOf(provider), row.baseURL);
123726
123777
  if (provider === "openai-compat") {
123727
- await props.scope.set("openaiCompatEditFormat", row.editFormat);
123728
- await props.scope.set("openaiCompatEditExtra", editExtra);
123778
+ await saveSetting("openaiCompatEditFormat", row.editFormat);
123779
+ await saveSetting("openaiCompatEditExtra", editExtra);
123729
123780
  }
123730
123781
  if (provider === "seedream") {
123731
- await props.scope.set("seedreamOutputFormat", row.outputFormat);
123732
- await props.scope.set("seedreamWatermark", row.watermark);
123733
- await props.scope.set("seedreamBackground", row.background);
123782
+ await saveSetting("seedreamOutputFormat", row.outputFormat);
123783
+ await saveSetting("seedreamWatermark", row.watermark);
123784
+ await saveSetting("seedreamBackground", row.background);
123734
123785
  }
123735
123786
  if (row.keyInput.trim().length > 0) {
123736
123787
  const keyRef = cloudCredentialRef(provider);
@@ -124021,7 +124072,7 @@ img.ProseMirror-separator {
124021
124072
  setSaveToWorkspace(next);
124022
124073
  setWorkspaceMessage("");
124023
124074
  setWorkspaceMessageIsError(false);
124024
- props.scope.set("saveToWorkspace", next).catch((cause) => {
124075
+ saveSetting("saveToWorkspace", next).catch((cause) => {
124025
124076
  setSaveToWorkspace(snapshot.value?.saveToWorkspace ?? true);
124026
124077
  setWorkspaceMessage(cause instanceof Error ? cause.message : String(cause));
124027
124078
  setWorkspaceMessageIsError(true);
@@ -124033,7 +124084,7 @@ img.ProseMirror-separator {
124033
124084
  if (next === (snapshot.value?.workspaceFolder ?? "dsh-image-gen")) return;
124034
124085
  setWorkspaceMessage("");
124035
124086
  setWorkspaceMessageIsError(false);
124036
- props.scope.set("workspaceFolder", next).then(() => {
124087
+ saveSetting("workspaceFolder", next).then(() => {
124037
124088
  setWorkspaceMessage(t("saved"));
124038
124089
  }).catch((cause) => {
124039
124090
  setWorkspaceFolder(snapshot.value?.workspaceFolder ?? "dsh-image-gen");
@@ -124046,7 +124097,7 @@ img.ProseMirror-separator {
124046
124097
  setShowPill(next);
124047
124098
  setUiMessage("");
124048
124099
  setUiMessageIsError(false);
124049
- props.scope.set("showProviderPill", next).catch((cause) => {
124100
+ saveSetting("showProviderPill", next).catch((cause) => {
124050
124101
  setShowPill(snapshot.value?.showProviderPill === true);
124051
124102
  setUiMessage(cause instanceof Error ? cause.message : String(cause));
124052
124103
  setUiMessageIsError(true);