dsh-image-gen 0.6.10 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +351 -43
- package/lib/client.js.map +1 -1
- package/lib/index.js +761 -1076
- package/lib/types/client/browser-image-utils.d.ts +9 -0
- package/lib/types/client/gallery-store.d.ts +4 -0
- package/lib/types/client/gallery-view.d.ts +32 -2
- package/lib/types/client/index.d.ts +9 -2
- package/lib/types/client/settings-write.d.ts +4 -0
- package/lib/types/client/tl/studio-tl-canvas.d.ts +7 -0
- package/lib/types/config.d.ts +7 -1
- package/lib/types/shared.d.ts +6 -0
- package/package.json +20 -20
- package/lib/types/client/canvas/canvas-bridge.d.ts +0 -10
- package/lib/types/client/canvas/canvas-model.d.ts +0 -129
- package/lib/types/client/canvas/canvas-nodes.d.ts +0 -23
- package/lib/types/client/canvas/canvas-store.d.ts +0 -29
- package/lib/types/client/canvas/canvas-style.d.ts +0 -1
- package/lib/types/client/canvas/canvas-view.d.ts +0 -8
- package/lib/types/client/canvas/rf-base-css.d.ts +0 -2
- 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
|
@@ -515,6 +515,31 @@ window.__ModuleLoader__.load({
|
|
|
515
515
|
notifyListeners();
|
|
516
516
|
}
|
|
517
517
|
/**
|
|
518
|
+
* Bulk update favorite status for multiple gallery records in a single transaction.
|
|
519
|
+
*/
|
|
520
|
+
async function bulkSetFavoriteGalleryItems(ids, isFavorite) {
|
|
521
|
+
if (ids.length === 0) return;
|
|
522
|
+
const db = await getDB();
|
|
523
|
+
await new Promise((resolve, reject) => {
|
|
524
|
+
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
525
|
+
const store = tx.objectStore(STORE_NAME);
|
|
526
|
+
for (const id of ids) {
|
|
527
|
+
const getReq = store.get(id);
|
|
528
|
+
getReq.onsuccess = () => {
|
|
529
|
+
const item = getReq.result;
|
|
530
|
+
if (item && item.isFavorite !== isFavorite) {
|
|
531
|
+
item.isFavorite = isFavorite;
|
|
532
|
+
store.put(item);
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
tx.oncomplete = () => resolve();
|
|
537
|
+
tx.onerror = () => reject(tx.error ?? /* @__PURE__ */ new Error("IndexedDB transaction failed"));
|
|
538
|
+
tx.onabort = () => reject(tx.error ?? /* @__PURE__ */ new Error("IndexedDB transaction aborted"));
|
|
539
|
+
});
|
|
540
|
+
notifyListeners();
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
518
543
|
* Standardize path format across Windows and POSIX (lowercase, forward slashes, no trailing slash).
|
|
519
544
|
*/
|
|
520
545
|
function normalizeWorkspacePath(rawPath) {
|
|
@@ -1254,6 +1279,19 @@ window.__ModuleLoader__.load({
|
|
|
1254
1279
|
* This source code is licensed under the ISC license.
|
|
1255
1280
|
* See the LICENSE file in the root directory of this source tree.
|
|
1256
1281
|
*/
|
|
1282
|
+
const SquareCheckBig = createLucideIcon("square-check-big", [["path", {
|
|
1283
|
+
d: "M21 10.656V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h12.344",
|
|
1284
|
+
key: "2acyp4"
|
|
1285
|
+
}], ["path", {
|
|
1286
|
+
d: "m9 11 3 3L22 4",
|
|
1287
|
+
key: "1pflzl"
|
|
1288
|
+
}]]);
|
|
1289
|
+
/**
|
|
1290
|
+
* @license lucide-react v1.38.0 - ISC
|
|
1291
|
+
*
|
|
1292
|
+
* This source code is licensed under the ISC license.
|
|
1293
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1294
|
+
*/
|
|
1257
1295
|
const Star = createLucideIcon("star", [["path", {
|
|
1258
1296
|
d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",
|
|
1259
1297
|
key: "r04s7s"
|
|
@@ -1563,6 +1601,89 @@ window.__ModuleLoader__.load({
|
|
|
1563
1601
|
const d = new Date(timestamp);
|
|
1564
1602
|
return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`;
|
|
1565
1603
|
}
|
|
1604
|
+
const CRC_TABLE = /* @__PURE__ */ new Uint32Array(256);
|
|
1605
|
+
for (let i = 0; i < 256; i++) {
|
|
1606
|
+
let c = i;
|
|
1607
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1608
|
+
CRC_TABLE[i] = c;
|
|
1609
|
+
}
|
|
1610
|
+
function crc32(buf) {
|
|
1611
|
+
let crc = 4294967295;
|
|
1612
|
+
for (let i = 0; i < buf.length; i++) {
|
|
1613
|
+
const byte = buf[i] ?? 0;
|
|
1614
|
+
crc = (CRC_TABLE[(crc ^ byte) & 255] ?? 0) ^ crc >>> 8;
|
|
1615
|
+
}
|
|
1616
|
+
return (crc ^ 4294967295) >>> 0;
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Create a standard zero-compression (Store mode 0) PKZip Blob from an array of files.
|
|
1620
|
+
* Zero external dependencies, pure browser ArrayBuffer/Blob, fast and universally compatible with OS extractors.
|
|
1621
|
+
*/
|
|
1622
|
+
function createZipBlob(files) {
|
|
1623
|
+
const parts = [];
|
|
1624
|
+
const centralRecords = [];
|
|
1625
|
+
let offset = 0;
|
|
1626
|
+
const encoder = new TextEncoder();
|
|
1627
|
+
for (const file of files) {
|
|
1628
|
+
const nameBytes = encoder.encode(file.name);
|
|
1629
|
+
const data = file.data;
|
|
1630
|
+
const crc = crc32(data);
|
|
1631
|
+
const size = data.length;
|
|
1632
|
+
const localHeader = /* @__PURE__ */ new Uint8Array(30);
|
|
1633
|
+
const lv = new DataView(localHeader.buffer);
|
|
1634
|
+
lv.setUint32(0, 67324752, true);
|
|
1635
|
+
lv.setUint16(4, 20, true);
|
|
1636
|
+
lv.setUint16(6, 0, true);
|
|
1637
|
+
lv.setUint16(8, 0, true);
|
|
1638
|
+
lv.setUint16(10, 0, true);
|
|
1639
|
+
lv.setUint16(12, 0, true);
|
|
1640
|
+
lv.setUint32(14, crc, true);
|
|
1641
|
+
lv.setUint32(18, size, true);
|
|
1642
|
+
lv.setUint32(22, size, true);
|
|
1643
|
+
lv.setUint16(26, nameBytes.length, true);
|
|
1644
|
+
lv.setUint16(28, 0, true);
|
|
1645
|
+
parts.push(localHeader, nameBytes, data);
|
|
1646
|
+
const cdRecord = /* @__PURE__ */ new Uint8Array(46);
|
|
1647
|
+
const cv = new DataView(cdRecord.buffer);
|
|
1648
|
+
cv.setUint32(0, 33639248, true);
|
|
1649
|
+
cv.setUint16(4, 20, true);
|
|
1650
|
+
cv.setUint16(6, 20, true);
|
|
1651
|
+
cv.setUint16(8, 0, true);
|
|
1652
|
+
cv.setUint16(10, 0, true);
|
|
1653
|
+
cv.setUint16(12, 0, true);
|
|
1654
|
+
cv.setUint16(14, 0, true);
|
|
1655
|
+
cv.setUint32(16, crc, true);
|
|
1656
|
+
cv.setUint32(20, size, true);
|
|
1657
|
+
cv.setUint32(24, size, true);
|
|
1658
|
+
cv.setUint16(28, nameBytes.length, true);
|
|
1659
|
+
cv.setUint16(30, 0, true);
|
|
1660
|
+
cv.setUint16(32, 0, true);
|
|
1661
|
+
cv.setUint16(34, 0, true);
|
|
1662
|
+
cv.setUint16(36, 0, true);
|
|
1663
|
+
cv.setUint32(38, 0, true);
|
|
1664
|
+
cv.setUint32(42, offset, true);
|
|
1665
|
+
centralRecords.push(cdRecord, nameBytes);
|
|
1666
|
+
offset += localHeader.length + nameBytes.length + data.length;
|
|
1667
|
+
}
|
|
1668
|
+
const cdOffset = offset;
|
|
1669
|
+
let cdSize = 0;
|
|
1670
|
+
for (const part of centralRecords) cdSize += part.byteLength;
|
|
1671
|
+
const eocd = /* @__PURE__ */ new Uint8Array(22);
|
|
1672
|
+
const ev = new DataView(eocd.buffer);
|
|
1673
|
+
ev.setUint32(0, 101010256, true);
|
|
1674
|
+
ev.setUint16(4, 0, true);
|
|
1675
|
+
ev.setUint16(6, 0, true);
|
|
1676
|
+
ev.setUint16(8, files.length, true);
|
|
1677
|
+
ev.setUint16(10, files.length, true);
|
|
1678
|
+
ev.setUint32(12, cdSize, true);
|
|
1679
|
+
ev.setUint32(16, cdOffset, true);
|
|
1680
|
+
ev.setUint16(20, 0, true);
|
|
1681
|
+
return new Blob([
|
|
1682
|
+
...parts,
|
|
1683
|
+
...centralRecords,
|
|
1684
|
+
eocd
|
|
1685
|
+
], { type: "application/zip" });
|
|
1686
|
+
}
|
|
1566
1687
|
//#endregion
|
|
1567
1688
|
//#region lib/types/client/multi-model-compare.js
|
|
1568
1689
|
/** Map one shared output intent to settings accepted by every target model. */
|
|
@@ -116106,6 +116227,7 @@ img.ProseMirror-separator {
|
|
|
116106
116227
|
const GRID_GAP = 40;
|
|
116107
116228
|
const GRID_COLS = 3;
|
|
116108
116229
|
const mountedCanvases = /* @__PURE__ */ new Set();
|
|
116230
|
+
const mountedEditors = /* @__PURE__ */ new Set();
|
|
116109
116231
|
/** Reset canvas-owned data only. Gallery records and host attachments are untouched. */
|
|
116110
116232
|
function clearStudioTlCanvases() {
|
|
116111
116233
|
if (mountedCanvases.size === 0) return false;
|
|
@@ -116115,6 +116237,17 @@ img.ProseMirror-separator {
|
|
|
116115
116237
|
return true;
|
|
116116
116238
|
}
|
|
116117
116239
|
/**
|
|
116240
|
+
* Cancel in-editor selection on every mounted canvas, keeping shapes and sync
|
|
116241
|
+
* untouched. Selection is interaction state tied to the surface: leaving the
|
|
116242
|
+
* studio page cancels it so a stale selection can neither resurface on return
|
|
116243
|
+
* nor linger in the host mirror.
|
|
116244
|
+
*/
|
|
116245
|
+
function deselectStudioTlCanvases() {
|
|
116246
|
+
if (mountedEditors.size === 0) return false;
|
|
116247
|
+
for (const editor of mountedEditors) editor.run(() => editor.selectNone());
|
|
116248
|
+
return true;
|
|
116249
|
+
}
|
|
116250
|
+
/**
|
|
116118
116251
|
* Dock the stock toolbar vertically along the left edge (Figma-style) instead
|
|
116119
116252
|
* of tldraw's signature bottom-center pill. It is still the same
|
|
116120
116253
|
* DefaultToolbar with the same tools and overflow handling — only the
|
|
@@ -116289,6 +116422,8 @@ img.ProseMirror-separator {
|
|
|
116289
116422
|
const retrySync = (0, react.useRef)(() => {});
|
|
116290
116423
|
const onMount = (0, react.useCallback)((editor) => {
|
|
116291
116424
|
let active = true;
|
|
116425
|
+
editor.run(() => editor.selectNone());
|
|
116426
|
+
mountedEditors.add(editor);
|
|
116292
116427
|
applyBrandTheme(editor);
|
|
116293
116428
|
editor.updateInstanceState({ isGridMode: true });
|
|
116294
116429
|
console.info(`[dsh-image-gen] tldraw mounted (instance ${editor.id})`);
|
|
@@ -116322,6 +116457,7 @@ img.ProseMirror-separator {
|
|
|
116322
116457
|
mountedCanvases.add(clear);
|
|
116323
116458
|
return () => {
|
|
116324
116459
|
active = false;
|
|
116460
|
+
mountedEditors.delete(editor);
|
|
116325
116461
|
mountedCanvases.delete(clear);
|
|
116326
116462
|
stopLanding();
|
|
116327
116463
|
stopSync();
|
|
@@ -119654,12 +119790,22 @@ img.ProseMirror-separator {
|
|
|
119654
119790
|
close: "关闭 (Esc)",
|
|
119655
119791
|
prevImage: "上一张 (←)",
|
|
119656
119792
|
nextImage: "下一张 (→)",
|
|
119657
|
-
manage: "
|
|
119793
|
+
manage: "批量管理",
|
|
119658
119794
|
exitManage: "退出选择",
|
|
119659
119795
|
selectedCount: "已选 {n} 项",
|
|
119660
119796
|
selectAll: "全选",
|
|
119661
119797
|
invertSelect: "反选",
|
|
119662
119798
|
clearSelect: "清空",
|
|
119799
|
+
batchFavorite: "批量收藏",
|
|
119800
|
+
batchUnfavorite: "批量取消收藏",
|
|
119801
|
+
batchFavoritedToast: "已将 {count} 张图片加入收藏",
|
|
119802
|
+
batchUnfavoritedToast: "已取消 {count} 张图片的收藏",
|
|
119803
|
+
batchFavoriteFailed: "收藏操作失败,请重试",
|
|
119804
|
+
batchDownload: "批量下载",
|
|
119805
|
+
batchDownloading: "打包中 ({current}/{total})…",
|
|
119806
|
+
batchDownloadSingleToast: "已下载 1 张图片",
|
|
119807
|
+
batchDownloadZipToast: "已打包下载 {count} 张图片",
|
|
119808
|
+
batchDownloadFailed: "批量下载失败,请重试",
|
|
119663
119809
|
batchDelete: "批量删除",
|
|
119664
119810
|
batchDeleteTitle: "确认批量删除选中的 {count} 张图片?",
|
|
119665
119811
|
batchDeleteTitleSingle: "确认从画廊中删除这张图片?",
|
|
@@ -119740,12 +119886,22 @@ img.ProseMirror-separator {
|
|
|
119740
119886
|
close: "Close (Esc)",
|
|
119741
119887
|
prevImage: "Previous (←)",
|
|
119742
119888
|
nextImage: "Next (→)",
|
|
119743
|
-
manage: "Batch
|
|
119889
|
+
manage: "Batch Manage",
|
|
119744
119890
|
exitManage: "Done",
|
|
119745
119891
|
selectedCount: "{n} selected",
|
|
119746
119892
|
selectAll: "Select All",
|
|
119747
119893
|
invertSelect: "Invert",
|
|
119748
119894
|
clearSelect: "Clear",
|
|
119895
|
+
batchFavorite: "Batch Favorite",
|
|
119896
|
+
batchUnfavorite: "Batch Unfavorite",
|
|
119897
|
+
batchFavoritedToast: "Added {count} images to favorites",
|
|
119898
|
+
batchUnfavoritedToast: "Removed {count} images from favorites",
|
|
119899
|
+
batchFavoriteFailed: "Favorite operation failed, please retry",
|
|
119900
|
+
batchDownload: "Batch Download",
|
|
119901
|
+
batchDownloading: "Packaging ({current}/{total})…",
|
|
119902
|
+
batchDownloadSingleToast: "Downloaded 1 image",
|
|
119903
|
+
batchDownloadZipToast: "Packaged & downloaded {count} images",
|
|
119904
|
+
batchDownloadFailed: "Batch download failed, please retry",
|
|
119749
119905
|
batchDelete: "Batch Delete",
|
|
119750
119906
|
batchDeleteTitle: "Delete {count} selected images?",
|
|
119751
119907
|
batchDeleteTitleSingle: "Delete this image from gallery?",
|
|
@@ -119828,7 +119984,7 @@ img.ProseMirror-separator {
|
|
|
119828
119984
|
return "1:1";
|
|
119829
119985
|
}
|
|
119830
119986
|
const GalleryViewTab = (props) => {
|
|
119831
|
-
const { locale, sessionId, useSessions, useWorkspaces, credentialEvents, inSidebar, defaultTab, initialCanvasSurface } = props;
|
|
119987
|
+
const { locale, sessionId, useSessions, useWorkspaces, credentialEvents, inSidebar, defaultTab, initialCanvasSurface, useTabInfo } = props;
|
|
119832
119988
|
const [activeTab, setActiveTab] = (0, react.useState)(defaultTab ?? "gallery");
|
|
119833
119989
|
const [studioDraft, setStudioDraft] = (0, react.useState)(void 0);
|
|
119834
119990
|
const [items, setItems] = (0, react.useState)([]);
|
|
@@ -119851,6 +120007,10 @@ img.ProseMirror-separator {
|
|
|
119851
120007
|
localStorage.setItem("dsh-ig-only-current-workspace", String(onlyCurrentWorkspace));
|
|
119852
120008
|
} catch {}
|
|
119853
120009
|
}, [onlyCurrentWorkspace]);
|
|
120010
|
+
const tabVisible = useTabInfo?.().tab?.visible;
|
|
120011
|
+
(0, react.useEffect)(() => {
|
|
120012
|
+
if (tabVisible === false) deselectStudioTlCanvases();
|
|
120013
|
+
}, [tabVisible]);
|
|
119854
120014
|
(0, react.useEffect)(() => {
|
|
119855
120015
|
let mounted = true;
|
|
119856
120016
|
fetch(STUDIO_ROUTE).then((res) => res.ok ? res.json() : null).then((data) => {
|
|
@@ -120273,6 +120433,91 @@ img.ProseMirror-separator {
|
|
|
120273
120433
|
setSelectedIds(/* @__PURE__ */ new Set());
|
|
120274
120434
|
lastSelectedIndexRef.current = null;
|
|
120275
120435
|
};
|
|
120436
|
+
const selectedItems = (0, react.useMemo)(() => {
|
|
120437
|
+
if (selectedIds.size === 0) return [];
|
|
120438
|
+
return items.filter((item) => selectedIds.has(item.id));
|
|
120439
|
+
}, [items, selectedIds]);
|
|
120440
|
+
const allSelectedAreFavorites = (0, react.useMemo)(() => {
|
|
120441
|
+
if (selectedItems.length === 0) return false;
|
|
120442
|
+
return selectedItems.every((item) => Boolean(item.isFavorite));
|
|
120443
|
+
}, [selectedItems]);
|
|
120444
|
+
const handleBatchToggleFavorite = async () => {
|
|
120445
|
+
if (selectedIds.size === 0) return;
|
|
120446
|
+
const targetFavorite = !allSelectedAreFavorites;
|
|
120447
|
+
const targetIds = Array.from(selectedIds);
|
|
120448
|
+
setItems((prev) => prev.map((item) => selectedIds.has(item.id) ? {
|
|
120449
|
+
...item,
|
|
120450
|
+
isFavorite: targetFavorite
|
|
120451
|
+
} : item));
|
|
120452
|
+
try {
|
|
120453
|
+
await bulkSetFavoriteGalleryItems(targetIds, targetFavorite);
|
|
120454
|
+
showToast(targetFavorite ? t("batchFavoritedToast", { count: String(targetIds.length) }) : t("batchUnfavoritedToast", { count: String(targetIds.length) }));
|
|
120455
|
+
} catch (err) {
|
|
120456
|
+
console.error("[dsh-image-gen] Batch favorite failed:", err);
|
|
120457
|
+
getGalleryItems().then((res) => setItems(res));
|
|
120458
|
+
showToast(t("batchFavoriteFailed"));
|
|
120459
|
+
}
|
|
120460
|
+
};
|
|
120461
|
+
const [isDownloadingBatch, setIsDownloadingBatch] = (0, react.useState)(false);
|
|
120462
|
+
const [batchDownloadProgress, setBatchDownloadProgress] = (0, react.useState)(null);
|
|
120463
|
+
const handleBatchDownload = async () => {
|
|
120464
|
+
if (selectedIds.size === 0 || isDownloadingBatch) return;
|
|
120465
|
+
const itemsToDownload = selectedItems;
|
|
120466
|
+
if (itemsToDownload.length === 0) return;
|
|
120467
|
+
setIsDownloadingBatch(true);
|
|
120468
|
+
try {
|
|
120469
|
+
if (itemsToDownload.length === 1) {
|
|
120470
|
+
const single = itemsToDownload[0];
|
|
120471
|
+
if (!single) return;
|
|
120472
|
+
let blob = blobCache.get(single.id);
|
|
120473
|
+
if (!blob) {
|
|
120474
|
+
blob = await fetchAttachmentBlob(single.attachment);
|
|
120475
|
+
blobCache.set(single.id, blob);
|
|
120476
|
+
}
|
|
120477
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
120478
|
+
downloadBlobUrl(objectUrl, `dsh-${single.provider}-${single.id}.png`);
|
|
120479
|
+
URL.revokeObjectURL(objectUrl);
|
|
120480
|
+
showToast(t("batchDownloadSingleToast"));
|
|
120481
|
+
return;
|
|
120482
|
+
}
|
|
120483
|
+
const zipFiles = [];
|
|
120484
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
120485
|
+
for (let i = 0; i < itemsToDownload.length; i++) {
|
|
120486
|
+
const it = itemsToDownload[i];
|
|
120487
|
+
if (!it) continue;
|
|
120488
|
+
setBatchDownloadProgress(`${i + 1}/${itemsToDownload.length}`);
|
|
120489
|
+
let blob = blobCache.get(it.id);
|
|
120490
|
+
if (!blob) {
|
|
120491
|
+
blob = await fetchAttachmentBlob(it.attachment);
|
|
120492
|
+
blobCache.set(it.id, blob);
|
|
120493
|
+
}
|
|
120494
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
120495
|
+
const ext = blob.type === "image/jpeg" ? "jpg" : blob.type === "image/webp" ? "webp" : "png";
|
|
120496
|
+
let baseName = `dsh-${it.provider}-${it.id.slice(0, 8)}.${ext}`;
|
|
120497
|
+
let count = 1;
|
|
120498
|
+
while (usedNames.has(baseName)) {
|
|
120499
|
+
baseName = `dsh-${it.provider}-${it.id.slice(0, 8)}-${count}.${ext}`;
|
|
120500
|
+
count++;
|
|
120501
|
+
}
|
|
120502
|
+
usedNames.add(baseName);
|
|
120503
|
+
zipFiles.push({
|
|
120504
|
+
name: baseName,
|
|
120505
|
+
data: new Uint8Array(arrayBuffer)
|
|
120506
|
+
});
|
|
120507
|
+
}
|
|
120508
|
+
const zipBlob = createZipBlob(zipFiles);
|
|
120509
|
+
const zipUrl = URL.createObjectURL(zipBlob);
|
|
120510
|
+
downloadBlobUrl(zipUrl, `dsh-gallery-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "")}.zip`);
|
|
120511
|
+
URL.revokeObjectURL(zipUrl);
|
|
120512
|
+
showToast(t("batchDownloadZipToast", { count: String(itemsToDownload.length) }));
|
|
120513
|
+
} catch (err) {
|
|
120514
|
+
console.error("[dsh-image-gen] Batch download failed:", err);
|
|
120515
|
+
showToast(t("batchDownloadFailed"));
|
|
120516
|
+
} finally {
|
|
120517
|
+
setIsDownloadingBatch(false);
|
|
120518
|
+
setBatchDownloadProgress(null);
|
|
120519
|
+
}
|
|
120520
|
+
};
|
|
120276
120521
|
const requestSingleDelete = (item, e) => {
|
|
120277
120522
|
if (e) e.stopPropagation();
|
|
120278
120523
|
setSelectedIds(/* @__PURE__ */ new Set([item.id]));
|
|
@@ -120505,13 +120750,13 @@ img.ProseMirror-separator {
|
|
|
120505
120750
|
})]
|
|
120506
120751
|
}), (0, react_jsx_runtime.jsxs)("button", {
|
|
120507
120752
|
type: "button",
|
|
120508
|
-
className: `dsh-ig-studio-btn
|
|
120753
|
+
className: `dsh-ig-studio-btn ${isManageMode ? "is-active" : ""}`,
|
|
120509
120754
|
title: isManageMode ? t("exitManage") : t("manage"),
|
|
120510
120755
|
onClick: () => {
|
|
120511
120756
|
if (isManageMode) handleExitManage();
|
|
120512
120757
|
else setIsManageMode(true);
|
|
120513
120758
|
},
|
|
120514
|
-
children: [(0, react_jsx_runtime.jsx)(
|
|
120759
|
+
children: [(0, react_jsx_runtime.jsx)(SquareCheckBig, { size: 13 }), (0, react_jsx_runtime.jsx)("span", { children: isManageMode ? t("exitManage") : t("manage") })]
|
|
120515
120760
|
})]
|
|
120516
120761
|
})]
|
|
120517
120762
|
}),
|
|
@@ -120628,18 +120873,45 @@ img.ProseMirror-separator {
|
|
|
120628
120873
|
]
|
|
120629
120874
|
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
120630
120875
|
className: "dsh-ig-batch-bar-right",
|
|
120631
|
-
children: [
|
|
120632
|
-
|
|
120633
|
-
|
|
120634
|
-
|
|
120635
|
-
|
|
120636
|
-
|
|
120637
|
-
|
|
120638
|
-
|
|
120639
|
-
|
|
120640
|
-
|
|
120641
|
-
|
|
120642
|
-
|
|
120876
|
+
children: [
|
|
120877
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
120878
|
+
type: "button",
|
|
120879
|
+
className: `dsh-ig-batch-btn ${allSelectedAreFavorites ? "is-favorited" : ""}`,
|
|
120880
|
+
disabled: selectedIds.size === 0,
|
|
120881
|
+
onClick: () => void handleBatchToggleFavorite(),
|
|
120882
|
+
title: allSelectedAreFavorites ? t("batchUnfavorite") : t("batchFavorite"),
|
|
120883
|
+
children: [(0, react_jsx_runtime.jsx)(Heart, {
|
|
120884
|
+
size: 13,
|
|
120885
|
+
fill: allSelectedAreFavorites ? "#ef4444" : "none",
|
|
120886
|
+
color: allSelectedAreFavorites ? "#ef4444" : "currentColor"
|
|
120887
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: allSelectedAreFavorites ? t("batchUnfavorite") : t("batchFavorite") })]
|
|
120888
|
+
}),
|
|
120889
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
120890
|
+
type: "button",
|
|
120891
|
+
className: "dsh-ig-batch-btn dsh-ig-batch-btn-download",
|
|
120892
|
+
disabled: selectedIds.size === 0 || isDownloadingBatch,
|
|
120893
|
+
onClick: () => void handleBatchDownload(),
|
|
120894
|
+
title: t("batchDownload"),
|
|
120895
|
+
children: [(0, react_jsx_runtime.jsx)(Download, { size: 13 }), (0, react_jsx_runtime.jsx)("span", { children: isDownloadingBatch && batchDownloadProgress ? t("batchDownloading", {
|
|
120896
|
+
current: batchDownloadProgress.split("/")[0] ?? "",
|
|
120897
|
+
total: batchDownloadProgress.split("/")[1] ?? ""
|
|
120898
|
+
}) : t("batchDownload") })]
|
|
120899
|
+
}),
|
|
120900
|
+
(0, react_jsx_runtime.jsx)("span", { className: "dsh-ig-batch-divider" }),
|
|
120901
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
120902
|
+
type: "button",
|
|
120903
|
+
className: "dsh-ig-batch-btn dsh-ig-batch-btn-danger",
|
|
120904
|
+
disabled: selectedIds.size === 0,
|
|
120905
|
+
onClick: () => setShowDeleteModal(true),
|
|
120906
|
+
children: [(0, react_jsx_runtime.jsx)(Trash2, { size: 13 }), (0, react_jsx_runtime.jsxs)("span", { children: [t("batchDelete"), selectedIds.size > 0 ? ` (${selectedIds.size})` : ""] })]
|
|
120907
|
+
}),
|
|
120908
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
120909
|
+
type: "button",
|
|
120910
|
+
className: "dsh-ig-batch-btn dsh-ig-batch-btn-exit",
|
|
120911
|
+
onClick: handleExitManage,
|
|
120912
|
+
children: t("exitManage")
|
|
120913
|
+
})
|
|
120914
|
+
]
|
|
120643
120915
|
})]
|
|
120644
120916
|
}),
|
|
120645
120917
|
showDeleteModal && (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -121785,6 +122057,12 @@ img.ProseMirror-separator {
|
|
|
121785
122057
|
return typeof candidate.attachmentId === "string" && (candidate.mediaType === "image/png" || candidate.mediaType === "image/jpeg" || candidate.mediaType === "image/webp" || candidate.mediaType === "image/gif") && typeof candidate.bytes === "number" && typeof candidate.width === "number" && typeof candidate.height === "number";
|
|
121786
122058
|
}
|
|
121787
122059
|
//#endregion
|
|
122060
|
+
//#region lib/types/client/settings-write.js
|
|
122061
|
+
/** DSH 0.1.7 resolves refused writes to false; older settings scopes resolve to void. */
|
|
122062
|
+
async function writeSetting(scope, field, value, rejectedMessage) {
|
|
122063
|
+
if (await scope.set(field, value) === false) throw new Error(rejectedMessage);
|
|
122064
|
+
}
|
|
122065
|
+
//#endregion
|
|
121788
122066
|
//#region lib/types/client/provider-pill.js
|
|
121789
122067
|
/**
|
|
121790
122068
|
* Composer tool-row pill: switch the default image provider without leaving
|
|
@@ -121805,7 +122083,8 @@ img.ProseMirror-separator {
|
|
|
121805
122083
|
subSignedOut: "未登录",
|
|
121806
122084
|
subStatusUnknown: "状态未知",
|
|
121807
122085
|
readOnly: "图像设置为只读,无法在此切换",
|
|
121808
|
-
switchFailed: "切换失败"
|
|
122086
|
+
switchFailed: "切换失败",
|
|
122087
|
+
writeRejected: "设置未保存,请重试"
|
|
121809
122088
|
},
|
|
121810
122089
|
en: {
|
|
121811
122090
|
pillLabel: "Image",
|
|
@@ -121819,7 +122098,8 @@ img.ProseMirror-separator {
|
|
|
121819
122098
|
subSignedOut: "Signed out",
|
|
121820
122099
|
subStatusUnknown: "Unknown",
|
|
121821
122100
|
readOnly: "Image settings are read-only; switch them in the config source",
|
|
121822
|
-
switchFailed: "Switch failed"
|
|
122101
|
+
switchFailed: "Switch failed",
|
|
122102
|
+
writeRejected: "Setting was not saved. Please try again"
|
|
121823
122103
|
}
|
|
121824
122104
|
};
|
|
121825
122105
|
/** Provider row names, per locale. Brand words stay untranslated; only the
|
|
@@ -122027,7 +122307,7 @@ img.ProseMirror-separator {
|
|
|
122027
122307
|
}
|
|
122028
122308
|
setError("");
|
|
122029
122309
|
setPending(provider);
|
|
122030
|
-
props.scope
|
|
122310
|
+
writeSetting(props.scope, "provider", provider, t("writeRejected")).then(() => {
|
|
122031
122311
|
setOpen(false);
|
|
122032
122312
|
}).catch((cause) => {
|
|
122033
122313
|
setError(cause instanceof Error ? cause.message : String(cause));
|
|
@@ -122220,7 +122500,7 @@ img.ProseMirror-separator {
|
|
|
122220
122500
|
`;
|
|
122221
122501
|
//#endregion
|
|
122222
122502
|
//#region lib/types/client/index.js
|
|
122223
|
-
console.info(`[dsh-image-gen] canvas bundle 2026-09-
|
|
122503
|
+
console.info(`[dsh-image-gen] canvas bundle 2026-09-26T07:20:53.102Z`);
|
|
122224
122504
|
/** Right-sidebar tab-type registration id (DSH 0.1.5 `sidebarRightTabs`). */
|
|
122225
122505
|
const SIDEBAR_STUDIO_TAB_ID = "dsh-image-gen/studio";
|
|
122226
122506
|
/** The tab kind: a page type opened by kind, recognizing no resource address. */
|
|
@@ -122345,6 +122625,7 @@ img.ProseMirror-separator {
|
|
|
122345
122625
|
saving: "保存中…",
|
|
122346
122626
|
save: "保存",
|
|
122347
122627
|
saved: "已保存",
|
|
122628
|
+
settingsWriteRejected: "有一项设置未能保存,请检查后重试。",
|
|
122348
122629
|
savedToPath: "已保存到",
|
|
122349
122630
|
generating: "正在生成图片…",
|
|
122350
122631
|
loading: "正在加载图片…",
|
|
@@ -122488,6 +122769,7 @@ img.ProseMirror-separator {
|
|
|
122488
122769
|
saving: "Saving…",
|
|
122489
122770
|
save: "Save",
|
|
122490
122771
|
saved: "Saved",
|
|
122772
|
+
settingsWriteRejected: "A setting could not be saved. Please check and try again.",
|
|
122491
122773
|
savedToPath: "Saved to",
|
|
122492
122774
|
generating: "Generating image…",
|
|
122493
122775
|
loading: "Loading image…",
|
|
@@ -122754,6 +123036,10 @@ img.ProseMirror-separator {
|
|
|
122754
123036
|
.dsh-ig-batch-btn{appearance:none;border:1px solid rgba(255,255,255,0.15);background:rgba(255,255,255,0.08);color:#fff;border-radius:20px;padding:5px 12px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:background .15s,border-color .15s,color .15s}
|
|
122755
123037
|
.dsh-ig-batch-btn:hover:not(:disabled){background:rgba(255,255,255,0.2)}
|
|
122756
123038
|
.dsh-ig-batch-btn:disabled{opacity:0.4;cursor:not-allowed}
|
|
123039
|
+
.dsh-ig-batch-btn.is-favorited{background:rgba(239,68,68,0.15);border-color:rgba(239,68,68,0.4);color:#fca5a5}
|
|
123040
|
+
.dsh-ig-batch-btn.is-favorited:hover:not(:disabled){background:rgba(239,68,68,0.25)!important;border-color:rgba(239,68,68,0.6)!important;color:#fff!important}
|
|
123041
|
+
.dsh-ig-batch-btn-download:hover:not(:disabled){background:rgba(59,130,246,0.2)!important;border-color:rgba(59,130,246,0.5)!important;color:#93c5fd!important}
|
|
123042
|
+
.dsh-ig-batch-divider{width:1px;height:16px;background:rgba(255,255,255,0.18);margin:0 2px;display:inline-block}
|
|
122757
123043
|
.dsh-ig-batch-btn-danger{background:rgba(239,68,68,0.2);border-color:rgba(239,68,68,0.5);color:#fca5a5}
|
|
122758
123044
|
.dsh-ig-batch-btn-danger:hover:not(:disabled){background:rgba(239,68,68,0.4)!important;border-color:rgba(239,68,68,0.8)!important;color:#fff!important}
|
|
122759
123045
|
.dsh-ig-batch-btn-exit{border-color:transparent;background:transparent;color:rgba(255,255,255,0.7)}
|
|
@@ -122800,19 +123086,40 @@ img.ProseMirror-separator {
|
|
|
122800
123086
|
:has(.dsh-ig-gallery-page:not(.dsh-ig-page-in-sidebar)) [class*="widthHandle"],
|
|
122801
123087
|
.root:has(.dsh-ig-gallery-page:not(.dsh-ig-page-in-sidebar)) [class*="widthHandle"]{display:none!important}
|
|
122802
123088
|
`;
|
|
122803
|
-
/**
|
|
123089
|
+
/**
|
|
123090
|
+
* Required browser services. The settings form reader is deliberately absent:
|
|
123091
|
+
* DSH 0.1.7 serves it as `configForms` (keyed by the Loader entry id) while
|
|
123092
|
+
* 0.1.5/0.1.6 expose `settingsScope` (keyed by the legacy namespace), so it is
|
|
123093
|
+
* probed at runtime in `apply`. A static inject of either name would park this
|
|
123094
|
+
* fiber forever on hosts from the other generation — the issue #55 failure.
|
|
123095
|
+
*/
|
|
122804
123096
|
const inject = [
|
|
122805
123097
|
"slots",
|
|
122806
123098
|
"connection",
|
|
122807
123099
|
"remote",
|
|
122808
|
-
"settingsScope",
|
|
122809
123100
|
"locale"
|
|
122810
123101
|
];
|
|
123102
|
+
/** Host with neither settings generation: reads empty, writes refuse, never throws. */
|
|
123103
|
+
function degradedScope() {
|
|
123104
|
+
return {
|
|
123105
|
+
getSnapshot: () => ({
|
|
123106
|
+
value: void 0,
|
|
123107
|
+
writable: false
|
|
123108
|
+
}),
|
|
123109
|
+
subscribe: () => () => {},
|
|
123110
|
+
set: async () => false
|
|
123111
|
+
};
|
|
123112
|
+
}
|
|
122811
123113
|
/** Mount the settings card, generated-image card, and native conversation gallery view. */
|
|
122812
123114
|
function apply(ctx) {
|
|
122813
|
-
const
|
|
122814
|
-
const
|
|
122815
|
-
const
|
|
123115
|
+
const configForms = ctx.get("configForms");
|
|
123116
|
+
const settingsScopeApi = configForms === void 0 ? ctx.get("settingsScope") : void 0;
|
|
123117
|
+
const scope = configForms?.get("image-gen") ?? settingsScopeApi?.bind({ namespace: "image-generation" }) ?? degradedScope();
|
|
123118
|
+
const chatScope = configForms?.get("ui-chat") ?? settingsScopeApi?.bind({ namespace: "ui-chat" }) ?? degradedScope();
|
|
123119
|
+
const isCompactTranscript = () => {
|
|
123120
|
+
const mode = chatScope.getSnapshot().value?.transcriptView;
|
|
123121
|
+
return configForms === void 0 ? mode !== "normal" : mode !== "standard" && mode !== "detailed" && mode !== "verbose";
|
|
123122
|
+
};
|
|
122816
123123
|
const locale = ctx.get("locale");
|
|
122817
123124
|
const promotion = { enabled: false };
|
|
122818
123125
|
ctx.inject(["sessions"], (owner) => {
|
|
@@ -123305,6 +123612,7 @@ img.ProseMirror-separator {
|
|
|
123305
123612
|
if (params) for (const [k, v] of Object.entries(params)) text = text.replace(`{${k}}`, v);
|
|
123306
123613
|
return text;
|
|
123307
123614
|
};
|
|
123615
|
+
const saveSetting = (field, value) => writeSetting(props.scope, field, value, t("settingsWriteRejected"));
|
|
123308
123616
|
const providerLabels = {
|
|
123309
123617
|
google: t("providerGoogle"),
|
|
123310
123618
|
openai: t("providerOpenAI"),
|
|
@@ -123416,7 +123724,7 @@ img.ProseMirror-separator {
|
|
|
123416
123724
|
setDefaultProvider(provider);
|
|
123417
123725
|
setProviderMessage("");
|
|
123418
123726
|
setProviderMessageIsError(false);
|
|
123419
|
-
|
|
123727
|
+
saveSetting("provider", provider).catch((cause) => {
|
|
123420
123728
|
setDefaultProvider(snapshot.value?.provider ?? "google");
|
|
123421
123729
|
setProviderMessage(cause instanceof Error ? cause.message : String(cause));
|
|
123422
123730
|
setProviderMessageIsError(true);
|
|
@@ -123442,12 +123750,12 @@ img.ProseMirror-separator {
|
|
|
123442
123750
|
}
|
|
123443
123751
|
if (new Set(entries.map((entry) => entry.name)).size !== entries.length) throw new Error(t("workflowDuplicateName"));
|
|
123444
123752
|
const activeEntry = entries.find((entry) => entry.name === row.activeWorkflow) ?? entries[0];
|
|
123445
|
-
await
|
|
123446
|
-
await
|
|
123447
|
-
await
|
|
123448
|
-
await
|
|
123449
|
-
await
|
|
123450
|
-
await
|
|
123753
|
+
await saveSetting("comfyuiBaseURL", row.baseURL);
|
|
123754
|
+
await saveSetting("comfyuiWorkflows", entries);
|
|
123755
|
+
await saveSetting("comfyuiActiveWorkflow", activeEntry === void 0 ? "" : activeEntry.name);
|
|
123756
|
+
await saveSetting("comfyuiWorkflowJson", activeEntry === void 0 ? "" : activeEntry.json);
|
|
123757
|
+
await saveSetting("comfyuiWorkflowName", activeEntry === void 0 ? "" : activeEntry.name);
|
|
123758
|
+
await saveSetting("comfyuiTimeoutMs", Math.max(1, Math.round(row.timeoutSeconds)) * 1e3);
|
|
123451
123759
|
} else if (isSubscriptionProvider(provider)) await Promise.resolve();
|
|
123452
123760
|
else {
|
|
123453
123761
|
let editExtra = {};
|
|
@@ -123464,16 +123772,16 @@ img.ProseMirror-separator {
|
|
|
123464
123772
|
editExtra = parsed;
|
|
123465
123773
|
}
|
|
123466
123774
|
}
|
|
123467
|
-
await
|
|
123468
|
-
await
|
|
123775
|
+
await saveSetting(modelFieldOf(provider), row.model);
|
|
123776
|
+
await saveSetting(baseURLFieldOf(provider), row.baseURL);
|
|
123469
123777
|
if (provider === "openai-compat") {
|
|
123470
|
-
await
|
|
123471
|
-
await
|
|
123778
|
+
await saveSetting("openaiCompatEditFormat", row.editFormat);
|
|
123779
|
+
await saveSetting("openaiCompatEditExtra", editExtra);
|
|
123472
123780
|
}
|
|
123473
123781
|
if (provider === "seedream") {
|
|
123474
|
-
await
|
|
123475
|
-
await
|
|
123476
|
-
await
|
|
123782
|
+
await saveSetting("seedreamOutputFormat", row.outputFormat);
|
|
123783
|
+
await saveSetting("seedreamWatermark", row.watermark);
|
|
123784
|
+
await saveSetting("seedreamBackground", row.background);
|
|
123477
123785
|
}
|
|
123478
123786
|
if (row.keyInput.trim().length > 0) {
|
|
123479
123787
|
const keyRef = cloudCredentialRef(provider);
|
|
@@ -123764,7 +124072,7 @@ img.ProseMirror-separator {
|
|
|
123764
124072
|
setSaveToWorkspace(next);
|
|
123765
124073
|
setWorkspaceMessage("");
|
|
123766
124074
|
setWorkspaceMessageIsError(false);
|
|
123767
|
-
|
|
124075
|
+
saveSetting("saveToWorkspace", next).catch((cause) => {
|
|
123768
124076
|
setSaveToWorkspace(snapshot.value?.saveToWorkspace ?? true);
|
|
123769
124077
|
setWorkspaceMessage(cause instanceof Error ? cause.message : String(cause));
|
|
123770
124078
|
setWorkspaceMessageIsError(true);
|
|
@@ -123776,7 +124084,7 @@ img.ProseMirror-separator {
|
|
|
123776
124084
|
if (next === (snapshot.value?.workspaceFolder ?? "dsh-image-gen")) return;
|
|
123777
124085
|
setWorkspaceMessage("");
|
|
123778
124086
|
setWorkspaceMessageIsError(false);
|
|
123779
|
-
|
|
124087
|
+
saveSetting("workspaceFolder", next).then(() => {
|
|
123780
124088
|
setWorkspaceMessage(t("saved"));
|
|
123781
124089
|
}).catch((cause) => {
|
|
123782
124090
|
setWorkspaceFolder(snapshot.value?.workspaceFolder ?? "dsh-image-gen");
|
|
@@ -123789,7 +124097,7 @@ img.ProseMirror-separator {
|
|
|
123789
124097
|
setShowPill(next);
|
|
123790
124098
|
setUiMessage("");
|
|
123791
124099
|
setUiMessageIsError(false);
|
|
123792
|
-
|
|
124100
|
+
saveSetting("showProviderPill", next).catch((cause) => {
|
|
123793
124101
|
setShowPill(snapshot.value?.showProviderPill === true);
|
|
123794
124102
|
setUiMessage(cause instanceof Error ? cause.message : String(cause));
|
|
123795
124103
|
setUiMessageIsError(true);
|