dsh-image-gen 0.6.5 → 0.6.8
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 +52 -14
- package/README.md +53 -15
- package/docs/assets/readme/canvas-edit.webp +0 -0
- package/docs/assets/readme/canvas-generate.webp +0 -0
- package/docs/assets/readme/chat-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/other-features.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 +644 -390
- package/lib/client.js.map +1 -1
- package/lib/index.js +200 -37
- package/lib/types/client/conversation-image-revisions.d.ts +2 -2
- package/lib/types/client/gallery-view.d.ts +4 -0
- package/lib/types/client/multi-model-compare.d.ts +3 -3
- package/lib/types/client/provider-pill.d.ts +1 -1
- package/lib/types/client/studio-view.d.ts +4 -0
- 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/shared.d.ts +16 -4
- package/lib/types/studio.d.ts +3 -2
- package/lib/types/subscription/manager.d.ts +15 -0
- package/lib/types/subscription/vendors/antigravity.d.ts +10 -1
- package/lib/types/subscription/vendors/codex.d.ts +2 -0
- package/lib/types/subscription/vendors/grok.d.ts +2 -0
- package/lib/types/subscription.d.ts +7 -5
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -129,6 +129,12 @@ window.__ModuleLoader__.load({
|
|
|
129
129
|
"xai",
|
|
130
130
|
"zhipu"
|
|
131
131
|
];
|
|
132
|
+
/**
|
|
133
|
+
* Providers the browser workbench can drive: the BYOK cloud set plus the
|
|
134
|
+
* logged-in subscription channels. ComfyUI stays out; it has its own workflow
|
|
135
|
+
* pipeline and no shared request shape.
|
|
136
|
+
*/
|
|
137
|
+
const STUDIO_PROVIDERS = [...CLOUD_IMAGE_PROVIDERS, ...SUBSCRIPTION_PROVIDERS];
|
|
132
138
|
/** The credential reference each cloud provider's API key is stored under. */
|
|
133
139
|
const CLOUD_CREDENTIAL_REFS = {
|
|
134
140
|
google: "GEMINI_API_KEY",
|
|
@@ -343,9 +349,9 @@ window.__ModuleLoader__.load({
|
|
|
343
349
|
}
|
|
344
350
|
});
|
|
345
351
|
}
|
|
346
|
-
const listeners
|
|
352
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
347
353
|
function notifyListeners() {
|
|
348
|
-
for (const listener of listeners
|
|
354
|
+
for (const listener of listeners) try {
|
|
349
355
|
listener();
|
|
350
356
|
} catch (err) {
|
|
351
357
|
console.error("[dsh-image-gen] Gallery listener error:", err);
|
|
@@ -355,9 +361,9 @@ window.__ModuleLoader__.load({
|
|
|
355
361
|
* Subscribe to gallery mutations (insert/delete/clear).
|
|
356
362
|
*/
|
|
357
363
|
function subscribeGallery(listener) {
|
|
358
|
-
listeners
|
|
364
|
+
listeners.add(listener);
|
|
359
365
|
return () => {
|
|
360
|
-
listeners
|
|
366
|
+
listeners.delete(listener);
|
|
361
367
|
};
|
|
362
368
|
}
|
|
363
369
|
/**
|
|
@@ -1440,6 +1446,11 @@ window.__ModuleLoader__.load({
|
|
|
1440
1446
|
cache.delete(key);
|
|
1441
1447
|
}
|
|
1442
1448
|
}
|
|
1449
|
+
/** Clear all in-memory image caches. */
|
|
1450
|
+
function clearAttachmentCache() {
|
|
1451
|
+
cache.clear();
|
|
1452
|
+
currentCacheBytes = 0;
|
|
1453
|
+
}
|
|
1443
1454
|
function enforceLimits() {
|
|
1444
1455
|
while (cache.size > 30 || currentCacheBytes > 134217728) {
|
|
1445
1456
|
const oldestKey = cache.keys().next().value;
|
|
@@ -76042,14 +76053,14 @@ img.ProseMirror-separator {
|
|
|
76042
76053
|
});
|
|
76043
76054
|
var useReactId = react[" useId ".trim().toString()] || (() => void 0);
|
|
76044
76055
|
var count$1 = 0;
|
|
76045
|
-
function useId(deterministicId) {
|
|
76056
|
+
function useId$1(deterministicId) {
|
|
76046
76057
|
const [id, setId] = react.useState(useReactId());
|
|
76047
76058
|
useLayoutEffect2(() => {
|
|
76048
76059
|
if (!deterministicId) setId((reactId) => reactId ?? String(count$1++));
|
|
76049
76060
|
}, [deterministicId]);
|
|
76050
76061
|
return deterministicId || (id ? `radix-${id}` : "");
|
|
76051
76062
|
}
|
|
76052
|
-
__name$25(useId, "useId");
|
|
76063
|
+
__name$25(useId$1, "useId");
|
|
76053
76064
|
//#endregion
|
|
76054
76065
|
//#region node_modules/.pnpm/@radix-ui+react-direction@1_8310f062454398ef3b849e7d93575bcb/node_modules/@radix-ui/react-direction/dist/index.mjs
|
|
76055
76066
|
var __defProp$27 = Object.defineProperty;
|
|
@@ -78670,9 +78681,9 @@ img.ProseMirror-separator {
|
|
|
78670
78681
|
scope: __scopeDialog,
|
|
78671
78682
|
triggerRef,
|
|
78672
78683
|
contentRef,
|
|
78673
|
-
contentId: useId(),
|
|
78674
|
-
titleId: useId(),
|
|
78675
|
-
descriptionId: useId(),
|
|
78684
|
+
contentId: useId$1(),
|
|
78685
|
+
titleId: useId$1(),
|
|
78686
|
+
descriptionId: useId$1(),
|
|
78676
78687
|
titlePresent: titleCount > 0,
|
|
78677
78688
|
descriptionPresent: descriptionCount > 0,
|
|
78678
78689
|
setTitleCount,
|
|
@@ -80944,7 +80955,7 @@ img.ProseMirror-separator {
|
|
|
80944
80955
|
var ITEM_NAME$3 = "RovingFocusGroupItem";
|
|
80945
80956
|
var RovingFocusGroupItem = /* @__PURE__ */ react.forwardRef(/* @__PURE__ */ __name$13(function RovingFocusGroupItem2(props, forwardedRef) {
|
|
80946
80957
|
const { __scopeRovingFocusGroup, focusable = true, active = false, tabStopId, children, ...itemProps } = props;
|
|
80947
|
-
const autoId = useId();
|
|
80958
|
+
const autoId = useId$1();
|
|
80948
80959
|
const id = tabStopId || autoId;
|
|
80949
80960
|
const context = useRovingFocusContext(ITEM_NAME$3, __scopeRovingFocusGroup);
|
|
80950
80961
|
const isCurrentTabStop = context.currentTabStopId === id;
|
|
@@ -81503,8 +81514,8 @@ img.ProseMirror-separator {
|
|
|
81503
81514
|
onContentChange: setContent,
|
|
81504
81515
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSubProvider, {
|
|
81505
81516
|
scope: __scopeMenu,
|
|
81506
|
-
contentId: useId(),
|
|
81507
|
-
triggerId: useId(),
|
|
81517
|
+
contentId: useId$1(),
|
|
81518
|
+
triggerId: useId$1(),
|
|
81508
81519
|
trigger,
|
|
81509
81520
|
onTriggerChange: setTrigger,
|
|
81510
81521
|
children
|
|
@@ -81962,9 +81973,9 @@ img.ProseMirror-separator {
|
|
|
81962
81973
|
});
|
|
81963
81974
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuProvider, {
|
|
81964
81975
|
scope: __scopeDropdownMenu,
|
|
81965
|
-
triggerId: useId(),
|
|
81976
|
+
triggerId: useId$1(),
|
|
81966
81977
|
triggerRef,
|
|
81967
|
-
contentId: useId(),
|
|
81978
|
+
contentId: useId$1(),
|
|
81968
81979
|
open,
|
|
81969
81980
|
onOpenChange: setOpen,
|
|
81970
81981
|
onOpenToggle: react.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
@@ -82196,7 +82207,7 @@ img.ProseMirror-separator {
|
|
|
82196
82207
|
...popperScope,
|
|
82197
82208
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverProvider, {
|
|
82198
82209
|
scope: __scopePopover,
|
|
82199
|
-
contentId: useId(),
|
|
82210
|
+
contentId: useId$1(),
|
|
82200
82211
|
triggerRef,
|
|
82201
82212
|
open,
|
|
82202
82213
|
onOpenChange: setOpen,
|
|
@@ -82428,7 +82439,7 @@ img.ProseMirror-separator {
|
|
|
82428
82439
|
]);
|
|
82429
82440
|
const isFormControl = trigger ? !!form || !!trigger.closest("form") : true;
|
|
82430
82441
|
const [nativeOptionsSet, setNativeOptionsSet] = react.useState(/* @__PURE__ */ new Set());
|
|
82431
|
-
const contentId = useId();
|
|
82442
|
+
const contentId = useId$1();
|
|
82432
82443
|
const nativeSelectKey = Array.from(nativeOptionsSet).map((option) => option.props.value).join(";");
|
|
82433
82444
|
const handleNativeOptionAdd = react.useCallback((option) => {
|
|
82434
82445
|
setNativeOptionsSet((prev) => new Set(prev).add(option));
|
|
@@ -83997,7 +84008,7 @@ img.ProseMirror-separator {
|
|
|
83997
84008
|
const popperScope = usePopperScope(__scopeTooltip);
|
|
83998
84009
|
const [trigger, setTrigger] = react.useState(null);
|
|
83999
84010
|
const [contentIdState, setContentId] = react.useState(void 0);
|
|
84000
|
-
const generatedContentId = useId();
|
|
84011
|
+
const generatedContentId = useId$1();
|
|
84001
84012
|
const openTimerRef = react.useRef(0);
|
|
84002
84013
|
const disableHoverableContent = disableHoverableContentProp ?? providerContext.disableHoverableContent;
|
|
84003
84014
|
const delayDuration = delayDurationProp ?? providerContext.delayDuration;
|
|
@@ -108118,7 +108129,7 @@ img.ProseMirror-separator {
|
|
|
108118
108129
|
}
|
|
108119
108130
|
const MAX_SIZE = 256;
|
|
108120
108131
|
const alphaCache = new LruCache(100);
|
|
108121
|
-
const pending$
|
|
108132
|
+
const pending$2 = /* @__PURE__ */ new Set();
|
|
108122
108133
|
let offscreenCanvas = null;
|
|
108123
108134
|
function getOffscreenCanvas(w, h) {
|
|
108124
108135
|
if (!offscreenCanvas) offscreenCanvas = new OffscreenCanvas(w, h);
|
|
@@ -108138,12 +108149,12 @@ img.ProseMirror-separator {
|
|
|
108138
108149
|
function preloadAlphaData(url, cacheKey) {
|
|
108139
108150
|
if (typeof OffscreenCanvas === "undefined") return;
|
|
108140
108151
|
const key = cacheKey ?? url;
|
|
108141
|
-
if (alphaCache.has(key) || pending$
|
|
108142
|
-
pending$
|
|
108152
|
+
if (alphaCache.has(key) || pending$2.has(key)) return;
|
|
108153
|
+
pending$2.add(key);
|
|
108143
108154
|
const img = Image$1();
|
|
108144
108155
|
img.crossOrigin = "anonymous";
|
|
108145
108156
|
img.onload = async () => {
|
|
108146
|
-
pending$
|
|
108157
|
+
pending$2.delete(key);
|
|
108147
108158
|
const { width: origW, height: origH } = img;
|
|
108148
108159
|
if (origW === 0 || origH === 0) return;
|
|
108149
108160
|
const scale = Math.min(1, MAX_SIZE / Math.max(origW, origH));
|
|
@@ -108170,7 +108181,7 @@ img.ProseMirror-separator {
|
|
|
108170
108181
|
});
|
|
108171
108182
|
};
|
|
108172
108183
|
img.onerror = () => {
|
|
108173
|
-
pending$
|
|
108184
|
+
pending$2.delete(key);
|
|
108174
108185
|
};
|
|
108175
108186
|
img.src = url;
|
|
108176
108187
|
}
|
|
@@ -115622,43 +115633,6 @@ img.ProseMirror-separator {
|
|
|
115622
115633
|
});
|
|
115623
115634
|
}
|
|
115624
115635
|
//#endregion
|
|
115625
|
-
//#region lib/types/client/tl/tl-canvas-bridge.js
|
|
115626
|
-
const items = [];
|
|
115627
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
115628
|
-
/** Queue freshly generated images for the tldraw canvases. Safe to call anywhere. */
|
|
115629
|
-
function pushTlLandings(newItems) {
|
|
115630
|
-
if (newItems.length === 0) return;
|
|
115631
|
-
const known = new Set(items.map((item) => item.galleryId));
|
|
115632
|
-
let pushed = 0;
|
|
115633
|
-
for (const item of newItems) {
|
|
115634
|
-
if (known.has(item.galleryId)) continue;
|
|
115635
|
-
items.push(item);
|
|
115636
|
-
known.add(item.galleryId);
|
|
115637
|
-
pushed += 1;
|
|
115638
|
-
}
|
|
115639
|
-
if (pushed === 0) return;
|
|
115640
|
-
for (const listener of listeners) listener();
|
|
115641
|
-
}
|
|
115642
|
-
/**
|
|
115643
|
-
* Read every item landed so far in this browser session.
|
|
115644
|
-
*
|
|
115645
|
-
* The list is the in-memory session-scratch record shared by all canvases:
|
|
115646
|
-
* reading never removes items, so a canvas mounted later (or a second canvas
|
|
115647
|
-
* mounted beside the first) reconciles the same set. The list dies with the
|
|
115648
|
-
* page, which is exactly the "unsaved content does not survive a restart"
|
|
115649
|
-
* semantic the studio canvas keeps.
|
|
115650
|
-
*/
|
|
115651
|
-
function getTlLandings() {
|
|
115652
|
-
return items;
|
|
115653
|
-
}
|
|
115654
|
-
/** Subscribe to queue pushes. Returns an unsubscribe function. */
|
|
115655
|
-
function subscribeTlLandings(listener) {
|
|
115656
|
-
listeners.add(listener);
|
|
115657
|
-
return () => {
|
|
115658
|
-
listeners.delete(listener);
|
|
115659
|
-
};
|
|
115660
|
-
}
|
|
115661
|
-
//#endregion
|
|
115662
115636
|
//#region lib/types/client/tl/canvas-sync.js
|
|
115663
115637
|
/** Debounce for ordinary document/session changes. */
|
|
115664
115638
|
const STATE_DEBOUNCE_MS = 600;
|
|
@@ -115708,21 +115682,18 @@ img.ProseMirror-separator {
|
|
|
115708
115682
|
* the selection identity list, so a selected node reads exactly like its
|
|
115709
115683
|
* canvas node twin (same name, attachment id, dimensions).
|
|
115710
115684
|
*/
|
|
115711
|
-
function describeShape(editor, shape
|
|
115685
|
+
function describeShape(editor, shape) {
|
|
115712
115686
|
const node = { kind: kindOf(shape.type) };
|
|
115713
115687
|
if (shape.type === "image") {
|
|
115714
115688
|
const meta = shape.meta;
|
|
115715
115689
|
const galleryId = meta?.galleryId;
|
|
115716
|
-
if (typeof galleryId === "string" && galleryId.length > 0)
|
|
115717
|
-
|
|
115718
|
-
const attachmentId = attachmentByGallery.get(galleryId);
|
|
115719
|
-
if (attachmentId !== void 0) node.attachmentId = attachmentId;
|
|
115720
|
-
}
|
|
115690
|
+
if (typeof galleryId === "string" && galleryId.length > 0) node.galleryId = galleryId;
|
|
115691
|
+
if (typeof meta?.attachmentId === "string" && meta.attachmentId.length > 0) node.attachmentId = meta.attachmentId;
|
|
115721
115692
|
if (typeof meta?.prompt === "string" && meta.prompt.trim().length > 0) node.prompt = meta.prompt.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
115722
115693
|
if (typeof meta?.provider === "string" && meta.provider.length > 0) node.provider = meta.provider;
|
|
115723
115694
|
if (typeof meta?.model === "string" && meta.model.length > 0) node.model = meta.model;
|
|
115724
115695
|
const asset = shape.props.assetId === null ? void 0 : editor.getAsset(shape.props.assetId);
|
|
115725
|
-
const assetName = asset !== void 0 && asset.type === "image" ? asset.props.name : void 0;
|
|
115696
|
+
const assetName = typeof meta?.name === "string" && meta.name.length > 0 ? meta.name : asset !== void 0 && asset.type === "image" ? asset.props.name : void 0;
|
|
115726
115697
|
if (typeof assetName === "string" && assetName.length > 0) node.name = assetName.slice(0, 128);
|
|
115727
115698
|
if (asset !== void 0 && asset.type === "image") {
|
|
115728
115699
|
node.width = Math.round(asset.props.w);
|
|
@@ -115745,31 +115716,19 @@ img.ProseMirror-separator {
|
|
|
115745
115716
|
return node;
|
|
115746
115717
|
}
|
|
115747
115718
|
function summarize(editor) {
|
|
115748
|
-
const attachmentByGallery = /* @__PURE__ */ new Map();
|
|
115749
|
-
const conversationGallery = /* @__PURE__ */ new Set();
|
|
115750
|
-
for (const landing of getTlLandings()) {
|
|
115751
|
-
attachmentByGallery.set(landing.galleryId, String(landing.attachment.attachmentId));
|
|
115752
|
-
if (landing.fromConversation) conversationGallery.add(landing.galleryId);
|
|
115753
|
-
}
|
|
115754
115719
|
const shapes = editor.getCurrentPageShapes();
|
|
115755
115720
|
const nodes = [];
|
|
115756
|
-
for (const shape of shapes.slice(0, 48)) nodes.push(describeShape(editor, shape
|
|
115721
|
+
for (const shape of shapes.slice(0, 48)) nodes.push(describeShape(editor, shape));
|
|
115757
115722
|
const selected = editor.getSelectedShapes();
|
|
115758
115723
|
const selectionKinds = [...new Set(selected.map((shape) => kindOf(shape.type)))].slice(0, 8);
|
|
115759
|
-
const selectionItems = selected.slice(0, 16).map((shape) => describeShape(editor, shape
|
|
115724
|
+
const selectionItems = selected.slice(0, 16).map((shape) => describeShape(editor, shape));
|
|
115760
115725
|
const selectionKey = selectionKeyOf(editor);
|
|
115761
|
-
const selectionAllConversationImages = selected.length > 0 && selected.every((shape) => {
|
|
115762
|
-
if (shape.type !== "image") return false;
|
|
115763
|
-
const galleryId = shape.meta?.galleryId;
|
|
115764
|
-
return typeof galleryId === "string" && conversationGallery.has(galleryId);
|
|
115765
|
-
});
|
|
115766
115726
|
return {
|
|
115767
115727
|
nodeCount: shapes.length,
|
|
115768
115728
|
nodes,
|
|
115769
115729
|
selectionCount: selected.length,
|
|
115770
115730
|
selectionKinds,
|
|
115771
115731
|
selectionItems,
|
|
115772
|
-
selectionAllConversationImages,
|
|
115773
115732
|
stateKey: JSON.stringify([
|
|
115774
115733
|
shapes.length,
|
|
115775
115734
|
nodes,
|
|
@@ -115836,7 +115795,7 @@ img.ProseMirror-separator {
|
|
|
115836
115795
|
return;
|
|
115837
115796
|
}
|
|
115838
115797
|
let selectionImage;
|
|
115839
|
-
if (withScreenshot && summary.selectionCount > 0
|
|
115798
|
+
if (withScreenshot && summary.selectionCount > 0) try {
|
|
115840
115799
|
const selectedBefore = editor.getSelectedShapes();
|
|
115841
115800
|
const { url } = await editor.toImageDataUrl(selectedBefore, {
|
|
115842
115801
|
background: true,
|
|
@@ -115844,6 +115803,7 @@ img.ProseMirror-separator {
|
|
|
115844
115803
|
scale: 1,
|
|
115845
115804
|
pixelRatio: 2
|
|
115846
115805
|
});
|
|
115806
|
+
if (disposed) return;
|
|
115847
115807
|
const selectedAfter = editor.getSelectedShapes();
|
|
115848
115808
|
const idsBefore = new Set(selectedBefore.map((shape) => String(shape.id)));
|
|
115849
115809
|
if (!(selectedBefore.length === selectedAfter.length && selectedAfter.every((shape) => idsBefore.has(String(shape.id))))) return;
|
|
@@ -115886,6 +115846,10 @@ img.ProseMirror-separator {
|
|
|
115886
115846
|
stateTimer = void 0;
|
|
115887
115847
|
flush(false);
|
|
115888
115848
|
}, STATE_DEBOUNCE_MS);
|
|
115849
|
+
if (editor.getSelectedShapeIds().length > 0) exportTimer = setTimeout(() => {
|
|
115850
|
+
exportTimer = void 0;
|
|
115851
|
+
flush(true);
|
|
115852
|
+
}, SELECTION_EXPORT_DELAY_MS);
|
|
115889
115853
|
return () => {
|
|
115890
115854
|
if (disposed) return;
|
|
115891
115855
|
disposed = true;
|
|
@@ -115908,25 +115872,81 @@ img.ProseMirror-separator {
|
|
|
115908
115872
|
};
|
|
115909
115873
|
}
|
|
115910
115874
|
//#endregion
|
|
115875
|
+
//#region lib/types/client/tl/tl-canvas-bridge.js
|
|
115876
|
+
const pending$1 = /* @__PURE__ */ new Map();
|
|
115877
|
+
const consumers = /* @__PURE__ */ new Set();
|
|
115878
|
+
let delivering = false;
|
|
115879
|
+
let generation = 0;
|
|
115880
|
+
/** Invalidates async landings that started before the user cleared the canvas. */
|
|
115881
|
+
function getTlLandingGeneration() {
|
|
115882
|
+
return generation;
|
|
115883
|
+
}
|
|
115884
|
+
function clearTlLandings() {
|
|
115885
|
+
generation += 1;
|
|
115886
|
+
pending$1.clear();
|
|
115887
|
+
}
|
|
115888
|
+
/** Queue freshly generated images for the tldraw canvases. Safe to call anywhere. */
|
|
115889
|
+
function pushTlLandings(newItems) {
|
|
115890
|
+
for (const item of newItems) if (!pending$1.has(item.galleryId)) pending$1.set(item.galleryId, item);
|
|
115891
|
+
deliver();
|
|
115892
|
+
}
|
|
115893
|
+
/** Conversation results are admitted only while a canvas is mounted. */
|
|
115894
|
+
function pushTlLandingsLive(items) {
|
|
115895
|
+
if (consumers.size > 0) pushTlLandings(items);
|
|
115896
|
+
}
|
|
115897
|
+
/** Register a ready editor. False means it unmounted before committing. */
|
|
115898
|
+
function registerTlLandingConsumer(consumer) {
|
|
115899
|
+
consumers.add(consumer);
|
|
115900
|
+
deliver();
|
|
115901
|
+
return () => {
|
|
115902
|
+
consumers.delete(consumer);
|
|
115903
|
+
if (consumers.size === 0) {
|
|
115904
|
+
for (const [id, item] of pending$1) if (item.fromConversation) pending$1.delete(id);
|
|
115905
|
+
}
|
|
115906
|
+
};
|
|
115907
|
+
}
|
|
115908
|
+
async function deliver() {
|
|
115909
|
+
if (delivering) return;
|
|
115910
|
+
delivering = true;
|
|
115911
|
+
try {
|
|
115912
|
+
while (pending$1.size > 0) {
|
|
115913
|
+
const consumer = consumers.values().next().value;
|
|
115914
|
+
if (consumer === void 0) break;
|
|
115915
|
+
const batch = [...pending$1.values()];
|
|
115916
|
+
let completed = true;
|
|
115917
|
+
try {
|
|
115918
|
+
completed = await consumer(batch);
|
|
115919
|
+
} catch (error) {
|
|
115920
|
+
console.warn("[dsh-image-gen] canvas landing failed:", error);
|
|
115921
|
+
}
|
|
115922
|
+
if (completed) {
|
|
115923
|
+
for (const item of batch) if (pending$1.get(item.galleryId) === item) pending$1.delete(item.galleryId);
|
|
115924
|
+
}
|
|
115925
|
+
}
|
|
115926
|
+
} finally {
|
|
115927
|
+
delivering = false;
|
|
115928
|
+
}
|
|
115929
|
+
}
|
|
115930
|
+
//#endregion
|
|
115911
115931
|
//#region lib/types/client/tl/studio-tl-canvas.js
|
|
115912
115932
|
/**
|
|
115913
115933
|
* Infinite canvas surface for the Studio workbench, backed by tldraw.
|
|
115914
115934
|
*
|
|
115915
|
-
*
|
|
115916
|
-
*
|
|
115917
|
-
* image assets with data-URL sources. The bus broadcasts, so this surface
|
|
115918
|
-
* RECONCILES instead of draining: it lands the bus items its own page does
|
|
115919
|
-
* not carry yet. The conversation-view gallery tab and the right-sidebar
|
|
115920
|
-
* studio tab can both be mounted, and each mirrors the full session-scratch
|
|
115921
|
-
* set independently. The surface deliberately runs WITHOUT persistenceKey:
|
|
115922
|
-
* unsaved generations vanish on restart, matching the plugin's save-first
|
|
115923
|
-
* philosophy; data-URL assets therefore never touch disk. Shapes carry
|
|
115924
|
-
* `meta.galleryId` so re-landing the same generation is a no-op. One batch
|
|
115925
|
-
* = one createShapes transaction = one undo step.
|
|
115935
|
+
* Both surfaces share one locally persisted document. The landing queue is
|
|
115936
|
+
* consumed once; tldraw owns subsequent edits, deletion, undo and persistence.
|
|
115926
115937
|
*/
|
|
115927
115938
|
const MAX_DISPLAY_SIDE = 380;
|
|
115928
115939
|
const GRID_GAP = 40;
|
|
115929
115940
|
const GRID_COLS = 3;
|
|
115941
|
+
const mountedCanvases = /* @__PURE__ */ new Set();
|
|
115942
|
+
/** Reset canvas-owned data only. Gallery records and host attachments are untouched. */
|
|
115943
|
+
function clearStudioTlCanvases() {
|
|
115944
|
+
if (mountedCanvases.size === 0) return false;
|
|
115945
|
+
clearTlLandings();
|
|
115946
|
+
for (const clear of mountedCanvases) clear();
|
|
115947
|
+
clearAttachmentCache();
|
|
115948
|
+
return true;
|
|
115949
|
+
}
|
|
115930
115950
|
/**
|
|
115931
115951
|
* Dock the stock toolbar vertically along the left edge (Figma-style) instead
|
|
115932
115952
|
* of tldraw's signature bottom-center pill. It is still the same
|
|
@@ -115948,11 +115968,11 @@ img.ProseMirror-separator {
|
|
|
115948
115968
|
h: Math.round(height * scale)
|
|
115949
115969
|
};
|
|
115950
115970
|
}
|
|
115951
|
-
/**
|
|
115971
|
+
/** Dedupe across the whole document, including images moved to other pages. */
|
|
115952
115972
|
function landedIdsOf(editor) {
|
|
115953
115973
|
const landedIds = /* @__PURE__ */ new Set();
|
|
115954
|
-
for (const shape of editor.
|
|
115955
|
-
if (shape.type !== "image") continue;
|
|
115974
|
+
for (const shape of editor.store.allRecords()) {
|
|
115975
|
+
if (shape.typeName !== "shape" || shape.type !== "image") continue;
|
|
115956
115976
|
const galleryId = shape.meta?.galleryId;
|
|
115957
115977
|
if (typeof galleryId === "string") landedIds.add(galleryId);
|
|
115958
115978
|
}
|
|
@@ -115964,13 +115984,14 @@ img.ProseMirror-separator {
|
|
|
115964
115984
|
const collapsed = prompt.replace(/\s+/g, " ").trim();
|
|
115965
115985
|
return collapsed.length === 0 ? void 0 : collapsed.slice(0, 120);
|
|
115966
115986
|
}
|
|
115967
|
-
async function landTlItems(editor, items,
|
|
115987
|
+
async function landTlItems(editor, items, isActive) {
|
|
115968
115988
|
const landedIds = landedIdsOf(editor);
|
|
115969
115989
|
const pending = items.filter((item) => !landedIds.has(item.galleryId));
|
|
115970
|
-
if (pending.length === 0) return;
|
|
115990
|
+
if (pending.length === 0) return true;
|
|
115971
115991
|
const assets = [];
|
|
115972
115992
|
const landed = [];
|
|
115973
115993
|
for (const item of pending) {
|
|
115994
|
+
if (!isActive()) return false;
|
|
115974
115995
|
const assetId = tlAssetIdFor(item.galleryId);
|
|
115975
115996
|
if (editor.getAsset(assetId) === void 0) try {
|
|
115976
115997
|
const blob = await fetchAttachmentBlob(item.attachment);
|
|
@@ -115991,7 +116012,6 @@ img.ProseMirror-separator {
|
|
|
115991
116012
|
}
|
|
115992
116013
|
});
|
|
115993
116014
|
} catch (error) {
|
|
115994
|
-
failedIds.add(item.galleryId);
|
|
115995
116015
|
console.warn("[dsh-image-gen] canvas landing skipped (attachment unreadable):", item.galleryId, error);
|
|
115996
116016
|
continue;
|
|
115997
116017
|
}
|
|
@@ -116000,6 +116020,9 @@ img.ProseMirror-separator {
|
|
|
116000
116020
|
landed.push({
|
|
116001
116021
|
galleryId: item.galleryId,
|
|
116002
116022
|
assetId,
|
|
116023
|
+
attachmentId: String(item.attachment.attachmentId),
|
|
116024
|
+
name: item.attachment.name ?? item.galleryId,
|
|
116025
|
+
fromConversation: item.fromConversation === true,
|
|
116003
116026
|
w: size.w,
|
|
116004
116027
|
h: size.h,
|
|
116005
116028
|
...prompt !== void 0 ? { prompt } : {},
|
|
@@ -116007,10 +116030,13 @@ img.ProseMirror-separator {
|
|
|
116007
116030
|
...typeof item.model === "string" && item.model.length > 0 ? { model: item.model } : {}
|
|
116008
116031
|
});
|
|
116009
116032
|
}
|
|
116010
|
-
if (
|
|
116033
|
+
if (!isActive()) return false;
|
|
116034
|
+
const nowLanded = landedIdsOf(editor);
|
|
116035
|
+
const ready = landed.filter((item) => !nowLanded.has(item.galleryId));
|
|
116036
|
+
if (ready.length === 0) return true;
|
|
116011
116037
|
if (assets.length > 0) editor.createAssets(assets);
|
|
116012
116038
|
const rows = [];
|
|
116013
|
-
for (let index = 0; index <
|
|
116039
|
+
for (let index = 0; index < ready.length; index += GRID_COLS) rows.push(ready.slice(index, index + GRID_COLS));
|
|
116014
116040
|
const rowSizes = rows.map((row) => ({
|
|
116015
116041
|
width: row.reduce((sum, cell) => sum + cell.w, 0) + GRID_GAP * (row.length - 1),
|
|
116016
116042
|
height: Math.max(...row.map((cell) => cell.h))
|
|
@@ -116045,7 +116071,7 @@ img.ProseMirror-separator {
|
|
|
116045
116071
|
let cursorX = originX + (blockWidth - rowSize.width) / 2;
|
|
116046
116072
|
for (const cell of row) {
|
|
116047
116073
|
shapes.push({
|
|
116048
|
-
id: createShapeId(),
|
|
116074
|
+
id: createShapeId(`ig-${cell.galleryId}`),
|
|
116049
116075
|
type: "image",
|
|
116050
116076
|
x: Math.round(cursorX),
|
|
116051
116077
|
y: Math.round(cursorY),
|
|
@@ -116056,6 +116082,9 @@ img.ProseMirror-separator {
|
|
|
116056
116082
|
},
|
|
116057
116083
|
meta: {
|
|
116058
116084
|
galleryId: cell.galleryId,
|
|
116085
|
+
attachmentId: cell.attachmentId,
|
|
116086
|
+
name: cell.name,
|
|
116087
|
+
fromConversation: cell.fromConversation,
|
|
116059
116088
|
...cell.prompt !== void 0 ? { prompt: cell.prompt } : {},
|
|
116060
116089
|
...cell.provider !== void 0 ? { provider: cell.provider } : {},
|
|
116061
116090
|
...cell.model !== void 0 ? { model: cell.model } : {}
|
|
@@ -116081,42 +116110,53 @@ img.ProseMirror-separator {
|
|
|
116081
116110
|
inset: GRID_GAP
|
|
116082
116111
|
});
|
|
116083
116112
|
}
|
|
116113
|
+
return true;
|
|
116084
116114
|
}
|
|
116085
116115
|
const StudioTlCanvas = (0, react.memo)(function StudioTlCanvas() {
|
|
116086
|
-
const
|
|
116087
|
-
const landingRef = (0, react.useRef)(false);
|
|
116088
|
-
/** Per-canvas attachment misses, so one bad blob cannot spin the reconcile. */
|
|
116089
|
-
const failedRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
116090
|
-
const processQueue = (0, react.useCallback)(() => {
|
|
116091
|
-
const editor = editorRef.current;
|
|
116092
|
-
if (editor === null) return;
|
|
116093
|
-
if (landingRef.current) return;
|
|
116094
|
-
const landedIds = landedIdsOf(editor);
|
|
116095
|
-
const items = getTlLandings().filter((item) => !landedIds.has(item.galleryId) && !failedRef.current.has(item.galleryId));
|
|
116096
|
-
if (items.length === 0) return;
|
|
116097
|
-
landingRef.current = true;
|
|
116098
|
-
landTlItems(editor, items, failedRef.current).catch((error) => {
|
|
116099
|
-
console.warn("[dsh-image-gen] tldraw canvas landing failed:", error);
|
|
116100
|
-
}).finally(() => {
|
|
116101
|
-
landingRef.current = false;
|
|
116102
|
-
processQueue();
|
|
116103
|
-
});
|
|
116104
|
-
}, []);
|
|
116105
|
-
(0, react.useEffect)(() => subscribeTlLandings(processQueue), [processQueue]);
|
|
116116
|
+
const sessionId = (0, react.useId)();
|
|
116106
116117
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
116107
116118
|
className: "dsh-ig-tl-canvas",
|
|
116108
116119
|
children: (0, react_jsx_runtime.jsx)(Tldraw, {
|
|
116120
|
+
persistenceKey: "dsh-image-gen-workbench-v1",
|
|
116121
|
+
sessionId,
|
|
116109
116122
|
components: { Toolbar: StudioToolbar },
|
|
116110
116123
|
onMount: (editor) => {
|
|
116111
|
-
|
|
116124
|
+
let active = true;
|
|
116112
116125
|
applyBrandTheme(editor);
|
|
116113
116126
|
editor.updateInstanceState({ isGridMode: true });
|
|
116114
116127
|
console.info(`[dsh-image-gen] tldraw mounted (instance ${editor.id})`);
|
|
116115
|
-
|
|
116116
|
-
|
|
116128
|
+
const stopLanding = registerTlLandingConsumer((items) => {
|
|
116129
|
+
const generation = getTlLandingGeneration();
|
|
116130
|
+
return landTlItems(editor, items, () => active && generation === getTlLandingGeneration());
|
|
116131
|
+
});
|
|
116132
|
+
let stopSync = startCanvasSync(editor);
|
|
116133
|
+
const clear = () => {
|
|
116134
|
+
stopSync();
|
|
116135
|
+
editor.complete();
|
|
116136
|
+
editor.run(() => {
|
|
116137
|
+
editor.selectNone();
|
|
116138
|
+
const pages = editor.getPages();
|
|
116139
|
+
for (const page of pages) editor.deleteShapes([...editor.getPageShapeIds(page.id)]);
|
|
116140
|
+
for (const page of pages.slice(1)) editor.deletePage(page.id);
|
|
116141
|
+
editor.deleteAssets(editor.getAssets());
|
|
116142
|
+
editor.setCamera({
|
|
116143
|
+
x: 0,
|
|
116144
|
+
y: 0,
|
|
116145
|
+
z: 1
|
|
116146
|
+
});
|
|
116147
|
+
}, {
|
|
116148
|
+
history: "ignore",
|
|
116149
|
+
ignoreShapeLock: true
|
|
116150
|
+
});
|
|
116151
|
+
editor.clearHistory();
|
|
116152
|
+
stopSync = startCanvasSync(editor);
|
|
116153
|
+
};
|
|
116154
|
+
mountedCanvases.add(clear);
|
|
116117
116155
|
return () => {
|
|
116156
|
+
active = false;
|
|
116157
|
+
mountedCanvases.delete(clear);
|
|
116158
|
+
stopLanding();
|
|
116118
116159
|
stopSync();
|
|
116119
|
-
editorRef.current = null;
|
|
116120
116160
|
};
|
|
116121
116161
|
}
|
|
116122
116162
|
})
|
|
@@ -116221,13 +116261,19 @@ img.ProseMirror-separator {
|
|
|
116221
116261
|
configuredCount: "API 已配置 · {count}",
|
|
116222
116262
|
unconfiguredStatus: "API 未配置",
|
|
116223
116263
|
providerStatus: "云端提供商与模型状态",
|
|
116264
|
+
configuredSplit: "API {api} · 订阅 {sub}",
|
|
116265
|
+
subSignedIn: "已登录",
|
|
116266
|
+
subSignedOut: "未登录",
|
|
116224
116267
|
collapseSidebar: "折叠最近生成",
|
|
116225
116268
|
expandSidebar: "展开最近生成",
|
|
116226
116269
|
collapseGenerate: "折叠生成面板",
|
|
116227
116270
|
expandGenerate: "展开生成面板",
|
|
116228
116271
|
generatePanel: "生成面板",
|
|
116229
116272
|
findInspiration: "找灵感",
|
|
116230
|
-
infiniteCanvasHint: "无限画布与侧边栏会话联动,才是完整功能。打开 DSH 右侧边栏的「图像工作台」即可解锁。"
|
|
116273
|
+
infiniteCanvasHint: "无限画布与侧边栏会话联动,才是完整功能。打开 DSH 右侧边栏的「图像工作台」即可解锁。",
|
|
116274
|
+
clearCanvas: "清空画布",
|
|
116275
|
+
clearCanvasDescription: "将永久清空所有画布页面的内容、图片资产和撤销历史。画廊已保存的卡片及原图不受影响,此操作无法撤销。",
|
|
116276
|
+
canvasCleared: "画布已清空,画廊不受影响"
|
|
116231
116277
|
},
|
|
116232
116278
|
en: {
|
|
116233
116279
|
title: "Cloud Image Studio",
|
|
@@ -116323,18 +116369,24 @@ img.ProseMirror-separator {
|
|
|
116323
116369
|
configuredCount: "API configured · {count}",
|
|
116324
116370
|
unconfiguredStatus: "Not configured",
|
|
116325
116371
|
providerStatus: "Provider & Model Status",
|
|
116372
|
+
configuredSplit: "API {api} · Sub {sub}",
|
|
116373
|
+
subSignedIn: "Signed in",
|
|
116374
|
+
subSignedOut: "Signed out",
|
|
116326
116375
|
collapseSidebar: "Collapse sidebar",
|
|
116327
116376
|
expandSidebar: "Expand recent list",
|
|
116328
116377
|
collapseGenerate: "Collapse generate panel",
|
|
116329
116378
|
expandGenerate: "Expand generate panel",
|
|
116330
116379
|
generatePanel: "Generate",
|
|
116331
116380
|
findInspiration: "Find inspiration",
|
|
116332
|
-
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."
|
|
116381
|
+
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.",
|
|
116382
|
+
clearCanvas: "Clear canvas",
|
|
116383
|
+
clearCanvasDescription: "Permanently clear all canvas pages, image assets, and undo history. Saved gallery cards and original images are unaffected. This cannot be undone.",
|
|
116384
|
+
canvasCleared: "Canvas cleared. Gallery unchanged."
|
|
116333
116385
|
}
|
|
116334
116386
|
};
|
|
116335
116387
|
/** localStorage flag remembering that the one-time infinite-canvas hint was dismissed. */
|
|
116336
116388
|
const INFINITE_CANVAS_HINT_KEY = "dsh-ig-infinite-canvas-hint-dismissed";
|
|
116337
|
-
const StudioView = ({ locale, workspace, initialPrompt, initialCanvasSurface, showInfiniteCanvasHint, onInitialPromptApplied, onOpenInspiration }) => {
|
|
116389
|
+
const StudioView = ({ locale, credentialEvents, workspace, initialPrompt, initialCanvasSurface, showInfiniteCanvasHint, onInitialPromptApplied, onOpenInspiration }) => {
|
|
116338
116390
|
const [lang, setLang] = (0, react.useState)(() => locale?.getSnapshot?.().active?.startsWith("en") ? "en" : "zh");
|
|
116339
116391
|
const [config, setConfig] = (0, react.useState)(null);
|
|
116340
116392
|
const [configLoading, setConfigLoading] = (0, react.useState)(true);
|
|
@@ -116377,6 +116429,15 @@ img.ProseMirror-separator {
|
|
|
116377
116429
|
const dragStartRef = (0, react.useRef)(null);
|
|
116378
116430
|
const hasDraggedRef = (0, react.useRef)(false);
|
|
116379
116431
|
const [canvasSurface, setCanvasSurface] = (0, react.useState)(initialCanvasSurface ?? "preview");
|
|
116432
|
+
const [showClearCanvasModal, setShowClearCanvasModal] = (0, react.useState)(false);
|
|
116433
|
+
(0, react.useEffect)(() => {
|
|
116434
|
+
if (!showClearCanvasModal) return;
|
|
116435
|
+
const close = (event) => {
|
|
116436
|
+
if (event.key === "Escape") setShowClearCanvasModal(false);
|
|
116437
|
+
};
|
|
116438
|
+
window.addEventListener("keydown", close);
|
|
116439
|
+
return () => window.removeEventListener("keydown", close);
|
|
116440
|
+
}, [showClearCanvasModal]);
|
|
116380
116441
|
/** Generate-panel state from before the infinite canvas was opened, so the
|
|
116381
116442
|
* panel is restored (not just forced open) when returning to preview. */
|
|
116382
116443
|
const generatePanelBeforeInfiniteRef = (0, react.useRef)(false);
|
|
@@ -116432,15 +116493,28 @@ img.ProseMirror-separator {
|
|
|
116432
116493
|
const maxReferences = comparisonEnabled ? comparisonProviders.includes("dashscope") ? 3 : 5 : provider === "dashscope" ? 3 : 5;
|
|
116433
116494
|
const referencesRef = (0, react.useRef)(references);
|
|
116434
116495
|
referencesRef.current = references;
|
|
116496
|
+
const comparisonSelectionsAllSubscription = comparisonEnabled && comparisonProviders.length > 0 && comparisonProviders.every((provider) => isSubscriptionProvider(provider));
|
|
116497
|
+
const singleProviderIsSubscription = !comparisonEnabled && isSubscriptionProvider(provider);
|
|
116435
116498
|
const t = (key, values) => {
|
|
116436
116499
|
let text = COPY$1[lang][key];
|
|
116437
116500
|
for (const [name, value] of Object.entries(values ?? {})) text = text.replace(`{${name}}`, value);
|
|
116438
116501
|
return text;
|
|
116439
116502
|
};
|
|
116440
116503
|
(0, react.useEffect)(() => locale?.subscribe?.(() => setLang(locale.getSnapshot().active?.startsWith("en") ? "en" : "zh")), [locale]);
|
|
116441
|
-
const
|
|
116442
|
-
|
|
116443
|
-
|
|
116504
|
+
const formStateRef = (0, react.useRef)({
|
|
116505
|
+
provider,
|
|
116506
|
+
model
|
|
116507
|
+
});
|
|
116508
|
+
formStateRef.current = {
|
|
116509
|
+
provider,
|
|
116510
|
+
model
|
|
116511
|
+
};
|
|
116512
|
+
const loadConfig = (0, react.useCallback)(async (options) => {
|
|
116513
|
+
const silent = options?.silent === true;
|
|
116514
|
+
if (!silent) {
|
|
116515
|
+
setConfigLoading(true);
|
|
116516
|
+
setConfigError(null);
|
|
116517
|
+
}
|
|
116444
116518
|
const controller = new AbortController();
|
|
116445
116519
|
try {
|
|
116446
116520
|
const response = await fetch(STUDIO_ROUTE, {
|
|
@@ -116450,20 +116524,35 @@ img.ProseMirror-separator {
|
|
|
116450
116524
|
const payload = await response.json();
|
|
116451
116525
|
if (!response.ok || !("providers" in payload)) throw new Error("error" in payload && payload.error ? payload.error : "Studio unavailable");
|
|
116452
116526
|
setConfig(payload);
|
|
116527
|
+
if (silent) {
|
|
116528
|
+
const { provider: currentProvider, model: currentModel } = formStateRef.current;
|
|
116529
|
+
const activeProfile = payload.providers.find((item) => item.provider === currentProvider);
|
|
116530
|
+
if (activeProfile !== void 0 && activeProfile.model !== currentModel) setModel(activeProfile.model);
|
|
116531
|
+
setComparisonProviders((current) => current.filter((selectedProvider) => payload.providers.some((item) => item.provider === selectedProvider && item.configured)));
|
|
116532
|
+
return;
|
|
116533
|
+
}
|
|
116453
116534
|
const initial = payload.providers.find((item) => item.provider === payload.activeProvider) ?? payload.providers[0];
|
|
116454
116535
|
if (initial !== void 0) {
|
|
116455
116536
|
applyProvider(initial);
|
|
116456
116537
|
setComparisonProviders(initialComparisonProviders(payload.providers, initial.provider));
|
|
116457
116538
|
}
|
|
116458
116539
|
} catch (fetchError) {
|
|
116459
|
-
if (!controller.signal.aborted) setConfigError(messageOf(fetchError));
|
|
116540
|
+
if (!silent && !controller.signal.aborted) setConfigError(messageOf(fetchError));
|
|
116460
116541
|
} finally {
|
|
116461
|
-
setConfigLoading(false);
|
|
116542
|
+
if (!silent) setConfigLoading(false);
|
|
116462
116543
|
}
|
|
116463
116544
|
}, []);
|
|
116464
116545
|
(0, react.useEffect)(() => {
|
|
116465
116546
|
loadConfig();
|
|
116466
116547
|
}, [loadConfig]);
|
|
116548
|
+
(0, react.useEffect)(() => {
|
|
116549
|
+
if (credentialEvents === void 0) return;
|
|
116550
|
+
return credentialEvents.listen(() => {
|
|
116551
|
+
window.setTimeout(() => {
|
|
116552
|
+
loadConfig({ silent: true });
|
|
116553
|
+
}, 1e3);
|
|
116554
|
+
});
|
|
116555
|
+
}, [credentialEvents, loadConfig]);
|
|
116467
116556
|
(0, react.useEffect)(() => {
|
|
116468
116557
|
let mounted = true;
|
|
116469
116558
|
const load = () => void getGalleryItems().then((next) => {
|
|
@@ -116535,6 +116624,8 @@ img.ProseMirror-separator {
|
|
|
116535
116624
|
}, []);
|
|
116536
116625
|
const activeProfile = (0, react.useMemo)(() => config?.providers.find((item) => item.provider === provider), [config, provider]);
|
|
116537
116626
|
const configuredCount = config?.providers.filter((item) => item.configured).length ?? 0;
|
|
116627
|
+
const apiConfiguredCount = config?.providers.filter((item) => item.configured && !isSubscriptionProvider(item.provider)).length ?? 0;
|
|
116628
|
+
const subscriptionSignedInCount = config?.providers.filter((item) => item.configured && isSubscriptionProvider(item.provider)).length ?? 0;
|
|
116538
116629
|
const displayItems = (0, react.useMemo)(() => items.slice(0, visibleLimit), [items, visibleLimit]);
|
|
116539
116630
|
const comparisonProfiles = (0, react.useMemo)(() => (config?.providers ?? []).filter((item) => item.configured && (mode === "generate" || item.supportsEditing)), [config, mode]);
|
|
116540
116631
|
const comparisonTargets = (0, react.useMemo)(() => buildComparisonTargets(comparisonProfiles, comparisonProviders, ratio, quality), [
|
|
@@ -117130,7 +117221,10 @@ img.ProseMirror-separator {
|
|
|
117130
117221
|
(0, react_jsx_runtime.jsx)("span", { className: `dsh-ig-status-dot ${configuredCount > 0 ? "is-ready" : "is-muted"}` }),
|
|
117131
117222
|
(0, react_jsx_runtime.jsx)("span", {
|
|
117132
117223
|
className: "dsh-ig-status-text",
|
|
117133
|
-
children: configuredCount > 0 ?
|
|
117224
|
+
children: configuredCount > 0 ? subscriptionSignedInCount > 0 ? t("configuredSplit", {
|
|
117225
|
+
api: String(apiConfiguredCount),
|
|
117226
|
+
sub: String(subscriptionSignedInCount)
|
|
117227
|
+
}) : t("configuredCount", { count: String(apiConfiguredCount) }) : t("unconfiguredStatus")
|
|
117134
117228
|
}),
|
|
117135
117229
|
(0, react_jsx_runtime.jsx)(ChevronDown, {
|
|
117136
117230
|
size: 11,
|
|
@@ -117151,7 +117245,7 @@ img.ProseMirror-separator {
|
|
|
117151
117245
|
children: [(0, react_jsx_runtime.jsx)("strong", { children: p.label }), (0, react_jsx_runtime.jsx)("small", { children: p.model })]
|
|
117152
117246
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
117153
117247
|
className: "dsh-ig-status-item-tag",
|
|
117154
|
-
children: p.configured ? t("configured") : t("unconfigured")
|
|
117248
|
+
children: isSubscriptionProvider(p.provider) ? p.configured ? t("subSignedIn") : t("subSignedOut") : p.configured ? t("configured") : t("unconfigured")
|
|
117155
117249
|
})]
|
|
117156
117250
|
}, p.provider))
|
|
117157
117251
|
})]
|
|
@@ -117214,6 +117308,12 @@ img.ProseMirror-separator {
|
|
|
117214
117308
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
117215
117309
|
className: "dsh-ig-canvas-toolbar-right",
|
|
117216
117310
|
children: [
|
|
117311
|
+
canvasSurface === "infinite" && (0, react_jsx_runtime.jsxs)("button", {
|
|
117312
|
+
type: "button",
|
|
117313
|
+
onClick: () => setShowClearCanvasModal(true),
|
|
117314
|
+
title: t("clearCanvas"),
|
|
117315
|
+
children: [(0, react_jsx_runtime.jsx)(Trash2, { size: 14 }), (0, react_jsx_runtime.jsx)("span", { children: t("clearCanvas") })]
|
|
117316
|
+
}),
|
|
117217
117317
|
canvasSurface === "preview" && selected !== null && (0, react_jsx_runtime.jsxs)("button", {
|
|
117218
117318
|
type: "button",
|
|
117219
117319
|
onClick: startNew,
|
|
@@ -117591,7 +117691,26 @@ img.ProseMirror-separator {
|
|
|
117591
117691
|
}),
|
|
117592
117692
|
(0, react_jsx_runtime.jsx)("p", { children: t("compareParameterHint") })
|
|
117593
117693
|
]
|
|
117594
|
-
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
117694
|
+
}), comparisonSelectionsAllSubscription ? (0, react_jsx_runtime.jsxs)("div", {
|
|
117695
|
+
className: "dsh-ig-field-grid",
|
|
117696
|
+
children: [(0, react_jsx_runtime.jsx)(FieldSelect, {
|
|
117697
|
+
label: t("ratio"),
|
|
117698
|
+
value: "auto",
|
|
117699
|
+
onChange: () => {},
|
|
117700
|
+
options: [{
|
|
117701
|
+
value: "auto",
|
|
117702
|
+
label: lang === "en" ? "Channel default" : "通道默认"
|
|
117703
|
+
}]
|
|
117704
|
+
}), (0, react_jsx_runtime.jsx)(FieldSelect, {
|
|
117705
|
+
label: t("quality"),
|
|
117706
|
+
value: "auto",
|
|
117707
|
+
onChange: () => {},
|
|
117708
|
+
options: [{
|
|
117709
|
+
value: "auto",
|
|
117710
|
+
label: lang === "en" ? "Channel default" : "通道默认"
|
|
117711
|
+
}]
|
|
117712
|
+
})]
|
|
117713
|
+
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
117595
117714
|
className: "dsh-ig-field-grid",
|
|
117596
117715
|
children: [(0, react_jsx_runtime.jsx)(FieldSelect, {
|
|
117597
117716
|
label: t("ratio"),
|
|
@@ -117625,7 +117744,26 @@ img.ProseMirror-separator {
|
|
|
117625
117744
|
}]
|
|
117626
117745
|
})]
|
|
117627
117746
|
}),
|
|
117628
|
-
(0, react_jsx_runtime.jsxs)("div", {
|
|
117747
|
+
singleProviderIsSubscription ? (0, react_jsx_runtime.jsxs)("div", {
|
|
117748
|
+
className: "dsh-ig-field-grid",
|
|
117749
|
+
children: [(0, react_jsx_runtime.jsx)(FieldSelect, {
|
|
117750
|
+
label: t("ratio"),
|
|
117751
|
+
value: "auto",
|
|
117752
|
+
onChange: () => {},
|
|
117753
|
+
options: [{
|
|
117754
|
+
value: "auto",
|
|
117755
|
+
label: lang === "en" ? "Channel default" : "通道默认"
|
|
117756
|
+
}]
|
|
117757
|
+
}), (0, react_jsx_runtime.jsx)(FieldSelect, {
|
|
117758
|
+
label: t("quality"),
|
|
117759
|
+
value: "auto",
|
|
117760
|
+
onChange: () => {},
|
|
117761
|
+
options: [{
|
|
117762
|
+
value: "auto",
|
|
117763
|
+
label: lang === "en" ? "Channel default" : "通道默认"
|
|
117764
|
+
}]
|
|
117765
|
+
})]
|
|
117766
|
+
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
117629
117767
|
className: "dsh-ig-field-grid",
|
|
117630
117768
|
children: [(0, react_jsx_runtime.jsx)(FieldSelect, {
|
|
117631
117769
|
label: t("ratio"),
|
|
@@ -117820,6 +117958,57 @@ img.ProseMirror-separator {
|
|
|
117820
117958
|
})
|
|
117821
117959
|
]
|
|
117822
117960
|
}),
|
|
117961
|
+
showClearCanvasModal && (0, react_jsx_runtime.jsx)("div", {
|
|
117962
|
+
className: "dsh-ig-workbench-modal-backdrop",
|
|
117963
|
+
onClick: () => setShowClearCanvasModal(false),
|
|
117964
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
117965
|
+
className: "dsh-ig-workbench-modal-box",
|
|
117966
|
+
role: "dialog",
|
|
117967
|
+
"aria-modal": "true",
|
|
117968
|
+
"aria-label": t("clearCanvas"),
|
|
117969
|
+
onClick: (event) => event.stopPropagation(),
|
|
117970
|
+
children: [
|
|
117971
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
117972
|
+
className: "dsh-ig-workbench-modal-header",
|
|
117973
|
+
children: [(0, react_jsx_runtime.jsx)(TriangleAlert, {
|
|
117974
|
+
size: 20,
|
|
117975
|
+
color: "#dc2626"
|
|
117976
|
+
}), (0, react_jsx_runtime.jsx)("strong", { children: t("clearCanvas") })]
|
|
117977
|
+
}),
|
|
117978
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
117979
|
+
className: "dsh-ig-workbench-modal-desc",
|
|
117980
|
+
children: t("clearCanvasDescription")
|
|
117981
|
+
}),
|
|
117982
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
117983
|
+
className: "dsh-ig-workbench-modal-actions",
|
|
117984
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
117985
|
+
autoFocus: true,
|
|
117986
|
+
type: "button",
|
|
117987
|
+
className: "dsh-ig-workbench-modal-cancel",
|
|
117988
|
+
onClick: () => setShowClearCanvasModal(false),
|
|
117989
|
+
children: t("cancel")
|
|
117990
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
117991
|
+
type: "button",
|
|
117992
|
+
className: "dsh-ig-workbench-modal-danger",
|
|
117993
|
+
onClick: () => {
|
|
117994
|
+
try {
|
|
117995
|
+
if (!clearStudioTlCanvases()) {
|
|
117996
|
+
flash(t("loading"));
|
|
117997
|
+
return;
|
|
117998
|
+
}
|
|
117999
|
+
setShowClearCanvasModal(false);
|
|
118000
|
+
flash(t("canvasCleared"));
|
|
118001
|
+
} catch (error) {
|
|
118002
|
+
setError(messageOf(error));
|
|
118003
|
+
setShowClearCanvasModal(false);
|
|
118004
|
+
}
|
|
118005
|
+
},
|
|
118006
|
+
children: t("clearCanvas")
|
|
118007
|
+
})]
|
|
118008
|
+
})
|
|
118009
|
+
]
|
|
118010
|
+
})
|
|
118011
|
+
}),
|
|
117823
118012
|
showDeleteModal && selected !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
117824
118013
|
className: "dsh-ig-workbench-modal-backdrop",
|
|
117825
118014
|
onClick: () => setShowDeleteModal(false),
|
|
@@ -119427,7 +119616,7 @@ img.ProseMirror-separator {
|
|
|
119427
119616
|
return "1:1";
|
|
119428
119617
|
}
|
|
119429
119618
|
const GalleryViewTab = (props) => {
|
|
119430
|
-
const { locale, sessionId, useSessions, useWorkspaces, inSidebar, defaultTab, initialCanvasSurface } = props;
|
|
119619
|
+
const { locale, sessionId, useSessions, useWorkspaces, credentialEvents, inSidebar, defaultTab, initialCanvasSurface } = props;
|
|
119431
119620
|
const [activeTab, setActiveTab] = (0, react.useState)(defaultTab ?? "gallery");
|
|
119432
119621
|
const [studioDraft, setStudioDraft] = (0, react.useState)(void 0);
|
|
119433
119622
|
const [items, setItems] = (0, react.useState)([]);
|
|
@@ -120184,6 +120373,7 @@ img.ProseMirror-separator {
|
|
|
120184
120373
|
onUsePrompt: useInspirationPrompt
|
|
120185
120374
|
}) : (0, react_jsx_runtime.jsx)(StudioView, {
|
|
120186
120375
|
locale,
|
|
120376
|
+
credentialEvents,
|
|
120187
120377
|
workspace: activeWorkspace,
|
|
120188
120378
|
initialPrompt: studioDraft,
|
|
120189
120379
|
initialCanvasSurface,
|
|
@@ -120709,6 +120899,266 @@ img.ProseMirror-separator {
|
|
|
120709
120899
|
if (fromBlockContent?.type === "image") return fromBlockContent.attachment;
|
|
120710
120900
|
}
|
|
120711
120901
|
//#endregion
|
|
120902
|
+
//#region lib/types/client/image-result-node.js
|
|
120903
|
+
const IMAGE_RESULT_NODE_KIND = "dsh-image-result";
|
|
120904
|
+
/**
|
|
120905
|
+
* One image result row per Turn, anchored at its own tool/result position while
|
|
120906
|
+
* the Turn runs, then optionally re-anchored beside the final answer once the
|
|
120907
|
+
* Turn closes (only needed under the folding Compact transcript view).
|
|
120908
|
+
*/
|
|
120909
|
+
function createImageResultDefinition(options = {}) {
|
|
120910
|
+
return {
|
|
120911
|
+
kind: IMAGE_RESULT_NODE_KIND,
|
|
120912
|
+
target: "chat",
|
|
120913
|
+
match: (event) => {
|
|
120914
|
+
if (event.type === "tool/ptc-dispatch") {
|
|
120915
|
+
const subCallId = event.data.subCallId;
|
|
120916
|
+
if (typeof subCallId !== "string" || subCallId === "") return null;
|
|
120917
|
+
return imageResultFromPtcDispatch(event) === void 0 ? null : {
|
|
120918
|
+
id: `ptc:${subCallId}`,
|
|
120919
|
+
role: "start"
|
|
120920
|
+
};
|
|
120921
|
+
}
|
|
120922
|
+
const turn = eventTurn(event);
|
|
120923
|
+
if (turn === void 0) return null;
|
|
120924
|
+
if (event.type === "turn/start") return {
|
|
120925
|
+
id: String(turn),
|
|
120926
|
+
role: "start"
|
|
120927
|
+
};
|
|
120928
|
+
if (event.type === "turn/end") return {
|
|
120929
|
+
id: String(turn),
|
|
120930
|
+
role: "update"
|
|
120931
|
+
};
|
|
120932
|
+
if (event.type === "assistant/message") return {
|
|
120933
|
+
id: String(turn),
|
|
120934
|
+
role: "update"
|
|
120935
|
+
};
|
|
120936
|
+
if (event.type === "tool/result" && imageResultFromMeta(event.data.meta) !== void 0) return {
|
|
120937
|
+
id: String(turn),
|
|
120938
|
+
role: "update"
|
|
120939
|
+
};
|
|
120940
|
+
return null;
|
|
120941
|
+
},
|
|
120942
|
+
start: (_context, match) => {
|
|
120943
|
+
if (match.event.type === "tool/ptc-dispatch") {
|
|
120944
|
+
const result = imageResultFromPtcDispatch(match.event);
|
|
120945
|
+
if (result === void 0) throw new Error("dsh-image-result start requires a plugin image dispatch");
|
|
120946
|
+
return { results: [{
|
|
120947
|
+
...result,
|
|
120948
|
+
seq: match.event.seq
|
|
120949
|
+
}] };
|
|
120950
|
+
}
|
|
120951
|
+
const turn = eventTurn(match.event);
|
|
120952
|
+
if (match.event.type !== "turn/start" || turn === void 0) throw new Error("dsh-image-result start requires turn/start");
|
|
120953
|
+
return {
|
|
120954
|
+
turn,
|
|
120955
|
+
results: []
|
|
120956
|
+
};
|
|
120957
|
+
},
|
|
120958
|
+
update: (context, match) => {
|
|
120959
|
+
if (match.event.type === "turn/end") return {
|
|
120960
|
+
...context.state,
|
|
120961
|
+
endSeq: match.event.seq
|
|
120962
|
+
};
|
|
120963
|
+
if (match.event.type === "assistant/message") return {
|
|
120964
|
+
...context.state,
|
|
120965
|
+
answerSeq: match.event.seq
|
|
120966
|
+
};
|
|
120967
|
+
if (match.event.type === "tool/ptc-dispatch") {
|
|
120968
|
+
const result = imageResultFromPtcDispatch(match.event);
|
|
120969
|
+
return result === void 0 ? context.state : appendResult(context.state, result, match.event.seq);
|
|
120970
|
+
}
|
|
120971
|
+
if (match.event.type !== "tool/result") return context.state;
|
|
120972
|
+
const result = imageResultFromMeta(match.event.data.meta);
|
|
120973
|
+
if (result === void 0) return context.state;
|
|
120974
|
+
return appendResult(context.state, result, match.event.seq);
|
|
120975
|
+
},
|
|
120976
|
+
buildViewNode: (context) => {
|
|
120977
|
+
const state = context.state;
|
|
120978
|
+
const last = state?.results.at(-1);
|
|
120979
|
+
if (state === void 0 || last === void 0) return null;
|
|
120980
|
+
const location = context.matches.at(-1)?.location ?? context.start?.location ?? { kind: "unresolved" };
|
|
120981
|
+
return {
|
|
120982
|
+
key: context.key,
|
|
120983
|
+
kind: IMAGE_RESULT_NODE_KIND,
|
|
120984
|
+
id: context.id,
|
|
120985
|
+
target: "chat",
|
|
120986
|
+
anchorSeq: state.endSeq !== void 0 && compactTranscript(options) ? state.answerSeq ?? state.endSeq ?? last.seq : last.seq,
|
|
120987
|
+
location,
|
|
120988
|
+
visibility: "visible",
|
|
120989
|
+
data: {
|
|
120990
|
+
...state.turn !== void 0 ? { turn: state.turn } : {},
|
|
120991
|
+
results: state.results.map(({ seq: _seq, ...result }) => result)
|
|
120992
|
+
}
|
|
120993
|
+
};
|
|
120994
|
+
}
|
|
120995
|
+
};
|
|
120996
|
+
}
|
|
120997
|
+
/** Append a parsed result unless its attachment is already collected. */
|
|
120998
|
+
function appendResult(state, result, seq) {
|
|
120999
|
+
if (state.results.some((candidate) => candidate.attachment.attachmentId === result.attachment.attachmentId)) return state;
|
|
121000
|
+
return {
|
|
121001
|
+
...state,
|
|
121002
|
+
results: [...state.results, {
|
|
121003
|
+
...result,
|
|
121004
|
+
seq
|
|
121005
|
+
}]
|
|
121006
|
+
};
|
|
121007
|
+
}
|
|
121008
|
+
function compactTranscript(options) {
|
|
121009
|
+
try {
|
|
121010
|
+
return options.isCompactTranscript?.() ?? true;
|
|
121011
|
+
} catch {
|
|
121012
|
+
return true;
|
|
121013
|
+
}
|
|
121014
|
+
}
|
|
121015
|
+
/** Parse the plugin-owned durable presentation metadata from a Tool result event. */
|
|
121016
|
+
function imageResultFromMeta(value) {
|
|
121017
|
+
const meta = record(value);
|
|
121018
|
+
if (meta?.kind !== "dsh-image-gen") return void 0;
|
|
121019
|
+
const attachment = imageAttachment(meta.attachment);
|
|
121020
|
+
if (attachment === void 0) return void 0;
|
|
121021
|
+
return {
|
|
121022
|
+
attachment,
|
|
121023
|
+
prompt: stringValue(meta.prompt, "Generated Image"),
|
|
121024
|
+
provider: stringValue(meta.provider, "google"),
|
|
121025
|
+
model: stringValue(meta.model, ""),
|
|
121026
|
+
output: stringValue(meta.output, ""),
|
|
121027
|
+
...typeof meta.savedTo === "string" ? { savedTo: meta.savedTo } : {},
|
|
121028
|
+
...typeof meta.seed === "number" ? { seed: meta.seed } : {}
|
|
121029
|
+
};
|
|
121030
|
+
}
|
|
121031
|
+
function eventTurn(event) {
|
|
121032
|
+
const turn = event.data.turn;
|
|
121033
|
+
return typeof turn === "number" && Number.isSafeInteger(turn) && turn >= 0 ? turn : void 0;
|
|
121034
|
+
}
|
|
121035
|
+
/** Plugin tools whose PTC (run_code) dispatch results are image results. */
|
|
121036
|
+
const PTC_IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set(["generate_image", "edit_image"]);
|
|
121037
|
+
/**
|
|
121038
|
+
* Fixed summary text rendered by the tools' imageOutput(). PTC dispatch events
|
|
121039
|
+
* carry no presentationMeta, so provider/model/output are recovered from this
|
|
121040
|
+
* template rather than parsed out of natural language.
|
|
121041
|
+
*/
|
|
121042
|
+
const PTC_SUMMARY_PATTERN = /^(?:Generated|Edited) one image with ([^/\s]+)\/(\S+) \((.+)\)\. Attachment ID: /;
|
|
121043
|
+
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))/;
|
|
121044
|
+
/**
|
|
121045
|
+
* Parse a plugin image result from a tool/ptc-dispatch event (#38). PTC
|
|
121046
|
+
* run_code sub-calls carry neither a turn nor presentationMeta, so the result
|
|
121047
|
+
* is rebuilt from the dispatch payload itself: the image content block for the
|
|
121048
|
+
* attachment, arguments for the prompt (and edit source ids), and the tool's
|
|
121049
|
+
* fixed-format summary text for provider/model/output. Returns undefined for
|
|
121050
|
+
* anything that is not a successful image result from our own image tools.
|
|
121051
|
+
*/
|
|
121052
|
+
function imageResultFromPtcDispatch(event) {
|
|
121053
|
+
if (event.type !== "tool/ptc-dispatch") return void 0;
|
|
121054
|
+
const data = event.data;
|
|
121055
|
+
if (data.isError === true) return void 0;
|
|
121056
|
+
if (typeof data.name !== "string" || !PTC_IMAGE_TOOL_NAMES.has(data.name)) return void 0;
|
|
121057
|
+
const content = Array.isArray(data.content) ? data.content : [];
|
|
121058
|
+
let attachment;
|
|
121059
|
+
let summaryText = "";
|
|
121060
|
+
for (const block of content) {
|
|
121061
|
+
const candidate = record(block);
|
|
121062
|
+
if (candidate === void 0) continue;
|
|
121063
|
+
if (candidate.type === "image" && attachment === void 0) attachment = imageAttachment(candidate.attachment);
|
|
121064
|
+
else if (candidate.type === "text" && summaryText === "" && typeof candidate.text === "string") summaryText = candidate.text;
|
|
121065
|
+
}
|
|
121066
|
+
if (attachment === void 0) return void 0;
|
|
121067
|
+
const summary = PTC_SUMMARY_PATTERN.exec(summaryText);
|
|
121068
|
+
const savedTo = PTC_SAVED_TO_PATTERN.exec(summaryText)?.[1];
|
|
121069
|
+
const args = record(data.arguments);
|
|
121070
|
+
const sourceIds = sourceAttachmentIds(args);
|
|
121071
|
+
return {
|
|
121072
|
+
attachment,
|
|
121073
|
+
prompt: stringValue(args?.prompt, "Generated Image"),
|
|
121074
|
+
provider: summary?.[1] ?? "",
|
|
121075
|
+
model: summary?.[2] ?? "",
|
|
121076
|
+
output: summary?.[3] ?? "",
|
|
121077
|
+
...savedTo !== void 0 ? { savedTo } : {},
|
|
121078
|
+
...sourceIds !== void 0 ? { sourceAttachmentIds: sourceIds } : {}
|
|
121079
|
+
};
|
|
121080
|
+
}
|
|
121081
|
+
/** Collect edit_image source attachment ids (single or list form) from dispatch arguments. */
|
|
121082
|
+
function sourceAttachmentIds(args) {
|
|
121083
|
+
if (args === void 0) return void 0;
|
|
121084
|
+
const single = typeof args.source_attachment_id === "string" ? [args.source_attachment_id] : void 0;
|
|
121085
|
+
const list = Array.isArray(args.source_attachment_ids) ? args.source_attachment_ids.filter((id) => typeof id === "string") : void 0;
|
|
121086
|
+
const ids = single ?? list;
|
|
121087
|
+
return ids !== void 0 && ids.length > 0 ? ids : void 0;
|
|
121088
|
+
}
|
|
121089
|
+
function imageAttachment(value) {
|
|
121090
|
+
const candidate = record(value);
|
|
121091
|
+
if (candidate === void 0 || typeof candidate.attachmentId !== "string" || !isImageMediaType(candidate.mediaType) || !positiveInteger(candidate.bytes) || !positiveInteger(candidate.width) || !positiveInteger(candidate.height)) return void 0;
|
|
121092
|
+
const original = record(candidate.originalDimensions);
|
|
121093
|
+
if (candidate.originalDimensions !== void 0 && (original === void 0 || !positiveInteger(original.width) || !positiveInteger(original.height))) return void 0;
|
|
121094
|
+
return {
|
|
121095
|
+
attachmentId: candidate.attachmentId,
|
|
121096
|
+
mediaType: candidate.mediaType,
|
|
121097
|
+
bytes: candidate.bytes,
|
|
121098
|
+
width: candidate.width,
|
|
121099
|
+
height: candidate.height,
|
|
121100
|
+
...typeof candidate.name === "string" ? { name: candidate.name } : {},
|
|
121101
|
+
...original === void 0 ? {} : { originalDimensions: {
|
|
121102
|
+
width: original.width,
|
|
121103
|
+
height: original.height
|
|
121104
|
+
} }
|
|
121105
|
+
};
|
|
121106
|
+
}
|
|
121107
|
+
function isImageMediaType(value) {
|
|
121108
|
+
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
121109
|
+
}
|
|
121110
|
+
function positiveInteger(value) {
|
|
121111
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
121112
|
+
}
|
|
121113
|
+
function stringValue(value, fallback) {
|
|
121114
|
+
return typeof value === "string" ? value : fallback;
|
|
121115
|
+
}
|
|
121116
|
+
function record(value) {
|
|
121117
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
121118
|
+
}
|
|
121119
|
+
//#endregion
|
|
121120
|
+
//#region lib/types/client/tl/conversation-landings.js
|
|
121121
|
+
/** Subscribe to live additions, never to card rendering or history replay. */
|
|
121122
|
+
function startConversationLandings(sessions) {
|
|
121123
|
+
let source;
|
|
121124
|
+
let stopEvents;
|
|
121125
|
+
const followCurrent = () => {
|
|
121126
|
+
const id = sessions.list.getSnapshot().current;
|
|
121127
|
+
const next = id === void 0 ? void 0 : sessions.binding(id)?.eventSource;
|
|
121128
|
+
if (next === source) return;
|
|
121129
|
+
stopEvents?.();
|
|
121130
|
+
source = next;
|
|
121131
|
+
if (next === void 0) return;
|
|
121132
|
+
let lastSeq = next.getSnapshot().entries.reduce((seq, entry) => Math.max(seq, entry.event.seq), -1);
|
|
121133
|
+
stopEvents = next.subscribe(() => {
|
|
121134
|
+
const change = next.getSnapshot().change;
|
|
121135
|
+
for (const entry of change.entries ?? []) {
|
|
121136
|
+
if (entry.type !== "event") continue;
|
|
121137
|
+
const { event } = entry;
|
|
121138
|
+
const isNew = event.seq > lastSeq;
|
|
121139
|
+
lastSeq = Math.max(lastSeq, event.seq);
|
|
121140
|
+
if (change.kind !== "append" || !isNew) continue;
|
|
121141
|
+
const result = event.type === "tool/result" ? imageResultFromMeta(event.data.meta) : imageResultFromPtcDispatch(event);
|
|
121142
|
+
if (result === void 0) continue;
|
|
121143
|
+
pushTlLandingsLive([{
|
|
121144
|
+
galleryId: String(result.attachment.attachmentId),
|
|
121145
|
+
attachment: result.attachment,
|
|
121146
|
+
fromConversation: true,
|
|
121147
|
+
prompt: result.prompt,
|
|
121148
|
+
provider: result.provider,
|
|
121149
|
+
model: result.model
|
|
121150
|
+
}]);
|
|
121151
|
+
}
|
|
121152
|
+
});
|
|
121153
|
+
};
|
|
121154
|
+
const stopList = sessions.list.subscribe(followCurrent);
|
|
121155
|
+
followCurrent();
|
|
121156
|
+
return () => {
|
|
121157
|
+
stopList();
|
|
121158
|
+
stopEvents?.();
|
|
121159
|
+
};
|
|
121160
|
+
}
|
|
121161
|
+
//#endregion
|
|
120712
121162
|
//#region lib/types/client/studio-style.js
|
|
120713
121163
|
const STUDIO_STYLE = `
|
|
120714
121164
|
.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}
|
|
@@ -121050,224 +121500,6 @@ img.ProseMirror-separator {
|
|
|
121050
121500
|
.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)}
|
|
121051
121501
|
@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}}
|
|
121052
121502
|
`;
|
|
121053
|
-
//#endregion
|
|
121054
|
-
//#region lib/types/client/image-result-node.js
|
|
121055
|
-
const IMAGE_RESULT_NODE_KIND = "dsh-image-result";
|
|
121056
|
-
/**
|
|
121057
|
-
* One image result row per Turn, anchored at its own tool/result position while
|
|
121058
|
-
* the Turn runs, then optionally re-anchored beside the final answer once the
|
|
121059
|
-
* Turn closes (only needed under the folding Compact transcript view).
|
|
121060
|
-
*/
|
|
121061
|
-
function createImageResultDefinition(options = {}) {
|
|
121062
|
-
return {
|
|
121063
|
-
kind: IMAGE_RESULT_NODE_KIND,
|
|
121064
|
-
target: "chat",
|
|
121065
|
-
match: (event) => {
|
|
121066
|
-
if (event.type === "tool/ptc-dispatch") {
|
|
121067
|
-
const subCallId = event.data.subCallId;
|
|
121068
|
-
if (typeof subCallId !== "string" || subCallId === "") return null;
|
|
121069
|
-
return imageResultFromPtcDispatch(event) === void 0 ? null : {
|
|
121070
|
-
id: `ptc:${subCallId}`,
|
|
121071
|
-
role: "start"
|
|
121072
|
-
};
|
|
121073
|
-
}
|
|
121074
|
-
const turn = eventTurn(event);
|
|
121075
|
-
if (turn === void 0) return null;
|
|
121076
|
-
if (event.type === "turn/start") return {
|
|
121077
|
-
id: String(turn),
|
|
121078
|
-
role: "start"
|
|
121079
|
-
};
|
|
121080
|
-
if (event.type === "turn/end") return {
|
|
121081
|
-
id: String(turn),
|
|
121082
|
-
role: "update"
|
|
121083
|
-
};
|
|
121084
|
-
if (event.type === "assistant/message") return {
|
|
121085
|
-
id: String(turn),
|
|
121086
|
-
role: "update"
|
|
121087
|
-
};
|
|
121088
|
-
if (event.type === "tool/result" && imageResultFromMeta(event.data.meta) !== void 0) return {
|
|
121089
|
-
id: String(turn),
|
|
121090
|
-
role: "update"
|
|
121091
|
-
};
|
|
121092
|
-
return null;
|
|
121093
|
-
},
|
|
121094
|
-
start: (_context, match) => {
|
|
121095
|
-
if (match.event.type === "tool/ptc-dispatch") {
|
|
121096
|
-
const result = imageResultFromPtcDispatch(match.event);
|
|
121097
|
-
if (result === void 0) throw new Error("dsh-image-result start requires a plugin image dispatch");
|
|
121098
|
-
return { results: [{
|
|
121099
|
-
...result,
|
|
121100
|
-
seq: match.event.seq
|
|
121101
|
-
}] };
|
|
121102
|
-
}
|
|
121103
|
-
const turn = eventTurn(match.event);
|
|
121104
|
-
if (match.event.type !== "turn/start" || turn === void 0) throw new Error("dsh-image-result start requires turn/start");
|
|
121105
|
-
return {
|
|
121106
|
-
turn,
|
|
121107
|
-
results: []
|
|
121108
|
-
};
|
|
121109
|
-
},
|
|
121110
|
-
update: (context, match) => {
|
|
121111
|
-
if (match.event.type === "turn/end") return {
|
|
121112
|
-
...context.state,
|
|
121113
|
-
endSeq: match.event.seq
|
|
121114
|
-
};
|
|
121115
|
-
if (match.event.type === "assistant/message") return {
|
|
121116
|
-
...context.state,
|
|
121117
|
-
answerSeq: match.event.seq
|
|
121118
|
-
};
|
|
121119
|
-
if (match.event.type === "tool/ptc-dispatch") {
|
|
121120
|
-
const result = imageResultFromPtcDispatch(match.event);
|
|
121121
|
-
return result === void 0 ? context.state : appendResult(context.state, result, match.event.seq);
|
|
121122
|
-
}
|
|
121123
|
-
if (match.event.type !== "tool/result") return context.state;
|
|
121124
|
-
const result = imageResultFromMeta(match.event.data.meta);
|
|
121125
|
-
if (result === void 0) return context.state;
|
|
121126
|
-
return appendResult(context.state, result, match.event.seq);
|
|
121127
|
-
},
|
|
121128
|
-
buildViewNode: (context) => {
|
|
121129
|
-
const state = context.state;
|
|
121130
|
-
const last = state?.results.at(-1);
|
|
121131
|
-
if (state === void 0 || last === void 0) return null;
|
|
121132
|
-
const location = context.matches.at(-1)?.location ?? context.start?.location ?? { kind: "unresolved" };
|
|
121133
|
-
return {
|
|
121134
|
-
key: context.key,
|
|
121135
|
-
kind: IMAGE_RESULT_NODE_KIND,
|
|
121136
|
-
id: context.id,
|
|
121137
|
-
target: "chat",
|
|
121138
|
-
anchorSeq: state.endSeq !== void 0 && compactTranscript(options) ? state.answerSeq ?? state.endSeq ?? last.seq : last.seq,
|
|
121139
|
-
location,
|
|
121140
|
-
visibility: "visible",
|
|
121141
|
-
data: {
|
|
121142
|
-
...state.turn !== void 0 ? { turn: state.turn } : {},
|
|
121143
|
-
results: state.results.map(({ seq: _seq, ...result }) => result)
|
|
121144
|
-
}
|
|
121145
|
-
};
|
|
121146
|
-
}
|
|
121147
|
-
};
|
|
121148
|
-
}
|
|
121149
|
-
/** Append a parsed result unless its attachment is already collected. */
|
|
121150
|
-
function appendResult(state, result, seq) {
|
|
121151
|
-
if (state.results.some((candidate) => candidate.attachment.attachmentId === result.attachment.attachmentId)) return state;
|
|
121152
|
-
return {
|
|
121153
|
-
...state,
|
|
121154
|
-
results: [...state.results, {
|
|
121155
|
-
...result,
|
|
121156
|
-
seq
|
|
121157
|
-
}]
|
|
121158
|
-
};
|
|
121159
|
-
}
|
|
121160
|
-
function compactTranscript(options) {
|
|
121161
|
-
try {
|
|
121162
|
-
return options.isCompactTranscript?.() ?? true;
|
|
121163
|
-
} catch {
|
|
121164
|
-
return true;
|
|
121165
|
-
}
|
|
121166
|
-
}
|
|
121167
|
-
/** Parse the plugin-owned durable presentation metadata from a Tool result event. */
|
|
121168
|
-
function imageResultFromMeta(value) {
|
|
121169
|
-
const meta = record(value);
|
|
121170
|
-
if (meta?.kind !== "dsh-image-gen") return void 0;
|
|
121171
|
-
const attachment = imageAttachment(meta.attachment);
|
|
121172
|
-
if (attachment === void 0) return void 0;
|
|
121173
|
-
return {
|
|
121174
|
-
attachment,
|
|
121175
|
-
prompt: stringValue(meta.prompt, "Generated Image"),
|
|
121176
|
-
provider: stringValue(meta.provider, "google"),
|
|
121177
|
-
model: stringValue(meta.model, ""),
|
|
121178
|
-
output: stringValue(meta.output, ""),
|
|
121179
|
-
...typeof meta.savedTo === "string" ? { savedTo: meta.savedTo } : {},
|
|
121180
|
-
...typeof meta.seed === "number" ? { seed: meta.seed } : {}
|
|
121181
|
-
};
|
|
121182
|
-
}
|
|
121183
|
-
function eventTurn(event) {
|
|
121184
|
-
const turn = event.data.turn;
|
|
121185
|
-
return typeof turn === "number" && Number.isSafeInteger(turn) && turn >= 0 ? turn : void 0;
|
|
121186
|
-
}
|
|
121187
|
-
/** Plugin tools whose PTC (run_code) dispatch results are image results. */
|
|
121188
|
-
const PTC_IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set(["generate_image", "edit_image"]);
|
|
121189
|
-
/**
|
|
121190
|
-
* Fixed summary text rendered by the tools' imageOutput(). PTC dispatch events
|
|
121191
|
-
* carry no presentationMeta, so provider/model/output are recovered from this
|
|
121192
|
-
* template rather than parsed out of natural language.
|
|
121193
|
-
*/
|
|
121194
|
-
const PTC_SUMMARY_PATTERN = /^(?:Generated|Edited) one image with ([^/\s]+)\/(\S+) \((.+)\)\. Attachment ID: /;
|
|
121195
|
-
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))/;
|
|
121196
|
-
/**
|
|
121197
|
-
* Parse a plugin image result from a tool/ptc-dispatch event (#38). PTC
|
|
121198
|
-
* run_code sub-calls carry neither a turn nor presentationMeta, so the result
|
|
121199
|
-
* is rebuilt from the dispatch payload itself: the image content block for the
|
|
121200
|
-
* attachment, arguments for the prompt (and edit source ids), and the tool's
|
|
121201
|
-
* fixed-format summary text for provider/model/output. Returns undefined for
|
|
121202
|
-
* anything that is not a successful image result from our own image tools.
|
|
121203
|
-
*/
|
|
121204
|
-
function imageResultFromPtcDispatch(event) {
|
|
121205
|
-
if (event.type !== "tool/ptc-dispatch") return void 0;
|
|
121206
|
-
const data = event.data;
|
|
121207
|
-
if (data.isError === true) return void 0;
|
|
121208
|
-
if (typeof data.name !== "string" || !PTC_IMAGE_TOOL_NAMES.has(data.name)) return void 0;
|
|
121209
|
-
const content = Array.isArray(data.content) ? data.content : [];
|
|
121210
|
-
let attachment;
|
|
121211
|
-
let summaryText = "";
|
|
121212
|
-
for (const block of content) {
|
|
121213
|
-
const candidate = record(block);
|
|
121214
|
-
if (candidate === void 0) continue;
|
|
121215
|
-
if (candidate.type === "image" && attachment === void 0) attachment = imageAttachment(candidate.attachment);
|
|
121216
|
-
else if (candidate.type === "text" && summaryText === "" && typeof candidate.text === "string") summaryText = candidate.text;
|
|
121217
|
-
}
|
|
121218
|
-
if (attachment === void 0) return void 0;
|
|
121219
|
-
const summary = PTC_SUMMARY_PATTERN.exec(summaryText);
|
|
121220
|
-
const savedTo = PTC_SAVED_TO_PATTERN.exec(summaryText)?.[1];
|
|
121221
|
-
const args = record(data.arguments);
|
|
121222
|
-
const sourceIds = sourceAttachmentIds(args);
|
|
121223
|
-
return {
|
|
121224
|
-
attachment,
|
|
121225
|
-
prompt: stringValue(args?.prompt, "Generated Image"),
|
|
121226
|
-
provider: summary?.[1] ?? "",
|
|
121227
|
-
model: summary?.[2] ?? "",
|
|
121228
|
-
output: summary?.[3] ?? "",
|
|
121229
|
-
...savedTo !== void 0 ? { savedTo } : {},
|
|
121230
|
-
...sourceIds !== void 0 ? { sourceAttachmentIds: sourceIds } : {}
|
|
121231
|
-
};
|
|
121232
|
-
}
|
|
121233
|
-
/** Collect edit_image source attachment ids (single or list form) from dispatch arguments. */
|
|
121234
|
-
function sourceAttachmentIds(args) {
|
|
121235
|
-
if (args === void 0) return void 0;
|
|
121236
|
-
const single = typeof args.source_attachment_id === "string" ? [args.source_attachment_id] : void 0;
|
|
121237
|
-
const list = Array.isArray(args.source_attachment_ids) ? args.source_attachment_ids.filter((id) => typeof id === "string") : void 0;
|
|
121238
|
-
const ids = single ?? list;
|
|
121239
|
-
return ids !== void 0 && ids.length > 0 ? ids : void 0;
|
|
121240
|
-
}
|
|
121241
|
-
function imageAttachment(value) {
|
|
121242
|
-
const candidate = record(value);
|
|
121243
|
-
if (candidate === void 0 || typeof candidate.attachmentId !== "string" || !isImageMediaType(candidate.mediaType) || !positiveInteger(candidate.bytes) || !positiveInteger(candidate.width) || !positiveInteger(candidate.height)) return void 0;
|
|
121244
|
-
const original = record(candidate.originalDimensions);
|
|
121245
|
-
if (candidate.originalDimensions !== void 0 && (original === void 0 || !positiveInteger(original.width) || !positiveInteger(original.height))) return void 0;
|
|
121246
|
-
return {
|
|
121247
|
-
attachmentId: candidate.attachmentId,
|
|
121248
|
-
mediaType: candidate.mediaType,
|
|
121249
|
-
bytes: candidate.bytes,
|
|
121250
|
-
width: candidate.width,
|
|
121251
|
-
height: candidate.height,
|
|
121252
|
-
...typeof candidate.name === "string" ? { name: candidate.name } : {},
|
|
121253
|
-
...original === void 0 ? {} : { originalDimensions: {
|
|
121254
|
-
width: original.width,
|
|
121255
|
-
height: original.height
|
|
121256
|
-
} }
|
|
121257
|
-
};
|
|
121258
|
-
}
|
|
121259
|
-
function isImageMediaType(value) {
|
|
121260
|
-
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
121261
|
-
}
|
|
121262
|
-
function positiveInteger(value) {
|
|
121263
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
121264
|
-
}
|
|
121265
|
-
function stringValue(value, fallback) {
|
|
121266
|
-
return typeof value === "string" ? value : fallback;
|
|
121267
|
-
}
|
|
121268
|
-
function record(value) {
|
|
121269
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
121270
|
-
}
|
|
121271
121503
|
//#endregion
|
|
121272
121504
|
//#region lib/types/client/conversation-image-revisions.js
|
|
121273
121505
|
const STORAGE_PREFIX = "dsh_image_gen_conversation_revisions:";
|
|
@@ -121330,7 +121562,7 @@ img.ProseMirror-separator {
|
|
|
121330
121562
|
function isRevision(value) {
|
|
121331
121563
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
121332
121564
|
const candidate = value;
|
|
121333
|
-
return isAttachment(candidate.attachment) && typeof candidate.provider === "string" &&
|
|
121565
|
+
return isAttachment(candidate.attachment) && typeof candidate.provider === "string" && STUDIO_PROVIDERS.includes(candidate.provider) && typeof candidate.prompt === "string" && typeof candidate.model === "string" && typeof candidate.output === "string" && typeof candidate.createdAt === "number" && Number.isFinite(candidate.createdAt) && typeof candidate.ratio === "string" && typeof candidate.quality === "string";
|
|
121334
121566
|
}
|
|
121335
121567
|
function isAttachment(value) {
|
|
121336
121568
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
@@ -121375,19 +121607,36 @@ img.ProseMirror-separator {
|
|
|
121375
121607
|
switchFailed: "Switch failed"
|
|
121376
121608
|
}
|
|
121377
121609
|
};
|
|
121378
|
-
/**
|
|
121610
|
+
/** Provider row names, per locale. Brand words stay untranslated; only the
|
|
121611
|
+
* qualifier ("订阅" / "Sub") localizes, so the pill and the menu agree with the
|
|
121612
|
+
* settings card in both languages. */
|
|
121379
121613
|
const PILL_PROVIDER_LABELS = {
|
|
121380
|
-
|
|
121381
|
-
|
|
121382
|
-
|
|
121383
|
-
|
|
121384
|
-
|
|
121385
|
-
|
|
121386
|
-
|
|
121387
|
-
|
|
121388
|
-
|
|
121389
|
-
|
|
121390
|
-
|
|
121614
|
+
zh: {
|
|
121615
|
+
google: "Gemini",
|
|
121616
|
+
openai: "OpenAI",
|
|
121617
|
+
"openai-compat": "OpenAI 兼容",
|
|
121618
|
+
seedream: "Seedream",
|
|
121619
|
+
dashscope: "DashScope",
|
|
121620
|
+
xai: "Grok",
|
|
121621
|
+
zhipu: "智谱 GLM",
|
|
121622
|
+
comfyui: "ComfyUI",
|
|
121623
|
+
"chatgpt-sub": "ChatGPT 订阅",
|
|
121624
|
+
"grok-sub": "Grok 订阅",
|
|
121625
|
+
"google-sub": "Google 订阅"
|
|
121626
|
+
},
|
|
121627
|
+
en: {
|
|
121628
|
+
google: "Gemini",
|
|
121629
|
+
openai: "OpenAI",
|
|
121630
|
+
"openai-compat": "OpenAI Compatible",
|
|
121631
|
+
seedream: "Seedream",
|
|
121632
|
+
dashscope: "DashScope",
|
|
121633
|
+
xai: "Grok",
|
|
121634
|
+
zhipu: "Zhipu GLM",
|
|
121635
|
+
comfyui: "ComfyUI",
|
|
121636
|
+
"chatgpt-sub": "ChatGPT Sub",
|
|
121637
|
+
"grok-sub": "Grok Sub",
|
|
121638
|
+
"google-sub": "Google Sub"
|
|
121639
|
+
}
|
|
121391
121640
|
};
|
|
121392
121641
|
/** Compute the fixed-position anchor from the pill button: open upward when
|
|
121393
121642
|
* the chat pane leaves room above the composer, drop down otherwise. */
|
|
@@ -121639,7 +121888,7 @@ img.ProseMirror-separator {
|
|
|
121639
121888
|
}),
|
|
121640
121889
|
(0, react_jsx_runtime.jsx)("span", {
|
|
121641
121890
|
className: "dsh-ig-pill-provider",
|
|
121642
|
-
children: PILL_PROVIDER_LABELS[current]
|
|
121891
|
+
children: PILL_PROVIDER_LABELS[lang][current]
|
|
121643
121892
|
}),
|
|
121644
121893
|
(0, react_jsx_runtime.jsx)("span", {
|
|
121645
121894
|
className: `dsh-ig-pill-caret${open ? " dsh-ig-pill-caret-open" : ""}`,
|
|
@@ -121685,7 +121934,7 @@ img.ProseMirror-separator {
|
|
|
121685
121934
|
className: "dsh-ig-pill-option-text",
|
|
121686
121935
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
121687
121936
|
className: "dsh-ig-pill-option-name",
|
|
121688
|
-
children: PILL_PROVIDER_LABELS[provider]
|
|
121937
|
+
children: PILL_PROVIDER_LABELS[lang][provider]
|
|
121689
121938
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
121690
121939
|
className: "dsh-ig-pill-option-sub",
|
|
121691
121940
|
children: subtitleOf(provider)
|
|
@@ -121723,16 +121972,20 @@ img.ProseMirror-separator {
|
|
|
121723
121972
|
}
|
|
121724
121973
|
/** Styles for the pill and its fixed-position menu; injected by the client entry. */
|
|
121725
121974
|
const PROVIDER_PILL_STYLE = `
|
|
121726
|
-
.dsh-ig-pill-root{position:relative;display:inline-flex;align-items:center;height:
|
|
121727
|
-
.dsh-ig-pill-button{display:inline-flex;align-items:center;gap:5px;height:
|
|
121975
|
+
.dsh-ig-pill-root{position:relative;display:inline-flex;align-items:center;height:28px}
|
|
121976
|
+
.dsh-ig-pill-button{display:inline-flex;align-items:center;gap:5px;height:28px;padding:0 9px 0 8px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:13px;line-height:20px;cursor:pointer;transition:border-color .15s,background .15s,color .15s;-webkit-user-select:none;user-select:none}
|
|
121728
121977
|
.dsh-ig-pill-button:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed,#9ca3af);color:var(--dsw-alias-label-primary,#111827)}
|
|
121729
121978
|
.dsh-ig-pill-button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:1px}
|
|
121730
121979
|
.dsh-ig-pill-button:disabled{opacity:.5;cursor:not-allowed}
|
|
121731
121980
|
.dsh-ig-pill-button-open{border-color:var(--dsw-alias-brand-primary,#4c78ff);color:var(--dsw-alias-label-primary,#111827)}
|
|
121732
121981
|
.dsh-ig-pill-icon{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}
|
|
121733
|
-
.dsh-ig-pill-provider{max-width:
|
|
121982
|
+
.dsh-ig-pill-provider{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
121734
121983
|
.dsh-ig-pill-caret{display:inline-flex;flex:none;transition:transform .15s}
|
|
121735
121984
|
.dsh-ig-pill-caret-open{transform:rotate(180deg)}
|
|
121985
|
+
@container (max-width:700px){
|
|
121986
|
+
.dsh-ig-pill-caret{display:none}
|
|
121987
|
+
.dsh-ig-pill-button{padding:0 7px 0 6px}
|
|
121988
|
+
}
|
|
121736
121989
|
.dsh-ig-pill-menu{position:fixed;z-index:1000;min-width:264px;max-width:min(320px,calc(100vw - 24px));padding:6px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 12px 32px rgba(0,0,0,.16),0 2px 8px rgba(0,0,0,.08);max-height:min(320px,calc(100vh - 48px));overflow-y:auto}
|
|
121737
121990
|
.dsh-ig-pill-menu-title{padding:6px 8px 4px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary,#6b7280)}
|
|
121738
121991
|
.dsh-ig-pill-option{display:flex;width:100%;align-items:center;gap:9px;padding:7px 8px;border:0;border-radius:8px;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer}
|
|
@@ -121752,7 +122005,7 @@ img.ProseMirror-separator {
|
|
|
121752
122005
|
`;
|
|
121753
122006
|
//#endregion
|
|
121754
122007
|
//#region lib/types/client/index.js
|
|
121755
|
-
console.info(`[dsh-image-gen] canvas bundle 2026-09-
|
|
122008
|
+
console.info(`[dsh-image-gen] canvas bundle 2026-09-14T15:26:26.831Z`);
|
|
121756
122009
|
/** Right-sidebar tab-type registration id (DSH 0.1.5 `sidebarRightTabs`). */
|
|
121757
122010
|
const SIDEBAR_STUDIO_TAB_ID = "dsh-image-gen/studio";
|
|
121758
122011
|
/** The tab kind: a page type opened by kind, recognizing no resource address. */
|
|
@@ -121791,8 +122044,8 @@ img.ProseMirror-separator {
|
|
|
121791
122044
|
subLoginOk: "登录成功",
|
|
121792
122045
|
subModelLabel: "订阅模型",
|
|
121793
122046
|
subModelHint: "由订阅通道固定,不可更改。",
|
|
121794
|
-
|
|
121795
|
-
subDefaultHint: "订阅 Provider
|
|
122047
|
+
subEditCapability: "订阅生图 · 图生图:支持。对话 edit_image、Studio 图生图与多模型对比均可上传参考图(最多 5 张)。",
|
|
122048
|
+
subDefaultHint: "订阅 Provider 已支持文字生图与图生图;图生图使用各订阅渠道默认参数。",
|
|
121796
122049
|
apiKeyLabel: "{provider} API Key",
|
|
121797
122050
|
apiKeyPlaceholder: "留空即可保留已配置的 Key",
|
|
121798
122051
|
apiKeyHint: "安全保存为 {key};页面不会读回明文。",
|
|
@@ -121920,8 +122173,8 @@ img.ProseMirror-separator {
|
|
|
121920
122173
|
subLoginOk: "Signed in",
|
|
121921
122174
|
subModelLabel: "Subscription model",
|
|
121922
122175
|
subModelHint: "Fixed by the subscription channel; not changeable.",
|
|
121923
|
-
|
|
121924
|
-
subDefaultHint: "Subscription providers
|
|
122176
|
+
subEditCapability: "Subscription image editing is now supported: edit_image in chats, Studio and multi-model compare all accept reference images (up to 5).",
|
|
122177
|
+
subDefaultHint: "Subscription providers support both text-to-image and image editing; edits use each channel's default parameters.",
|
|
121925
122178
|
apiKeyLabel: "{provider} API Key",
|
|
121926
122179
|
apiKeyPlaceholder: "Leave empty to keep configured key",
|
|
121927
122180
|
apiKeyHint: "Securely saved as {key}; never read back in plaintext.",
|
|
@@ -122319,6 +122572,11 @@ img.ProseMirror-separator {
|
|
|
122319
122572
|
const isCompactTranscript = () => chatScope.getSnapshot().value?.transcriptView !== "normal";
|
|
122320
122573
|
const locale = ctx.get("locale");
|
|
122321
122574
|
const promotion = { enabled: false };
|
|
122575
|
+
ctx.inject(["sessions"], (owner) => {
|
|
122576
|
+
const sessions = owner.get("sessions");
|
|
122577
|
+
if (typeof sessions?.binding !== "function" || typeof sessions.list?.subscribe !== "function") return;
|
|
122578
|
+
owner.effect(() => startConversationLandings(sessions), "dsh-image-gen: live canvas images");
|
|
122579
|
+
});
|
|
122322
122580
|
ctx.effect(() => {
|
|
122323
122581
|
const style = document.createElement("style");
|
|
122324
122582
|
style.dataset.plugin = "dsh-image-gen";
|
|
@@ -122444,7 +122702,10 @@ img.ProseMirror-separator {
|
|
|
122444
122702
|
label: () => {
|
|
122445
122703
|
return (locale?.getSnapshot?.()?.active)?.startsWith("en") ? "Gallery" : "画廊";
|
|
122446
122704
|
},
|
|
122447
|
-
inject: () => ({
|
|
122705
|
+
inject: () => ({
|
|
122706
|
+
locale,
|
|
122707
|
+
credentialEvents
|
|
122708
|
+
})
|
|
122448
122709
|
}, GalleryViewTab));
|
|
122449
122710
|
ctx.inject(["sidebarRightTabs"], (owner) => {
|
|
122450
122711
|
const sidebarTabs = owner.get("sidebarRightTabs");
|
|
@@ -122467,6 +122728,7 @@ img.ProseMirror-separator {
|
|
|
122467
122728
|
key: SIDEBAR_STUDIO_TAB_ID,
|
|
122468
122729
|
inject: () => ({
|
|
122469
122730
|
locale,
|
|
122731
|
+
credentialEvents,
|
|
122470
122732
|
inSidebar: true,
|
|
122471
122733
|
defaultTab: "studio",
|
|
122472
122734
|
initialCanvasSurface: "infinite"
|
|
@@ -123718,8 +123980,8 @@ img.ProseMirror-separator {
|
|
|
123718
123980
|
(0, react_jsx_runtime.jsx)("div", {
|
|
123719
123981
|
className: "dsh-ig-field",
|
|
123720
123982
|
children: (0, react_jsx_runtime.jsx)("span", {
|
|
123721
|
-
className: "dsh-ig-
|
|
123722
|
-
children: t("
|
|
123983
|
+
className: "dsh-ig-hint",
|
|
123984
|
+
children: t("subEditCapability")
|
|
123723
123985
|
})
|
|
123724
123986
|
}),
|
|
123725
123987
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -124037,14 +124299,6 @@ img.ProseMirror-separator {
|
|
|
124037
124299
|
};
|
|
124038
124300
|
(0, react.useEffect)(() => {
|
|
124039
124301
|
if (result === void 0) return;
|
|
124040
|
-
pushTlLandings([{
|
|
124041
|
-
galleryId: result.attachment.attachmentId,
|
|
124042
|
-
attachment: result.attachment,
|
|
124043
|
-
fromConversation: true,
|
|
124044
|
-
prompt: result.prompt,
|
|
124045
|
-
provider: result.provider,
|
|
124046
|
-
model: result.model
|
|
124047
|
-
}]);
|
|
124048
124302
|
saveGalleryItem({
|
|
124049
124303
|
id: result.attachment.attachmentId,
|
|
124050
124304
|
attachment: result.attachment,
|