dsh-image-gen 0.6.1 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +381 -345
- package/README.md +381 -345
- package/README.zh-CN.md +3 -3
- package/docs/assets/readme/canvas-edit.webp +0 -0
- package/docs/assets/readme/canvas-generate.webp +0 -0
- package/docs/assets/readme/hero-en.webp +0 -0
- package/docs/assets/readme/hero-zh.webp +0 -0
- package/docs/assets/readme/workflow-overview-en.webp +0 -0
- package/docs/assets/readme/workflow-overview.webp +0 -0
- package/lib/client.js +1046 -374
- package/lib/client.js.map +1 -1
- package/lib/index.js +1421 -103
- package/lib/types/client/index.d.ts +4 -0
- package/lib/types/client/inspiration-api.d.ts +10 -0
- package/lib/types/client/inspiration-detail.d.ts +14 -0
- package/lib/types/client/provider-pill.d.ts +1 -1
- package/lib/types/client/tl/canvas-sync.d.ts +3 -3
- package/lib/types/client/tl/conversation-landings.d.ts +33 -0
- package/lib/types/client/tl/studio-tl-canvas.d.ts +2 -0
- package/lib/types/client/tl/tl-canvas-bridge.d.ts +12 -32
- package/lib/types/client/tldraw/generated/tldraw-assets.d.ts +12 -0
- package/lib/types/client/tldraw/generated/tldraw-css.d.ts +1 -0
- package/lib/types/client/tldraw/tldraw-style.d.ts +1 -0
- package/lib/types/client/tldraw/tldraw-workspace.d.ts +1 -0
- package/lib/types/config.d.ts +24 -0
- package/lib/types/inspiration/cache.d.ts +20 -0
- package/lib/types/inspiration/downloader.d.ts +27 -0
- package/lib/types/inspiration/routes.d.ts +13 -0
- package/lib/types/inspiration/sources.d.ts +33 -0
- package/lib/types/inspiration/types.d.ts +65 -0
- package/lib/types/inspiration/updater.d.ts +7 -0
- package/lib/types/openai-compatible.d.ts +15 -0
- package/lib/types/shared.d.ts +16 -1
- package/lib/types/subscription/blob.d.ts +23 -0
- package/lib/types/subscription/loopback.d.ts +11 -0
- package/lib/types/subscription/manager.d.ts +94 -0
- package/lib/types/subscription/oauth.d.ts +25 -0
- package/lib/types/subscription/subscription-route.d.ts +5 -0
- package/lib/types/subscription/vendors/antigravity.d.ts +66 -0
- package/lib/types/subscription/vendors/codex.d.ts +30 -0
- package/lib/types/subscription/vendors/grok.d.ts +32 -0
- package/lib/types/subscription.d.ts +42 -0
- package/lib/types/test-route.d.ts +9 -0
- package/package.json +8 -10
- package/skills/install-dsh-image-gen/SKILL.md +22 -22
- package/lib/types/client/studio-chat-panel.d.ts +0 -23
- package/lib/types/studio-chat.d.ts +0 -48
package/lib/client.js
CHANGED
|
@@ -87,6 +87,8 @@ window.__ModuleLoader__.load({
|
|
|
87
87
|
const TEST_CONNECTION_ROUTE = "/plugins/dsh-image-gen/test";
|
|
88
88
|
/** Browser route the workbench infinite canvas pushes live state through. */
|
|
89
89
|
const CANVAS_STATE_ROUTE = "/plugins/dsh-image-gen/canvas-state";
|
|
90
|
+
const SUBSCRIPTION_LOGIN_ROUTE = "/plugins/dsh-image-gen/subscription-login";
|
|
91
|
+
const SUBSCRIPTION_STATUS_ROUTE = "/plugins/dsh-image-gen/subscription-status";
|
|
90
92
|
/** Namespace persisted through DSH Settings. */
|
|
91
93
|
const IMAGE_GENERATION_NAMESPACE = "image-generation";
|
|
92
94
|
/** Supported providers. */
|
|
@@ -98,8 +100,25 @@ window.__ModuleLoader__.load({
|
|
|
98
100
|
"dashscope",
|
|
99
101
|
"xai",
|
|
100
102
|
"zhipu",
|
|
101
|
-
"comfyui"
|
|
103
|
+
"comfyui",
|
|
104
|
+
"chatgpt-sub",
|
|
105
|
+
"grok-sub",
|
|
106
|
+
"google-sub"
|
|
102
107
|
];
|
|
108
|
+
/**
|
|
109
|
+
* Providers that generate through a logged-in subscription account instead
|
|
110
|
+
* of an API key. They use no credential reference and never join the
|
|
111
|
+
* cloud/BYOK sets.
|
|
112
|
+
*/
|
|
113
|
+
const SUBSCRIPTION_PROVIDERS = [
|
|
114
|
+
"chatgpt-sub",
|
|
115
|
+
"grok-sub",
|
|
116
|
+
"google-sub"
|
|
117
|
+
];
|
|
118
|
+
/** True when the provider generates through a logged-in subscription account. */
|
|
119
|
+
function isSubscriptionProvider(provider) {
|
|
120
|
+
return SUBSCRIPTION_PROVIDERS.includes(provider);
|
|
121
|
+
}
|
|
103
122
|
/** Providers supported by the first browser workbench release. */
|
|
104
123
|
const CLOUD_IMAGE_PROVIDERS = [
|
|
105
124
|
"google",
|
|
@@ -184,6 +203,12 @@ window.__ModuleLoader__.load({
|
|
|
184
203
|
if (!existing.includes(candidate)) return candidate;
|
|
185
204
|
}
|
|
186
205
|
}
|
|
206
|
+
/** Default models for the subscription channels; fixed by the bridge protocol. */
|
|
207
|
+
const DEFAULT_SUBSCRIPTION_MODELS = {
|
|
208
|
+
"chatgpt-sub": "gpt-image-2.5-flare",
|
|
209
|
+
"grok-sub": "grok-imagine-image-2.0",
|
|
210
|
+
"google-sub": "gemini-3-pro-image"
|
|
211
|
+
};
|
|
187
212
|
const DEFAULT_MODELS = {
|
|
188
213
|
google: DEFAULT_GOOGLE_MODEL,
|
|
189
214
|
openai: DEFAULT_OPENAI_MODEL,
|
|
@@ -192,7 +217,10 @@ window.__ModuleLoader__.load({
|
|
|
192
217
|
dashscope: DEFAULT_DASHSCOPE_MODEL,
|
|
193
218
|
xai: DEFAULT_XAI_MODEL,
|
|
194
219
|
zhipu: DEFAULT_ZHIPU_MODEL,
|
|
195
|
-
comfyui: DEFAULT_COMFYUI_WORKFLOW_LABEL
|
|
220
|
+
comfyui: DEFAULT_COMFYUI_WORKFLOW_LABEL,
|
|
221
|
+
"chatgpt-sub": DEFAULT_SUBSCRIPTION_MODELS["chatgpt-sub"],
|
|
222
|
+
"grok-sub": DEFAULT_SUBSCRIPTION_MODELS["grok-sub"],
|
|
223
|
+
"google-sub": DEFAULT_SUBSCRIPTION_MODELS["google-sub"]
|
|
196
224
|
};
|
|
197
225
|
const DEFAULT_BASE_URLS = {
|
|
198
226
|
google: DEFAULT_GOOGLE_ENDPOINT,
|
|
@@ -202,7 +230,10 @@ window.__ModuleLoader__.load({
|
|
|
202
230
|
dashscope: DEFAULT_DASHSCOPE_ENDPOINT,
|
|
203
231
|
xai: DEFAULT_XAI_BASE_URL,
|
|
204
232
|
zhipu: DEFAULT_ZHIPU_BASE_URL,
|
|
205
|
-
comfyui: DEFAULT_COMFYUI_BASE_URL
|
|
233
|
+
comfyui: DEFAULT_COMFYUI_BASE_URL,
|
|
234
|
+
"chatgpt-sub": "",
|
|
235
|
+
"grok-sub": "",
|
|
236
|
+
"google-sub": ""
|
|
206
237
|
};
|
|
207
238
|
//#endregion
|
|
208
239
|
//#region lib/types/comfyui-workflow.js
|
|
@@ -312,9 +343,9 @@ window.__ModuleLoader__.load({
|
|
|
312
343
|
}
|
|
313
344
|
});
|
|
314
345
|
}
|
|
315
|
-
const listeners
|
|
346
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
316
347
|
function notifyListeners() {
|
|
317
|
-
for (const listener of listeners
|
|
348
|
+
for (const listener of listeners) try {
|
|
318
349
|
listener();
|
|
319
350
|
} catch (err) {
|
|
320
351
|
console.error("[dsh-image-gen] Gallery listener error:", err);
|
|
@@ -324,9 +355,9 @@ window.__ModuleLoader__.load({
|
|
|
324
355
|
* Subscribe to gallery mutations (insert/delete/clear).
|
|
325
356
|
*/
|
|
326
357
|
function subscribeGallery(listener) {
|
|
327
|
-
listeners
|
|
358
|
+
listeners.add(listener);
|
|
328
359
|
return () => {
|
|
329
|
-
listeners
|
|
360
|
+
listeners.delete(listener);
|
|
330
361
|
};
|
|
331
362
|
}
|
|
332
363
|
/**
|
|
@@ -1409,6 +1440,11 @@ window.__ModuleLoader__.load({
|
|
|
1409
1440
|
cache.delete(key);
|
|
1410
1441
|
}
|
|
1411
1442
|
}
|
|
1443
|
+
/** Clear all in-memory image caches. */
|
|
1444
|
+
function clearAttachmentCache() {
|
|
1445
|
+
cache.clear();
|
|
1446
|
+
currentCacheBytes = 0;
|
|
1447
|
+
}
|
|
1412
1448
|
function enforceLimits() {
|
|
1413
1449
|
while (cache.size > 30 || currentCacheBytes > 134217728) {
|
|
1414
1450
|
const oldestKey = cache.keys().next().value;
|
|
@@ -76011,14 +76047,14 @@ img.ProseMirror-separator {
|
|
|
76011
76047
|
});
|
|
76012
76048
|
var useReactId = react[" useId ".trim().toString()] || (() => void 0);
|
|
76013
76049
|
var count$1 = 0;
|
|
76014
|
-
function useId(deterministicId) {
|
|
76050
|
+
function useId$1(deterministicId) {
|
|
76015
76051
|
const [id, setId] = react.useState(useReactId());
|
|
76016
76052
|
useLayoutEffect2(() => {
|
|
76017
76053
|
if (!deterministicId) setId((reactId) => reactId ?? String(count$1++));
|
|
76018
76054
|
}, [deterministicId]);
|
|
76019
76055
|
return deterministicId || (id ? `radix-${id}` : "");
|
|
76020
76056
|
}
|
|
76021
|
-
__name$25(useId, "useId");
|
|
76057
|
+
__name$25(useId$1, "useId");
|
|
76022
76058
|
//#endregion
|
|
76023
76059
|
//#region node_modules/.pnpm/@radix-ui+react-direction@1_8310f062454398ef3b849e7d93575bcb/node_modules/@radix-ui/react-direction/dist/index.mjs
|
|
76024
76060
|
var __defProp$27 = Object.defineProperty;
|
|
@@ -78639,9 +78675,9 @@ img.ProseMirror-separator {
|
|
|
78639
78675
|
scope: __scopeDialog,
|
|
78640
78676
|
triggerRef,
|
|
78641
78677
|
contentRef,
|
|
78642
|
-
contentId: useId(),
|
|
78643
|
-
titleId: useId(),
|
|
78644
|
-
descriptionId: useId(),
|
|
78678
|
+
contentId: useId$1(),
|
|
78679
|
+
titleId: useId$1(),
|
|
78680
|
+
descriptionId: useId$1(),
|
|
78645
78681
|
titlePresent: titleCount > 0,
|
|
78646
78682
|
descriptionPresent: descriptionCount > 0,
|
|
78647
78683
|
setTitleCount,
|
|
@@ -80913,7 +80949,7 @@ img.ProseMirror-separator {
|
|
|
80913
80949
|
var ITEM_NAME$3 = "RovingFocusGroupItem";
|
|
80914
80950
|
var RovingFocusGroupItem = /* @__PURE__ */ react.forwardRef(/* @__PURE__ */ __name$13(function RovingFocusGroupItem2(props, forwardedRef) {
|
|
80915
80951
|
const { __scopeRovingFocusGroup, focusable = true, active = false, tabStopId, children, ...itemProps } = props;
|
|
80916
|
-
const autoId = useId();
|
|
80952
|
+
const autoId = useId$1();
|
|
80917
80953
|
const id = tabStopId || autoId;
|
|
80918
80954
|
const context = useRovingFocusContext(ITEM_NAME$3, __scopeRovingFocusGroup);
|
|
80919
80955
|
const isCurrentTabStop = context.currentTabStopId === id;
|
|
@@ -81472,8 +81508,8 @@ img.ProseMirror-separator {
|
|
|
81472
81508
|
onContentChange: setContent,
|
|
81473
81509
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSubProvider, {
|
|
81474
81510
|
scope: __scopeMenu,
|
|
81475
|
-
contentId: useId(),
|
|
81476
|
-
triggerId: useId(),
|
|
81511
|
+
contentId: useId$1(),
|
|
81512
|
+
triggerId: useId$1(),
|
|
81477
81513
|
trigger,
|
|
81478
81514
|
onTriggerChange: setTrigger,
|
|
81479
81515
|
children
|
|
@@ -81931,9 +81967,9 @@ img.ProseMirror-separator {
|
|
|
81931
81967
|
});
|
|
81932
81968
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuProvider, {
|
|
81933
81969
|
scope: __scopeDropdownMenu,
|
|
81934
|
-
triggerId: useId(),
|
|
81970
|
+
triggerId: useId$1(),
|
|
81935
81971
|
triggerRef,
|
|
81936
|
-
contentId: useId(),
|
|
81972
|
+
contentId: useId$1(),
|
|
81937
81973
|
open,
|
|
81938
81974
|
onOpenChange: setOpen,
|
|
81939
81975
|
onOpenToggle: react.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
@@ -82165,7 +82201,7 @@ img.ProseMirror-separator {
|
|
|
82165
82201
|
...popperScope,
|
|
82166
82202
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverProvider, {
|
|
82167
82203
|
scope: __scopePopover,
|
|
82168
|
-
contentId: useId(),
|
|
82204
|
+
contentId: useId$1(),
|
|
82169
82205
|
triggerRef,
|
|
82170
82206
|
open,
|
|
82171
82207
|
onOpenChange: setOpen,
|
|
@@ -82397,7 +82433,7 @@ img.ProseMirror-separator {
|
|
|
82397
82433
|
]);
|
|
82398
82434
|
const isFormControl = trigger ? !!form || !!trigger.closest("form") : true;
|
|
82399
82435
|
const [nativeOptionsSet, setNativeOptionsSet] = react.useState(/* @__PURE__ */ new Set());
|
|
82400
|
-
const contentId = useId();
|
|
82436
|
+
const contentId = useId$1();
|
|
82401
82437
|
const nativeSelectKey = Array.from(nativeOptionsSet).map((option) => option.props.value).join(";");
|
|
82402
82438
|
const handleNativeOptionAdd = react.useCallback((option) => {
|
|
82403
82439
|
setNativeOptionsSet((prev) => new Set(prev).add(option));
|
|
@@ -83966,7 +84002,7 @@ img.ProseMirror-separator {
|
|
|
83966
84002
|
const popperScope = usePopperScope(__scopeTooltip);
|
|
83967
84003
|
const [trigger, setTrigger] = react.useState(null);
|
|
83968
84004
|
const [contentIdState, setContentId] = react.useState(void 0);
|
|
83969
|
-
const generatedContentId = useId();
|
|
84005
|
+
const generatedContentId = useId$1();
|
|
83970
84006
|
const openTimerRef = react.useRef(0);
|
|
83971
84007
|
const disableHoverableContent = disableHoverableContentProp ?? providerContext.disableHoverableContent;
|
|
83972
84008
|
const delayDuration = delayDurationProp ?? providerContext.delayDuration;
|
|
@@ -108087,7 +108123,7 @@ img.ProseMirror-separator {
|
|
|
108087
108123
|
}
|
|
108088
108124
|
const MAX_SIZE = 256;
|
|
108089
108125
|
const alphaCache = new LruCache(100);
|
|
108090
|
-
const pending$
|
|
108126
|
+
const pending$2 = /* @__PURE__ */ new Set();
|
|
108091
108127
|
let offscreenCanvas = null;
|
|
108092
108128
|
function getOffscreenCanvas(w, h) {
|
|
108093
108129
|
if (!offscreenCanvas) offscreenCanvas = new OffscreenCanvas(w, h);
|
|
@@ -108107,12 +108143,12 @@ img.ProseMirror-separator {
|
|
|
108107
108143
|
function preloadAlphaData(url, cacheKey) {
|
|
108108
108144
|
if (typeof OffscreenCanvas === "undefined") return;
|
|
108109
108145
|
const key = cacheKey ?? url;
|
|
108110
|
-
if (alphaCache.has(key) || pending$
|
|
108111
|
-
pending$
|
|
108146
|
+
if (alphaCache.has(key) || pending$2.has(key)) return;
|
|
108147
|
+
pending$2.add(key);
|
|
108112
108148
|
const img = Image$1();
|
|
108113
108149
|
img.crossOrigin = "anonymous";
|
|
108114
108150
|
img.onload = async () => {
|
|
108115
|
-
pending$
|
|
108151
|
+
pending$2.delete(key);
|
|
108116
108152
|
const { width: origW, height: origH } = img;
|
|
108117
108153
|
if (origW === 0 || origH === 0) return;
|
|
108118
108154
|
const scale = Math.min(1, MAX_SIZE / Math.max(origW, origH));
|
|
@@ -108139,7 +108175,7 @@ img.ProseMirror-separator {
|
|
|
108139
108175
|
});
|
|
108140
108176
|
};
|
|
108141
108177
|
img.onerror = () => {
|
|
108142
|
-
pending$
|
|
108178
|
+
pending$2.delete(key);
|
|
108143
108179
|
};
|
|
108144
108180
|
img.src = url;
|
|
108145
108181
|
}
|
|
@@ -115591,43 +115627,6 @@ img.ProseMirror-separator {
|
|
|
115591
115627
|
});
|
|
115592
115628
|
}
|
|
115593
115629
|
//#endregion
|
|
115594
|
-
//#region lib/types/client/tl/tl-canvas-bridge.js
|
|
115595
|
-
const items = [];
|
|
115596
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
115597
|
-
/** Queue freshly generated images for the tldraw canvases. Safe to call anywhere. */
|
|
115598
|
-
function pushTlLandings(newItems) {
|
|
115599
|
-
if (newItems.length === 0) return;
|
|
115600
|
-
const known = new Set(items.map((item) => item.galleryId));
|
|
115601
|
-
let pushed = 0;
|
|
115602
|
-
for (const item of newItems) {
|
|
115603
|
-
if (known.has(item.galleryId)) continue;
|
|
115604
|
-
items.push(item);
|
|
115605
|
-
known.add(item.galleryId);
|
|
115606
|
-
pushed += 1;
|
|
115607
|
-
}
|
|
115608
|
-
if (pushed === 0) return;
|
|
115609
|
-
for (const listener of listeners) listener();
|
|
115610
|
-
}
|
|
115611
|
-
/**
|
|
115612
|
-
* Read every item landed so far in this browser session.
|
|
115613
|
-
*
|
|
115614
|
-
* The list is the in-memory session-scratch record shared by all canvases:
|
|
115615
|
-
* reading never removes items, so a canvas mounted later (or a second canvas
|
|
115616
|
-
* mounted beside the first) reconciles the same set. The list dies with the
|
|
115617
|
-
* page, which is exactly the "unsaved content does not survive a restart"
|
|
115618
|
-
* semantic the studio canvas keeps.
|
|
115619
|
-
*/
|
|
115620
|
-
function getTlLandings() {
|
|
115621
|
-
return items;
|
|
115622
|
-
}
|
|
115623
|
-
/** Subscribe to queue pushes. Returns an unsubscribe function. */
|
|
115624
|
-
function subscribeTlLandings(listener) {
|
|
115625
|
-
listeners.add(listener);
|
|
115626
|
-
return () => {
|
|
115627
|
-
listeners.delete(listener);
|
|
115628
|
-
};
|
|
115629
|
-
}
|
|
115630
|
-
//#endregion
|
|
115631
115630
|
//#region lib/types/client/tl/canvas-sync.js
|
|
115632
115631
|
/** Debounce for ordinary document/session changes. */
|
|
115633
115632
|
const STATE_DEBOUNCE_MS = 600;
|
|
@@ -115677,21 +115676,18 @@ img.ProseMirror-separator {
|
|
|
115677
115676
|
* the selection identity list, so a selected node reads exactly like its
|
|
115678
115677
|
* canvas node twin (same name, attachment id, dimensions).
|
|
115679
115678
|
*/
|
|
115680
|
-
function describeShape(editor, shape
|
|
115679
|
+
function describeShape(editor, shape) {
|
|
115681
115680
|
const node = { kind: kindOf(shape.type) };
|
|
115682
115681
|
if (shape.type === "image") {
|
|
115683
115682
|
const meta = shape.meta;
|
|
115684
115683
|
const galleryId = meta?.galleryId;
|
|
115685
|
-
if (typeof galleryId === "string" && galleryId.length > 0)
|
|
115686
|
-
|
|
115687
|
-
const attachmentId = attachmentByGallery.get(galleryId);
|
|
115688
|
-
if (attachmentId !== void 0) node.attachmentId = attachmentId;
|
|
115689
|
-
}
|
|
115684
|
+
if (typeof galleryId === "string" && galleryId.length > 0) node.galleryId = galleryId;
|
|
115685
|
+
if (typeof meta?.attachmentId === "string" && meta.attachmentId.length > 0) node.attachmentId = meta.attachmentId;
|
|
115690
115686
|
if (typeof meta?.prompt === "string" && meta.prompt.trim().length > 0) node.prompt = meta.prompt.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
115691
115687
|
if (typeof meta?.provider === "string" && meta.provider.length > 0) node.provider = meta.provider;
|
|
115692
115688
|
if (typeof meta?.model === "string" && meta.model.length > 0) node.model = meta.model;
|
|
115693
115689
|
const asset = shape.props.assetId === null ? void 0 : editor.getAsset(shape.props.assetId);
|
|
115694
|
-
const assetName = asset !== void 0 && asset.type === "image" ? asset.props.name : void 0;
|
|
115690
|
+
const assetName = typeof meta?.name === "string" && meta.name.length > 0 ? meta.name : asset !== void 0 && asset.type === "image" ? asset.props.name : void 0;
|
|
115695
115691
|
if (typeof assetName === "string" && assetName.length > 0) node.name = assetName.slice(0, 128);
|
|
115696
115692
|
if (asset !== void 0 && asset.type === "image") {
|
|
115697
115693
|
node.width = Math.round(asset.props.w);
|
|
@@ -115714,31 +115710,19 @@ img.ProseMirror-separator {
|
|
|
115714
115710
|
return node;
|
|
115715
115711
|
}
|
|
115716
115712
|
function summarize(editor) {
|
|
115717
|
-
const attachmentByGallery = /* @__PURE__ */ new Map();
|
|
115718
|
-
const conversationGallery = /* @__PURE__ */ new Set();
|
|
115719
|
-
for (const landing of getTlLandings()) {
|
|
115720
|
-
attachmentByGallery.set(landing.galleryId, String(landing.attachment.attachmentId));
|
|
115721
|
-
if (landing.fromConversation) conversationGallery.add(landing.galleryId);
|
|
115722
|
-
}
|
|
115723
115713
|
const shapes = editor.getCurrentPageShapes();
|
|
115724
115714
|
const nodes = [];
|
|
115725
|
-
for (const shape of shapes.slice(0, 48)) nodes.push(describeShape(editor, shape
|
|
115715
|
+
for (const shape of shapes.slice(0, 48)) nodes.push(describeShape(editor, shape));
|
|
115726
115716
|
const selected = editor.getSelectedShapes();
|
|
115727
115717
|
const selectionKinds = [...new Set(selected.map((shape) => kindOf(shape.type)))].slice(0, 8);
|
|
115728
|
-
const selectionItems = selected.slice(0, 16).map((shape) => describeShape(editor, shape
|
|
115718
|
+
const selectionItems = selected.slice(0, 16).map((shape) => describeShape(editor, shape));
|
|
115729
115719
|
const selectionKey = selectionKeyOf(editor);
|
|
115730
|
-
const selectionAllConversationImages = selected.length > 0 && selected.every((shape) => {
|
|
115731
|
-
if (shape.type !== "image") return false;
|
|
115732
|
-
const galleryId = shape.meta?.galleryId;
|
|
115733
|
-
return typeof galleryId === "string" && conversationGallery.has(galleryId);
|
|
115734
|
-
});
|
|
115735
115720
|
return {
|
|
115736
115721
|
nodeCount: shapes.length,
|
|
115737
115722
|
nodes,
|
|
115738
115723
|
selectionCount: selected.length,
|
|
115739
115724
|
selectionKinds,
|
|
115740
115725
|
selectionItems,
|
|
115741
|
-
selectionAllConversationImages,
|
|
115742
115726
|
stateKey: JSON.stringify([
|
|
115743
115727
|
shapes.length,
|
|
115744
115728
|
nodes,
|
|
@@ -115805,7 +115789,7 @@ img.ProseMirror-separator {
|
|
|
115805
115789
|
return;
|
|
115806
115790
|
}
|
|
115807
115791
|
let selectionImage;
|
|
115808
|
-
if (withScreenshot && summary.selectionCount > 0
|
|
115792
|
+
if (withScreenshot && summary.selectionCount > 0) try {
|
|
115809
115793
|
const selectedBefore = editor.getSelectedShapes();
|
|
115810
115794
|
const { url } = await editor.toImageDataUrl(selectedBefore, {
|
|
115811
115795
|
background: true,
|
|
@@ -115813,6 +115797,7 @@ img.ProseMirror-separator {
|
|
|
115813
115797
|
scale: 1,
|
|
115814
115798
|
pixelRatio: 2
|
|
115815
115799
|
});
|
|
115800
|
+
if (disposed) return;
|
|
115816
115801
|
const selectedAfter = editor.getSelectedShapes();
|
|
115817
115802
|
const idsBefore = new Set(selectedBefore.map((shape) => String(shape.id)));
|
|
115818
115803
|
if (!(selectedBefore.length === selectedAfter.length && selectedAfter.every((shape) => idsBefore.has(String(shape.id))))) return;
|
|
@@ -115855,6 +115840,10 @@ img.ProseMirror-separator {
|
|
|
115855
115840
|
stateTimer = void 0;
|
|
115856
115841
|
flush(false);
|
|
115857
115842
|
}, STATE_DEBOUNCE_MS);
|
|
115843
|
+
if (editor.getSelectedShapeIds().length > 0) exportTimer = setTimeout(() => {
|
|
115844
|
+
exportTimer = void 0;
|
|
115845
|
+
flush(true);
|
|
115846
|
+
}, SELECTION_EXPORT_DELAY_MS);
|
|
115858
115847
|
return () => {
|
|
115859
115848
|
if (disposed) return;
|
|
115860
115849
|
disposed = true;
|
|
@@ -115877,25 +115866,81 @@ img.ProseMirror-separator {
|
|
|
115877
115866
|
};
|
|
115878
115867
|
}
|
|
115879
115868
|
//#endregion
|
|
115869
|
+
//#region lib/types/client/tl/tl-canvas-bridge.js
|
|
115870
|
+
const pending$1 = /* @__PURE__ */ new Map();
|
|
115871
|
+
const consumers = /* @__PURE__ */ new Set();
|
|
115872
|
+
let delivering = false;
|
|
115873
|
+
let generation = 0;
|
|
115874
|
+
/** Invalidates async landings that started before the user cleared the canvas. */
|
|
115875
|
+
function getTlLandingGeneration() {
|
|
115876
|
+
return generation;
|
|
115877
|
+
}
|
|
115878
|
+
function clearTlLandings() {
|
|
115879
|
+
generation += 1;
|
|
115880
|
+
pending$1.clear();
|
|
115881
|
+
}
|
|
115882
|
+
/** Queue freshly generated images for the tldraw canvases. Safe to call anywhere. */
|
|
115883
|
+
function pushTlLandings(newItems) {
|
|
115884
|
+
for (const item of newItems) if (!pending$1.has(item.galleryId)) pending$1.set(item.galleryId, item);
|
|
115885
|
+
deliver();
|
|
115886
|
+
}
|
|
115887
|
+
/** Conversation results are admitted only while a canvas is mounted. */
|
|
115888
|
+
function pushTlLandingsLive(items) {
|
|
115889
|
+
if (consumers.size > 0) pushTlLandings(items);
|
|
115890
|
+
}
|
|
115891
|
+
/** Register a ready editor. False means it unmounted before committing. */
|
|
115892
|
+
function registerTlLandingConsumer(consumer) {
|
|
115893
|
+
consumers.add(consumer);
|
|
115894
|
+
deliver();
|
|
115895
|
+
return () => {
|
|
115896
|
+
consumers.delete(consumer);
|
|
115897
|
+
if (consumers.size === 0) {
|
|
115898
|
+
for (const [id, item] of pending$1) if (item.fromConversation) pending$1.delete(id);
|
|
115899
|
+
}
|
|
115900
|
+
};
|
|
115901
|
+
}
|
|
115902
|
+
async function deliver() {
|
|
115903
|
+
if (delivering) return;
|
|
115904
|
+
delivering = true;
|
|
115905
|
+
try {
|
|
115906
|
+
while (pending$1.size > 0) {
|
|
115907
|
+
const consumer = consumers.values().next().value;
|
|
115908
|
+
if (consumer === void 0) break;
|
|
115909
|
+
const batch = [...pending$1.values()];
|
|
115910
|
+
let completed = true;
|
|
115911
|
+
try {
|
|
115912
|
+
completed = await consumer(batch);
|
|
115913
|
+
} catch (error) {
|
|
115914
|
+
console.warn("[dsh-image-gen] canvas landing failed:", error);
|
|
115915
|
+
}
|
|
115916
|
+
if (completed) {
|
|
115917
|
+
for (const item of batch) if (pending$1.get(item.galleryId) === item) pending$1.delete(item.galleryId);
|
|
115918
|
+
}
|
|
115919
|
+
}
|
|
115920
|
+
} finally {
|
|
115921
|
+
delivering = false;
|
|
115922
|
+
}
|
|
115923
|
+
}
|
|
115924
|
+
//#endregion
|
|
115880
115925
|
//#region lib/types/client/tl/studio-tl-canvas.js
|
|
115881
115926
|
/**
|
|
115882
115927
|
* Infinite canvas surface for the Studio workbench, backed by tldraw.
|
|
115883
115928
|
*
|
|
115884
|
-
*
|
|
115885
|
-
*
|
|
115886
|
-
* image assets with data-URL sources. The bus broadcasts, so this surface
|
|
115887
|
-
* RECONCILES instead of draining: it lands the bus items its own page does
|
|
115888
|
-
* not carry yet. The conversation-view gallery tab and the right-sidebar
|
|
115889
|
-
* studio tab can both be mounted, and each mirrors the full session-scratch
|
|
115890
|
-
* set independently. The surface deliberately runs WITHOUT persistenceKey:
|
|
115891
|
-
* unsaved generations vanish on restart, matching the plugin's save-first
|
|
115892
|
-
* philosophy; data-URL assets therefore never touch disk. Shapes carry
|
|
115893
|
-
* `meta.galleryId` so re-landing the same generation is a no-op. One batch
|
|
115894
|
-
* = one createShapes transaction = one undo step.
|
|
115929
|
+
* Both surfaces share one locally persisted document. The landing queue is
|
|
115930
|
+
* consumed once; tldraw owns subsequent edits, deletion, undo and persistence.
|
|
115895
115931
|
*/
|
|
115896
115932
|
const MAX_DISPLAY_SIDE = 380;
|
|
115897
115933
|
const GRID_GAP = 40;
|
|
115898
115934
|
const GRID_COLS = 3;
|
|
115935
|
+
const mountedCanvases = /* @__PURE__ */ new Set();
|
|
115936
|
+
/** Reset canvas-owned data only. Gallery records and host attachments are untouched. */
|
|
115937
|
+
function clearStudioTlCanvases() {
|
|
115938
|
+
if (mountedCanvases.size === 0) return false;
|
|
115939
|
+
clearTlLandings();
|
|
115940
|
+
for (const clear of mountedCanvases) clear();
|
|
115941
|
+
clearAttachmentCache();
|
|
115942
|
+
return true;
|
|
115943
|
+
}
|
|
115899
115944
|
/**
|
|
115900
115945
|
* Dock the stock toolbar vertically along the left edge (Figma-style) instead
|
|
115901
115946
|
* of tldraw's signature bottom-center pill. It is still the same
|
|
@@ -115917,11 +115962,11 @@ img.ProseMirror-separator {
|
|
|
115917
115962
|
h: Math.round(height * scale)
|
|
115918
115963
|
};
|
|
115919
115964
|
}
|
|
115920
|
-
/**
|
|
115965
|
+
/** Dedupe across the whole document, including images moved to other pages. */
|
|
115921
115966
|
function landedIdsOf(editor) {
|
|
115922
115967
|
const landedIds = /* @__PURE__ */ new Set();
|
|
115923
|
-
for (const shape of editor.
|
|
115924
|
-
if (shape.type !== "image") continue;
|
|
115968
|
+
for (const shape of editor.store.allRecords()) {
|
|
115969
|
+
if (shape.typeName !== "shape" || shape.type !== "image") continue;
|
|
115925
115970
|
const galleryId = shape.meta?.galleryId;
|
|
115926
115971
|
if (typeof galleryId === "string") landedIds.add(galleryId);
|
|
115927
115972
|
}
|
|
@@ -115933,13 +115978,14 @@ img.ProseMirror-separator {
|
|
|
115933
115978
|
const collapsed = prompt.replace(/\s+/g, " ").trim();
|
|
115934
115979
|
return collapsed.length === 0 ? void 0 : collapsed.slice(0, 120);
|
|
115935
115980
|
}
|
|
115936
|
-
async function landTlItems(editor, items,
|
|
115981
|
+
async function landTlItems(editor, items, isActive) {
|
|
115937
115982
|
const landedIds = landedIdsOf(editor);
|
|
115938
115983
|
const pending = items.filter((item) => !landedIds.has(item.galleryId));
|
|
115939
|
-
if (pending.length === 0) return;
|
|
115984
|
+
if (pending.length === 0) return true;
|
|
115940
115985
|
const assets = [];
|
|
115941
115986
|
const landed = [];
|
|
115942
115987
|
for (const item of pending) {
|
|
115988
|
+
if (!isActive()) return false;
|
|
115943
115989
|
const assetId = tlAssetIdFor(item.galleryId);
|
|
115944
115990
|
if (editor.getAsset(assetId) === void 0) try {
|
|
115945
115991
|
const blob = await fetchAttachmentBlob(item.attachment);
|
|
@@ -115960,7 +116006,6 @@ img.ProseMirror-separator {
|
|
|
115960
116006
|
}
|
|
115961
116007
|
});
|
|
115962
116008
|
} catch (error) {
|
|
115963
|
-
failedIds.add(item.galleryId);
|
|
115964
116009
|
console.warn("[dsh-image-gen] canvas landing skipped (attachment unreadable):", item.galleryId, error);
|
|
115965
116010
|
continue;
|
|
115966
116011
|
}
|
|
@@ -115969,6 +116014,9 @@ img.ProseMirror-separator {
|
|
|
115969
116014
|
landed.push({
|
|
115970
116015
|
galleryId: item.galleryId,
|
|
115971
116016
|
assetId,
|
|
116017
|
+
attachmentId: String(item.attachment.attachmentId),
|
|
116018
|
+
name: item.attachment.name ?? item.galleryId,
|
|
116019
|
+
fromConversation: item.fromConversation === true,
|
|
115972
116020
|
w: size.w,
|
|
115973
116021
|
h: size.h,
|
|
115974
116022
|
...prompt !== void 0 ? { prompt } : {},
|
|
@@ -115976,10 +116024,13 @@ img.ProseMirror-separator {
|
|
|
115976
116024
|
...typeof item.model === "string" && item.model.length > 0 ? { model: item.model } : {}
|
|
115977
116025
|
});
|
|
115978
116026
|
}
|
|
115979
|
-
if (
|
|
116027
|
+
if (!isActive()) return false;
|
|
116028
|
+
const nowLanded = landedIdsOf(editor);
|
|
116029
|
+
const ready = landed.filter((item) => !nowLanded.has(item.galleryId));
|
|
116030
|
+
if (ready.length === 0) return true;
|
|
115980
116031
|
if (assets.length > 0) editor.createAssets(assets);
|
|
115981
116032
|
const rows = [];
|
|
115982
|
-
for (let index = 0; index <
|
|
116033
|
+
for (let index = 0; index < ready.length; index += GRID_COLS) rows.push(ready.slice(index, index + GRID_COLS));
|
|
115983
116034
|
const rowSizes = rows.map((row) => ({
|
|
115984
116035
|
width: row.reduce((sum, cell) => sum + cell.w, 0) + GRID_GAP * (row.length - 1),
|
|
115985
116036
|
height: Math.max(...row.map((cell) => cell.h))
|
|
@@ -116014,7 +116065,7 @@ img.ProseMirror-separator {
|
|
|
116014
116065
|
let cursorX = originX + (blockWidth - rowSize.width) / 2;
|
|
116015
116066
|
for (const cell of row) {
|
|
116016
116067
|
shapes.push({
|
|
116017
|
-
id: createShapeId(),
|
|
116068
|
+
id: createShapeId(`ig-${cell.galleryId}`),
|
|
116018
116069
|
type: "image",
|
|
116019
116070
|
x: Math.round(cursorX),
|
|
116020
116071
|
y: Math.round(cursorY),
|
|
@@ -116025,6 +116076,9 @@ img.ProseMirror-separator {
|
|
|
116025
116076
|
},
|
|
116026
116077
|
meta: {
|
|
116027
116078
|
galleryId: cell.galleryId,
|
|
116079
|
+
attachmentId: cell.attachmentId,
|
|
116080
|
+
name: cell.name,
|
|
116081
|
+
fromConversation: cell.fromConversation,
|
|
116028
116082
|
...cell.prompt !== void 0 ? { prompt: cell.prompt } : {},
|
|
116029
116083
|
...cell.provider !== void 0 ? { provider: cell.provider } : {},
|
|
116030
116084
|
...cell.model !== void 0 ? { model: cell.model } : {}
|
|
@@ -116050,42 +116104,53 @@ img.ProseMirror-separator {
|
|
|
116050
116104
|
inset: GRID_GAP
|
|
116051
116105
|
});
|
|
116052
116106
|
}
|
|
116107
|
+
return true;
|
|
116053
116108
|
}
|
|
116054
116109
|
const StudioTlCanvas = (0, react.memo)(function StudioTlCanvas() {
|
|
116055
|
-
const
|
|
116056
|
-
const landingRef = (0, react.useRef)(false);
|
|
116057
|
-
/** Per-canvas attachment misses, so one bad blob cannot spin the reconcile. */
|
|
116058
|
-
const failedRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
116059
|
-
const processQueue = (0, react.useCallback)(() => {
|
|
116060
|
-
const editor = editorRef.current;
|
|
116061
|
-
if (editor === null) return;
|
|
116062
|
-
if (landingRef.current) return;
|
|
116063
|
-
const landedIds = landedIdsOf(editor);
|
|
116064
|
-
const items = getTlLandings().filter((item) => !landedIds.has(item.galleryId) && !failedRef.current.has(item.galleryId));
|
|
116065
|
-
if (items.length === 0) return;
|
|
116066
|
-
landingRef.current = true;
|
|
116067
|
-
landTlItems(editor, items, failedRef.current).catch((error) => {
|
|
116068
|
-
console.warn("[dsh-image-gen] tldraw canvas landing failed:", error);
|
|
116069
|
-
}).finally(() => {
|
|
116070
|
-
landingRef.current = false;
|
|
116071
|
-
processQueue();
|
|
116072
|
-
});
|
|
116073
|
-
}, []);
|
|
116074
|
-
(0, react.useEffect)(() => subscribeTlLandings(processQueue), [processQueue]);
|
|
116110
|
+
const sessionId = (0, react.useId)();
|
|
116075
116111
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
116076
116112
|
className: "dsh-ig-tl-canvas",
|
|
116077
116113
|
children: (0, react_jsx_runtime.jsx)(Tldraw, {
|
|
116114
|
+
persistenceKey: "dsh-image-gen-workbench-v1",
|
|
116115
|
+
sessionId,
|
|
116078
116116
|
components: { Toolbar: StudioToolbar },
|
|
116079
116117
|
onMount: (editor) => {
|
|
116080
|
-
|
|
116118
|
+
let active = true;
|
|
116081
116119
|
applyBrandTheme(editor);
|
|
116082
116120
|
editor.updateInstanceState({ isGridMode: true });
|
|
116083
116121
|
console.info(`[dsh-image-gen] tldraw mounted (instance ${editor.id})`);
|
|
116084
|
-
|
|
116085
|
-
|
|
116122
|
+
const stopLanding = registerTlLandingConsumer((items) => {
|
|
116123
|
+
const generation = getTlLandingGeneration();
|
|
116124
|
+
return landTlItems(editor, items, () => active && generation === getTlLandingGeneration());
|
|
116125
|
+
});
|
|
116126
|
+
let stopSync = startCanvasSync(editor);
|
|
116127
|
+
const clear = () => {
|
|
116128
|
+
stopSync();
|
|
116129
|
+
editor.complete();
|
|
116130
|
+
editor.run(() => {
|
|
116131
|
+
editor.selectNone();
|
|
116132
|
+
const pages = editor.getPages();
|
|
116133
|
+
for (const page of pages) editor.deleteShapes([...editor.getPageShapeIds(page.id)]);
|
|
116134
|
+
for (const page of pages.slice(1)) editor.deletePage(page.id);
|
|
116135
|
+
editor.deleteAssets(editor.getAssets());
|
|
116136
|
+
editor.setCamera({
|
|
116137
|
+
x: 0,
|
|
116138
|
+
y: 0,
|
|
116139
|
+
z: 1
|
|
116140
|
+
});
|
|
116141
|
+
}, {
|
|
116142
|
+
history: "ignore",
|
|
116143
|
+
ignoreShapeLock: true
|
|
116144
|
+
});
|
|
116145
|
+
editor.clearHistory();
|
|
116146
|
+
stopSync = startCanvasSync(editor);
|
|
116147
|
+
};
|
|
116148
|
+
mountedCanvases.add(clear);
|
|
116086
116149
|
return () => {
|
|
116150
|
+
active = false;
|
|
116151
|
+
mountedCanvases.delete(clear);
|
|
116152
|
+
stopLanding();
|
|
116087
116153
|
stopSync();
|
|
116088
|
-
editorRef.current = null;
|
|
116089
116154
|
};
|
|
116090
116155
|
}
|
|
116091
116156
|
})
|
|
@@ -116196,7 +116261,10 @@ img.ProseMirror-separator {
|
|
|
116196
116261
|
expandGenerate: "展开生成面板",
|
|
116197
116262
|
generatePanel: "生成面板",
|
|
116198
116263
|
findInspiration: "找灵感",
|
|
116199
|
-
infiniteCanvasHint: "无限画布与侧边栏会话联动,才是完整功能。打开 DSH 右侧边栏的「图像工作台」即可解锁。"
|
|
116264
|
+
infiniteCanvasHint: "无限画布与侧边栏会话联动,才是完整功能。打开 DSH 右侧边栏的「图像工作台」即可解锁。",
|
|
116265
|
+
clearCanvas: "清空画布",
|
|
116266
|
+
clearCanvasDescription: "将永久清空所有画布页面的内容、图片资产和撤销历史。画廊已保存的卡片及原图不受影响,此操作无法撤销。",
|
|
116267
|
+
canvasCleared: "画布已清空,画廊不受影响"
|
|
116200
116268
|
},
|
|
116201
116269
|
en: {
|
|
116202
116270
|
title: "Cloud Image Studio",
|
|
@@ -116298,7 +116366,10 @@ img.ProseMirror-separator {
|
|
|
116298
116366
|
expandGenerate: "Expand generate panel",
|
|
116299
116367
|
generatePanel: "Generate",
|
|
116300
116368
|
findInspiration: "Find inspiration",
|
|
116301
|
-
infiniteCanvasHint: "The infinite canvas is at its best when linked with the sidebar conversation. Open Image Studio from the DSH right sidebar to unlock the full experience."
|
|
116369
|
+
infiniteCanvasHint: "The infinite canvas is at its best when linked with the sidebar conversation. Open Image Studio from the DSH right sidebar to unlock the full experience.",
|
|
116370
|
+
clearCanvas: "Clear canvas",
|
|
116371
|
+
clearCanvasDescription: "Permanently clear all canvas pages, image assets, and undo history. Saved gallery cards and original images are unaffected. This cannot be undone.",
|
|
116372
|
+
canvasCleared: "Canvas cleared. Gallery unchanged."
|
|
116302
116373
|
}
|
|
116303
116374
|
};
|
|
116304
116375
|
/** localStorage flag remembering that the one-time infinite-canvas hint was dismissed. */
|
|
@@ -116346,6 +116417,15 @@ img.ProseMirror-separator {
|
|
|
116346
116417
|
const dragStartRef = (0, react.useRef)(null);
|
|
116347
116418
|
const hasDraggedRef = (0, react.useRef)(false);
|
|
116348
116419
|
const [canvasSurface, setCanvasSurface] = (0, react.useState)(initialCanvasSurface ?? "preview");
|
|
116420
|
+
const [showClearCanvasModal, setShowClearCanvasModal] = (0, react.useState)(false);
|
|
116421
|
+
(0, react.useEffect)(() => {
|
|
116422
|
+
if (!showClearCanvasModal) return;
|
|
116423
|
+
const close = (event) => {
|
|
116424
|
+
if (event.key === "Escape") setShowClearCanvasModal(false);
|
|
116425
|
+
};
|
|
116426
|
+
window.addEventListener("keydown", close);
|
|
116427
|
+
return () => window.removeEventListener("keydown", close);
|
|
116428
|
+
}, [showClearCanvasModal]);
|
|
116349
116429
|
/** Generate-panel state from before the infinite canvas was opened, so the
|
|
116350
116430
|
* panel is restored (not just forced open) when returning to preview. */
|
|
116351
116431
|
const generatePanelBeforeInfiniteRef = (0, react.useRef)(false);
|
|
@@ -117183,6 +117263,12 @@ img.ProseMirror-separator {
|
|
|
117183
117263
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
117184
117264
|
className: "dsh-ig-canvas-toolbar-right",
|
|
117185
117265
|
children: [
|
|
117266
|
+
canvasSurface === "infinite" && (0, react_jsx_runtime.jsxs)("button", {
|
|
117267
|
+
type: "button",
|
|
117268
|
+
onClick: () => setShowClearCanvasModal(true),
|
|
117269
|
+
title: t("clearCanvas"),
|
|
117270
|
+
children: [(0, react_jsx_runtime.jsx)(Trash2, { size: 14 }), (0, react_jsx_runtime.jsx)("span", { children: t("clearCanvas") })]
|
|
117271
|
+
}),
|
|
117186
117272
|
canvasSurface === "preview" && selected !== null && (0, react_jsx_runtime.jsxs)("button", {
|
|
117187
117273
|
type: "button",
|
|
117188
117274
|
onClick: startNew,
|
|
@@ -117789,6 +117875,57 @@ img.ProseMirror-separator {
|
|
|
117789
117875
|
})
|
|
117790
117876
|
]
|
|
117791
117877
|
}),
|
|
117878
|
+
showClearCanvasModal && (0, react_jsx_runtime.jsx)("div", {
|
|
117879
|
+
className: "dsh-ig-workbench-modal-backdrop",
|
|
117880
|
+
onClick: () => setShowClearCanvasModal(false),
|
|
117881
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
117882
|
+
className: "dsh-ig-workbench-modal-box",
|
|
117883
|
+
role: "dialog",
|
|
117884
|
+
"aria-modal": "true",
|
|
117885
|
+
"aria-label": t("clearCanvas"),
|
|
117886
|
+
onClick: (event) => event.stopPropagation(),
|
|
117887
|
+
children: [
|
|
117888
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
117889
|
+
className: "dsh-ig-workbench-modal-header",
|
|
117890
|
+
children: [(0, react_jsx_runtime.jsx)(TriangleAlert, {
|
|
117891
|
+
size: 20,
|
|
117892
|
+
color: "#dc2626"
|
|
117893
|
+
}), (0, react_jsx_runtime.jsx)("strong", { children: t("clearCanvas") })]
|
|
117894
|
+
}),
|
|
117895
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
117896
|
+
className: "dsh-ig-workbench-modal-desc",
|
|
117897
|
+
children: t("clearCanvasDescription")
|
|
117898
|
+
}),
|
|
117899
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
117900
|
+
className: "dsh-ig-workbench-modal-actions",
|
|
117901
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
117902
|
+
autoFocus: true,
|
|
117903
|
+
type: "button",
|
|
117904
|
+
className: "dsh-ig-workbench-modal-cancel",
|
|
117905
|
+
onClick: () => setShowClearCanvasModal(false),
|
|
117906
|
+
children: t("cancel")
|
|
117907
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
117908
|
+
type: "button",
|
|
117909
|
+
className: "dsh-ig-workbench-modal-danger",
|
|
117910
|
+
onClick: () => {
|
|
117911
|
+
try {
|
|
117912
|
+
if (!clearStudioTlCanvases()) {
|
|
117913
|
+
flash(t("loading"));
|
|
117914
|
+
return;
|
|
117915
|
+
}
|
|
117916
|
+
setShowClearCanvasModal(false);
|
|
117917
|
+
flash(t("canvasCleared"));
|
|
117918
|
+
} catch (error) {
|
|
117919
|
+
setError(messageOf(error));
|
|
117920
|
+
setShowClearCanvasModal(false);
|
|
117921
|
+
}
|
|
117922
|
+
},
|
|
117923
|
+
children: t("clearCanvas")
|
|
117924
|
+
})]
|
|
117925
|
+
})
|
|
117926
|
+
]
|
|
117927
|
+
})
|
|
117928
|
+
}),
|
|
117792
117929
|
showDeleteModal && selected !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
117793
117930
|
className: "dsh-ig-workbench-modal-backdrop",
|
|
117794
117931
|
onClick: () => setShowDeleteModal(false),
|
|
@@ -120678,6 +120815,266 @@ img.ProseMirror-separator {
|
|
|
120678
120815
|
if (fromBlockContent?.type === "image") return fromBlockContent.attachment;
|
|
120679
120816
|
}
|
|
120680
120817
|
//#endregion
|
|
120818
|
+
//#region lib/types/client/image-result-node.js
|
|
120819
|
+
const IMAGE_RESULT_NODE_KIND = "dsh-image-result";
|
|
120820
|
+
/**
|
|
120821
|
+
* One image result row per Turn, anchored at its own tool/result position while
|
|
120822
|
+
* the Turn runs, then optionally re-anchored beside the final answer once the
|
|
120823
|
+
* Turn closes (only needed under the folding Compact transcript view).
|
|
120824
|
+
*/
|
|
120825
|
+
function createImageResultDefinition(options = {}) {
|
|
120826
|
+
return {
|
|
120827
|
+
kind: IMAGE_RESULT_NODE_KIND,
|
|
120828
|
+
target: "chat",
|
|
120829
|
+
match: (event) => {
|
|
120830
|
+
if (event.type === "tool/ptc-dispatch") {
|
|
120831
|
+
const subCallId = event.data.subCallId;
|
|
120832
|
+
if (typeof subCallId !== "string" || subCallId === "") return null;
|
|
120833
|
+
return imageResultFromPtcDispatch(event) === void 0 ? null : {
|
|
120834
|
+
id: `ptc:${subCallId}`,
|
|
120835
|
+
role: "start"
|
|
120836
|
+
};
|
|
120837
|
+
}
|
|
120838
|
+
const turn = eventTurn(event);
|
|
120839
|
+
if (turn === void 0) return null;
|
|
120840
|
+
if (event.type === "turn/start") return {
|
|
120841
|
+
id: String(turn),
|
|
120842
|
+
role: "start"
|
|
120843
|
+
};
|
|
120844
|
+
if (event.type === "turn/end") return {
|
|
120845
|
+
id: String(turn),
|
|
120846
|
+
role: "update"
|
|
120847
|
+
};
|
|
120848
|
+
if (event.type === "assistant/message") return {
|
|
120849
|
+
id: String(turn),
|
|
120850
|
+
role: "update"
|
|
120851
|
+
};
|
|
120852
|
+
if (event.type === "tool/result" && imageResultFromMeta(event.data.meta) !== void 0) return {
|
|
120853
|
+
id: String(turn),
|
|
120854
|
+
role: "update"
|
|
120855
|
+
};
|
|
120856
|
+
return null;
|
|
120857
|
+
},
|
|
120858
|
+
start: (_context, match) => {
|
|
120859
|
+
if (match.event.type === "tool/ptc-dispatch") {
|
|
120860
|
+
const result = imageResultFromPtcDispatch(match.event);
|
|
120861
|
+
if (result === void 0) throw new Error("dsh-image-result start requires a plugin image dispatch");
|
|
120862
|
+
return { results: [{
|
|
120863
|
+
...result,
|
|
120864
|
+
seq: match.event.seq
|
|
120865
|
+
}] };
|
|
120866
|
+
}
|
|
120867
|
+
const turn = eventTurn(match.event);
|
|
120868
|
+
if (match.event.type !== "turn/start" || turn === void 0) throw new Error("dsh-image-result start requires turn/start");
|
|
120869
|
+
return {
|
|
120870
|
+
turn,
|
|
120871
|
+
results: []
|
|
120872
|
+
};
|
|
120873
|
+
},
|
|
120874
|
+
update: (context, match) => {
|
|
120875
|
+
if (match.event.type === "turn/end") return {
|
|
120876
|
+
...context.state,
|
|
120877
|
+
endSeq: match.event.seq
|
|
120878
|
+
};
|
|
120879
|
+
if (match.event.type === "assistant/message") return {
|
|
120880
|
+
...context.state,
|
|
120881
|
+
answerSeq: match.event.seq
|
|
120882
|
+
};
|
|
120883
|
+
if (match.event.type === "tool/ptc-dispatch") {
|
|
120884
|
+
const result = imageResultFromPtcDispatch(match.event);
|
|
120885
|
+
return result === void 0 ? context.state : appendResult(context.state, result, match.event.seq);
|
|
120886
|
+
}
|
|
120887
|
+
if (match.event.type !== "tool/result") return context.state;
|
|
120888
|
+
const result = imageResultFromMeta(match.event.data.meta);
|
|
120889
|
+
if (result === void 0) return context.state;
|
|
120890
|
+
return appendResult(context.state, result, match.event.seq);
|
|
120891
|
+
},
|
|
120892
|
+
buildViewNode: (context) => {
|
|
120893
|
+
const state = context.state;
|
|
120894
|
+
const last = state?.results.at(-1);
|
|
120895
|
+
if (state === void 0 || last === void 0) return null;
|
|
120896
|
+
const location = context.matches.at(-1)?.location ?? context.start?.location ?? { kind: "unresolved" };
|
|
120897
|
+
return {
|
|
120898
|
+
key: context.key,
|
|
120899
|
+
kind: IMAGE_RESULT_NODE_KIND,
|
|
120900
|
+
id: context.id,
|
|
120901
|
+
target: "chat",
|
|
120902
|
+
anchorSeq: state.endSeq !== void 0 && compactTranscript(options) ? state.answerSeq ?? state.endSeq ?? last.seq : last.seq,
|
|
120903
|
+
location,
|
|
120904
|
+
visibility: "visible",
|
|
120905
|
+
data: {
|
|
120906
|
+
...state.turn !== void 0 ? { turn: state.turn } : {},
|
|
120907
|
+
results: state.results.map(({ seq: _seq, ...result }) => result)
|
|
120908
|
+
}
|
|
120909
|
+
};
|
|
120910
|
+
}
|
|
120911
|
+
};
|
|
120912
|
+
}
|
|
120913
|
+
/** Append a parsed result unless its attachment is already collected. */
|
|
120914
|
+
function appendResult(state, result, seq) {
|
|
120915
|
+
if (state.results.some((candidate) => candidate.attachment.attachmentId === result.attachment.attachmentId)) return state;
|
|
120916
|
+
return {
|
|
120917
|
+
...state,
|
|
120918
|
+
results: [...state.results, {
|
|
120919
|
+
...result,
|
|
120920
|
+
seq
|
|
120921
|
+
}]
|
|
120922
|
+
};
|
|
120923
|
+
}
|
|
120924
|
+
function compactTranscript(options) {
|
|
120925
|
+
try {
|
|
120926
|
+
return options.isCompactTranscript?.() ?? true;
|
|
120927
|
+
} catch {
|
|
120928
|
+
return true;
|
|
120929
|
+
}
|
|
120930
|
+
}
|
|
120931
|
+
/** Parse the plugin-owned durable presentation metadata from a Tool result event. */
|
|
120932
|
+
function imageResultFromMeta(value) {
|
|
120933
|
+
const meta = record(value);
|
|
120934
|
+
if (meta?.kind !== "dsh-image-gen") return void 0;
|
|
120935
|
+
const attachment = imageAttachment(meta.attachment);
|
|
120936
|
+
if (attachment === void 0) return void 0;
|
|
120937
|
+
return {
|
|
120938
|
+
attachment,
|
|
120939
|
+
prompt: stringValue(meta.prompt, "Generated Image"),
|
|
120940
|
+
provider: stringValue(meta.provider, "google"),
|
|
120941
|
+
model: stringValue(meta.model, ""),
|
|
120942
|
+
output: stringValue(meta.output, ""),
|
|
120943
|
+
...typeof meta.savedTo === "string" ? { savedTo: meta.savedTo } : {},
|
|
120944
|
+
...typeof meta.seed === "number" ? { seed: meta.seed } : {}
|
|
120945
|
+
};
|
|
120946
|
+
}
|
|
120947
|
+
function eventTurn(event) {
|
|
120948
|
+
const turn = event.data.turn;
|
|
120949
|
+
return typeof turn === "number" && Number.isSafeInteger(turn) && turn >= 0 ? turn : void 0;
|
|
120950
|
+
}
|
|
120951
|
+
/** Plugin tools whose PTC (run_code) dispatch results are image results. */
|
|
120952
|
+
const PTC_IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set(["generate_image", "edit_image"]);
|
|
120953
|
+
/**
|
|
120954
|
+
* Fixed summary text rendered by the tools' imageOutput(). PTC dispatch events
|
|
120955
|
+
* carry no presentationMeta, so provider/model/output are recovered from this
|
|
120956
|
+
* template rather than parsed out of natural language.
|
|
120957
|
+
*/
|
|
120958
|
+
const PTC_SUMMARY_PATTERN = /^(?:Generated|Edited) one image with ([^/\s]+)\/(\S+) \((.+)\)\. Attachment ID: /;
|
|
120959
|
+
const PTC_SAVED_TO_PATTERN = / It was also saved to the workspace as (.+)\. (?:(?:Saving it to the workspace failed)|(?:It has no local file path)|(?:Respond to the user))/;
|
|
120960
|
+
/**
|
|
120961
|
+
* Parse a plugin image result from a tool/ptc-dispatch event (#38). PTC
|
|
120962
|
+
* run_code sub-calls carry neither a turn nor presentationMeta, so the result
|
|
120963
|
+
* is rebuilt from the dispatch payload itself: the image content block for the
|
|
120964
|
+
* attachment, arguments for the prompt (and edit source ids), and the tool's
|
|
120965
|
+
* fixed-format summary text for provider/model/output. Returns undefined for
|
|
120966
|
+
* anything that is not a successful image result from our own image tools.
|
|
120967
|
+
*/
|
|
120968
|
+
function imageResultFromPtcDispatch(event) {
|
|
120969
|
+
if (event.type !== "tool/ptc-dispatch") return void 0;
|
|
120970
|
+
const data = event.data;
|
|
120971
|
+
if (data.isError === true) return void 0;
|
|
120972
|
+
if (typeof data.name !== "string" || !PTC_IMAGE_TOOL_NAMES.has(data.name)) return void 0;
|
|
120973
|
+
const content = Array.isArray(data.content) ? data.content : [];
|
|
120974
|
+
let attachment;
|
|
120975
|
+
let summaryText = "";
|
|
120976
|
+
for (const block of content) {
|
|
120977
|
+
const candidate = record(block);
|
|
120978
|
+
if (candidate === void 0) continue;
|
|
120979
|
+
if (candidate.type === "image" && attachment === void 0) attachment = imageAttachment(candidate.attachment);
|
|
120980
|
+
else if (candidate.type === "text" && summaryText === "" && typeof candidate.text === "string") summaryText = candidate.text;
|
|
120981
|
+
}
|
|
120982
|
+
if (attachment === void 0) return void 0;
|
|
120983
|
+
const summary = PTC_SUMMARY_PATTERN.exec(summaryText);
|
|
120984
|
+
const savedTo = PTC_SAVED_TO_PATTERN.exec(summaryText)?.[1];
|
|
120985
|
+
const args = record(data.arguments);
|
|
120986
|
+
const sourceIds = sourceAttachmentIds(args);
|
|
120987
|
+
return {
|
|
120988
|
+
attachment,
|
|
120989
|
+
prompt: stringValue(args?.prompt, "Generated Image"),
|
|
120990
|
+
provider: summary?.[1] ?? "",
|
|
120991
|
+
model: summary?.[2] ?? "",
|
|
120992
|
+
output: summary?.[3] ?? "",
|
|
120993
|
+
...savedTo !== void 0 ? { savedTo } : {},
|
|
120994
|
+
...sourceIds !== void 0 ? { sourceAttachmentIds: sourceIds } : {}
|
|
120995
|
+
};
|
|
120996
|
+
}
|
|
120997
|
+
/** Collect edit_image source attachment ids (single or list form) from dispatch arguments. */
|
|
120998
|
+
function sourceAttachmentIds(args) {
|
|
120999
|
+
if (args === void 0) return void 0;
|
|
121000
|
+
const single = typeof args.source_attachment_id === "string" ? [args.source_attachment_id] : void 0;
|
|
121001
|
+
const list = Array.isArray(args.source_attachment_ids) ? args.source_attachment_ids.filter((id) => typeof id === "string") : void 0;
|
|
121002
|
+
const ids = single ?? list;
|
|
121003
|
+
return ids !== void 0 && ids.length > 0 ? ids : void 0;
|
|
121004
|
+
}
|
|
121005
|
+
function imageAttachment(value) {
|
|
121006
|
+
const candidate = record(value);
|
|
121007
|
+
if (candidate === void 0 || typeof candidate.attachmentId !== "string" || !isImageMediaType(candidate.mediaType) || !positiveInteger(candidate.bytes) || !positiveInteger(candidate.width) || !positiveInteger(candidate.height)) return void 0;
|
|
121008
|
+
const original = record(candidate.originalDimensions);
|
|
121009
|
+
if (candidate.originalDimensions !== void 0 && (original === void 0 || !positiveInteger(original.width) || !positiveInteger(original.height))) return void 0;
|
|
121010
|
+
return {
|
|
121011
|
+
attachmentId: candidate.attachmentId,
|
|
121012
|
+
mediaType: candidate.mediaType,
|
|
121013
|
+
bytes: candidate.bytes,
|
|
121014
|
+
width: candidate.width,
|
|
121015
|
+
height: candidate.height,
|
|
121016
|
+
...typeof candidate.name === "string" ? { name: candidate.name } : {},
|
|
121017
|
+
...original === void 0 ? {} : { originalDimensions: {
|
|
121018
|
+
width: original.width,
|
|
121019
|
+
height: original.height
|
|
121020
|
+
} }
|
|
121021
|
+
};
|
|
121022
|
+
}
|
|
121023
|
+
function isImageMediaType(value) {
|
|
121024
|
+
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
121025
|
+
}
|
|
121026
|
+
function positiveInteger(value) {
|
|
121027
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
121028
|
+
}
|
|
121029
|
+
function stringValue(value, fallback) {
|
|
121030
|
+
return typeof value === "string" ? value : fallback;
|
|
121031
|
+
}
|
|
121032
|
+
function record(value) {
|
|
121033
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
121034
|
+
}
|
|
121035
|
+
//#endregion
|
|
121036
|
+
//#region lib/types/client/tl/conversation-landings.js
|
|
121037
|
+
/** Subscribe to live additions, never to card rendering or history replay. */
|
|
121038
|
+
function startConversationLandings(sessions) {
|
|
121039
|
+
let source;
|
|
121040
|
+
let stopEvents;
|
|
121041
|
+
const followCurrent = () => {
|
|
121042
|
+
const id = sessions.list.getSnapshot().current;
|
|
121043
|
+
const next = id === void 0 ? void 0 : sessions.binding(id)?.eventSource;
|
|
121044
|
+
if (next === source) return;
|
|
121045
|
+
stopEvents?.();
|
|
121046
|
+
source = next;
|
|
121047
|
+
if (next === void 0) return;
|
|
121048
|
+
let lastSeq = next.getSnapshot().entries.reduce((seq, entry) => Math.max(seq, entry.event.seq), -1);
|
|
121049
|
+
stopEvents = next.subscribe(() => {
|
|
121050
|
+
const change = next.getSnapshot().change;
|
|
121051
|
+
for (const entry of change.entries ?? []) {
|
|
121052
|
+
if (entry.type !== "event") continue;
|
|
121053
|
+
const { event } = entry;
|
|
121054
|
+
const isNew = event.seq > lastSeq;
|
|
121055
|
+
lastSeq = Math.max(lastSeq, event.seq);
|
|
121056
|
+
if (change.kind !== "append" || !isNew) continue;
|
|
121057
|
+
const result = event.type === "tool/result" ? imageResultFromMeta(event.data.meta) : imageResultFromPtcDispatch(event);
|
|
121058
|
+
if (result === void 0) continue;
|
|
121059
|
+
pushTlLandingsLive([{
|
|
121060
|
+
galleryId: String(result.attachment.attachmentId),
|
|
121061
|
+
attachment: result.attachment,
|
|
121062
|
+
fromConversation: true,
|
|
121063
|
+
prompt: result.prompt,
|
|
121064
|
+
provider: result.provider,
|
|
121065
|
+
model: result.model
|
|
121066
|
+
}]);
|
|
121067
|
+
}
|
|
121068
|
+
});
|
|
121069
|
+
};
|
|
121070
|
+
const stopList = sessions.list.subscribe(followCurrent);
|
|
121071
|
+
followCurrent();
|
|
121072
|
+
return () => {
|
|
121073
|
+
stopList();
|
|
121074
|
+
stopEvents?.();
|
|
121075
|
+
};
|
|
121076
|
+
}
|
|
121077
|
+
//#endregion
|
|
120681
121078
|
//#region lib/types/client/studio-style.js
|
|
120682
121079
|
const STUDIO_STYLE = `
|
|
120683
121080
|
.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}
|
|
@@ -121019,224 +121416,6 @@ img.ProseMirror-separator {
|
|
|
121019
121416
|
.dsh-ig-inspiration-lightbox{position:fixed;inset:0;z-index:10000;background:rgba(8,12,22,.88);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box}.dsh-ig-inspiration-lightbox-content{position:relative;max-width:90vw;max-height:90vh;display:flex;flex-direction:column;align-items:center}.dsh-ig-inspiration-lightbox-img-wrap{max-width:85vw;max-height:82vh;border-radius:10px;overflow:hidden;box-shadow:0 25px 60px rgba(0,0,0,.7);background:#05070d}.dsh-ig-inspiration-lightbox-img-wrap img{width:auto;height:auto;max-width:85vw;max-height:82vh;object-fit:contain;display:block}.dsh-ig-inspiration-lightbox-close{position:absolute;top:-16px;right:-16px;width:34px;height:34px;border-radius:50%;background:#1e293b;color:#fff;border:1px solid rgba(255,255,255,.2);cursor:pointer;display:grid;place-items:center;transition:background .15s,transform .15s;z-index:10}.dsh-ig-inspiration-lightbox-close:hover{background:#334155;transform:scale(1.1)}.dsh-ig-inspiration-lightbox-caption{margin-top:12px;color:rgba(255,255,255,.9);font-size:13px;font-weight:500;text-shadow:0 2px 4px rgba(0,0,0,.6)}
|
|
121020
121417
|
@media(max-width:980px){.dsh-ig-inspiration-layout{grid-template-columns:1fr}.dsh-ig-inspiration-inspector{position:relative;top:auto}.dsh-ig-inspiration-inspector-empty{min-height:150px}.dsh-ig-inspiration-inspector-image{max-height:290px}.dsh-ig-inspiration-inspector-image img{object-fit:contain;background:#f1f4fa}}@media(max-width:700px){.dsh-ig-inspiration{padding:14px}.dsh-ig-inspiration-head{align-items:center}.dsh-ig-inspiration-toolbar{grid-template-columns:1fr 1fr}.dsh-ig-inspiration-search{grid-column:1/-1}.dsh-ig-inspiration-fav-filter{grid-column:1/-1}.dsh-ig-inspiration-grid{grid-template-columns:repeat(auto-fill,minmax(145px,1fr));gap:9px}}@media(max-width:420px){.dsh-ig-inspiration-toolbar{grid-template-columns:1fr}.dsh-ig-inspiration-search{grid-column:auto}}
|
|
121021
121418
|
`;
|
|
121022
|
-
//#endregion
|
|
121023
|
-
//#region lib/types/client/image-result-node.js
|
|
121024
|
-
const IMAGE_RESULT_NODE_KIND = "dsh-image-result";
|
|
121025
|
-
/**
|
|
121026
|
-
* One image result row per Turn, anchored at its own tool/result position while
|
|
121027
|
-
* the Turn runs, then optionally re-anchored beside the final answer once the
|
|
121028
|
-
* Turn closes (only needed under the folding Compact transcript view).
|
|
121029
|
-
*/
|
|
121030
|
-
function createImageResultDefinition(options = {}) {
|
|
121031
|
-
return {
|
|
121032
|
-
kind: IMAGE_RESULT_NODE_KIND,
|
|
121033
|
-
target: "chat",
|
|
121034
|
-
match: (event) => {
|
|
121035
|
-
if (event.type === "tool/ptc-dispatch") {
|
|
121036
|
-
const subCallId = event.data.subCallId;
|
|
121037
|
-
if (typeof subCallId !== "string" || subCallId === "") return null;
|
|
121038
|
-
return imageResultFromPtcDispatch(event) === void 0 ? null : {
|
|
121039
|
-
id: `ptc:${subCallId}`,
|
|
121040
|
-
role: "start"
|
|
121041
|
-
};
|
|
121042
|
-
}
|
|
121043
|
-
const turn = eventTurn(event);
|
|
121044
|
-
if (turn === void 0) return null;
|
|
121045
|
-
if (event.type === "turn/start") return {
|
|
121046
|
-
id: String(turn),
|
|
121047
|
-
role: "start"
|
|
121048
|
-
};
|
|
121049
|
-
if (event.type === "turn/end") return {
|
|
121050
|
-
id: String(turn),
|
|
121051
|
-
role: "update"
|
|
121052
|
-
};
|
|
121053
|
-
if (event.type === "assistant/message") return {
|
|
121054
|
-
id: String(turn),
|
|
121055
|
-
role: "update"
|
|
121056
|
-
};
|
|
121057
|
-
if (event.type === "tool/result" && imageResultFromMeta(event.data.meta) !== void 0) return {
|
|
121058
|
-
id: String(turn),
|
|
121059
|
-
role: "update"
|
|
121060
|
-
};
|
|
121061
|
-
return null;
|
|
121062
|
-
},
|
|
121063
|
-
start: (_context, match) => {
|
|
121064
|
-
if (match.event.type === "tool/ptc-dispatch") {
|
|
121065
|
-
const result = imageResultFromPtcDispatch(match.event);
|
|
121066
|
-
if (result === void 0) throw new Error("dsh-image-result start requires a plugin image dispatch");
|
|
121067
|
-
return { results: [{
|
|
121068
|
-
...result,
|
|
121069
|
-
seq: match.event.seq
|
|
121070
|
-
}] };
|
|
121071
|
-
}
|
|
121072
|
-
const turn = eventTurn(match.event);
|
|
121073
|
-
if (match.event.type !== "turn/start" || turn === void 0) throw new Error("dsh-image-result start requires turn/start");
|
|
121074
|
-
return {
|
|
121075
|
-
turn,
|
|
121076
|
-
results: []
|
|
121077
|
-
};
|
|
121078
|
-
},
|
|
121079
|
-
update: (context, match) => {
|
|
121080
|
-
if (match.event.type === "turn/end") return {
|
|
121081
|
-
...context.state,
|
|
121082
|
-
endSeq: match.event.seq
|
|
121083
|
-
};
|
|
121084
|
-
if (match.event.type === "assistant/message") return {
|
|
121085
|
-
...context.state,
|
|
121086
|
-
answerSeq: match.event.seq
|
|
121087
|
-
};
|
|
121088
|
-
if (match.event.type === "tool/ptc-dispatch") {
|
|
121089
|
-
const result = imageResultFromPtcDispatch(match.event);
|
|
121090
|
-
return result === void 0 ? context.state : appendResult(context.state, result, match.event.seq);
|
|
121091
|
-
}
|
|
121092
|
-
if (match.event.type !== "tool/result") return context.state;
|
|
121093
|
-
const result = imageResultFromMeta(match.event.data.meta);
|
|
121094
|
-
if (result === void 0) return context.state;
|
|
121095
|
-
return appendResult(context.state, result, match.event.seq);
|
|
121096
|
-
},
|
|
121097
|
-
buildViewNode: (context) => {
|
|
121098
|
-
const state = context.state;
|
|
121099
|
-
const last = state?.results.at(-1);
|
|
121100
|
-
if (state === void 0 || last === void 0) return null;
|
|
121101
|
-
const location = context.matches.at(-1)?.location ?? context.start?.location ?? { kind: "unresolved" };
|
|
121102
|
-
return {
|
|
121103
|
-
key: context.key,
|
|
121104
|
-
kind: IMAGE_RESULT_NODE_KIND,
|
|
121105
|
-
id: context.id,
|
|
121106
|
-
target: "chat",
|
|
121107
|
-
anchorSeq: state.endSeq !== void 0 && compactTranscript(options) ? state.answerSeq ?? state.endSeq ?? last.seq : last.seq,
|
|
121108
|
-
location,
|
|
121109
|
-
visibility: "visible",
|
|
121110
|
-
data: {
|
|
121111
|
-
...state.turn !== void 0 ? { turn: state.turn } : {},
|
|
121112
|
-
results: state.results.map(({ seq: _seq, ...result }) => result)
|
|
121113
|
-
}
|
|
121114
|
-
};
|
|
121115
|
-
}
|
|
121116
|
-
};
|
|
121117
|
-
}
|
|
121118
|
-
/** Append a parsed result unless its attachment is already collected. */
|
|
121119
|
-
function appendResult(state, result, seq) {
|
|
121120
|
-
if (state.results.some((candidate) => candidate.attachment.attachmentId === result.attachment.attachmentId)) return state;
|
|
121121
|
-
return {
|
|
121122
|
-
...state,
|
|
121123
|
-
results: [...state.results, {
|
|
121124
|
-
...result,
|
|
121125
|
-
seq
|
|
121126
|
-
}]
|
|
121127
|
-
};
|
|
121128
|
-
}
|
|
121129
|
-
function compactTranscript(options) {
|
|
121130
|
-
try {
|
|
121131
|
-
return options.isCompactTranscript?.() ?? true;
|
|
121132
|
-
} catch {
|
|
121133
|
-
return true;
|
|
121134
|
-
}
|
|
121135
|
-
}
|
|
121136
|
-
/** Parse the plugin-owned durable presentation metadata from a Tool result event. */
|
|
121137
|
-
function imageResultFromMeta(value) {
|
|
121138
|
-
const meta = record(value);
|
|
121139
|
-
if (meta?.kind !== "dsh-image-gen") return void 0;
|
|
121140
|
-
const attachment = imageAttachment(meta.attachment);
|
|
121141
|
-
if (attachment === void 0) return void 0;
|
|
121142
|
-
return {
|
|
121143
|
-
attachment,
|
|
121144
|
-
prompt: stringValue(meta.prompt, "Generated Image"),
|
|
121145
|
-
provider: stringValue(meta.provider, "google"),
|
|
121146
|
-
model: stringValue(meta.model, ""),
|
|
121147
|
-
output: stringValue(meta.output, ""),
|
|
121148
|
-
...typeof meta.savedTo === "string" ? { savedTo: meta.savedTo } : {},
|
|
121149
|
-
...typeof meta.seed === "number" ? { seed: meta.seed } : {}
|
|
121150
|
-
};
|
|
121151
|
-
}
|
|
121152
|
-
function eventTurn(event) {
|
|
121153
|
-
const turn = event.data.turn;
|
|
121154
|
-
return typeof turn === "number" && Number.isSafeInteger(turn) && turn >= 0 ? turn : void 0;
|
|
121155
|
-
}
|
|
121156
|
-
/** Plugin tools whose PTC (run_code) dispatch results are image results. */
|
|
121157
|
-
const PTC_IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set(["generate_image", "edit_image"]);
|
|
121158
|
-
/**
|
|
121159
|
-
* Fixed summary text rendered by the tools' imageOutput(). PTC dispatch events
|
|
121160
|
-
* carry no presentationMeta, so provider/model/output are recovered from this
|
|
121161
|
-
* template rather than parsed out of natural language.
|
|
121162
|
-
*/
|
|
121163
|
-
const PTC_SUMMARY_PATTERN = /^(?:Generated|Edited) one image with ([^/\s]+)\/(\S+) \((.+)\)\. Attachment ID: /;
|
|
121164
|
-
const PTC_SAVED_TO_PATTERN = / It was also saved to the workspace as (.+)\. (?:(?:Saving it to the workspace failed)|(?:It has no local file path)|(?:Respond to the user))/;
|
|
121165
|
-
/**
|
|
121166
|
-
* Parse a plugin image result from a tool/ptc-dispatch event (#38). PTC
|
|
121167
|
-
* run_code sub-calls carry neither a turn nor presentationMeta, so the result
|
|
121168
|
-
* is rebuilt from the dispatch payload itself: the image content block for the
|
|
121169
|
-
* attachment, arguments for the prompt (and edit source ids), and the tool's
|
|
121170
|
-
* fixed-format summary text for provider/model/output. Returns undefined for
|
|
121171
|
-
* anything that is not a successful image result from our own image tools.
|
|
121172
|
-
*/
|
|
121173
|
-
function imageResultFromPtcDispatch(event) {
|
|
121174
|
-
if (event.type !== "tool/ptc-dispatch") return void 0;
|
|
121175
|
-
const data = event.data;
|
|
121176
|
-
if (data.isError === true) return void 0;
|
|
121177
|
-
if (typeof data.name !== "string" || !PTC_IMAGE_TOOL_NAMES.has(data.name)) return void 0;
|
|
121178
|
-
const content = Array.isArray(data.content) ? data.content : [];
|
|
121179
|
-
let attachment;
|
|
121180
|
-
let summaryText = "";
|
|
121181
|
-
for (const block of content) {
|
|
121182
|
-
const candidate = record(block);
|
|
121183
|
-
if (candidate === void 0) continue;
|
|
121184
|
-
if (candidate.type === "image" && attachment === void 0) attachment = imageAttachment(candidate.attachment);
|
|
121185
|
-
else if (candidate.type === "text" && summaryText === "" && typeof candidate.text === "string") summaryText = candidate.text;
|
|
121186
|
-
}
|
|
121187
|
-
if (attachment === void 0) return void 0;
|
|
121188
|
-
const summary = PTC_SUMMARY_PATTERN.exec(summaryText);
|
|
121189
|
-
const savedTo = PTC_SAVED_TO_PATTERN.exec(summaryText)?.[1];
|
|
121190
|
-
const args = record(data.arguments);
|
|
121191
|
-
const sourceIds = sourceAttachmentIds(args);
|
|
121192
|
-
return {
|
|
121193
|
-
attachment,
|
|
121194
|
-
prompt: stringValue(args?.prompt, "Generated Image"),
|
|
121195
|
-
provider: summary?.[1] ?? "",
|
|
121196
|
-
model: summary?.[2] ?? "",
|
|
121197
|
-
output: summary?.[3] ?? "",
|
|
121198
|
-
...savedTo !== void 0 ? { savedTo } : {},
|
|
121199
|
-
...sourceIds !== void 0 ? { sourceAttachmentIds: sourceIds } : {}
|
|
121200
|
-
};
|
|
121201
|
-
}
|
|
121202
|
-
/** Collect edit_image source attachment ids (single or list form) from dispatch arguments. */
|
|
121203
|
-
function sourceAttachmentIds(args) {
|
|
121204
|
-
if (args === void 0) return void 0;
|
|
121205
|
-
const single = typeof args.source_attachment_id === "string" ? [args.source_attachment_id] : void 0;
|
|
121206
|
-
const list = Array.isArray(args.source_attachment_ids) ? args.source_attachment_ids.filter((id) => typeof id === "string") : void 0;
|
|
121207
|
-
const ids = single ?? list;
|
|
121208
|
-
return ids !== void 0 && ids.length > 0 ? ids : void 0;
|
|
121209
|
-
}
|
|
121210
|
-
function imageAttachment(value) {
|
|
121211
|
-
const candidate = record(value);
|
|
121212
|
-
if (candidate === void 0 || typeof candidate.attachmentId !== "string" || !isImageMediaType(candidate.mediaType) || !positiveInteger(candidate.bytes) || !positiveInteger(candidate.width) || !positiveInteger(candidate.height)) return void 0;
|
|
121213
|
-
const original = record(candidate.originalDimensions);
|
|
121214
|
-
if (candidate.originalDimensions !== void 0 && (original === void 0 || !positiveInteger(original.width) || !positiveInteger(original.height))) return void 0;
|
|
121215
|
-
return {
|
|
121216
|
-
attachmentId: candidate.attachmentId,
|
|
121217
|
-
mediaType: candidate.mediaType,
|
|
121218
|
-
bytes: candidate.bytes,
|
|
121219
|
-
width: candidate.width,
|
|
121220
|
-
height: candidate.height,
|
|
121221
|
-
...typeof candidate.name === "string" ? { name: candidate.name } : {},
|
|
121222
|
-
...original === void 0 ? {} : { originalDimensions: {
|
|
121223
|
-
width: original.width,
|
|
121224
|
-
height: original.height
|
|
121225
|
-
} }
|
|
121226
|
-
};
|
|
121227
|
-
}
|
|
121228
|
-
function isImageMediaType(value) {
|
|
121229
|
-
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
121230
|
-
}
|
|
121231
|
-
function positiveInteger(value) {
|
|
121232
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
121233
|
-
}
|
|
121234
|
-
function stringValue(value, fallback) {
|
|
121235
|
-
return typeof value === "string" ? value : fallback;
|
|
121236
|
-
}
|
|
121237
|
-
function record(value) {
|
|
121238
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
121239
|
-
}
|
|
121240
121419
|
//#endregion
|
|
121241
121420
|
//#region lib/types/client/conversation-image-revisions.js
|
|
121242
121421
|
const STORAGE_PREFIX = "dsh_image_gen_conversation_revisions:";
|
|
@@ -121322,6 +121501,10 @@ img.ProseMirror-separator {
|
|
|
121322
121501
|
keyMissing: "未配置 Key",
|
|
121323
121502
|
noKeyNeeded: "无需 Key",
|
|
121324
121503
|
noWorkflow: "未导入工作流",
|
|
121504
|
+
subNoKey: "订阅生图",
|
|
121505
|
+
subSignedIn: "已登录",
|
|
121506
|
+
subSignedOut: "未登录",
|
|
121507
|
+
subStatusUnknown: "状态未知",
|
|
121325
121508
|
readOnly: "图像设置为只读,无法在此切换",
|
|
121326
121509
|
switchFailed: "切换失败"
|
|
121327
121510
|
},
|
|
@@ -121332,6 +121515,10 @@ img.ProseMirror-separator {
|
|
|
121332
121515
|
keyMissing: "No key",
|
|
121333
121516
|
noKeyNeeded: "No key needed",
|
|
121334
121517
|
noWorkflow: "No workflow",
|
|
121518
|
+
subNoKey: "Subscription",
|
|
121519
|
+
subSignedIn: "Signed in",
|
|
121520
|
+
subSignedOut: "Signed out",
|
|
121521
|
+
subStatusUnknown: "Unknown",
|
|
121335
121522
|
readOnly: "Image settings are read-only; switch them in the config source",
|
|
121336
121523
|
switchFailed: "Switch failed"
|
|
121337
121524
|
}
|
|
@@ -121345,8 +121532,25 @@ img.ProseMirror-separator {
|
|
|
121345
121532
|
dashscope: "DashScope",
|
|
121346
121533
|
xai: "Grok",
|
|
121347
121534
|
zhipu: "智谱 GLM",
|
|
121348
|
-
comfyui: "ComfyUI"
|
|
121349
|
-
|
|
121535
|
+
comfyui: "ComfyUI",
|
|
121536
|
+
"chatgpt-sub": "ChatGPT 订阅",
|
|
121537
|
+
"grok-sub": "Grok 订阅",
|
|
121538
|
+
"google-sub": "Google 订阅"
|
|
121539
|
+
};
|
|
121540
|
+
/** Compute the fixed-position anchor from the pill button: open upward when
|
|
121541
|
+
* the chat pane leaves room above the composer, drop down otherwise. */
|
|
121542
|
+
function anchorOf(button) {
|
|
121543
|
+
const rect = button?.getBoundingClientRect();
|
|
121544
|
+
if (rect === void 0) return void 0;
|
|
121545
|
+
if (rect.top >= 332) return {
|
|
121546
|
+
left: rect.left,
|
|
121547
|
+
bottom: window.innerHeight - rect.top + 6
|
|
121548
|
+
};
|
|
121549
|
+
return {
|
|
121550
|
+
left: rect.left,
|
|
121551
|
+
top: rect.bottom + 6
|
|
121552
|
+
};
|
|
121553
|
+
}
|
|
121350
121554
|
/** Whether the user opted into the composer pill; hidden (not merely inert) when off. */
|
|
121351
121555
|
function pillVisible(value) {
|
|
121352
121556
|
return value?.showProviderPill === true;
|
|
@@ -121357,6 +121561,7 @@ img.ProseMirror-separator {
|
|
|
121357
121561
|
const workflow = activeComfyUIWorkflow(value ?? {});
|
|
121358
121562
|
return workflow === void 0 ? "" : workflow.name;
|
|
121359
121563
|
}
|
|
121564
|
+
if (SUBSCRIPTION_PROVIDERS.includes(provider)) return DEFAULT_MODELS[provider];
|
|
121360
121565
|
const stored = provider === "google" ? value?.googleModel : provider === "openai" ? value?.openaiModel : provider === "openai-compat" ? value?.openaiCompatModel : provider === "seedream" ? value?.seedreamModel : provider === "dashscope" ? value?.dashscopeModel : provider === "xai" ? value?.xaiModel : value?.zhipuModel;
|
|
121361
121566
|
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_MODELS[provider];
|
|
121362
121567
|
}
|
|
@@ -121377,6 +121582,11 @@ img.ProseMirror-separator {
|
|
|
121377
121582
|
zhipu: "unknown"
|
|
121378
121583
|
});
|
|
121379
121584
|
const [keyTick, setKeyTick] = (0, react.useState)(0);
|
|
121585
|
+
const [subDots, setSubDots] = (0, react.useState)(() => ({
|
|
121586
|
+
"chatgpt-sub": "unknown",
|
|
121587
|
+
"grok-sub": "unknown",
|
|
121588
|
+
"google-sub": "unknown"
|
|
121589
|
+
}));
|
|
121380
121590
|
const buttonRef = (0, react.useRef)(null);
|
|
121381
121591
|
const [menuStyle, setMenuStyle] = (0, react.useState)(null);
|
|
121382
121592
|
(0, react.useEffect)(() => props.scope.subscribe(() => {
|
|
@@ -121419,6 +121629,31 @@ img.ProseMirror-separator {
|
|
|
121419
121629
|
active = false;
|
|
121420
121630
|
};
|
|
121421
121631
|
}, [props.credentials, keyTick]);
|
|
121632
|
+
(0, react.useEffect)(() => {
|
|
121633
|
+
if (!open) return;
|
|
121634
|
+
let active = true;
|
|
121635
|
+
const probe = async () => {
|
|
121636
|
+
try {
|
|
121637
|
+
const response = await fetch(SUBSCRIPTION_STATUS_ROUTE, {
|
|
121638
|
+
method: "POST",
|
|
121639
|
+
cache: "no-store"
|
|
121640
|
+
});
|
|
121641
|
+
if (!active || !response.ok) return;
|
|
121642
|
+
const payload = await response.json();
|
|
121643
|
+
if (!active || payload.statuses === void 0) return;
|
|
121644
|
+
const next = {};
|
|
121645
|
+
for (const provider of SUBSCRIPTION_PROVIDERS) {
|
|
121646
|
+
const row = payload.statuses[provider];
|
|
121647
|
+
next[provider] = row?.state === "logged-in" ? "logged-in" : row?.state === "logged-out" ? "logged-out" : "unknown";
|
|
121648
|
+
}
|
|
121649
|
+
setSubDots(next);
|
|
121650
|
+
} catch {}
|
|
121651
|
+
};
|
|
121652
|
+
probe();
|
|
121653
|
+
return () => {
|
|
121654
|
+
active = false;
|
|
121655
|
+
};
|
|
121656
|
+
}, [open, keyTick]);
|
|
121422
121657
|
const t = (keyName) => (lang === "en" ? PILL_DICT.en : PILL_DICT.zh)[keyName];
|
|
121423
121658
|
const current = snapshot.value?.provider ?? "google";
|
|
121424
121659
|
const writable = snapshot.writable;
|
|
@@ -121427,11 +121662,15 @@ img.ProseMirror-separator {
|
|
|
121427
121662
|
setMenuStyle(null);
|
|
121428
121663
|
return;
|
|
121429
121664
|
}
|
|
121430
|
-
const
|
|
121431
|
-
|
|
121432
|
-
|
|
121433
|
-
|
|
121434
|
-
|
|
121665
|
+
const anchor = () => {
|
|
121666
|
+
const next = anchorOf(buttonRef.current);
|
|
121667
|
+
if (next !== void 0) setMenuStyle(next);
|
|
121668
|
+
};
|
|
121669
|
+
anchor();
|
|
121670
|
+
window.addEventListener("resize", anchor);
|
|
121671
|
+
return () => {
|
|
121672
|
+
window.removeEventListener("resize", anchor);
|
|
121673
|
+
};
|
|
121435
121674
|
}, [open]);
|
|
121436
121675
|
(0, react.useEffect)(() => {
|
|
121437
121676
|
if (!open) return;
|
|
@@ -121442,8 +121681,15 @@ img.ProseMirror-separator {
|
|
|
121442
121681
|
const onKeyDown = (event) => {
|
|
121443
121682
|
if (event.key === "Escape") setOpen(false);
|
|
121444
121683
|
};
|
|
121445
|
-
const onReflow = () => {
|
|
121446
|
-
|
|
121684
|
+
const onReflow = (event) => {
|
|
121685
|
+
if (event.target instanceof Element && event.target.closest(".dsh-ig-pill-menu") !== null) return;
|
|
121686
|
+
const rect = buttonRef.current?.getBoundingClientRect();
|
|
121687
|
+
if (rect !== void 0 && (rect.bottom < 0 || rect.top > window.innerHeight)) {
|
|
121688
|
+
setOpen(false);
|
|
121689
|
+
return;
|
|
121690
|
+
}
|
|
121691
|
+
const next = anchorOf(buttonRef.current);
|
|
121692
|
+
if (next !== void 0) setMenuStyle(next);
|
|
121447
121693
|
};
|
|
121448
121694
|
document.addEventListener("mousedown", onPointerDown, true);
|
|
121449
121695
|
document.addEventListener("keydown", onKeyDown, true);
|
|
@@ -121473,8 +121719,12 @@ img.ProseMirror-separator {
|
|
|
121473
121719
|
setPending(void 0);
|
|
121474
121720
|
});
|
|
121475
121721
|
};
|
|
121722
|
+
/** Plan A dots: ComfyUI is always fixed blue (theme-independent); each
|
|
121723
|
+
* subscription reflects its login state (green in, gray out); cloud rows
|
|
121724
|
+
* reflect their API-key state as before. */
|
|
121476
121725
|
const dotClassOf = (provider) => {
|
|
121477
121726
|
if (provider === "comfyui") return "dsh-ig-pill-dot-neutral";
|
|
121727
|
+
if (isSubscriptionProvider(provider)) return subDots[provider] === "logged-in" ? "dsh-ig-pill-dot-ok" : "dsh-ig-pill-dot-missing";
|
|
121478
121728
|
const dot = dots[provider];
|
|
121479
121729
|
return dot === "configured" ? "dsh-ig-pill-dot-ok" : dot === "missing" ? "dsh-ig-pill-dot-missing" : "dsh-ig-pill-dot-neutral";
|
|
121480
121730
|
};
|
|
@@ -121483,6 +121733,11 @@ img.ProseMirror-separator {
|
|
|
121483
121733
|
const workflow = pillModelOf(provider, snapshot.value);
|
|
121484
121734
|
return workflow.length > 0 ? `${t("noKeyNeeded")} · ${workflow}` : `${t("noKeyNeeded")} · ${t("noWorkflow")}`;
|
|
121485
121735
|
}
|
|
121736
|
+
if (isSubscriptionProvider(provider)) {
|
|
121737
|
+
const state = subDots[provider];
|
|
121738
|
+
const stateText = state === "logged-in" ? t("subSignedIn") : state === "logged-out" ? t("subSignedOut") : t("subStatusUnknown");
|
|
121739
|
+
return `${t("subNoKey")} · ${stateText} · ${pillModelOf(provider, snapshot.value)}`;
|
|
121740
|
+
}
|
|
121486
121741
|
const dot = dots[provider];
|
|
121487
121742
|
const model = pillModelOf(provider, snapshot.value);
|
|
121488
121743
|
if (dot === "configured") return `${t("keyConfigured")} · ${model}`;
|
|
@@ -121635,8 +121890,8 @@ img.ProseMirror-separator {
|
|
|
121635
121890
|
.dsh-ig-pill-option-active{background:var(--dsw-alias-brand-primary-soft,rgba(76,120,255,.10))}
|
|
121636
121891
|
.dsh-ig-pill-dot{flex:none;width:7px;height:7px;border-radius:50%}
|
|
121637
121892
|
.dsh-ig-pill-dot-ok{background:#22c55e}
|
|
121638
|
-
.dsh-ig-pill-dot-missing{background
|
|
121639
|
-
.dsh-ig-pill-dot-neutral{background
|
|
121893
|
+
.dsh-ig-pill-dot-missing{background:#9ca3af}
|
|
121894
|
+
.dsh-ig-pill-dot-neutral{background:#4c78ff}
|
|
121640
121895
|
.dsh-ig-pill-option-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
|
|
121641
121896
|
.dsh-ig-pill-option-name{font-size:12.5px;font-weight:500;color:var(--dsw-alias-label-primary,#111827);line-height:1.2}
|
|
121642
121897
|
.dsh-ig-pill-option-sub{font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
@@ -121645,7 +121900,7 @@ img.ProseMirror-separator {
|
|
|
121645
121900
|
`;
|
|
121646
121901
|
//#endregion
|
|
121647
121902
|
//#region lib/types/client/index.js
|
|
121648
|
-
console.info(`[dsh-image-gen] canvas bundle 2026-09-
|
|
121903
|
+
console.info(`[dsh-image-gen] canvas bundle 2026-09-14T07:49:16.664Z`);
|
|
121649
121904
|
/** Right-sidebar tab-type registration id (DSH 0.1.5 `sidebarRightTabs`). */
|
|
121650
121905
|
const SIDEBAR_STUDIO_TAB_ID = "dsh-image-gen/studio";
|
|
121651
121906
|
/** The tab kind: a page type opened by kind, recognizing no resource address. */
|
|
@@ -121665,6 +121920,27 @@ img.ProseMirror-separator {
|
|
|
121665
121920
|
providerXAI: "xAI Grok Imagine",
|
|
121666
121921
|
providerZhipu: "智谱 GLM-Image",
|
|
121667
121922
|
providerComfyUI: "本地 ComfyUI",
|
|
121923
|
+
providerChatGPTSub: "ChatGPT 订阅",
|
|
121924
|
+
providerGrokSub: "Grok 订阅",
|
|
121925
|
+
providerGoogleSub: "Google 订阅",
|
|
121926
|
+
subBadgeLoggedIn: "已登录",
|
|
121927
|
+
subBadgeLoggedOut: "未登录",
|
|
121928
|
+
subBadgeUnknown: "状态未知",
|
|
121929
|
+
subSectionTitle: "订阅生图",
|
|
121930
|
+
subHint: "通过已登录的订阅账号生图,无需 API Key。登录在你的授权下进行,不会修改任何 API Key 或默认 Provider。",
|
|
121931
|
+
subAccountLabel: "账号",
|
|
121932
|
+
subAccountEmail: "已登录:{email}",
|
|
121933
|
+
subAccountNone: "未登录",
|
|
121934
|
+
subLogin: "登录",
|
|
121935
|
+
subLoggingIn: "正在登录…",
|
|
121936
|
+
subLogout: "退出登录",
|
|
121937
|
+
subLoggingOut: "正在退出…",
|
|
121938
|
+
subWaitingLogin: "已打开授权页面,请在浏览器中完成登录…",
|
|
121939
|
+
subLoginOk: "登录成功",
|
|
121940
|
+
subModelLabel: "订阅模型",
|
|
121941
|
+
subModelHint: "由订阅通道固定,不可更改。",
|
|
121942
|
+
subEditUnsupported: "订阅通道暂不支持图生图(编辑图片),仅支持文字生图。",
|
|
121943
|
+
subDefaultHint: "订阅 Provider 只能生成,不能编辑图片;需要编辑时请切换到 API Key Provider。",
|
|
121668
121944
|
apiKeyLabel: "{provider} API Key",
|
|
121669
121945
|
apiKeyPlaceholder: "留空即可保留已配置的 Key",
|
|
121670
121946
|
apiKeyHint: "安全保存为 {key};页面不会读回明文。",
|
|
@@ -121692,6 +121968,14 @@ img.ProseMirror-separator {
|
|
|
121692
121968
|
endpointHintGoogle: "Google 官方地址或反代端点(全路径)。",
|
|
121693
121969
|
endpointHintOpenAI: "官方 api.openai.com 的 /v1 地址;中转站请使用下方「OpenAI 兼容」行。",
|
|
121694
121970
|
endpointHintOpenAICompat: "中转站/自建服务的 OpenAI 兼容 /v1 地址(必填),例如 https://your-relay.example.com/v1。",
|
|
121971
|
+
editFormat: "图生图请求形态",
|
|
121972
|
+
editFormatMultipart: "标准 multipart(OpenAI 官方)",
|
|
121973
|
+
editFormatJsonImageUrlArray: "JSON images 数组(商汤等中转)",
|
|
121974
|
+
editFormatHint: "文生图不受影响。仅当中转站的图生图接口使用自有 JSON 契约(如商汤 SenseNova)时才需要切换。",
|
|
121975
|
+
editExtra: "附加 JSON 字段",
|
|
121976
|
+
editExtraPlaceholder: "{\"watermark\": false, \"prompt_extend\": true}",
|
|
121977
|
+
editExtraHint: "仅 JSON 形态生效;会合并到请求体末尾,可覆盖默认字段。留空表示不附加。",
|
|
121978
|
+
editExtraInvalid: "附加 JSON 字段必须是合法的 JSON 对象。",
|
|
121695
121979
|
endpointHintSeedream: "火山方舟兼容的 /api/v3 地址。",
|
|
121696
121980
|
endpointHintDashScope: "阿里云百炼 DashScope 官方接口地址。",
|
|
121697
121981
|
endpointHintXAI: "xAI 官方 api.x.ai 的 /v1 地址。",
|
|
@@ -121765,6 +122049,27 @@ img.ProseMirror-separator {
|
|
|
121765
122049
|
providerXAI: "xAI Grok Imagine",
|
|
121766
122050
|
providerZhipu: "Zhipu GLM-Image",
|
|
121767
122051
|
providerComfyUI: "Local ComfyUI",
|
|
122052
|
+
providerChatGPTSub: "ChatGPT Subscription",
|
|
122053
|
+
providerGrokSub: "Grok Subscription",
|
|
122054
|
+
providerGoogleSub: "Google Subscription",
|
|
122055
|
+
subBadgeLoggedIn: "Signed in",
|
|
122056
|
+
subBadgeLoggedOut: "Signed out",
|
|
122057
|
+
subBadgeUnknown: "Unknown",
|
|
122058
|
+
subSectionTitle: "Subscription generation",
|
|
122059
|
+
subHint: "Generates through a logged-in subscription account; no API key needed. Signing in never changes any API key or the default provider.",
|
|
122060
|
+
subAccountLabel: "Account",
|
|
122061
|
+
subAccountEmail: "Signed in: {email}",
|
|
122062
|
+
subAccountNone: "Not signed in",
|
|
122063
|
+
subLogin: "Sign in",
|
|
122064
|
+
subLoggingIn: "Signing in…",
|
|
122065
|
+
subLogout: "Sign out",
|
|
122066
|
+
subLoggingOut: "Signing out…",
|
|
122067
|
+
subWaitingLogin: "Authorization page opened; complete sign-in in your browser…",
|
|
122068
|
+
subLoginOk: "Signed in",
|
|
122069
|
+
subModelLabel: "Subscription model",
|
|
122070
|
+
subModelHint: "Fixed by the subscription channel; not changeable.",
|
|
122071
|
+
subEditUnsupported: "The subscription channel does not support image editing yet; text-to-image only.",
|
|
122072
|
+
subDefaultHint: "Subscription providers can only generate; switch to an API-key provider for editing.",
|
|
121768
122073
|
apiKeyLabel: "{provider} API Key",
|
|
121769
122074
|
apiKeyPlaceholder: "Leave empty to keep configured key",
|
|
121770
122075
|
apiKeyHint: "Securely saved as {key}; never read back in plaintext.",
|
|
@@ -121792,6 +122097,14 @@ img.ProseMirror-separator {
|
|
|
121792
122097
|
endpointHintGoogle: "Official Google endpoint or reverse proxy (full path).",
|
|
121793
122098
|
endpointHintOpenAI: "Official api.openai.com /v1 base URL; for relays use the \"OpenAI-compatible\" row below.",
|
|
121794
122099
|
endpointHintOpenAICompat: "OpenAI-compatible /v1 base URL of your relay or self-hosted service (required), e.g. https://your-relay.example.com/v1.",
|
|
122100
|
+
editFormat: "Edit request format",
|
|
122101
|
+
editFormatMultipart: "Standard multipart (official OpenAI)",
|
|
122102
|
+
editFormatJsonImageUrlArray: "JSON images array (SenseNova etc.)",
|
|
122103
|
+
editFormatHint: "Text-to-image is unaffected. Only switch when the relay runs image edits on its own JSON contract (e.g. SenseNova); size is pinned to \"auto\" in this format (the only value SenseNova's edits endpoint accepts) and can be overridden via extra fields.",
|
|
122104
|
+
editExtra: "Extra JSON fields",
|
|
122105
|
+
editExtraPlaceholder: "{\"watermark\": false, \"prompt_extend\": true}",
|
|
122106
|
+
editExtraHint: "Only used with the JSON format; merged into the request body last and may override defaults. Leave empty for none.",
|
|
122107
|
+
editExtraInvalid: "Extra JSON fields must be a valid JSON object.",
|
|
121795
122108
|
endpointHintSeedream: "Volcengine Ark compatible /api/v3 base URL.",
|
|
121796
122109
|
endpointHintDashScope: "Official Aliyun DashScope endpoint.",
|
|
121797
122110
|
endpointHintXAI: "Official xAI api.x.ai /v1 base URL.",
|
|
@@ -121868,6 +122181,7 @@ img.ProseMirror-separator {
|
|
|
121868
122181
|
.dsh-ig-label{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,inherit)}
|
|
121869
122182
|
.dsh-ig-input{box-sizing:border-box;width:100%;padding:8px 12px;font-size:13px;border:1px solid var(--dsw-alias-border-l2,#d7dbe0);border-radius:8px;background:var(--dsw-alias-bg-layer-3,transparent);color:inherit;outline:none;transition:border-color .15s}
|
|
121870
122183
|
.dsh-ig-input:focus{border-color:var(--dsw-alias-brand-primary,#4c78ff)}
|
|
122184
|
+
.dsh-ig-textarea{resize:vertical;min-height:56px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;line-height:1.5}
|
|
121871
122185
|
.dsh-ig-input-group{display:flex;gap:8px;align-items:center}
|
|
121872
122186
|
.dsh-ig-file-row{display:flex;align-items:center;gap:10px;min-width:0}
|
|
121873
122187
|
.dsh-ig-file-input{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;clip-path:inset(50%)}
|
|
@@ -122153,6 +122467,11 @@ img.ProseMirror-separator {
|
|
|
122153
122467
|
const isCompactTranscript = () => chatScope.getSnapshot().value?.transcriptView !== "normal";
|
|
122154
122468
|
const locale = ctx.get("locale");
|
|
122155
122469
|
const promotion = { enabled: false };
|
|
122470
|
+
ctx.inject(["sessions"], (owner) => {
|
|
122471
|
+
const sessions = owner.get("sessions");
|
|
122472
|
+
if (typeof sessions?.binding !== "function" || typeof sessions.list?.subscribe !== "function") return;
|
|
122473
|
+
owner.effect(() => startConversationLandings(sessions), "dsh-image-gen: live canvas images");
|
|
122474
|
+
});
|
|
122156
122475
|
ctx.effect(() => {
|
|
122157
122476
|
const style = document.createElement("style");
|
|
122158
122477
|
style.dataset.plugin = "dsh-image-gen";
|
|
@@ -122363,7 +122682,9 @@ img.ProseMirror-separator {
|
|
|
122363
122682
|
messageIsError: false,
|
|
122364
122683
|
workflows: [],
|
|
122365
122684
|
activeWorkflow: "",
|
|
122366
|
-
timeoutSeconds: DEFAULT_COMFYUI_TIMEOUT_MS / 1e3
|
|
122685
|
+
timeoutSeconds: DEFAULT_COMFYUI_TIMEOUT_MS / 1e3,
|
|
122686
|
+
editFormat: "multipart",
|
|
122687
|
+
editExtraText: ""
|
|
122367
122688
|
};
|
|
122368
122689
|
}
|
|
122369
122690
|
/** Settings field each cloud provider persists its model under. */
|
|
@@ -122408,6 +122729,15 @@ img.ProseMirror-separator {
|
|
|
122408
122729
|
function modelPullSupported(_provider) {
|
|
122409
122730
|
return true;
|
|
122410
122731
|
}
|
|
122732
|
+
/** Serialize the extra-fields record into editor text; empty record becomes ''. */
|
|
122733
|
+
function editExtraTextOf(value) {
|
|
122734
|
+
if (value === void 0 || Object.keys(value).length === 0) return "";
|
|
122735
|
+
try {
|
|
122736
|
+
return JSON.stringify(value);
|
|
122737
|
+
} catch {
|
|
122738
|
+
return "";
|
|
122739
|
+
}
|
|
122740
|
+
}
|
|
122411
122741
|
/** Build one row per provider from persisted settings, including ComfyUI extras. */
|
|
122412
122742
|
function rowsFromSettings(value) {
|
|
122413
122743
|
const rows = {};
|
|
@@ -122419,6 +122749,10 @@ img.ProseMirror-separator {
|
|
|
122419
122749
|
workflows: resolveComfyUIWorkflows(value ?? {}),
|
|
122420
122750
|
activeWorkflow: activeComfyUIWorkflow(value ?? {})?.name ?? "",
|
|
122421
122751
|
timeoutSeconds: Math.max(1, Math.round((value?.comfyuiTimeoutMs ?? 3e5) / 1e3))
|
|
122752
|
+
} : {},
|
|
122753
|
+
...provider === "openai-compat" ? {
|
|
122754
|
+
editFormat: value?.openaiCompatEditFormat === "jsonImageUrlArray" ? "jsonImageUrlArray" : "multipart",
|
|
122755
|
+
editExtraText: editExtraTextOf(value?.openaiCompatEditExtra)
|
|
122422
122756
|
} : {}
|
|
122423
122757
|
};
|
|
122424
122758
|
return rows;
|
|
@@ -122440,6 +122774,14 @@ img.ProseMirror-separator {
|
|
|
122440
122774
|
const [workspaceMessageIsError, setWorkspaceMessageIsError] = (0, react.useState)(false);
|
|
122441
122775
|
const [rows, setRows] = (0, react.useState)(() => rowsFromSettings(props.scope.getSnapshot().value));
|
|
122442
122776
|
const [keyTick, setKeyTick] = (0, react.useState)(0);
|
|
122777
|
+
const [subStatus, setSubStatus] = (0, react.useState)(() => ({
|
|
122778
|
+
"chatgpt-sub": { state: "unknown" },
|
|
122779
|
+
"grok-sub": { state: "unknown" },
|
|
122780
|
+
"google-sub": { state: "unknown" }
|
|
122781
|
+
}));
|
|
122782
|
+
const [subTick, setSubTick] = (0, react.useState)(0);
|
|
122783
|
+
const [subBusy, setSubBusy] = (0, react.useState)({});
|
|
122784
|
+
const [subPending, setSubPending] = (0, react.useState)([]);
|
|
122443
122785
|
(0, react.useEffect)(() => props.scope.subscribe(() => {
|
|
122444
122786
|
setSnapshot(props.scope.getSnapshot());
|
|
122445
122787
|
}), [props.scope]);
|
|
@@ -122452,6 +122794,143 @@ img.ProseMirror-separator {
|
|
|
122452
122794
|
(0, react.useEffect)(() => credentialEvents?.listen(() => {
|
|
122453
122795
|
setKeyTick((tick) => tick + 1);
|
|
122454
122796
|
}), [credentialEvents]);
|
|
122797
|
+
const pending = subPending;
|
|
122798
|
+
(0, react.useEffect)(() => {
|
|
122799
|
+
if (!open) return void 0;
|
|
122800
|
+
let active = true;
|
|
122801
|
+
const probe = async () => {
|
|
122802
|
+
try {
|
|
122803
|
+
const response = await fetch(SUBSCRIPTION_STATUS_ROUTE, {
|
|
122804
|
+
method: "POST",
|
|
122805
|
+
cache: "no-store"
|
|
122806
|
+
});
|
|
122807
|
+
if (!response.ok) return;
|
|
122808
|
+
const payload = await response.json();
|
|
122809
|
+
if (!active || payload.statuses === void 0) return;
|
|
122810
|
+
const next = {
|
|
122811
|
+
"chatgpt-sub": { state: "unknown" },
|
|
122812
|
+
"grok-sub": { state: "unknown" },
|
|
122813
|
+
"google-sub": { state: "unknown" }
|
|
122814
|
+
};
|
|
122815
|
+
for (const provider of SUBSCRIPTION_PROVIDERS) {
|
|
122816
|
+
const row = payload.statuses[provider];
|
|
122817
|
+
if (row?.state === "logged-in") next[provider] = {
|
|
122818
|
+
state: "logged-in",
|
|
122819
|
+
...typeof row.email === "string" ? { email: row.email } : {}
|
|
122820
|
+
};
|
|
122821
|
+
else if (row?.state === "logged-out") next[provider] = { state: "logged-out" };
|
|
122822
|
+
}
|
|
122823
|
+
setSubStatus(next);
|
|
122824
|
+
const landed = pending.filter((entry) => next[entry.provider].state === "logged-in");
|
|
122825
|
+
if (landed.length > 0) {
|
|
122826
|
+
setSubPending((current) => current.filter((entry) => next[entry.provider].state !== "logged-in"));
|
|
122827
|
+
for (const entry of landed) updateRow(entry.provider, {
|
|
122828
|
+
message: t("subLoginOk"),
|
|
122829
|
+
messageIsError: false
|
|
122830
|
+
});
|
|
122831
|
+
}
|
|
122832
|
+
const WINDOW_MS = 63e4;
|
|
122833
|
+
if (pending.filter((entry) => Date.now() - entry.startedAt > WINDOW_MS).length > 0) setSubPending((current) => current.filter((entry) => Date.now() - entry.startedAt <= WINDOW_MS));
|
|
122834
|
+
} catch {}
|
|
122835
|
+
};
|
|
122836
|
+
probe();
|
|
122837
|
+
let timer;
|
|
122838
|
+
if (pending.length > 0) timer = setInterval(() => {
|
|
122839
|
+
probe();
|
|
122840
|
+
}, 2e3);
|
|
122841
|
+
const onFocus = () => {
|
|
122842
|
+
probe();
|
|
122843
|
+
};
|
|
122844
|
+
window.addEventListener("focus", onFocus);
|
|
122845
|
+
return () => {
|
|
122846
|
+
active = false;
|
|
122847
|
+
if (timer !== void 0) clearInterval(timer);
|
|
122848
|
+
window.removeEventListener("focus", onFocus);
|
|
122849
|
+
};
|
|
122850
|
+
}, [
|
|
122851
|
+
open,
|
|
122852
|
+
subTick,
|
|
122853
|
+
pending
|
|
122854
|
+
]);
|
|
122855
|
+
/** Start a subscription login: the host registers PKCE and the loopback
|
|
122856
|
+
* catch server, then the browser opens the vendor's authorize URL. The
|
|
122857
|
+
* provider joins the pending set so the badge polls until login lands. */
|
|
122858
|
+
const subscriptionLogin = async (provider) => {
|
|
122859
|
+
updateRow(provider, {
|
|
122860
|
+
message: "",
|
|
122861
|
+
messageIsError: false
|
|
122862
|
+
});
|
|
122863
|
+
setSubBusy((current) => ({
|
|
122864
|
+
...current,
|
|
122865
|
+
[provider]: "login"
|
|
122866
|
+
}));
|
|
122867
|
+
try {
|
|
122868
|
+
const response = await fetch(SUBSCRIPTION_LOGIN_ROUTE, {
|
|
122869
|
+
method: "POST",
|
|
122870
|
+
headers: { "content-type": "application/json" },
|
|
122871
|
+
body: JSON.stringify({ provider })
|
|
122872
|
+
});
|
|
122873
|
+
const payload = await response.json().catch(() => ({}));
|
|
122874
|
+
if (!response.ok || payload.ok !== true || typeof payload.url !== "string") {
|
|
122875
|
+
const detail = typeof payload.error === "string" ? payload.error : payload.error?.message;
|
|
122876
|
+
throw new Error(detail ?? `HTTP ${String(response.status)}`);
|
|
122877
|
+
}
|
|
122878
|
+
setSubPending((current) => [...current.filter((entry) => entry.provider !== provider), {
|
|
122879
|
+
provider,
|
|
122880
|
+
startedAt: Date.now()
|
|
122881
|
+
}]);
|
|
122882
|
+
window.open(payload.url, "_blank", "noopener");
|
|
122883
|
+
updateRow(provider, {
|
|
122884
|
+
message: t("subWaitingLogin"),
|
|
122885
|
+
messageIsError: false
|
|
122886
|
+
});
|
|
122887
|
+
} catch (cause) {
|
|
122888
|
+
updateRow(provider, {
|
|
122889
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
122890
|
+
messageIsError: true
|
|
122891
|
+
});
|
|
122892
|
+
} finally {
|
|
122893
|
+
setSubBusy((current) => {
|
|
122894
|
+
const next = { ...current };
|
|
122895
|
+
delete next[provider];
|
|
122896
|
+
return next;
|
|
122897
|
+
});
|
|
122898
|
+
}
|
|
122899
|
+
};
|
|
122900
|
+
/** Sign out one subscription account; nothing else changes. */
|
|
122901
|
+
const subscriptionLogout = async (provider) => {
|
|
122902
|
+
setSubBusy((current) => ({
|
|
122903
|
+
...current,
|
|
122904
|
+
[provider]: "logout"
|
|
122905
|
+
}));
|
|
122906
|
+
try {
|
|
122907
|
+
const response = await fetch(SUBSCRIPTION_LOGIN_ROUTE, {
|
|
122908
|
+
method: "POST",
|
|
122909
|
+
headers: { "content-type": "application/json" },
|
|
122910
|
+
body: JSON.stringify({
|
|
122911
|
+
provider,
|
|
122912
|
+
action: "logout"
|
|
122913
|
+
})
|
|
122914
|
+
});
|
|
122915
|
+
if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
|
|
122916
|
+
updateRow(provider, {
|
|
122917
|
+
message: "",
|
|
122918
|
+
messageIsError: false
|
|
122919
|
+
});
|
|
122920
|
+
} catch (cause) {
|
|
122921
|
+
updateRow(provider, {
|
|
122922
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
122923
|
+
messageIsError: true
|
|
122924
|
+
});
|
|
122925
|
+
} finally {
|
|
122926
|
+
setSubBusy((current) => {
|
|
122927
|
+
const next = { ...current };
|
|
122928
|
+
delete next[provider];
|
|
122929
|
+
return next;
|
|
122930
|
+
});
|
|
122931
|
+
setSubTick((tick) => tick + 1);
|
|
122932
|
+
}
|
|
122933
|
+
};
|
|
122455
122934
|
const t = (keyName, params) => {
|
|
122456
122935
|
let text = (lang === "en" ? DICT.en : DICT.zh)[keyName] || DICT.zh[keyName] || keyName;
|
|
122457
122936
|
if (params) for (const [k, v] of Object.entries(params)) text = text.replace(`{${k}}`, v);
|
|
@@ -122465,7 +122944,10 @@ img.ProseMirror-separator {
|
|
|
122465
122944
|
dashscope: t("providerDashScope"),
|
|
122466
122945
|
xai: t("providerXAI"),
|
|
122467
122946
|
zhipu: t("providerZhipu"),
|
|
122468
|
-
comfyui: t("providerComfyUI")
|
|
122947
|
+
comfyui: t("providerComfyUI"),
|
|
122948
|
+
"chatgpt-sub": t("providerChatGPTSub"),
|
|
122949
|
+
"grok-sub": t("providerGrokSub"),
|
|
122950
|
+
"google-sub": t("providerGoogleSub")
|
|
122469
122951
|
};
|
|
122470
122952
|
(0, react.useEffect)(() => {
|
|
122471
122953
|
const value = snapshot.value;
|
|
@@ -122592,9 +123074,28 @@ img.ProseMirror-separator {
|
|
|
122592
123074
|
await props.scope.set("comfyuiWorkflowJson", activeEntry === void 0 ? "" : activeEntry.json);
|
|
122593
123075
|
await props.scope.set("comfyuiWorkflowName", activeEntry === void 0 ? "" : activeEntry.name);
|
|
122594
123076
|
await props.scope.set("comfyuiTimeoutMs", Math.max(1, Math.round(row.timeoutSeconds)) * 1e3);
|
|
122595
|
-
} else
|
|
123077
|
+
} else if (isSubscriptionProvider(provider)) await Promise.resolve();
|
|
123078
|
+
else {
|
|
123079
|
+
let editExtra = {};
|
|
123080
|
+
if (provider === "openai-compat") {
|
|
123081
|
+
const extraText = row.editExtraText.trim();
|
|
123082
|
+
if (extraText.length > 0) {
|
|
123083
|
+
let parsed;
|
|
123084
|
+
try {
|
|
123085
|
+
parsed = JSON.parse(extraText);
|
|
123086
|
+
} catch {
|
|
123087
|
+
throw new Error(t("editExtraInvalid"));
|
|
123088
|
+
}
|
|
123089
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(t("editExtraInvalid"));
|
|
123090
|
+
editExtra = parsed;
|
|
123091
|
+
}
|
|
123092
|
+
}
|
|
122596
123093
|
await props.scope.set(modelFieldOf(provider), row.model);
|
|
122597
123094
|
await props.scope.set(baseURLFieldOf(provider), row.baseURL);
|
|
123095
|
+
if (provider === "openai-compat") {
|
|
123096
|
+
await props.scope.set("openaiCompatEditFormat", row.editFormat);
|
|
123097
|
+
await props.scope.set("openaiCompatEditExtra", editExtra);
|
|
123098
|
+
}
|
|
122598
123099
|
if (row.keyInput.trim().length > 0) {
|
|
122599
123100
|
const keyRef = cloudCredentialRef(provider);
|
|
122600
123101
|
if (keyRef === void 0) throw new Error(t("comfyuiNoKey"));
|
|
@@ -122738,6 +123239,21 @@ img.ProseMirror-separator {
|
|
|
122738
123239
|
text: t("comfyuiNoKey"),
|
|
122739
123240
|
className: "dsh-ig-badge dsh-ig-badge-neutral"
|
|
122740
123241
|
};
|
|
123242
|
+
if (isSubscriptionProvider(provider)) {
|
|
123243
|
+
const status = subStatus[provider].state;
|
|
123244
|
+
if (status === "logged-in") return {
|
|
123245
|
+
text: t("subBadgeLoggedIn"),
|
|
123246
|
+
className: "dsh-ig-badge dsh-ig-badge-ok"
|
|
123247
|
+
};
|
|
123248
|
+
if (status === "logged-out") return {
|
|
123249
|
+
text: t("subBadgeLoggedOut"),
|
|
123250
|
+
className: "dsh-ig-badge dsh-ig-badge-missing"
|
|
123251
|
+
};
|
|
123252
|
+
return {
|
|
123253
|
+
text: t("subBadgeUnknown"),
|
|
123254
|
+
className: "dsh-ig-badge dsh-ig-badge-neutral"
|
|
123255
|
+
};
|
|
123256
|
+
}
|
|
122741
123257
|
const status = rows[provider].keyStatus;
|
|
122742
123258
|
if (status === "checking") return {
|
|
122743
123259
|
text: t("badgeChecking"),
|
|
@@ -123007,6 +123523,57 @@ img.ProseMirror-separator {
|
|
|
123007
123523
|
}) : null
|
|
123008
123524
|
]
|
|
123009
123525
|
}),
|
|
123526
|
+
provider === "openai-compat" ? (0, react_jsx_runtime.jsxs)("label", {
|
|
123527
|
+
className: "dsh-ig-field",
|
|
123528
|
+
children: [
|
|
123529
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123530
|
+
className: "dsh-ig-label",
|
|
123531
|
+
children: t("editFormat")
|
|
123532
|
+
}),
|
|
123533
|
+
(0, react_jsx_runtime.jsxs)("select", {
|
|
123534
|
+
className: "dsh-ig-input",
|
|
123535
|
+
value: row.editFormat,
|
|
123536
|
+
onChange: (event) => {
|
|
123537
|
+
updateRow(provider, { editFormat: event.target.value === "jsonImageUrlArray" ? "jsonImageUrlArray" : "multipart" });
|
|
123538
|
+
},
|
|
123539
|
+
disabled: !snapshot.writable,
|
|
123540
|
+
children: [(0, react_jsx_runtime.jsx)("option", {
|
|
123541
|
+
value: "multipart",
|
|
123542
|
+
children: t("editFormatMultipart")
|
|
123543
|
+
}), (0, react_jsx_runtime.jsx)("option", {
|
|
123544
|
+
value: "jsonImageUrlArray",
|
|
123545
|
+
children: t("editFormatJsonImageUrlArray")
|
|
123546
|
+
})]
|
|
123547
|
+
}),
|
|
123548
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123549
|
+
className: "dsh-ig-hint",
|
|
123550
|
+
children: t("editFormatHint")
|
|
123551
|
+
})
|
|
123552
|
+
]
|
|
123553
|
+
}) : null,
|
|
123554
|
+
provider === "openai-compat" && row.editFormat === "jsonImageUrlArray" ? (0, react_jsx_runtime.jsxs)("label", {
|
|
123555
|
+
className: "dsh-ig-field",
|
|
123556
|
+
children: [
|
|
123557
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123558
|
+
className: "dsh-ig-label",
|
|
123559
|
+
children: t("editExtra")
|
|
123560
|
+
}),
|
|
123561
|
+
(0, react_jsx_runtime.jsx)("textarea", {
|
|
123562
|
+
className: "dsh-ig-input dsh-ig-textarea",
|
|
123563
|
+
value: row.editExtraText,
|
|
123564
|
+
onChange: (event) => {
|
|
123565
|
+
updateRow(provider, { editExtraText: event.target.value });
|
|
123566
|
+
},
|
|
123567
|
+
placeholder: t("editExtraPlaceholder"),
|
|
123568
|
+
rows: 2,
|
|
123569
|
+
disabled: !snapshot.writable
|
|
123570
|
+
}),
|
|
123571
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123572
|
+
className: "dsh-ig-hint",
|
|
123573
|
+
children: t("editExtraHint")
|
|
123574
|
+
})
|
|
123575
|
+
]
|
|
123576
|
+
}) : null,
|
|
123010
123577
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
123011
123578
|
className: "dsh-ig-row-actions",
|
|
123012
123579
|
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
@@ -123224,6 +123791,119 @@ img.ProseMirror-separator {
|
|
|
123224
123791
|
})
|
|
123225
123792
|
});
|
|
123226
123793
|
};
|
|
123794
|
+
/** Subscription rows: account login/logout, fixed model, edit limitation. */
|
|
123795
|
+
const renderSubscriptionBody = (provider) => {
|
|
123796
|
+
const row = rows[provider];
|
|
123797
|
+
const status = subStatus[provider];
|
|
123798
|
+
const busy = subBusy[provider];
|
|
123799
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
123800
|
+
className: "dsh-ig-provider-body",
|
|
123801
|
+
children: (0, react_jsx_runtime.jsxs)("form", {
|
|
123802
|
+
onSubmit: (event) => {
|
|
123803
|
+
event.preventDefault();
|
|
123804
|
+
saveProviderRow(provider);
|
|
123805
|
+
},
|
|
123806
|
+
children: [
|
|
123807
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
123808
|
+
className: "dsh-ig-field",
|
|
123809
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
123810
|
+
className: "dsh-ig-label",
|
|
123811
|
+
children: t("subSectionTitle")
|
|
123812
|
+
}), (0, react_jsx_runtime.jsx)("p", {
|
|
123813
|
+
className: "dsh-ig-hint",
|
|
123814
|
+
children: t("subHint")
|
|
123815
|
+
})]
|
|
123816
|
+
}),
|
|
123817
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
123818
|
+
className: "dsh-ig-field",
|
|
123819
|
+
children: [
|
|
123820
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123821
|
+
className: "dsh-ig-label",
|
|
123822
|
+
children: t("subAccountLabel")
|
|
123823
|
+
}),
|
|
123824
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
123825
|
+
className: "dsh-ig-hint",
|
|
123826
|
+
children: status.state === "logged-in" ? t("subAccountEmail", { email: status.email ?? "" }) : status.state === "logged-out" ? t("subAccountNone") : t("subBadgeUnknown")
|
|
123827
|
+
}),
|
|
123828
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123829
|
+
className: "dsh-ig-row-buttons",
|
|
123830
|
+
children: status.state === "logged-in" ? (0, react_jsx_runtime.jsx)("button", {
|
|
123831
|
+
type: "button",
|
|
123832
|
+
className: "dsh-ig-btn-secondary",
|
|
123833
|
+
disabled: busy !== void 0,
|
|
123834
|
+
onClick: () => {
|
|
123835
|
+
subscriptionLogout(provider);
|
|
123836
|
+
},
|
|
123837
|
+
children: busy === "logout" ? t("subLoggingOut") : t("subLogout")
|
|
123838
|
+
}) : (0, react_jsx_runtime.jsx)("button", {
|
|
123839
|
+
type: "button",
|
|
123840
|
+
className: "dsh-ig-save",
|
|
123841
|
+
disabled: busy !== void 0,
|
|
123842
|
+
onClick: () => {
|
|
123843
|
+
subscriptionLogin(provider);
|
|
123844
|
+
setSubTick((tick) => tick + 1);
|
|
123845
|
+
},
|
|
123846
|
+
children: busy === "login" ? t("subLoggingIn") : t("subLogin")
|
|
123847
|
+
})
|
|
123848
|
+
})
|
|
123849
|
+
]
|
|
123850
|
+
}),
|
|
123851
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
123852
|
+
className: "dsh-ig-field",
|
|
123853
|
+
children: [
|
|
123854
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123855
|
+
className: "dsh-ig-label",
|
|
123856
|
+
children: t("subModelLabel")
|
|
123857
|
+
}),
|
|
123858
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
123859
|
+
className: "dsh-ig-input",
|
|
123860
|
+
type: "text",
|
|
123861
|
+
value: row.model,
|
|
123862
|
+
readOnly: true,
|
|
123863
|
+
disabled: true
|
|
123864
|
+
}),
|
|
123865
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
123866
|
+
className: "dsh-ig-hint",
|
|
123867
|
+
children: t("subModelHint")
|
|
123868
|
+
})
|
|
123869
|
+
]
|
|
123870
|
+
}),
|
|
123871
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
123872
|
+
className: "dsh-ig-field",
|
|
123873
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
123874
|
+
className: "dsh-ig-label",
|
|
123875
|
+
children: t("subEditUnsupported")
|
|
123876
|
+
})
|
|
123877
|
+
}),
|
|
123878
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
123879
|
+
className: "dsh-ig-row-actions",
|
|
123880
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
123881
|
+
className: `dsh-ig-status${row.messageIsError ? " dsh-ig-status-error" : ""}`,
|
|
123882
|
+
role: "status",
|
|
123883
|
+
children: row.message || testResultText(row.testResult)
|
|
123884
|
+
}), (0, react_jsx_runtime.jsxs)("span", {
|
|
123885
|
+
className: "dsh-ig-row-buttons",
|
|
123886
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
123887
|
+
type: "button",
|
|
123888
|
+
className: "dsh-ig-btn-secondary",
|
|
123889
|
+
disabled: row.testing,
|
|
123890
|
+
onClick: () => {
|
|
123891
|
+
testConnection(provider);
|
|
123892
|
+
setSubTick((tick) => tick + 1);
|
|
123893
|
+
},
|
|
123894
|
+
children: row.testing ? t("testing") : t("testConnection")
|
|
123895
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
123896
|
+
className: "dsh-ig-save",
|
|
123897
|
+
type: "submit",
|
|
123898
|
+
disabled: row.saving || !snapshot.writable,
|
|
123899
|
+
children: row.saving ? t("saving") : t("save")
|
|
123900
|
+
})]
|
|
123901
|
+
})]
|
|
123902
|
+
})
|
|
123903
|
+
]
|
|
123904
|
+
})
|
|
123905
|
+
});
|
|
123906
|
+
};
|
|
123227
123907
|
const renderProviderRow = (provider) => {
|
|
123228
123908
|
const row = rows[provider];
|
|
123229
123909
|
const badge = badgeOf(provider);
|
|
@@ -123265,7 +123945,7 @@ img.ProseMirror-separator {
|
|
|
123265
123945
|
})
|
|
123266
123946
|
})
|
|
123267
123947
|
]
|
|
123268
|
-
}), row.expanded ? provider === "comfyui" ? renderComfyUIBody() : renderCloudBody(provider) : null]
|
|
123948
|
+
}), row.expanded ? provider === "comfyui" ? renderComfyUIBody() : isSubscriptionProvider(provider) ? renderSubscriptionBody(provider) : renderCloudBody(provider) : null]
|
|
123269
123949
|
}, provider);
|
|
123270
123950
|
};
|
|
123271
123951
|
return (0, react_jsx_runtime.jsxs)("li", {
|
|
@@ -123510,14 +124190,6 @@ img.ProseMirror-separator {
|
|
|
123510
124190
|
};
|
|
123511
124191
|
(0, react.useEffect)(() => {
|
|
123512
124192
|
if (result === void 0) return;
|
|
123513
|
-
pushTlLandings([{
|
|
123514
|
-
galleryId: result.attachment.attachmentId,
|
|
123515
|
-
attachment: result.attachment,
|
|
123516
|
-
fromConversation: true,
|
|
123517
|
-
prompt: result.prompt,
|
|
123518
|
-
provider: result.provider,
|
|
123519
|
-
model: result.model
|
|
123520
|
-
}]);
|
|
123521
124193
|
saveGalleryItem({
|
|
123522
124194
|
id: result.attachment.attachmentId,
|
|
123523
124195
|
attachment: result.attachment,
|
|
@@ -124004,11 +124676,11 @@ img.ProseMirror-separator {
|
|
|
124004
124676
|
};
|
|
124005
124677
|
}
|
|
124006
124678
|
function modelOf(provider, value) {
|
|
124007
|
-
const stored = provider === "comfyui" ? activeComfyUIWorkflow(value ?? {})?.name : value?.[CLOUD_MODEL_FIELDS[provider]];
|
|
124679
|
+
const stored = provider === "comfyui" ? activeComfyUIWorkflow(value ?? {})?.name : isSubscriptionProvider(provider) ? DEFAULT_MODELS[provider] : value?.[CLOUD_MODEL_FIELDS[provider]];
|
|
124008
124680
|
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_MODELS[provider];
|
|
124009
124681
|
}
|
|
124010
124682
|
function baseURLOf(provider, value) {
|
|
124011
|
-
const stored = provider === "comfyui" ? value?.comfyuiBaseURL : value?.[CLOUD_URL_FIELDS[provider]];
|
|
124683
|
+
const stored = provider === "comfyui" ? value?.comfyuiBaseURL : isSubscriptionProvider(provider) ? "" : value?.[CLOUD_URL_FIELDS[provider]];
|
|
124012
124684
|
return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_BASE_URLS[provider];
|
|
124013
124685
|
}
|
|
124014
124686
|
//#endregion
|