dsh-image-gen 0.6.5 → 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 -355
- package/README.md +381 -355
- 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 +499 -354
- package/lib/client.js.map +1 -1
- package/lib/types/client/inspiration-api.d.ts +10 -0
- package/lib/types/client/inspiration-detail.d.ts +14 -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/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/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/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/client/tl/diagram-landing.d.ts +0 -75
- package/lib/types/client/tl/diagram-layout.d.ts +0 -76
- package/lib/types/client/tl/diagram-measure.d.ts +0 -40
- package/lib/types/diagram.d.ts +0 -42
- package/lib/types/studio-chat.d.ts +0 -48
package/lib/client.js
CHANGED
|
@@ -343,9 +343,9 @@ window.__ModuleLoader__.load({
|
|
|
343
343
|
}
|
|
344
344
|
});
|
|
345
345
|
}
|
|
346
|
-
const listeners
|
|
346
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
347
347
|
function notifyListeners() {
|
|
348
|
-
for (const listener of listeners
|
|
348
|
+
for (const listener of listeners) try {
|
|
349
349
|
listener();
|
|
350
350
|
} catch (err) {
|
|
351
351
|
console.error("[dsh-image-gen] Gallery listener error:", err);
|
|
@@ -355,9 +355,9 @@ window.__ModuleLoader__.load({
|
|
|
355
355
|
* Subscribe to gallery mutations (insert/delete/clear).
|
|
356
356
|
*/
|
|
357
357
|
function subscribeGallery(listener) {
|
|
358
|
-
listeners
|
|
358
|
+
listeners.add(listener);
|
|
359
359
|
return () => {
|
|
360
|
-
listeners
|
|
360
|
+
listeners.delete(listener);
|
|
361
361
|
};
|
|
362
362
|
}
|
|
363
363
|
/**
|
|
@@ -1440,6 +1440,11 @@ window.__ModuleLoader__.load({
|
|
|
1440
1440
|
cache.delete(key);
|
|
1441
1441
|
}
|
|
1442
1442
|
}
|
|
1443
|
+
/** Clear all in-memory image caches. */
|
|
1444
|
+
function clearAttachmentCache() {
|
|
1445
|
+
cache.clear();
|
|
1446
|
+
currentCacheBytes = 0;
|
|
1447
|
+
}
|
|
1443
1448
|
function enforceLimits() {
|
|
1444
1449
|
while (cache.size > 30 || currentCacheBytes > 134217728) {
|
|
1445
1450
|
const oldestKey = cache.keys().next().value;
|
|
@@ -76042,14 +76047,14 @@ img.ProseMirror-separator {
|
|
|
76042
76047
|
});
|
|
76043
76048
|
var useReactId = react[" useId ".trim().toString()] || (() => void 0);
|
|
76044
76049
|
var count$1 = 0;
|
|
76045
|
-
function useId(deterministicId) {
|
|
76050
|
+
function useId$1(deterministicId) {
|
|
76046
76051
|
const [id, setId] = react.useState(useReactId());
|
|
76047
76052
|
useLayoutEffect2(() => {
|
|
76048
76053
|
if (!deterministicId) setId((reactId) => reactId ?? String(count$1++));
|
|
76049
76054
|
}, [deterministicId]);
|
|
76050
76055
|
return deterministicId || (id ? `radix-${id}` : "");
|
|
76051
76056
|
}
|
|
76052
|
-
__name$25(useId, "useId");
|
|
76057
|
+
__name$25(useId$1, "useId");
|
|
76053
76058
|
//#endregion
|
|
76054
76059
|
//#region node_modules/.pnpm/@radix-ui+react-direction@1_8310f062454398ef3b849e7d93575bcb/node_modules/@radix-ui/react-direction/dist/index.mjs
|
|
76055
76060
|
var __defProp$27 = Object.defineProperty;
|
|
@@ -78670,9 +78675,9 @@ img.ProseMirror-separator {
|
|
|
78670
78675
|
scope: __scopeDialog,
|
|
78671
78676
|
triggerRef,
|
|
78672
78677
|
contentRef,
|
|
78673
|
-
contentId: useId(),
|
|
78674
|
-
titleId: useId(),
|
|
78675
|
-
descriptionId: useId(),
|
|
78678
|
+
contentId: useId$1(),
|
|
78679
|
+
titleId: useId$1(),
|
|
78680
|
+
descriptionId: useId$1(),
|
|
78676
78681
|
titlePresent: titleCount > 0,
|
|
78677
78682
|
descriptionPresent: descriptionCount > 0,
|
|
78678
78683
|
setTitleCount,
|
|
@@ -80944,7 +80949,7 @@ img.ProseMirror-separator {
|
|
|
80944
80949
|
var ITEM_NAME$3 = "RovingFocusGroupItem";
|
|
80945
80950
|
var RovingFocusGroupItem = /* @__PURE__ */ react.forwardRef(/* @__PURE__ */ __name$13(function RovingFocusGroupItem2(props, forwardedRef) {
|
|
80946
80951
|
const { __scopeRovingFocusGroup, focusable = true, active = false, tabStopId, children, ...itemProps } = props;
|
|
80947
|
-
const autoId = useId();
|
|
80952
|
+
const autoId = useId$1();
|
|
80948
80953
|
const id = tabStopId || autoId;
|
|
80949
80954
|
const context = useRovingFocusContext(ITEM_NAME$3, __scopeRovingFocusGroup);
|
|
80950
80955
|
const isCurrentTabStop = context.currentTabStopId === id;
|
|
@@ -81503,8 +81508,8 @@ img.ProseMirror-separator {
|
|
|
81503
81508
|
onContentChange: setContent,
|
|
81504
81509
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuSubProvider, {
|
|
81505
81510
|
scope: __scopeMenu,
|
|
81506
|
-
contentId: useId(),
|
|
81507
|
-
triggerId: useId(),
|
|
81511
|
+
contentId: useId$1(),
|
|
81512
|
+
triggerId: useId$1(),
|
|
81508
81513
|
trigger,
|
|
81509
81514
|
onTriggerChange: setTrigger,
|
|
81510
81515
|
children
|
|
@@ -81962,9 +81967,9 @@ img.ProseMirror-separator {
|
|
|
81962
81967
|
});
|
|
81963
81968
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DropdownMenuProvider, {
|
|
81964
81969
|
scope: __scopeDropdownMenu,
|
|
81965
|
-
triggerId: useId(),
|
|
81970
|
+
triggerId: useId$1(),
|
|
81966
81971
|
triggerRef,
|
|
81967
|
-
contentId: useId(),
|
|
81972
|
+
contentId: useId$1(),
|
|
81968
81973
|
open,
|
|
81969
81974
|
onOpenChange: setOpen,
|
|
81970
81975
|
onOpenToggle: react.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
@@ -82196,7 +82201,7 @@ img.ProseMirror-separator {
|
|
|
82196
82201
|
...popperScope,
|
|
82197
82202
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverProvider, {
|
|
82198
82203
|
scope: __scopePopover,
|
|
82199
|
-
contentId: useId(),
|
|
82204
|
+
contentId: useId$1(),
|
|
82200
82205
|
triggerRef,
|
|
82201
82206
|
open,
|
|
82202
82207
|
onOpenChange: setOpen,
|
|
@@ -82428,7 +82433,7 @@ img.ProseMirror-separator {
|
|
|
82428
82433
|
]);
|
|
82429
82434
|
const isFormControl = trigger ? !!form || !!trigger.closest("form") : true;
|
|
82430
82435
|
const [nativeOptionsSet, setNativeOptionsSet] = react.useState(/* @__PURE__ */ new Set());
|
|
82431
|
-
const contentId = useId();
|
|
82436
|
+
const contentId = useId$1();
|
|
82432
82437
|
const nativeSelectKey = Array.from(nativeOptionsSet).map((option) => option.props.value).join(";");
|
|
82433
82438
|
const handleNativeOptionAdd = react.useCallback((option) => {
|
|
82434
82439
|
setNativeOptionsSet((prev) => new Set(prev).add(option));
|
|
@@ -83997,7 +84002,7 @@ img.ProseMirror-separator {
|
|
|
83997
84002
|
const popperScope = usePopperScope(__scopeTooltip);
|
|
83998
84003
|
const [trigger, setTrigger] = react.useState(null);
|
|
83999
84004
|
const [contentIdState, setContentId] = react.useState(void 0);
|
|
84000
|
-
const generatedContentId = useId();
|
|
84005
|
+
const generatedContentId = useId$1();
|
|
84001
84006
|
const openTimerRef = react.useRef(0);
|
|
84002
84007
|
const disableHoverableContent = disableHoverableContentProp ?? providerContext.disableHoverableContent;
|
|
84003
84008
|
const delayDuration = delayDurationProp ?? providerContext.delayDuration;
|
|
@@ -108118,7 +108123,7 @@ img.ProseMirror-separator {
|
|
|
108118
108123
|
}
|
|
108119
108124
|
const MAX_SIZE = 256;
|
|
108120
108125
|
const alphaCache = new LruCache(100);
|
|
108121
|
-
const pending$
|
|
108126
|
+
const pending$2 = /* @__PURE__ */ new Set();
|
|
108122
108127
|
let offscreenCanvas = null;
|
|
108123
108128
|
function getOffscreenCanvas(w, h) {
|
|
108124
108129
|
if (!offscreenCanvas) offscreenCanvas = new OffscreenCanvas(w, h);
|
|
@@ -108138,12 +108143,12 @@ img.ProseMirror-separator {
|
|
|
108138
108143
|
function preloadAlphaData(url, cacheKey) {
|
|
108139
108144
|
if (typeof OffscreenCanvas === "undefined") return;
|
|
108140
108145
|
const key = cacheKey ?? url;
|
|
108141
|
-
if (alphaCache.has(key) || pending$
|
|
108142
|
-
pending$
|
|
108146
|
+
if (alphaCache.has(key) || pending$2.has(key)) return;
|
|
108147
|
+
pending$2.add(key);
|
|
108143
108148
|
const img = Image$1();
|
|
108144
108149
|
img.crossOrigin = "anonymous";
|
|
108145
108150
|
img.onload = async () => {
|
|
108146
|
-
pending$
|
|
108151
|
+
pending$2.delete(key);
|
|
108147
108152
|
const { width: origW, height: origH } = img;
|
|
108148
108153
|
if (origW === 0 || origH === 0) return;
|
|
108149
108154
|
const scale = Math.min(1, MAX_SIZE / Math.max(origW, origH));
|
|
@@ -108170,7 +108175,7 @@ img.ProseMirror-separator {
|
|
|
108170
108175
|
});
|
|
108171
108176
|
};
|
|
108172
108177
|
img.onerror = () => {
|
|
108173
|
-
pending$
|
|
108178
|
+
pending$2.delete(key);
|
|
108174
108179
|
};
|
|
108175
108180
|
img.src = url;
|
|
108176
108181
|
}
|
|
@@ -115622,43 +115627,6 @@ img.ProseMirror-separator {
|
|
|
115622
115627
|
});
|
|
115623
115628
|
}
|
|
115624
115629
|
//#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
115630
|
//#region lib/types/client/tl/canvas-sync.js
|
|
115663
115631
|
/** Debounce for ordinary document/session changes. */
|
|
115664
115632
|
const STATE_DEBOUNCE_MS = 600;
|
|
@@ -115708,21 +115676,18 @@ img.ProseMirror-separator {
|
|
|
115708
115676
|
* the selection identity list, so a selected node reads exactly like its
|
|
115709
115677
|
* canvas node twin (same name, attachment id, dimensions).
|
|
115710
115678
|
*/
|
|
115711
|
-
function describeShape(editor, shape
|
|
115679
|
+
function describeShape(editor, shape) {
|
|
115712
115680
|
const node = { kind: kindOf(shape.type) };
|
|
115713
115681
|
if (shape.type === "image") {
|
|
115714
115682
|
const meta = shape.meta;
|
|
115715
115683
|
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
|
-
}
|
|
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;
|
|
115721
115686
|
if (typeof meta?.prompt === "string" && meta.prompt.trim().length > 0) node.prompt = meta.prompt.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
115722
115687
|
if (typeof meta?.provider === "string" && meta.provider.length > 0) node.provider = meta.provider;
|
|
115723
115688
|
if (typeof meta?.model === "string" && meta.model.length > 0) node.model = meta.model;
|
|
115724
115689
|
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;
|
|
115690
|
+
const assetName = typeof meta?.name === "string" && meta.name.length > 0 ? meta.name : asset !== void 0 && asset.type === "image" ? asset.props.name : void 0;
|
|
115726
115691
|
if (typeof assetName === "string" && assetName.length > 0) node.name = assetName.slice(0, 128);
|
|
115727
115692
|
if (asset !== void 0 && asset.type === "image") {
|
|
115728
115693
|
node.width = Math.round(asset.props.w);
|
|
@@ -115745,31 +115710,19 @@ img.ProseMirror-separator {
|
|
|
115745
115710
|
return node;
|
|
115746
115711
|
}
|
|
115747
115712
|
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
115713
|
const shapes = editor.getCurrentPageShapes();
|
|
115755
115714
|
const nodes = [];
|
|
115756
|
-
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));
|
|
115757
115716
|
const selected = editor.getSelectedShapes();
|
|
115758
115717
|
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
|
|
115718
|
+
const selectionItems = selected.slice(0, 16).map((shape) => describeShape(editor, shape));
|
|
115760
115719
|
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
115720
|
return {
|
|
115767
115721
|
nodeCount: shapes.length,
|
|
115768
115722
|
nodes,
|
|
115769
115723
|
selectionCount: selected.length,
|
|
115770
115724
|
selectionKinds,
|
|
115771
115725
|
selectionItems,
|
|
115772
|
-
selectionAllConversationImages,
|
|
115773
115726
|
stateKey: JSON.stringify([
|
|
115774
115727
|
shapes.length,
|
|
115775
115728
|
nodes,
|
|
@@ -115836,7 +115789,7 @@ img.ProseMirror-separator {
|
|
|
115836
115789
|
return;
|
|
115837
115790
|
}
|
|
115838
115791
|
let selectionImage;
|
|
115839
|
-
if (withScreenshot && summary.selectionCount > 0
|
|
115792
|
+
if (withScreenshot && summary.selectionCount > 0) try {
|
|
115840
115793
|
const selectedBefore = editor.getSelectedShapes();
|
|
115841
115794
|
const { url } = await editor.toImageDataUrl(selectedBefore, {
|
|
115842
115795
|
background: true,
|
|
@@ -115844,6 +115797,7 @@ img.ProseMirror-separator {
|
|
|
115844
115797
|
scale: 1,
|
|
115845
115798
|
pixelRatio: 2
|
|
115846
115799
|
});
|
|
115800
|
+
if (disposed) return;
|
|
115847
115801
|
const selectedAfter = editor.getSelectedShapes();
|
|
115848
115802
|
const idsBefore = new Set(selectedBefore.map((shape) => String(shape.id)));
|
|
115849
115803
|
if (!(selectedBefore.length === selectedAfter.length && selectedAfter.every((shape) => idsBefore.has(String(shape.id))))) return;
|
|
@@ -115886,6 +115840,10 @@ img.ProseMirror-separator {
|
|
|
115886
115840
|
stateTimer = void 0;
|
|
115887
115841
|
flush(false);
|
|
115888
115842
|
}, STATE_DEBOUNCE_MS);
|
|
115843
|
+
if (editor.getSelectedShapeIds().length > 0) exportTimer = setTimeout(() => {
|
|
115844
|
+
exportTimer = void 0;
|
|
115845
|
+
flush(true);
|
|
115846
|
+
}, SELECTION_EXPORT_DELAY_MS);
|
|
115889
115847
|
return () => {
|
|
115890
115848
|
if (disposed) return;
|
|
115891
115849
|
disposed = true;
|
|
@@ -115908,25 +115866,81 @@ img.ProseMirror-separator {
|
|
|
115908
115866
|
};
|
|
115909
115867
|
}
|
|
115910
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
|
|
115911
115925
|
//#region lib/types/client/tl/studio-tl-canvas.js
|
|
115912
115926
|
/**
|
|
115913
115927
|
* Infinite canvas surface for the Studio workbench, backed by tldraw.
|
|
115914
115928
|
*
|
|
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.
|
|
115929
|
+
* Both surfaces share one locally persisted document. The landing queue is
|
|
115930
|
+
* consumed once; tldraw owns subsequent edits, deletion, undo and persistence.
|
|
115926
115931
|
*/
|
|
115927
115932
|
const MAX_DISPLAY_SIDE = 380;
|
|
115928
115933
|
const GRID_GAP = 40;
|
|
115929
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
|
+
}
|
|
115930
115944
|
/**
|
|
115931
115945
|
* Dock the stock toolbar vertically along the left edge (Figma-style) instead
|
|
115932
115946
|
* of tldraw's signature bottom-center pill. It is still the same
|
|
@@ -115948,11 +115962,11 @@ img.ProseMirror-separator {
|
|
|
115948
115962
|
h: Math.round(height * scale)
|
|
115949
115963
|
};
|
|
115950
115964
|
}
|
|
115951
|
-
/**
|
|
115965
|
+
/** Dedupe across the whole document, including images moved to other pages. */
|
|
115952
115966
|
function landedIdsOf(editor) {
|
|
115953
115967
|
const landedIds = /* @__PURE__ */ new Set();
|
|
115954
|
-
for (const shape of editor.
|
|
115955
|
-
if (shape.type !== "image") continue;
|
|
115968
|
+
for (const shape of editor.store.allRecords()) {
|
|
115969
|
+
if (shape.typeName !== "shape" || shape.type !== "image") continue;
|
|
115956
115970
|
const galleryId = shape.meta?.galleryId;
|
|
115957
115971
|
if (typeof galleryId === "string") landedIds.add(galleryId);
|
|
115958
115972
|
}
|
|
@@ -115964,13 +115978,14 @@ img.ProseMirror-separator {
|
|
|
115964
115978
|
const collapsed = prompt.replace(/\s+/g, " ").trim();
|
|
115965
115979
|
return collapsed.length === 0 ? void 0 : collapsed.slice(0, 120);
|
|
115966
115980
|
}
|
|
115967
|
-
async function landTlItems(editor, items,
|
|
115981
|
+
async function landTlItems(editor, items, isActive) {
|
|
115968
115982
|
const landedIds = landedIdsOf(editor);
|
|
115969
115983
|
const pending = items.filter((item) => !landedIds.has(item.galleryId));
|
|
115970
|
-
if (pending.length === 0) return;
|
|
115984
|
+
if (pending.length === 0) return true;
|
|
115971
115985
|
const assets = [];
|
|
115972
115986
|
const landed = [];
|
|
115973
115987
|
for (const item of pending) {
|
|
115988
|
+
if (!isActive()) return false;
|
|
115974
115989
|
const assetId = tlAssetIdFor(item.galleryId);
|
|
115975
115990
|
if (editor.getAsset(assetId) === void 0) try {
|
|
115976
115991
|
const blob = await fetchAttachmentBlob(item.attachment);
|
|
@@ -115991,7 +116006,6 @@ img.ProseMirror-separator {
|
|
|
115991
116006
|
}
|
|
115992
116007
|
});
|
|
115993
116008
|
} catch (error) {
|
|
115994
|
-
failedIds.add(item.galleryId);
|
|
115995
116009
|
console.warn("[dsh-image-gen] canvas landing skipped (attachment unreadable):", item.galleryId, error);
|
|
115996
116010
|
continue;
|
|
115997
116011
|
}
|
|
@@ -116000,6 +116014,9 @@ img.ProseMirror-separator {
|
|
|
116000
116014
|
landed.push({
|
|
116001
116015
|
galleryId: item.galleryId,
|
|
116002
116016
|
assetId,
|
|
116017
|
+
attachmentId: String(item.attachment.attachmentId),
|
|
116018
|
+
name: item.attachment.name ?? item.galleryId,
|
|
116019
|
+
fromConversation: item.fromConversation === true,
|
|
116003
116020
|
w: size.w,
|
|
116004
116021
|
h: size.h,
|
|
116005
116022
|
...prompt !== void 0 ? { prompt } : {},
|
|
@@ -116007,10 +116024,13 @@ img.ProseMirror-separator {
|
|
|
116007
116024
|
...typeof item.model === "string" && item.model.length > 0 ? { model: item.model } : {}
|
|
116008
116025
|
});
|
|
116009
116026
|
}
|
|
116010
|
-
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;
|
|
116011
116031
|
if (assets.length > 0) editor.createAssets(assets);
|
|
116012
116032
|
const rows = [];
|
|
116013
|
-
for (let index = 0; index <
|
|
116033
|
+
for (let index = 0; index < ready.length; index += GRID_COLS) rows.push(ready.slice(index, index + GRID_COLS));
|
|
116014
116034
|
const rowSizes = rows.map((row) => ({
|
|
116015
116035
|
width: row.reduce((sum, cell) => sum + cell.w, 0) + GRID_GAP * (row.length - 1),
|
|
116016
116036
|
height: Math.max(...row.map((cell) => cell.h))
|
|
@@ -116045,7 +116065,7 @@ img.ProseMirror-separator {
|
|
|
116045
116065
|
let cursorX = originX + (blockWidth - rowSize.width) / 2;
|
|
116046
116066
|
for (const cell of row) {
|
|
116047
116067
|
shapes.push({
|
|
116048
|
-
id: createShapeId(),
|
|
116068
|
+
id: createShapeId(`ig-${cell.galleryId}`),
|
|
116049
116069
|
type: "image",
|
|
116050
116070
|
x: Math.round(cursorX),
|
|
116051
116071
|
y: Math.round(cursorY),
|
|
@@ -116056,6 +116076,9 @@ img.ProseMirror-separator {
|
|
|
116056
116076
|
},
|
|
116057
116077
|
meta: {
|
|
116058
116078
|
galleryId: cell.galleryId,
|
|
116079
|
+
attachmentId: cell.attachmentId,
|
|
116080
|
+
name: cell.name,
|
|
116081
|
+
fromConversation: cell.fromConversation,
|
|
116059
116082
|
...cell.prompt !== void 0 ? { prompt: cell.prompt } : {},
|
|
116060
116083
|
...cell.provider !== void 0 ? { provider: cell.provider } : {},
|
|
116061
116084
|
...cell.model !== void 0 ? { model: cell.model } : {}
|
|
@@ -116081,42 +116104,53 @@ img.ProseMirror-separator {
|
|
|
116081
116104
|
inset: GRID_GAP
|
|
116082
116105
|
});
|
|
116083
116106
|
}
|
|
116107
|
+
return true;
|
|
116084
116108
|
}
|
|
116085
116109
|
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]);
|
|
116110
|
+
const sessionId = (0, react.useId)();
|
|
116106
116111
|
return (0, react_jsx_runtime.jsx)("div", {
|
|
116107
116112
|
className: "dsh-ig-tl-canvas",
|
|
116108
116113
|
children: (0, react_jsx_runtime.jsx)(Tldraw, {
|
|
116114
|
+
persistenceKey: "dsh-image-gen-workbench-v1",
|
|
116115
|
+
sessionId,
|
|
116109
116116
|
components: { Toolbar: StudioToolbar },
|
|
116110
116117
|
onMount: (editor) => {
|
|
116111
|
-
|
|
116118
|
+
let active = true;
|
|
116112
116119
|
applyBrandTheme(editor);
|
|
116113
116120
|
editor.updateInstanceState({ isGridMode: true });
|
|
116114
116121
|
console.info(`[dsh-image-gen] tldraw mounted (instance ${editor.id})`);
|
|
116115
|
-
|
|
116116
|
-
|
|
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);
|
|
116117
116149
|
return () => {
|
|
116150
|
+
active = false;
|
|
116151
|
+
mountedCanvases.delete(clear);
|
|
116152
|
+
stopLanding();
|
|
116118
116153
|
stopSync();
|
|
116119
|
-
editorRef.current = null;
|
|
116120
116154
|
};
|
|
116121
116155
|
}
|
|
116122
116156
|
})
|
|
@@ -116227,7 +116261,10 @@ img.ProseMirror-separator {
|
|
|
116227
116261
|
expandGenerate: "展开生成面板",
|
|
116228
116262
|
generatePanel: "生成面板",
|
|
116229
116263
|
findInspiration: "找灵感",
|
|
116230
|
-
infiniteCanvasHint: "无限画布与侧边栏会话联动,才是完整功能。打开 DSH 右侧边栏的「图像工作台」即可解锁。"
|
|
116264
|
+
infiniteCanvasHint: "无限画布与侧边栏会话联动,才是完整功能。打开 DSH 右侧边栏的「图像工作台」即可解锁。",
|
|
116265
|
+
clearCanvas: "清空画布",
|
|
116266
|
+
clearCanvasDescription: "将永久清空所有画布页面的内容、图片资产和撤销历史。画廊已保存的卡片及原图不受影响,此操作无法撤销。",
|
|
116267
|
+
canvasCleared: "画布已清空,画廊不受影响"
|
|
116231
116268
|
},
|
|
116232
116269
|
en: {
|
|
116233
116270
|
title: "Cloud Image Studio",
|
|
@@ -116329,7 +116366,10 @@ img.ProseMirror-separator {
|
|
|
116329
116366
|
expandGenerate: "Expand generate panel",
|
|
116330
116367
|
generatePanel: "Generate",
|
|
116331
116368
|
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."
|
|
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."
|
|
116333
116373
|
}
|
|
116334
116374
|
};
|
|
116335
116375
|
/** localStorage flag remembering that the one-time infinite-canvas hint was dismissed. */
|
|
@@ -116377,6 +116417,15 @@ img.ProseMirror-separator {
|
|
|
116377
116417
|
const dragStartRef = (0, react.useRef)(null);
|
|
116378
116418
|
const hasDraggedRef = (0, react.useRef)(false);
|
|
116379
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]);
|
|
116380
116429
|
/** Generate-panel state from before the infinite canvas was opened, so the
|
|
116381
116430
|
* panel is restored (not just forced open) when returning to preview. */
|
|
116382
116431
|
const generatePanelBeforeInfiniteRef = (0, react.useRef)(false);
|
|
@@ -117214,6 +117263,12 @@ img.ProseMirror-separator {
|
|
|
117214
117263
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
117215
117264
|
className: "dsh-ig-canvas-toolbar-right",
|
|
117216
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
|
+
}),
|
|
117217
117272
|
canvasSurface === "preview" && selected !== null && (0, react_jsx_runtime.jsxs)("button", {
|
|
117218
117273
|
type: "button",
|
|
117219
117274
|
onClick: startNew,
|
|
@@ -117820,6 +117875,57 @@ img.ProseMirror-separator {
|
|
|
117820
117875
|
})
|
|
117821
117876
|
]
|
|
117822
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
|
+
}),
|
|
117823
117929
|
showDeleteModal && selected !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
117824
117930
|
className: "dsh-ig-workbench-modal-backdrop",
|
|
117825
117931
|
onClick: () => setShowDeleteModal(false),
|
|
@@ -120709,6 +120815,266 @@ img.ProseMirror-separator {
|
|
|
120709
120815
|
if (fromBlockContent?.type === "image") return fromBlockContent.attachment;
|
|
120710
120816
|
}
|
|
120711
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
|
|
120712
121078
|
//#region lib/types/client/studio-style.js
|
|
120713
121079
|
const STUDIO_STYLE = `
|
|
120714
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}
|
|
@@ -121050,224 +121416,6 @@ img.ProseMirror-separator {
|
|
|
121050
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)}
|
|
121051
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}}
|
|
121052
121418
|
`;
|
|
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
121419
|
//#endregion
|
|
121272
121420
|
//#region lib/types/client/conversation-image-revisions.js
|
|
121273
121421
|
const STORAGE_PREFIX = "dsh_image_gen_conversation_revisions:";
|
|
@@ -121752,7 +121900,7 @@ img.ProseMirror-separator {
|
|
|
121752
121900
|
`;
|
|
121753
121901
|
//#endregion
|
|
121754
121902
|
//#region lib/types/client/index.js
|
|
121755
|
-
console.info(`[dsh-image-gen] canvas bundle 2026-09-
|
|
121903
|
+
console.info(`[dsh-image-gen] canvas bundle 2026-09-14T07:49:16.664Z`);
|
|
121756
121904
|
/** Right-sidebar tab-type registration id (DSH 0.1.5 `sidebarRightTabs`). */
|
|
121757
121905
|
const SIDEBAR_STUDIO_TAB_ID = "dsh-image-gen/studio";
|
|
121758
121906
|
/** The tab kind: a page type opened by kind, recognizing no resource address. */
|
|
@@ -122319,6 +122467,11 @@ img.ProseMirror-separator {
|
|
|
122319
122467
|
const isCompactTranscript = () => chatScope.getSnapshot().value?.transcriptView !== "normal";
|
|
122320
122468
|
const locale = ctx.get("locale");
|
|
122321
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
|
+
});
|
|
122322
122475
|
ctx.effect(() => {
|
|
122323
122476
|
const style = document.createElement("style");
|
|
122324
122477
|
style.dataset.plugin = "dsh-image-gen";
|
|
@@ -124037,14 +124190,6 @@ img.ProseMirror-separator {
|
|
|
124037
124190
|
};
|
|
124038
124191
|
(0, react.useEffect)(() => {
|
|
124039
124192
|
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
124193
|
saveGalleryItem({
|
|
124049
124194
|
id: result.attachment.attachmentId,
|
|
124050
124195
|
attachment: result.attachment,
|