dsh-image-gen 0.2.5 → 0.2.6
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 +1498 -382
- package/lib/client.js.map +1 -1
- package/lib/index.js +158 -13
- package/lib/types/client/gallery-store.d.ts +20 -4
- package/lib/types/client/gallery-view.d.ts +103 -10
- package/lib/types/image-route.d.ts +8 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/shared.d.ts +2 -0
- package/lib/types/workspace-save.d.ts +21 -0
- package/package.json +5 -2
package/lib/client.js
CHANGED
|
@@ -10,6 +10,8 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
/** Values shared by the Host and browser Bundle faces. */
|
|
11
11
|
/** Browser route used by the generated-image card. */
|
|
12
12
|
const IMAGE_ROUTE = "/plugins/dsh-image-gen/image";
|
|
13
|
+
/** Browser route used for deleting generated images and workspace files. */
|
|
14
|
+
const DELETE_ROUTE = "/plugins/dsh-image-gen/delete";
|
|
13
15
|
/** Namespace persisted through DSH Settings. */
|
|
14
16
|
const IMAGE_GENERATION_NAMESPACE = "image-generation";
|
|
15
17
|
/** Default endpoints and base URLs. */
|
|
@@ -210,19 +212,33 @@ window.__ModuleLoader__.load({
|
|
|
210
212
|
/**
|
|
211
213
|
* Save or update a gallery record by attachmentId.
|
|
212
214
|
* Skipped if the item was previously deleted (tombstoned).
|
|
215
|
+
* Preserves existing isFavorite, tags, and original createdAt on re-renders.
|
|
213
216
|
*/
|
|
214
217
|
async function saveGalleryItem(item) {
|
|
215
218
|
try {
|
|
216
219
|
const db = await getDB();
|
|
217
220
|
if ((await loadTombstones(db)).has(item.id)) return;
|
|
218
|
-
const record = {
|
|
219
|
-
...item,
|
|
220
|
-
createdAt: item.createdAt ?? Date.now()
|
|
221
|
-
};
|
|
222
221
|
await new Promise((resolve, reject) => {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
const store = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME);
|
|
223
|
+
const getReq = store.get(item.id);
|
|
224
|
+
getReq.onsuccess = () => {
|
|
225
|
+
const existing = getReq.result;
|
|
226
|
+
const fav = item.isFavorite !== void 0 ? item.isFavorite : existing?.isFavorite;
|
|
227
|
+
const userTags = item.tags !== void 0 ? item.tags : existing?.tags;
|
|
228
|
+
const savedPath = item.savedTo !== void 0 ? item.savedTo : existing?.savedTo;
|
|
229
|
+
const record = {
|
|
230
|
+
...existing,
|
|
231
|
+
...item,
|
|
232
|
+
createdAt: existing?.createdAt ?? item.createdAt ?? Date.now(),
|
|
233
|
+
...fav !== void 0 ? { isFavorite: fav } : {},
|
|
234
|
+
...userTags !== void 0 ? { tags: userTags } : {},
|
|
235
|
+
...savedPath !== void 0 ? { savedTo: savedPath } : {}
|
|
236
|
+
};
|
|
237
|
+
const putReq = store.put(record);
|
|
238
|
+
putReq.onsuccess = () => resolve();
|
|
239
|
+
putReq.onerror = () => reject(putReq.error);
|
|
240
|
+
};
|
|
241
|
+
getReq.onerror = () => reject(getReq.error);
|
|
226
242
|
});
|
|
227
243
|
notifyListeners();
|
|
228
244
|
} catch (err) {
|
|
@@ -253,52 +269,537 @@ window.__ModuleLoader__.load({
|
|
|
253
269
|
}
|
|
254
270
|
}
|
|
255
271
|
/**
|
|
256
|
-
*
|
|
272
|
+
* Toggle favorite status of a gallery item.
|
|
273
|
+
* Returns the new favorite status (true if favorited, false if unfavorited).
|
|
257
274
|
*/
|
|
258
|
-
async function
|
|
275
|
+
async function toggleFavoriteGalleryItem(id) {
|
|
259
276
|
try {
|
|
260
277
|
const db = await getDB();
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
278
|
+
const newStatus = await new Promise((resolve, reject) => {
|
|
279
|
+
const store = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME);
|
|
280
|
+
const getReq = store.get(id);
|
|
281
|
+
getReq.onsuccess = () => {
|
|
282
|
+
const item = getReq.result;
|
|
283
|
+
if (!item) {
|
|
284
|
+
resolve(false);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const nextFavorite = !item.isFavorite;
|
|
288
|
+
item.isFavorite = nextFavorite;
|
|
289
|
+
const putReq = store.put(item);
|
|
290
|
+
putReq.onsuccess = () => resolve(nextFavorite);
|
|
291
|
+
putReq.onerror = () => reject(putReq.error);
|
|
292
|
+
};
|
|
293
|
+
getReq.onerror = () => reject(getReq.error);
|
|
273
294
|
});
|
|
274
295
|
notifyListeners();
|
|
296
|
+
return newStatus;
|
|
275
297
|
} catch (err) {
|
|
276
|
-
console.warn("[dsh-image-gen] Failed to
|
|
298
|
+
console.warn("[dsh-image-gen] Failed to toggle favorite item in IndexedDB:", err);
|
|
299
|
+
return false;
|
|
277
300
|
}
|
|
278
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Bulk delete multiple gallery records by IDs and record tombstones in a single transaction.
|
|
304
|
+
*/
|
|
305
|
+
async function bulkDeleteGalleryItems(ids) {
|
|
306
|
+
if (ids.length === 0) return;
|
|
307
|
+
const db = await getDB();
|
|
308
|
+
const tombstones = await loadTombstones(db);
|
|
309
|
+
const now = Date.now();
|
|
310
|
+
await new Promise((resolve, reject) => {
|
|
311
|
+
const tx = db.transaction([STORE_NAME, TOMBSTONE_STORE], "readwrite");
|
|
312
|
+
const store = tx.objectStore(STORE_NAME);
|
|
313
|
+
const tombstoneStore = tx.objectStore(TOMBSTONE_STORE);
|
|
314
|
+
for (const id of ids) {
|
|
315
|
+
store.delete(id);
|
|
316
|
+
tombstoneStore.put({
|
|
317
|
+
id,
|
|
318
|
+
deletedAt: now
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
tx.oncomplete = () => {
|
|
322
|
+
for (const id of ids) tombstones.add(id);
|
|
323
|
+
resolve();
|
|
324
|
+
};
|
|
325
|
+
tx.onerror = () => reject(tx.error ?? /* @__PURE__ */ new Error("IndexedDB transaction failed"));
|
|
326
|
+
tx.onabort = () => reject(tx.error ?? /* @__PURE__ */ new Error("IndexedDB transaction aborted"));
|
|
327
|
+
});
|
|
328
|
+
notifyListeners();
|
|
329
|
+
}
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs
|
|
332
|
+
/**
|
|
333
|
+
* @license lucide-react v1.38.0 - ISC
|
|
334
|
+
*
|
|
335
|
+
* This source code is licensed under the ISC license.
|
|
336
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
337
|
+
*/
|
|
338
|
+
const mergeClasses = (...classes) => classes.filter((className, index, array) => {
|
|
339
|
+
return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
|
|
340
|
+
}).join(" ").trim();
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs
|
|
343
|
+
/**
|
|
344
|
+
* @license lucide-react v1.38.0 - ISC
|
|
345
|
+
*
|
|
346
|
+
* This source code is licensed under the ISC license.
|
|
347
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
348
|
+
*/
|
|
349
|
+
const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs
|
|
352
|
+
/**
|
|
353
|
+
* @license lucide-react v1.38.0 - ISC
|
|
354
|
+
*
|
|
355
|
+
* This source code is licensed under the ISC license.
|
|
356
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
357
|
+
*/
|
|
358
|
+
const toCamelCase = (string) => string.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase());
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs
|
|
361
|
+
/**
|
|
362
|
+
* @license lucide-react v1.38.0 - ISC
|
|
363
|
+
*
|
|
364
|
+
* This source code is licensed under the ISC license.
|
|
365
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
366
|
+
*/
|
|
367
|
+
const toPascalCase = (string) => {
|
|
368
|
+
const camelCase = toCamelCase(string);
|
|
369
|
+
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
|
370
|
+
};
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/defaultAttributes.mjs
|
|
373
|
+
/**
|
|
374
|
+
* @license lucide-react v1.38.0 - ISC
|
|
375
|
+
*
|
|
376
|
+
* This source code is licensed under the ISC license.
|
|
377
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
378
|
+
*/
|
|
379
|
+
var defaultAttributes = {
|
|
380
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
381
|
+
width: 24,
|
|
382
|
+
height: 24,
|
|
383
|
+
viewBox: "0 0 24 24",
|
|
384
|
+
fill: "none",
|
|
385
|
+
stroke: "currentColor",
|
|
386
|
+
strokeWidth: 2,
|
|
387
|
+
strokeLinecap: "round",
|
|
388
|
+
strokeLinejoin: "round"
|
|
389
|
+
};
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs
|
|
392
|
+
/**
|
|
393
|
+
* @license lucide-react v1.38.0 - ISC
|
|
394
|
+
*
|
|
395
|
+
* This source code is licensed under the ISC license.
|
|
396
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
397
|
+
*/
|
|
398
|
+
const hasA11yProp = (props) => {
|
|
399
|
+
for (const prop in props) if (prop.startsWith("aria-") || prop === "role" || prop === "title") return true;
|
|
400
|
+
return false;
|
|
401
|
+
};
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/context.mjs
|
|
404
|
+
/**
|
|
405
|
+
* @license lucide-react v1.38.0 - ISC
|
|
406
|
+
*
|
|
407
|
+
* This source code is licensed under the ISC license.
|
|
408
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
409
|
+
*/
|
|
410
|
+
const LucideContext = (0, react.createContext)({});
|
|
411
|
+
const useLucideContext = () => (0, react.useContext)(LucideContext);
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/Icon.mjs
|
|
414
|
+
/**
|
|
415
|
+
* @license lucide-react v1.38.0 - ISC
|
|
416
|
+
*
|
|
417
|
+
* This source code is licensed under the ISC license.
|
|
418
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
419
|
+
*/
|
|
420
|
+
const Icon = (0, react.forwardRef)(({ color, size, strokeWidth, absoluteStrokeWidth, className = "", children, iconNode, ...rest }, ref) => {
|
|
421
|
+
const { size: contextSize = 24, strokeWidth: contextStrokeWidth = 2, absoluteStrokeWidth: contextAbsoluteStrokeWidth = false, color: contextColor = "currentColor", className: contextClass = "" } = useLucideContext() ?? {};
|
|
422
|
+
const calculatedStrokeWidth = absoluteStrokeWidth ?? contextAbsoluteStrokeWidth ? Number(strokeWidth ?? contextStrokeWidth) * 24 / Number(size ?? contextSize) : strokeWidth ?? contextStrokeWidth;
|
|
423
|
+
return (0, react.createElement)("svg", {
|
|
424
|
+
ref,
|
|
425
|
+
...defaultAttributes,
|
|
426
|
+
width: size ?? contextSize ?? defaultAttributes.width,
|
|
427
|
+
height: size ?? contextSize ?? defaultAttributes.height,
|
|
428
|
+
stroke: color ?? contextColor,
|
|
429
|
+
strokeWidth: calculatedStrokeWidth,
|
|
430
|
+
className: mergeClasses("lucide", contextClass, className),
|
|
431
|
+
...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
|
|
432
|
+
...rest
|
|
433
|
+
}, [...iconNode.map(([tag, attrs]) => (0, react.createElement)(tag, attrs)), ...Array.isArray(children) ? children : [children]]);
|
|
434
|
+
});
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region node_modules/.pnpm/lucide-react@1.38.0_react@18.3.1/node_modules/lucide-react/dist/esm/createLucideIcon.mjs
|
|
437
|
+
/**
|
|
438
|
+
* @license lucide-react v1.38.0 - ISC
|
|
439
|
+
*
|
|
440
|
+
* This source code is licensed under the ISC license.
|
|
441
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
442
|
+
*/
|
|
443
|
+
const createLucideIcon = (iconName, iconNode) => {
|
|
444
|
+
const Component = (0, react.forwardRef)(({ className, ...props }, ref) => (0, react.createElement)(Icon, {
|
|
445
|
+
ref,
|
|
446
|
+
iconNode,
|
|
447
|
+
className: mergeClasses(`lucide-${toKebabCase(toPascalCase(iconName))}`, `lucide-${iconName}`, className),
|
|
448
|
+
...props
|
|
449
|
+
}));
|
|
450
|
+
Component.displayName = toPascalCase(iconName);
|
|
451
|
+
return Component;
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* @license lucide-react v1.38.0 - ISC
|
|
455
|
+
*
|
|
456
|
+
* This source code is licensed under the ISC license.
|
|
457
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
458
|
+
*/
|
|
459
|
+
const Check = createLucideIcon("check", [["path", {
|
|
460
|
+
d: "M20 6 9 17l-5-5",
|
|
461
|
+
key: "1gmf2c"
|
|
462
|
+
}]]);
|
|
463
|
+
/**
|
|
464
|
+
* @license lucide-react v1.38.0 - ISC
|
|
465
|
+
*
|
|
466
|
+
* This source code is licensed under the ISC license.
|
|
467
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
468
|
+
*/
|
|
469
|
+
const ChevronLeft = createLucideIcon("chevron-left", [["path", {
|
|
470
|
+
d: "m15 18-6-6 6-6",
|
|
471
|
+
key: "1wnfg3"
|
|
472
|
+
}]]);
|
|
473
|
+
/**
|
|
474
|
+
* @license lucide-react v1.38.0 - ISC
|
|
475
|
+
*
|
|
476
|
+
* This source code is licensed under the ISC license.
|
|
477
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
478
|
+
*/
|
|
479
|
+
const ChevronRight = createLucideIcon("chevron-right", [["path", {
|
|
480
|
+
d: "m9 18 6-6-6-6",
|
|
481
|
+
key: "mthhwq"
|
|
482
|
+
}]]);
|
|
483
|
+
/**
|
|
484
|
+
* @license lucide-react v1.38.0 - ISC
|
|
485
|
+
*
|
|
486
|
+
* This source code is licensed under the ISC license.
|
|
487
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
488
|
+
*/
|
|
489
|
+
const Copy = createLucideIcon("copy", [["rect", {
|
|
490
|
+
width: "14",
|
|
491
|
+
height: "14",
|
|
492
|
+
x: "8",
|
|
493
|
+
y: "8",
|
|
494
|
+
rx: "2",
|
|
495
|
+
ry: "2",
|
|
496
|
+
key: "17jyea"
|
|
497
|
+
}], ["path", {
|
|
498
|
+
d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
|
|
499
|
+
key: "zix9uf"
|
|
500
|
+
}]]);
|
|
501
|
+
/**
|
|
502
|
+
* @license lucide-react v1.38.0 - ISC
|
|
503
|
+
*
|
|
504
|
+
* This source code is licensed under the ISC license.
|
|
505
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
506
|
+
*/
|
|
507
|
+
const Download = createLucideIcon("download", [
|
|
508
|
+
["path", {
|
|
509
|
+
d: "M12 15V3",
|
|
510
|
+
key: "m9g1x1"
|
|
511
|
+
}],
|
|
512
|
+
["path", {
|
|
513
|
+
d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",
|
|
514
|
+
key: "ih7n3h"
|
|
515
|
+
}],
|
|
516
|
+
["path", {
|
|
517
|
+
d: "m7 10 5 5 5-5",
|
|
518
|
+
key: "brsn70"
|
|
519
|
+
}]
|
|
520
|
+
]);
|
|
521
|
+
/**
|
|
522
|
+
* @license lucide-react v1.38.0 - ISC
|
|
523
|
+
*
|
|
524
|
+
* This source code is licensed under the ISC license.
|
|
525
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
526
|
+
*/
|
|
527
|
+
const FileText = createLucideIcon("file-text", [
|
|
528
|
+
["path", {
|
|
529
|
+
d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
|
|
530
|
+
key: "1oefj6"
|
|
531
|
+
}],
|
|
532
|
+
["path", {
|
|
533
|
+
d: "M14 2v5a1 1 0 0 0 1 1h5",
|
|
534
|
+
key: "wfsgrz"
|
|
535
|
+
}],
|
|
536
|
+
["path", {
|
|
537
|
+
d: "M10 9H8",
|
|
538
|
+
key: "b1mrlr"
|
|
539
|
+
}],
|
|
540
|
+
["path", {
|
|
541
|
+
d: "M16 13H8",
|
|
542
|
+
key: "t4e002"
|
|
543
|
+
}],
|
|
544
|
+
["path", {
|
|
545
|
+
d: "M16 17H8",
|
|
546
|
+
key: "z1uh3a"
|
|
547
|
+
}]
|
|
548
|
+
]);
|
|
549
|
+
/**
|
|
550
|
+
* @license lucide-react v1.38.0 - ISC
|
|
551
|
+
*
|
|
552
|
+
* This source code is licensed under the ISC license.
|
|
553
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
554
|
+
*/
|
|
555
|
+
const GitCompare = createLucideIcon("git-compare", [
|
|
556
|
+
["circle", {
|
|
557
|
+
cx: "18",
|
|
558
|
+
cy: "18",
|
|
559
|
+
r: "3",
|
|
560
|
+
key: "1xkwt0"
|
|
561
|
+
}],
|
|
562
|
+
["circle", {
|
|
563
|
+
cx: "6",
|
|
564
|
+
cy: "6",
|
|
565
|
+
r: "3",
|
|
566
|
+
key: "1lh9wr"
|
|
567
|
+
}],
|
|
568
|
+
["path", {
|
|
569
|
+
d: "M13 6h3a2 2 0 0 1 2 2v7",
|
|
570
|
+
key: "1yeb86"
|
|
571
|
+
}],
|
|
572
|
+
["path", {
|
|
573
|
+
d: "M11 18H8a2 2 0 0 1-2-2V9",
|
|
574
|
+
key: "19pyzm"
|
|
575
|
+
}]
|
|
576
|
+
]);
|
|
577
|
+
/**
|
|
578
|
+
* @license lucide-react v1.38.0 - ISC
|
|
579
|
+
*
|
|
580
|
+
* This source code is licensed under the ISC license.
|
|
581
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
582
|
+
*/
|
|
583
|
+
const Heart = createLucideIcon("heart", [["path", {
|
|
584
|
+
d: "M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5",
|
|
585
|
+
key: "mvr1a0"
|
|
586
|
+
}]]);
|
|
587
|
+
/**
|
|
588
|
+
* @license lucide-react v1.38.0 - ISC
|
|
589
|
+
*
|
|
590
|
+
* This source code is licensed under the ISC license.
|
|
591
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
592
|
+
*/
|
|
593
|
+
const Image$1 = createLucideIcon("image", [
|
|
594
|
+
["rect", {
|
|
595
|
+
width: "18",
|
|
596
|
+
height: "18",
|
|
597
|
+
x: "3",
|
|
598
|
+
y: "3",
|
|
599
|
+
rx: "2",
|
|
600
|
+
ry: "2",
|
|
601
|
+
key: "1m3agn"
|
|
602
|
+
}],
|
|
603
|
+
["circle", {
|
|
604
|
+
cx: "9",
|
|
605
|
+
cy: "9",
|
|
606
|
+
r: "2",
|
|
607
|
+
key: "af1f0g"
|
|
608
|
+
}],
|
|
609
|
+
["path", {
|
|
610
|
+
d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21",
|
|
611
|
+
key: "1xmnt7"
|
|
612
|
+
}]
|
|
613
|
+
]);
|
|
614
|
+
/**
|
|
615
|
+
* @license lucide-react v1.38.0 - ISC
|
|
616
|
+
*
|
|
617
|
+
* This source code is licensed under the ISC license.
|
|
618
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
619
|
+
*/
|
|
620
|
+
const ListTodo = createLucideIcon("list-todo", [
|
|
621
|
+
["path", {
|
|
622
|
+
d: "M13 5h8",
|
|
623
|
+
key: "a7qcls"
|
|
624
|
+
}],
|
|
625
|
+
["path", {
|
|
626
|
+
d: "M13 12h8",
|
|
627
|
+
key: "h98zly"
|
|
628
|
+
}],
|
|
629
|
+
["path", {
|
|
630
|
+
d: "M13 19h8",
|
|
631
|
+
key: "c3s6r1"
|
|
632
|
+
}],
|
|
633
|
+
["path", {
|
|
634
|
+
d: "m3 17 2 2 4-4",
|
|
635
|
+
key: "1jhpwq"
|
|
636
|
+
}],
|
|
637
|
+
["rect", {
|
|
638
|
+
x: "3",
|
|
639
|
+
y: "4",
|
|
640
|
+
width: "6",
|
|
641
|
+
height: "6",
|
|
642
|
+
rx: "1",
|
|
643
|
+
key: "cif1o7"
|
|
644
|
+
}]
|
|
645
|
+
]);
|
|
646
|
+
/**
|
|
647
|
+
* @license lucide-react v1.38.0 - ISC
|
|
648
|
+
*
|
|
649
|
+
* This source code is licensed under the ISC license.
|
|
650
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
651
|
+
*/
|
|
652
|
+
const Search = createLucideIcon("search", [["path", {
|
|
653
|
+
d: "m21 21-4.34-4.34",
|
|
654
|
+
key: "14j7rj"
|
|
655
|
+
}], ["circle", {
|
|
656
|
+
cx: "11",
|
|
657
|
+
cy: "11",
|
|
658
|
+
r: "8",
|
|
659
|
+
key: "4ej97u"
|
|
660
|
+
}]]);
|
|
661
|
+
/**
|
|
662
|
+
* @license lucide-react v1.38.0 - ISC
|
|
663
|
+
*
|
|
664
|
+
* This source code is licensed under the ISC license.
|
|
665
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
666
|
+
*/
|
|
667
|
+
const SlidersHorizontal = createLucideIcon("sliders-horizontal", [
|
|
668
|
+
["path", {
|
|
669
|
+
d: "M10 5H3",
|
|
670
|
+
key: "1qgfaw"
|
|
671
|
+
}],
|
|
672
|
+
["path", {
|
|
673
|
+
d: "M12 19H3",
|
|
674
|
+
key: "yhmn1j"
|
|
675
|
+
}],
|
|
676
|
+
["path", {
|
|
677
|
+
d: "M14 3v4",
|
|
678
|
+
key: "1sua03"
|
|
679
|
+
}],
|
|
680
|
+
["path", {
|
|
681
|
+
d: "M16 17v4",
|
|
682
|
+
key: "1q0r14"
|
|
683
|
+
}],
|
|
684
|
+
["path", {
|
|
685
|
+
d: "M21 12h-9",
|
|
686
|
+
key: "1o4lsq"
|
|
687
|
+
}],
|
|
688
|
+
["path", {
|
|
689
|
+
d: "M21 19h-5",
|
|
690
|
+
key: "1rlt1p"
|
|
691
|
+
}],
|
|
692
|
+
["path", {
|
|
693
|
+
d: "M21 5h-7",
|
|
694
|
+
key: "1oszz2"
|
|
695
|
+
}],
|
|
696
|
+
["path", {
|
|
697
|
+
d: "M8 10v4",
|
|
698
|
+
key: "tgpxqk"
|
|
699
|
+
}],
|
|
700
|
+
["path", {
|
|
701
|
+
d: "M8 12H3",
|
|
702
|
+
key: "a7s4jb"
|
|
703
|
+
}]
|
|
704
|
+
]);
|
|
705
|
+
/**
|
|
706
|
+
* @license lucide-react v1.38.0 - ISC
|
|
707
|
+
*
|
|
708
|
+
* This source code is licensed under the ISC license.
|
|
709
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
710
|
+
*/
|
|
711
|
+
const Trash2 = createLucideIcon("trash-2", [
|
|
712
|
+
["path", {
|
|
713
|
+
d: "M10 11v6",
|
|
714
|
+
key: "nco0om"
|
|
715
|
+
}],
|
|
716
|
+
["path", {
|
|
717
|
+
d: "M14 11v6",
|
|
718
|
+
key: "outv1u"
|
|
719
|
+
}],
|
|
720
|
+
["path", {
|
|
721
|
+
d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",
|
|
722
|
+
key: "miytrc"
|
|
723
|
+
}],
|
|
724
|
+
["path", {
|
|
725
|
+
d: "M3 6h18",
|
|
726
|
+
key: "d0wm0j"
|
|
727
|
+
}],
|
|
728
|
+
["path", {
|
|
729
|
+
d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",
|
|
730
|
+
key: "e791ji"
|
|
731
|
+
}]
|
|
732
|
+
]);
|
|
733
|
+
/**
|
|
734
|
+
* @license lucide-react v1.38.0 - ISC
|
|
735
|
+
*
|
|
736
|
+
* This source code is licensed under the ISC license.
|
|
737
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
738
|
+
*/
|
|
739
|
+
const TriangleAlert = createLucideIcon("triangle-alert", [
|
|
740
|
+
["path", {
|
|
741
|
+
d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",
|
|
742
|
+
key: "wmoenq"
|
|
743
|
+
}],
|
|
744
|
+
["path", {
|
|
745
|
+
d: "M12 9v4",
|
|
746
|
+
key: "juzpu7"
|
|
747
|
+
}],
|
|
748
|
+
["path", {
|
|
749
|
+
d: "M12 17h.01",
|
|
750
|
+
key: "p32p05"
|
|
751
|
+
}]
|
|
752
|
+
]);
|
|
753
|
+
/**
|
|
754
|
+
* @license lucide-react v1.38.0 - ISC
|
|
755
|
+
*
|
|
756
|
+
* This source code is licensed under the ISC license.
|
|
757
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
758
|
+
*/
|
|
759
|
+
const X = createLucideIcon("x", [["path", {
|
|
760
|
+
d: "M18 6 6 18",
|
|
761
|
+
key: "1bl5f8"
|
|
762
|
+
}], ["path", {
|
|
763
|
+
d: "m6 6 12 12",
|
|
764
|
+
key: "d8bk6v"
|
|
765
|
+
}]]);
|
|
279
766
|
//#endregion
|
|
280
767
|
//#region lib/types/client/gallery-view.js
|
|
281
768
|
/**
|
|
282
|
-
* Native Workspace Gallery View Component for DSH `conversation.view` slot.
|
|
283
|
-
* Fully i18n-reactive (Chinese & English)
|
|
769
|
+
* Native Workspace Gallery & Studio View Component for DSH `conversation.view` slot.
|
|
770
|
+
* Fully i18n-reactive (Chinese & English) with modular tabs, multi-dimensional filters,
|
|
771
|
+
* responsive image grid, and placeholder routes.
|
|
284
772
|
*/
|
|
285
773
|
const DICT$1 = {
|
|
286
774
|
zh: {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
775
|
+
tabGallery: "图库",
|
|
776
|
+
tabStudio: "工作台",
|
|
777
|
+
tabFavorites: "收藏",
|
|
778
|
+
tabCompare: "对比",
|
|
779
|
+
tabTasks: "任务",
|
|
780
|
+
filterAllProviders: "全部提供商",
|
|
290
781
|
filterGoogle: "Google Gemini",
|
|
291
782
|
filterOpenAI: "OpenAI / 中转站",
|
|
292
783
|
filterSeedream: "字节 Seedream",
|
|
293
784
|
filterDashScope: "阿里 DashScope",
|
|
294
785
|
filterComfyUI: "本地 ComfyUI",
|
|
786
|
+
filterAllModels: "全部模型",
|
|
787
|
+
filterAllRatios: "全部比例",
|
|
788
|
+
searchPlaceholder: "搜索 Prompt、标签…",
|
|
789
|
+
sortNewest: "最新生成",
|
|
790
|
+
sortOldest: "最早生成",
|
|
791
|
+
totalCount: "共 {count} 张生成图片",
|
|
295
792
|
emptyTitle: "暂无生图记录",
|
|
296
793
|
emptyDesc: "在对话中让 Agent 生图后,生成的图片会自动收录到这里。",
|
|
794
|
+
favEmptyTitle: "暂无收藏图片",
|
|
795
|
+
favEmptyDesc: "在图库中点击卡片右下角的 ♡ 按钮,即可将喜爱的图片收录到这里。",
|
|
297
796
|
noMatchTitle: "未找到匹配结果",
|
|
298
|
-
noMatchDesc: "
|
|
797
|
+
noMatchDesc: "尝试更换搜索关键词或调整筛选条件。",
|
|
299
798
|
copiedPrompt: "已复制 Prompt",
|
|
300
799
|
copiedImage: "已复制图片",
|
|
301
800
|
copyFailed: "复制失败",
|
|
801
|
+
favoriteAdded: "已添加到收藏",
|
|
802
|
+
favoriteRemoved: "已取消收藏",
|
|
302
803
|
preview: "查看大图",
|
|
303
804
|
download: "下载图片",
|
|
304
805
|
copyImg: "复制图片",
|
|
@@ -308,24 +809,69 @@ window.__ModuleLoader__.load({
|
|
|
308
809
|
deleted: "已从画廊删除",
|
|
309
810
|
model: "模型",
|
|
310
811
|
prompt: "Prompt",
|
|
311
|
-
close: "关闭 (Esc)"
|
|
812
|
+
close: "关闭 (Esc)",
|
|
813
|
+
prevImage: "上一张 (←)",
|
|
814
|
+
nextImage: "下一张 (→)",
|
|
815
|
+
manage: "批量删除",
|
|
816
|
+
exitManage: "退出选择",
|
|
817
|
+
selectedCount: "已选 {n} 项",
|
|
818
|
+
selectAll: "全选",
|
|
819
|
+
invertSelect: "反选",
|
|
820
|
+
clearSelect: "清空",
|
|
821
|
+
batchDelete: "批量删除",
|
|
822
|
+
batchDeleteTitle: "确认批量删除选中的 {count} 张图片?",
|
|
823
|
+
batchDeleteTitleSingle: "确认从画廊中删除这张图片?",
|
|
824
|
+
batchDeleteDesc: "将从画廊历史记录中移除所选图片。",
|
|
825
|
+
deleteWorkspaceFilesOpt: "同时清理工作区磁盘物理文件(不可恢复)",
|
|
826
|
+
cancel: "取消",
|
|
827
|
+
confirmBatchDelete: "确认删除 ({count})",
|
|
828
|
+
confirmDeleteSingle: "确认删除",
|
|
829
|
+
batchDeletedToast: "已删除 {count} 张图片",
|
|
830
|
+
batchDeletedWithFilesToast: "已删除 {count} 张图片,并清理了 {files} 个工作区文件",
|
|
831
|
+
deleteFailedFileLocked: "已删除 {count} 张图片,但有 {files} 个文件因被系统占用未能删除",
|
|
832
|
+
deleteFailedDatabase: "删除失败:本地数据库操作异常",
|
|
833
|
+
justNow: "刚刚",
|
|
834
|
+
minutesAgo: "{n} 分钟前",
|
|
835
|
+
hoursAgo: "{n} 小时前",
|
|
836
|
+
daysAgo: "{n} 天前",
|
|
837
|
+
studioTitle: "AI 图像工作台 (Studio)",
|
|
838
|
+
studioDesc: "工作台模块正在紧锣密鼓开发中。在此你将体验大图精修、变体生成 (Variations)、参数重调并一键将生成结果无缝插回 DSH 正在进行的对话。",
|
|
839
|
+
studioTip: "💡 提示:目前你可以在“图库”中点击任意图片,在弹窗中进行查看、复制与下载。",
|
|
840
|
+
compareTitle: "多模型横向对比 (Compare)",
|
|
841
|
+
compareDesc: "支持单个 Prompt 一键同时调度 Gemini、Seedream、DashScope 及本地 ComfyUI 模型并排生成,直观横评画质与细节。",
|
|
842
|
+
tasksTitle: "异步任务队列 (Tasks)",
|
|
843
|
+
tasksDesc: "集中管理后台批量生图、多模型并发生成与本地 ComfyUI 耗时任务。支持状态追踪、失败重试与执行耗时分析。",
|
|
844
|
+
comingSoonBadge: "即将推出"
|
|
312
845
|
},
|
|
313
846
|
en: {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
847
|
+
tabGallery: "Gallery",
|
|
848
|
+
tabStudio: "Studio",
|
|
849
|
+
tabFavorites: "Favorites",
|
|
850
|
+
tabCompare: "Compare",
|
|
851
|
+
tabTasks: "Tasks",
|
|
852
|
+
filterAllProviders: "All Providers",
|
|
317
853
|
filterGoogle: "Google Gemini",
|
|
318
854
|
filterOpenAI: "OpenAI / Relay",
|
|
319
855
|
filterSeedream: "ByteDance Seedream",
|
|
320
856
|
filterDashScope: "Aliyun DashScope",
|
|
321
857
|
filterComfyUI: "Local ComfyUI",
|
|
858
|
+
filterAllModels: "All Models",
|
|
859
|
+
filterAllRatios: "All Ratios",
|
|
860
|
+
searchPlaceholder: "Search prompt, tags…",
|
|
861
|
+
sortNewest: "Newest first",
|
|
862
|
+
sortOldest: "Oldest first",
|
|
863
|
+
totalCount: "{count} images total",
|
|
322
864
|
emptyTitle: "No images generated yet",
|
|
323
865
|
emptyDesc: "Images generated during conversations will automatically appear here.",
|
|
866
|
+
favEmptyTitle: "No favorite images yet",
|
|
867
|
+
favEmptyDesc: "Click the ♡ button on any card in the gallery to collect your favorite images here.",
|
|
324
868
|
noMatchTitle: "No matching images",
|
|
325
|
-
noMatchDesc: "Try a different search keyword or
|
|
869
|
+
noMatchDesc: "Try a different search keyword or adjust filter criteria.",
|
|
326
870
|
copiedPrompt: "Prompt copied",
|
|
327
871
|
copiedImage: "Image copied",
|
|
328
872
|
copyFailed: "Copy failed",
|
|
873
|
+
favoriteAdded: "Added to favorites",
|
|
874
|
+
favoriteRemoved: "Removed from favorites",
|
|
329
875
|
preview: "Full Preview",
|
|
330
876
|
download: "Download",
|
|
331
877
|
copyImg: "Copy Image",
|
|
@@ -335,17 +881,123 @@ window.__ModuleLoader__.load({
|
|
|
335
881
|
deleted: "Deleted from gallery",
|
|
336
882
|
model: "Model",
|
|
337
883
|
prompt: "Prompt",
|
|
338
|
-
close: "Close (Esc)"
|
|
884
|
+
close: "Close (Esc)",
|
|
885
|
+
prevImage: "Previous (←)",
|
|
886
|
+
nextImage: "Next (→)",
|
|
887
|
+
manage: "Batch Delete",
|
|
888
|
+
exitManage: "Done",
|
|
889
|
+
selectedCount: "{n} selected",
|
|
890
|
+
selectAll: "Select All",
|
|
891
|
+
invertSelect: "Invert",
|
|
892
|
+
clearSelect: "Clear",
|
|
893
|
+
batchDelete: "Batch Delete",
|
|
894
|
+
batchDeleteTitle: "Delete {count} selected images?",
|
|
895
|
+
batchDeleteTitleSingle: "Delete this image from gallery?",
|
|
896
|
+
batchDeleteDesc: "These images will be removed from your gallery history.",
|
|
897
|
+
deleteWorkspaceFilesOpt: "Also delete files from workspace disk (cannot be undone)",
|
|
898
|
+
cancel: "Cancel",
|
|
899
|
+
confirmBatchDelete: "Delete ({count})",
|
|
900
|
+
confirmDeleteSingle: "Delete",
|
|
901
|
+
batchDeletedToast: "Deleted {count} images",
|
|
902
|
+
batchDeletedWithFilesToast: "Deleted {count} images and cleaned {files} workspace files",
|
|
903
|
+
deleteFailedFileLocked: "Deleted {count} images, but {files} files could not be deleted (busy/locked)",
|
|
904
|
+
deleteFailedDatabase: "Failed to delete: local database error",
|
|
905
|
+
justNow: "Just now",
|
|
906
|
+
minutesAgo: "{n}m ago",
|
|
907
|
+
hoursAgo: "{n}h ago",
|
|
908
|
+
daysAgo: "{n}d ago",
|
|
909
|
+
studioTitle: "AI Image Studio",
|
|
910
|
+
studioDesc: "Studio workbench is under active development. Fine-tune prompts, generate variations (2x/4x), and inject images directly into DSH chat.",
|
|
911
|
+
studioTip: "💡 Tip: You can currently click any image in the Gallery to preview, copy, or download it.",
|
|
912
|
+
compareTitle: "Model Comparison (Compare)",
|
|
913
|
+
compareDesc: "Side-by-side multi-model benchmarking coming soon. Test Gemini, Seedream, DashScope, and ComfyUI with a single prompt.",
|
|
914
|
+
tasksTitle: "Task Queue (Tasks)",
|
|
915
|
+
tasksDesc: "Centralized view for batch generation, asynchronous ComfyUI runs, live progress tracking, and retry controls.",
|
|
916
|
+
comingSoonBadge: "Coming Soon"
|
|
339
917
|
}
|
|
340
918
|
};
|
|
919
|
+
/** Format human-readable relative time */
|
|
920
|
+
function formatRelativeTime(timestamp, t) {
|
|
921
|
+
const diff = Date.now() - timestamp;
|
|
922
|
+
if (diff < 6e4) return t("justNow");
|
|
923
|
+
if (diff < 36e5) {
|
|
924
|
+
const mins = Math.max(1, Math.floor(diff / 6e4));
|
|
925
|
+
return t("minutesAgo", { n: String(mins) });
|
|
926
|
+
}
|
|
927
|
+
if (diff < 864e5) {
|
|
928
|
+
const hours = Math.floor(diff / 36e5);
|
|
929
|
+
return t("hoursAgo", { n: String(hours) });
|
|
930
|
+
}
|
|
931
|
+
if (diff < 2592e6) {
|
|
932
|
+
const days = Math.floor(diff / 864e5);
|
|
933
|
+
return t("daysAgo", { n: String(days) });
|
|
934
|
+
}
|
|
935
|
+
const d = new Date(timestamp);
|
|
936
|
+
return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`;
|
|
937
|
+
}
|
|
938
|
+
/** Extract standard aspect ratio for precise filtering */
|
|
939
|
+
function getItemRatio(item) {
|
|
940
|
+
if (item.aspectRatio && item.aspectRatio !== "custom") return item.aspectRatio;
|
|
941
|
+
if (item.output) {
|
|
942
|
+
const ratioMatch = item.output.match(/\b(1:1|16:9|9:16|4:3|3:4|3:2|2:3)\b/);
|
|
943
|
+
if (ratioMatch?.[1]) return ratioMatch[1];
|
|
944
|
+
const dimMatch = item.output.match(/(\d{3,4})\s*[×x*]\s*(\d{3,4})/);
|
|
945
|
+
if (dimMatch?.[1] && dimMatch[2]) {
|
|
946
|
+
const w = parseInt(dimMatch[1], 10);
|
|
947
|
+
const h = parseInt(dimMatch[2], 10);
|
|
948
|
+
if (w === h) return "1:1";
|
|
949
|
+
const approx = w / h;
|
|
950
|
+
if (Math.abs(approx - 16 / 9) < .05) return "16:9";
|
|
951
|
+
if (Math.abs(approx - 9 / 16) < .05) return "9:16";
|
|
952
|
+
if (Math.abs(approx - 4 / 3) < .05) return "4:3";
|
|
953
|
+
if (Math.abs(approx - 3 / 4) < .05) return "3:4";
|
|
954
|
+
if (Math.abs(approx - 3 / 2) < .05) return "3:2";
|
|
955
|
+
if (Math.abs(approx - 2 / 3) < .05) return "2:3";
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return "1:1";
|
|
959
|
+
}
|
|
960
|
+
/** Format aspect ratio or dimensions for card metadata (e.g., 1024×1024 or 16:9) */
|
|
961
|
+
function formatCardMeta(item) {
|
|
962
|
+
if (item.output) {
|
|
963
|
+
const dimMatch = item.output.match(/(\d{3,4})\s*[×x*]\s*(\d{3,4})/);
|
|
964
|
+
if (dimMatch?.[1] && dimMatch[2]) return `${dimMatch[1]}×${dimMatch[2]}`;
|
|
965
|
+
}
|
|
966
|
+
if (item.aspectRatio && item.aspectRatio !== "custom") return item.aspectRatio;
|
|
967
|
+
if (item.output) {
|
|
968
|
+
const ratioMatch = item.output.match(/\b(1:1|16:9|9:16|4:3|3:4|3:2|2:3)\b/);
|
|
969
|
+
if (ratioMatch?.[1]) return ratioMatch[1];
|
|
970
|
+
}
|
|
971
|
+
if (item.imageSize) return item.imageSize;
|
|
972
|
+
return "1:1";
|
|
973
|
+
}
|
|
341
974
|
const GalleryViewTab = ({ locale }) => {
|
|
975
|
+
const [activeTab, setActiveTab] = (0, react.useState)("gallery");
|
|
342
976
|
const [items, setItems] = (0, react.useState)([]);
|
|
343
977
|
const [search, setSearch] = (0, react.useState)("");
|
|
344
978
|
const [selectedProvider, setSelectedProvider] = (0, react.useState)("all");
|
|
979
|
+
const [selectedRatio, setSelectedRatio] = (0, react.useState)("all");
|
|
980
|
+
const [sortBy, setSortBy] = (0, react.useState)("newest");
|
|
345
981
|
const [previewItem, setPreviewItem] = (0, react.useState)(null);
|
|
346
982
|
const [previewUrl, setPreviewUrl] = (0, react.useState)(null);
|
|
347
983
|
const [previewBlob, setPreviewBlob] = (0, react.useState)(null);
|
|
348
984
|
const [toast, setToast] = (0, react.useState)(null);
|
|
985
|
+
const [isManageMode, setIsManageMode] = (0, react.useState)(false);
|
|
986
|
+
const [selectedIds, setSelectedIds] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
987
|
+
const lastSelectedIndexRef = (0, react.useRef)(null);
|
|
988
|
+
const [showDeleteModal, setShowDeleteModal] = (0, react.useState)(false);
|
|
989
|
+
const [deleteWorkspaceFiles, setDeleteWorkspaceFiles] = (0, react.useState)(true);
|
|
990
|
+
const [isDeleting, setIsDeleting] = (0, react.useState)(false);
|
|
991
|
+
(0, react.useEffect)(() => {
|
|
992
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
993
|
+
lastSelectedIndexRef.current = null;
|
|
994
|
+
}, [
|
|
995
|
+
activeTab,
|
|
996
|
+
search,
|
|
997
|
+
selectedProvider,
|
|
998
|
+
selectedRatio,
|
|
999
|
+
sortBy
|
|
1000
|
+
]);
|
|
349
1001
|
const [lang, setLang] = (0, react.useState)(() => {
|
|
350
1002
|
return (locale?.getSnapshot?.()?.active)?.startsWith("en") ? "en" : "zh";
|
|
351
1003
|
});
|
|
@@ -392,112 +1044,431 @@ window.__ModuleLoader__.load({
|
|
|
392
1044
|
unsubscribe();
|
|
393
1045
|
};
|
|
394
1046
|
}, []);
|
|
395
|
-
(0, react.
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
if (e.key === "Escape") {
|
|
399
|
-
setPreviewItem(null);
|
|
400
|
-
setPreviewUrl(null);
|
|
401
|
-
setPreviewBlob(null);
|
|
402
|
-
}
|
|
403
|
-
};
|
|
404
|
-
window.addEventListener("keydown", onKeyDown);
|
|
405
|
-
return () => window.removeEventListener("keydown", onKeyDown);
|
|
406
|
-
}, [previewItem]);
|
|
1047
|
+
const [previewLoading, setPreviewLoading] = (0, react.useState)(false);
|
|
1048
|
+
const blobCache = (0, react.useMemo)(() => /* @__PURE__ */ new Map(), []);
|
|
1049
|
+
const abortControllerRef = (0, react.useRef)(null);
|
|
407
1050
|
const filteredItems = (0, react.useMemo)(() => {
|
|
408
1051
|
return items.filter((item) => {
|
|
1052
|
+
if (activeTab === "favorites" && !item.isFavorite) return false;
|
|
409
1053
|
if (selectedProvider !== "all" && item.provider !== selectedProvider) return false;
|
|
1054
|
+
if (selectedRatio !== "all") {
|
|
1055
|
+
if (getItemRatio(item) !== selectedRatio) return false;
|
|
1056
|
+
}
|
|
410
1057
|
if (search.trim().length > 0) {
|
|
411
1058
|
const q = search.trim().toLowerCase();
|
|
412
1059
|
const matchPrompt = item.prompt?.toLowerCase().includes(q);
|
|
413
1060
|
const matchModel = item.model?.toLowerCase().includes(q);
|
|
414
|
-
|
|
1061
|
+
const matchProvider = item.provider?.toLowerCase().includes(q);
|
|
1062
|
+
const matchTags = item.tags?.some((t) => t.toLowerCase().includes(q));
|
|
1063
|
+
if (!matchPrompt && !matchModel && !matchProvider && !matchTags) return false;
|
|
415
1064
|
}
|
|
416
1065
|
return true;
|
|
1066
|
+
}).sort((a, b) => {
|
|
1067
|
+
if (sortBy === "oldest") return a.createdAt - b.createdAt;
|
|
1068
|
+
return b.createdAt - a.createdAt;
|
|
417
1069
|
});
|
|
418
1070
|
}, [
|
|
419
1071
|
items,
|
|
1072
|
+
activeTab,
|
|
1073
|
+
selectedProvider,
|
|
1074
|
+
selectedRatio,
|
|
420
1075
|
search,
|
|
421
|
-
|
|
1076
|
+
sortBy
|
|
1077
|
+
]);
|
|
1078
|
+
const currentPreviewIndex = (0, react.useMemo)(() => {
|
|
1079
|
+
if (!previewItem) return -1;
|
|
1080
|
+
return filteredItems.findIndex((i) => i.id === previewItem.id);
|
|
1081
|
+
}, [previewItem, filteredItems]);
|
|
1082
|
+
const hasPrev = currentPreviewIndex > 0;
|
|
1083
|
+
const hasNext = currentPreviewIndex >= 0 && currentPreviewIndex < filteredItems.length - 1;
|
|
1084
|
+
const openPreviewItem = (item, blob) => {
|
|
1085
|
+
if (abortControllerRef.current) {
|
|
1086
|
+
abortControllerRef.current.abort();
|
|
1087
|
+
abortControllerRef.current = null;
|
|
1088
|
+
}
|
|
1089
|
+
if (previewUrl) URL.revokeObjectURL(previewUrl);
|
|
1090
|
+
setPreviewItem(item);
|
|
1091
|
+
if (blob) {
|
|
1092
|
+
blobCache.set(item.id, blob);
|
|
1093
|
+
const url = URL.createObjectURL(blob);
|
|
1094
|
+
setPreviewBlob(blob);
|
|
1095
|
+
setPreviewUrl(url);
|
|
1096
|
+
setPreviewLoading(false);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
const cached = blobCache.get(item.id);
|
|
1100
|
+
if (cached) {
|
|
1101
|
+
const url = URL.createObjectURL(cached);
|
|
1102
|
+
setPreviewBlob(cached);
|
|
1103
|
+
setPreviewUrl(url);
|
|
1104
|
+
setPreviewLoading(false);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
setPreviewLoading(true);
|
|
1108
|
+
setPreviewBlob(null);
|
|
1109
|
+
setPreviewUrl(null);
|
|
1110
|
+
const controller = new AbortController();
|
|
1111
|
+
abortControllerRef.current = controller;
|
|
1112
|
+
fetch(IMAGE_ROUTE, {
|
|
1113
|
+
method: "POST",
|
|
1114
|
+
signal: controller.signal,
|
|
1115
|
+
headers: { "content-type": "application/json" },
|
|
1116
|
+
body: JSON.stringify({ attachment: item.attachment })
|
|
1117
|
+
}).then(async (res) => {
|
|
1118
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1119
|
+
return res.blob();
|
|
1120
|
+
}).then((fetchedBlob) => {
|
|
1121
|
+
if (controller.signal.aborted) return;
|
|
1122
|
+
blobCache.set(item.id, fetchedBlob);
|
|
1123
|
+
const url = URL.createObjectURL(fetchedBlob);
|
|
1124
|
+
setPreviewBlob(fetchedBlob);
|
|
1125
|
+
setPreviewUrl(url);
|
|
1126
|
+
setPreviewLoading(false);
|
|
1127
|
+
}).catch((err) => {
|
|
1128
|
+
if (controller.signal.aborted) return;
|
|
1129
|
+
setPreviewLoading(false);
|
|
1130
|
+
showToast(t("copyFailed"));
|
|
1131
|
+
});
|
|
1132
|
+
};
|
|
1133
|
+
const handleClosePreview = () => {
|
|
1134
|
+
if (abortControllerRef.current) {
|
|
1135
|
+
abortControllerRef.current.abort();
|
|
1136
|
+
abortControllerRef.current = null;
|
|
1137
|
+
}
|
|
1138
|
+
if (previewUrl) URL.revokeObjectURL(previewUrl);
|
|
1139
|
+
setPreviewItem(null);
|
|
1140
|
+
setPreviewUrl(null);
|
|
1141
|
+
setPreviewBlob(null);
|
|
1142
|
+
setPreviewLoading(false);
|
|
1143
|
+
};
|
|
1144
|
+
const goToPrev = () => {
|
|
1145
|
+
if (currentPreviewIndex > 0) {
|
|
1146
|
+
const prevItem = filteredItems[currentPreviewIndex - 1];
|
|
1147
|
+
if (prevItem) openPreviewItem(prevItem);
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
const goToNext = () => {
|
|
1151
|
+
if (currentPreviewIndex >= 0 && currentPreviewIndex < filteredItems.length - 1) {
|
|
1152
|
+
const nextItem = filteredItems[currentPreviewIndex + 1];
|
|
1153
|
+
if (nextItem) openPreviewItem(nextItem);
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
(0, react.useEffect)(() => {
|
|
1157
|
+
return () => {
|
|
1158
|
+
if (abortControllerRef.current) abortControllerRef.current.abort();
|
|
1159
|
+
if (previewUrl) URL.revokeObjectURL(previewUrl);
|
|
1160
|
+
};
|
|
1161
|
+
}, [previewUrl]);
|
|
1162
|
+
(0, react.useEffect)(() => {
|
|
1163
|
+
const onKeyDown = (e) => {
|
|
1164
|
+
if (["INPUT", "TEXTAREA"].includes(e.target?.tagName)) return;
|
|
1165
|
+
if (showDeleteModal) {
|
|
1166
|
+
if (e.key === "Escape" && !isDeleting) {
|
|
1167
|
+
e.preventDefault();
|
|
1168
|
+
setShowDeleteModal(false);
|
|
1169
|
+
if (!isManageMode) setSelectedIds(/* @__PURE__ */ new Set());
|
|
1170
|
+
}
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
if (previewItem) {
|
|
1174
|
+
if (e.key === "Escape") handleClosePreview();
|
|
1175
|
+
else if (e.key === "ArrowLeft" || e.key === "Left") {
|
|
1176
|
+
e.preventDefault();
|
|
1177
|
+
goToPrev();
|
|
1178
|
+
} else if (e.key === "ArrowRight" || e.key === "Right") {
|
|
1179
|
+
e.preventDefault();
|
|
1180
|
+
goToNext();
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1185
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1186
|
+
}, [
|
|
1187
|
+
showDeleteModal,
|
|
1188
|
+
isDeleting,
|
|
1189
|
+
isManageMode,
|
|
1190
|
+
previewItem,
|
|
1191
|
+
currentPreviewIndex,
|
|
1192
|
+
filteredItems
|
|
422
1193
|
]);
|
|
1194
|
+
const handleCardClick = (item, index, e, blob) => {
|
|
1195
|
+
if (e.shiftKey || isManageMode) {
|
|
1196
|
+
e.stopPropagation();
|
|
1197
|
+
if (!isManageMode) setIsManageMode(true);
|
|
1198
|
+
if (e.shiftKey && lastSelectedIndexRef.current !== null) {
|
|
1199
|
+
const from = Math.min(lastSelectedIndexRef.current, index);
|
|
1200
|
+
const to = Math.max(lastSelectedIndexRef.current, index);
|
|
1201
|
+
const rangeItems = filteredItems.slice(from, to + 1);
|
|
1202
|
+
setSelectedIds((prev) => {
|
|
1203
|
+
const next = new Set(prev);
|
|
1204
|
+
for (const r of rangeItems) next.add(r.id);
|
|
1205
|
+
return next;
|
|
1206
|
+
});
|
|
1207
|
+
} else {
|
|
1208
|
+
setSelectedIds((prev) => {
|
|
1209
|
+
const next = new Set(prev);
|
|
1210
|
+
if (next.has(item.id)) next.delete(item.id);
|
|
1211
|
+
else next.add(item.id);
|
|
1212
|
+
return next;
|
|
1213
|
+
});
|
|
1214
|
+
lastSelectedIndexRef.current = index;
|
|
1215
|
+
}
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
if (blob) openPreviewItem(item, blob);
|
|
1219
|
+
};
|
|
1220
|
+
const handleToggleSelect = (id, index, e) => {
|
|
1221
|
+
e.stopPropagation();
|
|
1222
|
+
if (!isManageMode) setIsManageMode(true);
|
|
1223
|
+
if (e.shiftKey && lastSelectedIndexRef.current !== null) {
|
|
1224
|
+
const from = Math.min(lastSelectedIndexRef.current, index);
|
|
1225
|
+
const to = Math.max(lastSelectedIndexRef.current, index);
|
|
1226
|
+
const rangeItems = filteredItems.slice(from, to + 1);
|
|
1227
|
+
setSelectedIds((prev) => {
|
|
1228
|
+
const next = new Set(prev);
|
|
1229
|
+
for (const r of rangeItems) next.add(r.id);
|
|
1230
|
+
return next;
|
|
1231
|
+
});
|
|
1232
|
+
} else {
|
|
1233
|
+
setSelectedIds((prev) => {
|
|
1234
|
+
const next = new Set(prev);
|
|
1235
|
+
if (next.has(id)) next.delete(id);
|
|
1236
|
+
else next.add(id);
|
|
1237
|
+
return next;
|
|
1238
|
+
});
|
|
1239
|
+
lastSelectedIndexRef.current = index;
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
const handleSelectAll = () => {
|
|
1243
|
+
setSelectedIds(new Set(filteredItems.map((i) => i.id)));
|
|
1244
|
+
};
|
|
1245
|
+
const handleInvertSelect = () => {
|
|
1246
|
+
setSelectedIds((prev) => {
|
|
1247
|
+
const next = /* @__PURE__ */ new Set();
|
|
1248
|
+
for (const item of filteredItems) if (!prev.has(item.id)) next.add(item.id);
|
|
1249
|
+
return next;
|
|
1250
|
+
});
|
|
1251
|
+
};
|
|
1252
|
+
const handleClearSelect = () => {
|
|
1253
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
1254
|
+
lastSelectedIndexRef.current = null;
|
|
1255
|
+
};
|
|
1256
|
+
const handleExitManage = () => {
|
|
1257
|
+
setIsManageMode(false);
|
|
1258
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
1259
|
+
lastSelectedIndexRef.current = null;
|
|
1260
|
+
};
|
|
1261
|
+
const requestSingleDelete = (item, e) => {
|
|
1262
|
+
if (e) e.stopPropagation();
|
|
1263
|
+
setSelectedIds(/* @__PURE__ */ new Set([item.id]));
|
|
1264
|
+
setShowDeleteModal(true);
|
|
1265
|
+
};
|
|
1266
|
+
const handleConfirmBatchDelete = async () => {
|
|
1267
|
+
if (selectedIds.size === 0 || isDeleting) return;
|
|
1268
|
+
setIsDeleting(true);
|
|
1269
|
+
const idsToDelete = Array.from(selectedIds);
|
|
1270
|
+
const itemsToDelete = items.filter((i) => selectedIds.has(i.id));
|
|
1271
|
+
const pathsToDelete = itemsToDelete.map((i) => i.savedTo).filter((p) => typeof p === "string" && p.trim().length > 0);
|
|
1272
|
+
const fallbackAttachmentIds = itemsToDelete.filter((i) => !i.savedTo || !i.savedTo.trim()).map((i) => i.id);
|
|
1273
|
+
let deletedFiles = 0;
|
|
1274
|
+
let failedFilesCount = 0;
|
|
1275
|
+
if (deleteWorkspaceFiles && (pathsToDelete.length > 0 || fallbackAttachmentIds.length > 0)) try {
|
|
1276
|
+
const res = await fetch(DELETE_ROUTE, {
|
|
1277
|
+
method: "POST",
|
|
1278
|
+
headers: { "content-type": "application/json" },
|
|
1279
|
+
body: JSON.stringify({
|
|
1280
|
+
paths: pathsToDelete,
|
|
1281
|
+
attachmentIds: fallbackAttachmentIds
|
|
1282
|
+
})
|
|
1283
|
+
});
|
|
1284
|
+
if (res.ok) {
|
|
1285
|
+
const data = await res.json();
|
|
1286
|
+
deletedFiles = data.deletedCount ?? 0;
|
|
1287
|
+
if (Array.isArray(data.failedFiles)) failedFilesCount = data.failedFiles.length;
|
|
1288
|
+
}
|
|
1289
|
+
} catch (err) {
|
|
1290
|
+
console.warn("[dsh-image-gen] Workspace file deletion failed:", err);
|
|
1291
|
+
}
|
|
1292
|
+
try {
|
|
1293
|
+
await bulkDeleteGalleryItems(idsToDelete);
|
|
1294
|
+
} catch (err) {
|
|
1295
|
+
setIsDeleting(false);
|
|
1296
|
+
showToast(t("deleteFailedDatabase"));
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (previewItem && selectedIds.has(previewItem.id)) handleClosePreview();
|
|
1300
|
+
setIsDeleting(false);
|
|
1301
|
+
setShowDeleteModal(false);
|
|
1302
|
+
if (isManageMode) handleExitManage();
|
|
1303
|
+
else {
|
|
1304
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
1305
|
+
lastSelectedIndexRef.current = null;
|
|
1306
|
+
}
|
|
1307
|
+
if (failedFilesCount > 0) showToast(t("deleteFailedFileLocked", {
|
|
1308
|
+
count: String(idsToDelete.length),
|
|
1309
|
+
files: String(failedFilesCount)
|
|
1310
|
+
}));
|
|
1311
|
+
else if (deletedFiles > 0) showToast(t("batchDeletedWithFilesToast", {
|
|
1312
|
+
count: String(idsToDelete.length),
|
|
1313
|
+
files: String(deletedFiles)
|
|
1314
|
+
}));
|
|
1315
|
+
else showToast(t("batchDeletedToast", { count: String(idsToDelete.length) }));
|
|
1316
|
+
};
|
|
423
1317
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
424
1318
|
className: "dsh-ig-gallery-page",
|
|
425
1319
|
children: [
|
|
426
1320
|
(0, react_jsx_runtime.jsxs)("header", {
|
|
427
|
-
className: "dsh-ig-
|
|
428
|
-
children: [
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
className:
|
|
432
|
-
|
|
1321
|
+
className: "dsh-ig-studio-tabs-bar",
|
|
1322
|
+
children: [
|
|
1323
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
1324
|
+
type: "button",
|
|
1325
|
+
className: `dsh-ig-studio-tab-btn ${activeTab === "gallery" ? "is-active" : ""}`,
|
|
1326
|
+
onClick: () => setActiveTab("gallery"),
|
|
1327
|
+
children: [(0, react_jsx_runtime.jsx)(Image$1, { size: 15 }), (0, react_jsx_runtime.jsx)("span", { children: t("tabGallery") })]
|
|
1328
|
+
}),
|
|
1329
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
1330
|
+
type: "button",
|
|
1331
|
+
className: `dsh-ig-studio-tab-btn ${activeTab === "studio" ? "is-active" : ""}`,
|
|
1332
|
+
onClick: () => setActiveTab("studio"),
|
|
1333
|
+
children: [(0, react_jsx_runtime.jsx)(SlidersHorizontal, { size: 15 }), (0, react_jsx_runtime.jsx)("span", { children: t("tabStudio") })]
|
|
1334
|
+
}),
|
|
1335
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
1336
|
+
type: "button",
|
|
1337
|
+
className: `dsh-ig-studio-tab-btn ${activeTab === "favorites" ? "is-active" : ""}`,
|
|
1338
|
+
onClick: () => setActiveTab("favorites"),
|
|
1339
|
+
children: [(0, react_jsx_runtime.jsx)(Heart, { size: 15 }), (0, react_jsx_runtime.jsx)("span", { children: t("tabFavorites") })]
|
|
1340
|
+
}),
|
|
1341
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
1342
|
+
type: "button",
|
|
1343
|
+
className: `dsh-ig-studio-tab-btn ${activeTab === "compare" ? "is-active" : ""}`,
|
|
1344
|
+
onClick: () => setActiveTab("compare"),
|
|
1345
|
+
children: [(0, react_jsx_runtime.jsx)(GitCompare, { size: 15 }), (0, react_jsx_runtime.jsx)("span", { children: t("tabCompare") })]
|
|
1346
|
+
}),
|
|
1347
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
1348
|
+
type: "button",
|
|
1349
|
+
className: `dsh-ig-studio-tab-btn ${activeTab === "tasks" ? "is-active" : ""}`,
|
|
1350
|
+
onClick: () => setActiveTab("tasks"),
|
|
1351
|
+
children: [(0, react_jsx_runtime.jsx)(ListTodo, { size: 15 }), (0, react_jsx_runtime.jsx)("span", { children: t("tabTasks") })]
|
|
433
1352
|
})
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
1353
|
+
]
|
|
1354
|
+
}),
|
|
1355
|
+
(activeTab === "gallery" || activeTab === "favorites") && (0, react_jsx_runtime.jsxs)("div", {
|
|
1356
|
+
className: "dsh-ig-studio-toolbar",
|
|
1357
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
1358
|
+
className: "dsh-ig-studio-toolbar-left",
|
|
1359
|
+
children: [
|
|
1360
|
+
(0, react_jsx_runtime.jsxs)("select", {
|
|
1361
|
+
className: "dsh-ig-studio-select",
|
|
1362
|
+
value: selectedProvider,
|
|
1363
|
+
onChange: (e) => setSelectedProvider(e.target.value),
|
|
1364
|
+
children: [
|
|
1365
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1366
|
+
value: "all",
|
|
1367
|
+
children: t("filterAllProviders")
|
|
1368
|
+
}),
|
|
1369
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1370
|
+
value: "google",
|
|
1371
|
+
children: t("filterGoogle")
|
|
1372
|
+
}),
|
|
1373
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1374
|
+
value: "openai",
|
|
1375
|
+
children: t("filterOpenAI")
|
|
1376
|
+
}),
|
|
1377
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1378
|
+
value: "seedream",
|
|
1379
|
+
children: t("filterSeedream")
|
|
1380
|
+
}),
|
|
1381
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1382
|
+
value: "dashscope",
|
|
1383
|
+
children: t("filterDashScope")
|
|
1384
|
+
}),
|
|
1385
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1386
|
+
value: "comfyui",
|
|
1387
|
+
children: t("filterComfyUI")
|
|
1388
|
+
})
|
|
1389
|
+
]
|
|
1390
|
+
}),
|
|
1391
|
+
(0, react_jsx_runtime.jsxs)("select", {
|
|
1392
|
+
className: "dsh-ig-studio-select",
|
|
1393
|
+
value: selectedRatio,
|
|
1394
|
+
onChange: (e) => setSelectedRatio(e.target.value),
|
|
1395
|
+
children: [
|
|
1396
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1397
|
+
value: "all",
|
|
1398
|
+
children: t("filterAllRatios")
|
|
1399
|
+
}),
|
|
1400
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1401
|
+
value: "1:1",
|
|
1402
|
+
children: "1:1"
|
|
1403
|
+
}),
|
|
1404
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1405
|
+
value: "16:9",
|
|
1406
|
+
children: "16:9"
|
|
1407
|
+
}),
|
|
1408
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1409
|
+
value: "9:16",
|
|
1410
|
+
children: "9:16"
|
|
1411
|
+
}),
|
|
1412
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1413
|
+
value: "4:3",
|
|
1414
|
+
children: "4:3"
|
|
1415
|
+
}),
|
|
1416
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1417
|
+
value: "3:4",
|
|
1418
|
+
children: "3:4"
|
|
1419
|
+
}),
|
|
1420
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1421
|
+
value: "3:2",
|
|
1422
|
+
children: "3:2"
|
|
1423
|
+
}),
|
|
1424
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
1425
|
+
value: "2:3",
|
|
1426
|
+
children: "2:3"
|
|
1427
|
+
})
|
|
1428
|
+
]
|
|
1429
|
+
}),
|
|
1430
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1431
|
+
className: "dsh-ig-studio-search-wrap",
|
|
1432
|
+
children: [(0, react_jsx_runtime.jsx)(Search, {
|
|
1433
|
+
className: "dsh-ig-studio-search-icon",
|
|
1434
|
+
size: 14
|
|
1435
|
+
}), (0, react_jsx_runtime.jsx)("input", {
|
|
1436
|
+
type: "text",
|
|
1437
|
+
className: "dsh-ig-studio-search-input",
|
|
1438
|
+
placeholder: t("searchPlaceholder"),
|
|
1439
|
+
value: search,
|
|
1440
|
+
onChange: (e) => setSearch(e.target.value)
|
|
457
1441
|
})]
|
|
458
|
-
})
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
1442
|
+
})
|
|
1443
|
+
]
|
|
1444
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1445
|
+
className: "dsh-ig-studio-toolbar-right",
|
|
1446
|
+
children: [(0, react_jsx_runtime.jsxs)("select", {
|
|
1447
|
+
className: "dsh-ig-studio-select dsh-ig-studio-select-sort",
|
|
1448
|
+
value: sortBy,
|
|
1449
|
+
onChange: (e) => setSortBy(e.target.value),
|
|
1450
|
+
children: [(0, react_jsx_runtime.jsx)("option", {
|
|
1451
|
+
value: "newest",
|
|
1452
|
+
children: t("sortNewest")
|
|
1453
|
+
}), (0, react_jsx_runtime.jsx)("option", {
|
|
1454
|
+
value: "oldest",
|
|
1455
|
+
children: t("sortOldest")
|
|
464
1456
|
})]
|
|
465
|
-
}), (0, react_jsx_runtime.jsxs)("
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
475
|
-
value: "google",
|
|
476
|
-
children: t("filterGoogle")
|
|
477
|
-
}),
|
|
478
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
479
|
-
value: "openai",
|
|
480
|
-
children: t("filterOpenAI")
|
|
481
|
-
}),
|
|
482
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
483
|
-
value: "seedream",
|
|
484
|
-
children: t("filterSeedream")
|
|
485
|
-
}),
|
|
486
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
487
|
-
value: "dashscope",
|
|
488
|
-
children: t("filterDashScope")
|
|
489
|
-
}),
|
|
490
|
-
(0, react_jsx_runtime.jsx)("option", {
|
|
491
|
-
value: "comfyui",
|
|
492
|
-
children: t("filterComfyUI")
|
|
493
|
-
})
|
|
494
|
-
]
|
|
1457
|
+
}), (0, react_jsx_runtime.jsxs)("button", {
|
|
1458
|
+
type: "button",
|
|
1459
|
+
className: `dsh-ig-studio-btn dsh-ig-studio-btn-danger ${isManageMode ? "is-active" : ""}`,
|
|
1460
|
+
title: isManageMode ? t("exitManage") : t("manage"),
|
|
1461
|
+
onClick: () => {
|
|
1462
|
+
if (isManageMode) handleExitManage();
|
|
1463
|
+
else setIsManageMode(true);
|
|
1464
|
+
},
|
|
1465
|
+
children: [(0, react_jsx_runtime.jsx)(Trash2, { size: 13 }), (0, react_jsx_runtime.jsx)("span", { children: isManageMode ? t("exitManage") : t("manage") })]
|
|
495
1466
|
})]
|
|
496
1467
|
})]
|
|
497
1468
|
}),
|
|
498
1469
|
(0, react_jsx_runtime.jsx)("div", {
|
|
499
1470
|
className: "dsh-ig-gallery-page-body",
|
|
500
|
-
children: items.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
1471
|
+
children: activeTab === "gallery" || activeTab === "favorites" ? items.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
501
1472
|
className: "dsh-ig-gallery-empty",
|
|
502
1473
|
children: [
|
|
503
1474
|
(0, react_jsx_runtime.jsx)("div", {
|
|
@@ -513,6 +1484,22 @@ window.__ModuleLoader__.load({
|
|
|
513
1484
|
children: t("emptyDesc")
|
|
514
1485
|
})
|
|
515
1486
|
]
|
|
1487
|
+
}) : activeTab === "favorites" && filteredItems.length === 0 && search === "" && selectedProvider === "all" && selectedRatio === "all" ? (0, react_jsx_runtime.jsxs)("div", {
|
|
1488
|
+
className: "dsh-ig-gallery-empty",
|
|
1489
|
+
children: [
|
|
1490
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1491
|
+
className: "dsh-ig-gallery-empty-icon",
|
|
1492
|
+
children: "🤍"
|
|
1493
|
+
}),
|
|
1494
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1495
|
+
className: "dsh-ig-gallery-empty-title",
|
|
1496
|
+
children: t("favEmptyTitle")
|
|
1497
|
+
}),
|
|
1498
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1499
|
+
className: "dsh-ig-gallery-empty-desc",
|
|
1500
|
+
children: t("favEmptyDesc")
|
|
1501
|
+
})
|
|
1502
|
+
]
|
|
516
1503
|
}) : filteredItems.length === 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
517
1504
|
className: "dsh-ig-gallery-empty",
|
|
518
1505
|
children: [
|
|
@@ -531,82 +1518,212 @@ window.__ModuleLoader__.load({
|
|
|
531
1518
|
]
|
|
532
1519
|
}) : (0, react_jsx_runtime.jsx)("div", {
|
|
533
1520
|
className: "dsh-ig-gallery-grid",
|
|
534
|
-
children: filteredItems.map((item) => (0, react_jsx_runtime.jsx)(GalleryCard, {
|
|
1521
|
+
children: filteredItems.map((item, idx) => (0, react_jsx_runtime.jsx)(GalleryCard, {
|
|
535
1522
|
item,
|
|
1523
|
+
index: idx,
|
|
1524
|
+
isManageMode,
|
|
1525
|
+
isSelected: selectedIds.has(item.id),
|
|
536
1526
|
t,
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1527
|
+
onClick: (e, blob) => handleCardClick(item, idx, e, blob),
|
|
1528
|
+
onToggleSelect: (e) => handleToggleSelect(item.id, idx, e),
|
|
1529
|
+
onRequestDelete: () => requestSingleDelete(item),
|
|
1530
|
+
onPreview: (blob) => openPreviewItem(item, blob),
|
|
1531
|
+
onBlobLoaded: (b) => blobCache.set(item.id, b),
|
|
542
1532
|
onToast: showToast
|
|
543
1533
|
}, item.id))
|
|
1534
|
+
}) : activeTab === "studio" ? (0, react_jsx_runtime.jsx)(PlaceholderView, {
|
|
1535
|
+
icon: "🎨",
|
|
1536
|
+
title: t("studioTitle"),
|
|
1537
|
+
description: t("studioDesc"),
|
|
1538
|
+
tip: t("studioTip"),
|
|
1539
|
+
badge: t("comingSoonBadge")
|
|
1540
|
+
}) : activeTab === "compare" ? (0, react_jsx_runtime.jsx)(PlaceholderView, {
|
|
1541
|
+
icon: "🔀",
|
|
1542
|
+
title: t("compareTitle"),
|
|
1543
|
+
description: t("compareDesc"),
|
|
1544
|
+
badge: t("comingSoonBadge")
|
|
1545
|
+
}) : (0, react_jsx_runtime.jsx)(PlaceholderView, {
|
|
1546
|
+
icon: "📋",
|
|
1547
|
+
title: t("tasksTitle"),
|
|
1548
|
+
description: t("tasksDesc"),
|
|
1549
|
+
badge: t("comingSoonBadge")
|
|
544
1550
|
})
|
|
545
1551
|
}),
|
|
546
1552
|
toast && (0, react_jsx_runtime.jsx)("div", {
|
|
547
1553
|
className: "dsh-ig-gallery-page-toast",
|
|
548
1554
|
children: toast
|
|
549
1555
|
}),
|
|
550
|
-
|
|
551
|
-
className: "dsh-ig-
|
|
1556
|
+
(isManageMode || selectedIds.size > 0) && (0, react_jsx_runtime.jsxs)("div", {
|
|
1557
|
+
className: "dsh-ig-batch-bar",
|
|
1558
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
1559
|
+
className: "dsh-ig-batch-bar-left",
|
|
1560
|
+
children: [
|
|
1561
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1562
|
+
className: "dsh-ig-batch-counter",
|
|
1563
|
+
children: t("selectedCount", { n: String(selectedIds.size) })
|
|
1564
|
+
}),
|
|
1565
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
1566
|
+
type: "button",
|
|
1567
|
+
className: "dsh-ig-batch-btn",
|
|
1568
|
+
onClick: handleSelectAll,
|
|
1569
|
+
children: t("selectAll")
|
|
1570
|
+
}),
|
|
1571
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
1572
|
+
type: "button",
|
|
1573
|
+
className: "dsh-ig-batch-btn",
|
|
1574
|
+
onClick: handleInvertSelect,
|
|
1575
|
+
children: t("invertSelect")
|
|
1576
|
+
}),
|
|
1577
|
+
selectedIds.size > 0 && (0, react_jsx_runtime.jsx)("button", {
|
|
1578
|
+
type: "button",
|
|
1579
|
+
className: "dsh-ig-batch-btn",
|
|
1580
|
+
onClick: handleClearSelect,
|
|
1581
|
+
children: t("clearSelect")
|
|
1582
|
+
})
|
|
1583
|
+
]
|
|
1584
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1585
|
+
className: "dsh-ig-batch-bar-right",
|
|
1586
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
1587
|
+
type: "button",
|
|
1588
|
+
className: "dsh-ig-batch-btn dsh-ig-batch-btn-danger",
|
|
1589
|
+
disabled: selectedIds.size === 0,
|
|
1590
|
+
onClick: () => setShowDeleteModal(true),
|
|
1591
|
+
children: [(0, react_jsx_runtime.jsx)(Trash2, { size: 13 }), (0, react_jsx_runtime.jsxs)("span", { children: [t("batchDelete"), selectedIds.size > 0 ? ` (${selectedIds.size})` : ""] })]
|
|
1592
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1593
|
+
type: "button",
|
|
1594
|
+
className: "dsh-ig-batch-btn dsh-ig-batch-btn-exit",
|
|
1595
|
+
onClick: handleExitManage,
|
|
1596
|
+
children: t("exitManage")
|
|
1597
|
+
})]
|
|
1598
|
+
})]
|
|
1599
|
+
}),
|
|
1600
|
+
showDeleteModal && (0, react_jsx_runtime.jsx)("div", {
|
|
1601
|
+
className: "dsh-ig-modal-backdrop",
|
|
552
1602
|
onClick: () => {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
1603
|
+
if (!isDeleting) {
|
|
1604
|
+
setShowDeleteModal(false);
|
|
1605
|
+
if (!isManageMode) setSelectedIds(/* @__PURE__ */ new Set());
|
|
1606
|
+
}
|
|
556
1607
|
},
|
|
1608
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
1609
|
+
className: "dsh-ig-modal-box",
|
|
1610
|
+
onClick: (e) => e.stopPropagation(),
|
|
1611
|
+
children: [
|
|
1612
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1613
|
+
className: "dsh-ig-modal-header",
|
|
1614
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
1615
|
+
className: "dsh-ig-modal-icon-danger",
|
|
1616
|
+
children: (0, react_jsx_runtime.jsx)(TriangleAlert, { size: 20 })
|
|
1617
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
1618
|
+
className: "dsh-ig-modal-title",
|
|
1619
|
+
children: selectedIds.size === 1 ? t("batchDeleteTitleSingle") : t("batchDeleteTitle", { count: String(selectedIds.size) })
|
|
1620
|
+
})]
|
|
1621
|
+
}),
|
|
1622
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1623
|
+
className: "dsh-ig-modal-body",
|
|
1624
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
1625
|
+
className: "dsh-ig-modal-desc",
|
|
1626
|
+
children: t("batchDeleteDesc")
|
|
1627
|
+
}), (0, react_jsx_runtime.jsxs)("label", {
|
|
1628
|
+
className: "dsh-ig-modal-checkbox-label",
|
|
1629
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
1630
|
+
type: "checkbox",
|
|
1631
|
+
checked: deleteWorkspaceFiles,
|
|
1632
|
+
onChange: (e) => setDeleteWorkspaceFiles(e.target.checked),
|
|
1633
|
+
disabled: isDeleting
|
|
1634
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t("deleteWorkspaceFilesOpt") })]
|
|
1635
|
+
})]
|
|
1636
|
+
}),
|
|
1637
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1638
|
+
className: "dsh-ig-modal-footer",
|
|
1639
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
1640
|
+
type: "button",
|
|
1641
|
+
className: "dsh-ig-modal-btn dsh-ig-modal-btn-cancel",
|
|
1642
|
+
onClick: () => {
|
|
1643
|
+
setShowDeleteModal(false);
|
|
1644
|
+
if (!isManageMode) setSelectedIds(/* @__PURE__ */ new Set());
|
|
1645
|
+
},
|
|
1646
|
+
disabled: isDeleting,
|
|
1647
|
+
children: t("cancel")
|
|
1648
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1649
|
+
type: "button",
|
|
1650
|
+
className: "dsh-ig-modal-btn dsh-ig-modal-btn-danger",
|
|
1651
|
+
onClick: handleConfirmBatchDelete,
|
|
1652
|
+
disabled: isDeleting,
|
|
1653
|
+
children: isDeleting ? "..." : selectedIds.size === 1 ? t("confirmDeleteSingle") : t("confirmBatchDelete", { count: String(selectedIds.size) })
|
|
1654
|
+
})]
|
|
1655
|
+
})
|
|
1656
|
+
]
|
|
1657
|
+
})
|
|
1658
|
+
}),
|
|
1659
|
+
previewItem && (0, react_jsx_runtime.jsxs)("div", {
|
|
1660
|
+
className: "dsh-ig-lightbox-backdrop",
|
|
1661
|
+
onClick: handleClosePreview,
|
|
557
1662
|
children: [
|
|
558
1663
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
559
1664
|
className: "dsh-ig-lightbox-topbar",
|
|
560
1665
|
onClick: (e) => e.stopPropagation(),
|
|
561
1666
|
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
562
1667
|
className: "dsh-ig-lightbox-meta",
|
|
563
|
-
children: [
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
1668
|
+
children: [
|
|
1669
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1670
|
+
className: "dsh-ig-tag",
|
|
1671
|
+
children: previewItem.provider
|
|
1672
|
+
}),
|
|
1673
|
+
previewItem.model ? (0, react_jsx_runtime.jsx)("span", {
|
|
1674
|
+
className: "dsh-ig-tag dsh-ig-tag-model",
|
|
1675
|
+
children: previewItem.model
|
|
1676
|
+
}) : null,
|
|
1677
|
+
currentPreviewIndex >= 0 && (0, react_jsx_runtime.jsxs)("span", {
|
|
1678
|
+
className: "dsh-ig-lightbox-counter",
|
|
1679
|
+
children: [
|
|
1680
|
+
currentPreviewIndex + 1,
|
|
1681
|
+
" / ",
|
|
1682
|
+
filteredItems.length
|
|
1683
|
+
]
|
|
1684
|
+
})
|
|
1685
|
+
]
|
|
570
1686
|
}), (0, react_jsx_runtime.jsx)("button", {
|
|
571
1687
|
type: "button",
|
|
572
1688
|
className: "dsh-ig-lightbox-close-btn",
|
|
573
1689
|
title: t("close"),
|
|
574
|
-
onClick:
|
|
575
|
-
|
|
576
|
-
setPreviewUrl(null);
|
|
577
|
-
setPreviewBlob(null);
|
|
578
|
-
},
|
|
579
|
-
children: (0, react_jsx_runtime.jsxs)("svg", {
|
|
580
|
-
width: "18",
|
|
581
|
-
height: "18",
|
|
582
|
-
viewBox: "0 0 24 24",
|
|
583
|
-
fill: "none",
|
|
584
|
-
stroke: "currentColor",
|
|
585
|
-
strokeWidth: "2",
|
|
586
|
-
strokeLinecap: "round",
|
|
587
|
-
strokeLinejoin: "round",
|
|
588
|
-
children: [(0, react_jsx_runtime.jsx)("line", {
|
|
589
|
-
x1: "18",
|
|
590
|
-
y1: "6",
|
|
591
|
-
x2: "6",
|
|
592
|
-
y2: "18"
|
|
593
|
-
}), (0, react_jsx_runtime.jsx)("line", {
|
|
594
|
-
x1: "6",
|
|
595
|
-
y1: "6",
|
|
596
|
-
x2: "18",
|
|
597
|
-
y2: "18"
|
|
598
|
-
})]
|
|
599
|
-
})
|
|
1690
|
+
onClick: handleClosePreview,
|
|
1691
|
+
children: (0, react_jsx_runtime.jsx)(X, { size: 18 })
|
|
600
1692
|
})]
|
|
601
1693
|
}),
|
|
1694
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
1695
|
+
type: "button",
|
|
1696
|
+
className: "dsh-ig-lightbox-nav-btn dsh-ig-lightbox-nav-prev",
|
|
1697
|
+
title: t("prevImage"),
|
|
1698
|
+
disabled: !hasPrev,
|
|
1699
|
+
onClick: (e) => {
|
|
1700
|
+
e.stopPropagation();
|
|
1701
|
+
goToPrev();
|
|
1702
|
+
},
|
|
1703
|
+
children: (0, react_jsx_runtime.jsx)(ChevronLeft, { size: 24 })
|
|
1704
|
+
}),
|
|
1705
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
1706
|
+
type: "button",
|
|
1707
|
+
className: "dsh-ig-lightbox-nav-btn dsh-ig-lightbox-nav-next",
|
|
1708
|
+
title: t("nextImage"),
|
|
1709
|
+
disabled: !hasNext,
|
|
1710
|
+
onClick: (e) => {
|
|
1711
|
+
e.stopPropagation();
|
|
1712
|
+
goToNext();
|
|
1713
|
+
},
|
|
1714
|
+
children: (0, react_jsx_runtime.jsx)(ChevronRight, { size: 24 })
|
|
1715
|
+
}),
|
|
602
1716
|
(0, react_jsx_runtime.jsx)("div", {
|
|
603
1717
|
className: "dsh-ig-lightbox-img-wrap",
|
|
604
1718
|
onClick: (e) => e.stopPropagation(),
|
|
605
|
-
children: (0, react_jsx_runtime.jsx)("
|
|
1719
|
+
children: previewLoading ? (0, react_jsx_runtime.jsx)("div", {
|
|
1720
|
+
className: "dsh-ig-lightbox-loading",
|
|
1721
|
+
children: (0, react_jsx_runtime.jsx)("div", { className: "dsh-ig-lightbox-spinner" })
|
|
1722
|
+
}) : previewUrl ? (0, react_jsx_runtime.jsx)("img", {
|
|
606
1723
|
className: "dsh-ig-lightbox-img",
|
|
607
1724
|
src: previewUrl,
|
|
608
1725
|
alt: previewItem.prompt
|
|
609
|
-
})
|
|
1726
|
+
}) : null
|
|
610
1727
|
}),
|
|
611
1728
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
612
1729
|
className: "dsh-ig-lightbox-bottombar",
|
|
@@ -626,24 +1743,7 @@ window.__ModuleLoader__.load({
|
|
|
626
1743
|
await navigator.clipboard.writeText(previewItem.prompt);
|
|
627
1744
|
showToast(t("copiedPrompt"));
|
|
628
1745
|
},
|
|
629
|
-
children: [(0, react_jsx_runtime.
|
|
630
|
-
width: "14",
|
|
631
|
-
height: "14",
|
|
632
|
-
viewBox: "0 0 24 24",
|
|
633
|
-
fill: "none",
|
|
634
|
-
stroke: "currentColor",
|
|
635
|
-
strokeWidth: "2",
|
|
636
|
-
strokeLinecap: "round",
|
|
637
|
-
strokeLinejoin: "round",
|
|
638
|
-
children: [(0, react_jsx_runtime.jsx)("path", { d: "M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" }), (0, react_jsx_runtime.jsx)("rect", {
|
|
639
|
-
x: "8",
|
|
640
|
-
y: "2",
|
|
641
|
-
width: "8",
|
|
642
|
-
height: "4",
|
|
643
|
-
rx: "1",
|
|
644
|
-
ry: "1"
|
|
645
|
-
})]
|
|
646
|
-
}), (0, react_jsx_runtime.jsx)("span", { children: t("copyPpt") })]
|
|
1746
|
+
children: [(0, react_jsx_runtime.jsx)(FileText, { size: 14 }), (0, react_jsx_runtime.jsx)("span", { children: t("copyPpt") })]
|
|
647
1747
|
}),
|
|
648
1748
|
(0, react_jsx_runtime.jsxs)("button", {
|
|
649
1749
|
type: "button",
|
|
@@ -654,30 +1754,14 @@ window.__ModuleLoader__.load({
|
|
|
654
1754
|
const ok = await copyImageBlob(previewBlob);
|
|
655
1755
|
showToast(ok ? t("copiedImage") : t("copyFailed"));
|
|
656
1756
|
},
|
|
657
|
-
children: [(0, react_jsx_runtime.
|
|
658
|
-
width: "14",
|
|
659
|
-
height: "14",
|
|
660
|
-
viewBox: "0 0 24 24",
|
|
661
|
-
fill: "none",
|
|
662
|
-
stroke: "currentColor",
|
|
663
|
-
strokeWidth: "2",
|
|
664
|
-
strokeLinecap: "round",
|
|
665
|
-
strokeLinejoin: "round",
|
|
666
|
-
children: [(0, react_jsx_runtime.jsx)("rect", {
|
|
667
|
-
x: "9",
|
|
668
|
-
y: "9",
|
|
669
|
-
width: "13",
|
|
670
|
-
height: "13",
|
|
671
|
-
rx: "2",
|
|
672
|
-
ry: "2"
|
|
673
|
-
}), (0, react_jsx_runtime.jsx)("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })]
|
|
674
|
-
}), (0, react_jsx_runtime.jsx)("span", { children: t("copyImg") })]
|
|
1757
|
+
children: [(0, react_jsx_runtime.jsx)(Copy, { size: 14 }), (0, react_jsx_runtime.jsx)("span", { children: t("copyImg") })]
|
|
675
1758
|
}),
|
|
676
1759
|
(0, react_jsx_runtime.jsxs)("button", {
|
|
677
1760
|
type: "button",
|
|
678
1761
|
className: "dsh-ig-lightbox-btn",
|
|
679
1762
|
title: t("download"),
|
|
680
1763
|
onClick: () => {
|
|
1764
|
+
if (!previewUrl) return;
|
|
681
1765
|
const a = document.createElement("a");
|
|
682
1766
|
a.href = previewUrl;
|
|
683
1767
|
a.download = `dsh-${previewItem.provider}-${previewItem.id}.png`;
|
|
@@ -685,65 +1769,16 @@ window.__ModuleLoader__.load({
|
|
|
685
1769
|
a.click();
|
|
686
1770
|
document.body.removeChild(a);
|
|
687
1771
|
},
|
|
688
|
-
children: [(0, react_jsx_runtime.
|
|
689
|
-
width: "14",
|
|
690
|
-
height: "14",
|
|
691
|
-
viewBox: "0 0 24 24",
|
|
692
|
-
fill: "none",
|
|
693
|
-
stroke: "currentColor",
|
|
694
|
-
strokeWidth: "2",
|
|
695
|
-
strokeLinecap: "round",
|
|
696
|
-
strokeLinejoin: "round",
|
|
697
|
-
children: [
|
|
698
|
-
(0, react_jsx_runtime.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
699
|
-
(0, react_jsx_runtime.jsx)("polyline", { points: "7 10 12 15 17 10" }),
|
|
700
|
-
(0, react_jsx_runtime.jsx)("line", {
|
|
701
|
-
x1: "12",
|
|
702
|
-
y1: "15",
|
|
703
|
-
x2: "12",
|
|
704
|
-
y2: "3"
|
|
705
|
-
})
|
|
706
|
-
]
|
|
707
|
-
}), (0, react_jsx_runtime.jsx)("span", { children: t("download") })]
|
|
1772
|
+
children: [(0, react_jsx_runtime.jsx)(Download, { size: 14 }), (0, react_jsx_runtime.jsx)("span", { children: t("download") })]
|
|
708
1773
|
}),
|
|
709
1774
|
(0, react_jsx_runtime.jsxs)("button", {
|
|
710
1775
|
type: "button",
|
|
711
1776
|
className: "dsh-ig-lightbox-btn dsh-ig-lightbox-btn-danger",
|
|
712
1777
|
title: t("delete"),
|
|
713
|
-
onClick:
|
|
714
|
-
if (
|
|
715
|
-
await deleteGalleryItem(previewItem.id);
|
|
716
|
-
setPreviewItem(null);
|
|
717
|
-
setPreviewUrl(null);
|
|
718
|
-
setPreviewBlob(null);
|
|
719
|
-
showToast(t("deleted"));
|
|
1778
|
+
onClick: () => {
|
|
1779
|
+
if (previewItem) requestSingleDelete(previewItem);
|
|
720
1780
|
},
|
|
721
|
-
children: [(0, react_jsx_runtime.
|
|
722
|
-
width: "14",
|
|
723
|
-
height: "14",
|
|
724
|
-
viewBox: "0 0 24 24",
|
|
725
|
-
fill: "none",
|
|
726
|
-
stroke: "currentColor",
|
|
727
|
-
strokeWidth: "2",
|
|
728
|
-
strokeLinecap: "round",
|
|
729
|
-
strokeLinejoin: "round",
|
|
730
|
-
children: [
|
|
731
|
-
(0, react_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
732
|
-
(0, react_jsx_runtime.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }),
|
|
733
|
-
(0, react_jsx_runtime.jsx)("line", {
|
|
734
|
-
x1: "10",
|
|
735
|
-
y1: "11",
|
|
736
|
-
x2: "10",
|
|
737
|
-
y2: "17"
|
|
738
|
-
}),
|
|
739
|
-
(0, react_jsx_runtime.jsx)("line", {
|
|
740
|
-
x1: "14",
|
|
741
|
-
y1: "11",
|
|
742
|
-
x2: "14",
|
|
743
|
-
y2: "17"
|
|
744
|
-
})
|
|
745
|
-
]
|
|
746
|
-
}), (0, react_jsx_runtime.jsx)("span", { children: t("delete") })]
|
|
1781
|
+
children: [(0, react_jsx_runtime.jsx)(Trash2, { size: 14 }), (0, react_jsx_runtime.jsx)("span", { children: t("delete") })]
|
|
747
1782
|
})
|
|
748
1783
|
]
|
|
749
1784
|
})]
|
|
@@ -753,7 +1788,7 @@ window.__ModuleLoader__.load({
|
|
|
753
1788
|
]
|
|
754
1789
|
});
|
|
755
1790
|
};
|
|
756
|
-
const GalleryCard = ({ item, t, onPreview, onToast }) => {
|
|
1791
|
+
const GalleryCard = ({ item, index: _index, isManageMode, isSelected, t, onClick, onToggleSelect, onRequestDelete, onPreview: _onPreview, onBlobLoaded, onToast }) => {
|
|
757
1792
|
const [url, setUrl] = (0, react.useState)();
|
|
758
1793
|
const [blob, setBlob] = (0, react.useState)();
|
|
759
1794
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
@@ -771,6 +1806,7 @@ window.__ModuleLoader__.load({
|
|
|
771
1806
|
const resBlob = await response.blob();
|
|
772
1807
|
if (controller.signal.aborted) return;
|
|
773
1808
|
setBlob(resBlob);
|
|
1809
|
+
onBlobLoaded?.(resBlob);
|
|
774
1810
|
objectUrl = URL.createObjectURL(resBlob);
|
|
775
1811
|
setUrl(objectUrl);
|
|
776
1812
|
setLoading(false);
|
|
@@ -809,14 +1845,27 @@ window.__ModuleLoader__.load({
|
|
|
809
1845
|
a.click();
|
|
810
1846
|
document.body.removeChild(a);
|
|
811
1847
|
};
|
|
1848
|
+
const toggleFavorite = async (e) => {
|
|
1849
|
+
e.stopPropagation();
|
|
1850
|
+
onToast(await toggleFavoriteGalleryItem(item.id) ? t("favoriteAdded") : t("favoriteRemoved"));
|
|
1851
|
+
};
|
|
1852
|
+
const badgeLabel = item.model || item.provider;
|
|
812
1853
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
813
|
-
className:
|
|
814
|
-
onClick: () =>
|
|
815
|
-
if (url && blob) onPreview(url, blob);
|
|
816
|
-
},
|
|
1854
|
+
className: `dsh-ig-gallery-card ${isSelected ? "is-selected" : ""} ${isManageMode ? "is-manage-mode" : ""}`,
|
|
1855
|
+
onClick: (e) => onClick(e, blob),
|
|
817
1856
|
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
818
1857
|
className: "dsh-ig-gallery-card-media",
|
|
819
1858
|
children: [
|
|
1859
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
1860
|
+
type: "button",
|
|
1861
|
+
className: `dsh-ig-card-checkbox ${isSelected ? "is-checked" : ""}`,
|
|
1862
|
+
title: isSelected ? t("clearSelect") : t("manage"),
|
|
1863
|
+
onClick: onToggleSelect,
|
|
1864
|
+
children: isSelected && (0, react_jsx_runtime.jsx)(Check, {
|
|
1865
|
+
size: 11,
|
|
1866
|
+
strokeWidth: 3
|
|
1867
|
+
})
|
|
1868
|
+
}),
|
|
820
1869
|
loading && (0, react_jsx_runtime.jsx)("div", {
|
|
821
1870
|
className: "dsh-ig-gallery-card-loading",
|
|
822
1871
|
children: "..."
|
|
@@ -841,50 +1890,14 @@ window.__ModuleLoader__.load({
|
|
|
841
1890
|
onClick: (e) => {
|
|
842
1891
|
copyImage(e);
|
|
843
1892
|
},
|
|
844
|
-
children: (0, react_jsx_runtime.
|
|
845
|
-
width: "14",
|
|
846
|
-
height: "14",
|
|
847
|
-
viewBox: "0 0 24 24",
|
|
848
|
-
fill: "none",
|
|
849
|
-
stroke: "currentColor",
|
|
850
|
-
strokeWidth: "2",
|
|
851
|
-
strokeLinecap: "round",
|
|
852
|
-
strokeLinejoin: "round",
|
|
853
|
-
children: [(0, react_jsx_runtime.jsx)("rect", {
|
|
854
|
-
x: "9",
|
|
855
|
-
y: "9",
|
|
856
|
-
width: "13",
|
|
857
|
-
height: "13",
|
|
858
|
-
rx: "2",
|
|
859
|
-
ry: "2"
|
|
860
|
-
}), (0, react_jsx_runtime.jsx)("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })]
|
|
861
|
-
})
|
|
1893
|
+
children: (0, react_jsx_runtime.jsx)(Copy, { size: 13 })
|
|
862
1894
|
}),
|
|
863
1895
|
(0, react_jsx_runtime.jsx)("button", {
|
|
864
1896
|
type: "button",
|
|
865
1897
|
className: "dsh-ig-tool-btn",
|
|
866
1898
|
title: t("download"),
|
|
867
1899
|
onClick: downloadImage,
|
|
868
|
-
children: (0, react_jsx_runtime.
|
|
869
|
-
width: "14",
|
|
870
|
-
height: "14",
|
|
871
|
-
viewBox: "0 0 24 24",
|
|
872
|
-
fill: "none",
|
|
873
|
-
stroke: "currentColor",
|
|
874
|
-
strokeWidth: "2",
|
|
875
|
-
strokeLinecap: "round",
|
|
876
|
-
strokeLinejoin: "round",
|
|
877
|
-
children: [
|
|
878
|
-
(0, react_jsx_runtime.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
879
|
-
(0, react_jsx_runtime.jsx)("polyline", { points: "7 10 12 15 17 10" }),
|
|
880
|
-
(0, react_jsx_runtime.jsx)("line", {
|
|
881
|
-
x1: "12",
|
|
882
|
-
y1: "15",
|
|
883
|
-
x2: "12",
|
|
884
|
-
y2: "3"
|
|
885
|
-
})
|
|
886
|
-
]
|
|
887
|
-
})
|
|
1900
|
+
children: (0, react_jsx_runtime.jsx)(Download, { size: 13 })
|
|
888
1901
|
}),
|
|
889
1902
|
(0, react_jsx_runtime.jsx)("button", {
|
|
890
1903
|
type: "button",
|
|
@@ -893,84 +1906,95 @@ window.__ModuleLoader__.load({
|
|
|
893
1906
|
onClick: (e) => {
|
|
894
1907
|
copyPrompt(e);
|
|
895
1908
|
},
|
|
896
|
-
children: (0, react_jsx_runtime.
|
|
897
|
-
width: "14",
|
|
898
|
-
height: "14",
|
|
899
|
-
viewBox: "0 0 24 24",
|
|
900
|
-
fill: "none",
|
|
901
|
-
stroke: "currentColor",
|
|
902
|
-
strokeWidth: "2",
|
|
903
|
-
strokeLinecap: "round",
|
|
904
|
-
strokeLinejoin: "round",
|
|
905
|
-
children: [(0, react_jsx_runtime.jsx)("path", { d: "M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" }), (0, react_jsx_runtime.jsx)("rect", {
|
|
906
|
-
x: "8",
|
|
907
|
-
y: "2",
|
|
908
|
-
width: "8",
|
|
909
|
-
height: "4",
|
|
910
|
-
rx: "1",
|
|
911
|
-
ry: "1"
|
|
912
|
-
})]
|
|
913
|
-
})
|
|
1909
|
+
children: (0, react_jsx_runtime.jsx)(FileText, { size: 13 })
|
|
914
1910
|
}),
|
|
915
1911
|
(0, react_jsx_runtime.jsx)("button", {
|
|
916
1912
|
type: "button",
|
|
917
1913
|
className: "dsh-ig-tool-btn dsh-ig-tool-btn-danger",
|
|
918
1914
|
title: t("delete"),
|
|
919
|
-
onClick:
|
|
1915
|
+
onClick: (e) => {
|
|
920
1916
|
e.stopPropagation();
|
|
921
|
-
|
|
922
|
-
await deleteGalleryItem(item.id);
|
|
923
|
-
onToast(t("deleted"));
|
|
1917
|
+
onRequestDelete();
|
|
924
1918
|
},
|
|
925
|
-
children: (0, react_jsx_runtime.
|
|
926
|
-
width: "14",
|
|
927
|
-
height: "14",
|
|
928
|
-
viewBox: "0 0 24 24",
|
|
929
|
-
fill: "none",
|
|
930
|
-
stroke: "currentColor",
|
|
931
|
-
strokeWidth: "2",
|
|
932
|
-
strokeLinecap: "round",
|
|
933
|
-
strokeLinejoin: "round",
|
|
934
|
-
children: [
|
|
935
|
-
(0, react_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
936
|
-
(0, react_jsx_runtime.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }),
|
|
937
|
-
(0, react_jsx_runtime.jsx)("line", {
|
|
938
|
-
x1: "10",
|
|
939
|
-
y1: "11",
|
|
940
|
-
x2: "10",
|
|
941
|
-
y2: "17"
|
|
942
|
-
}),
|
|
943
|
-
(0, react_jsx_runtime.jsx)("line", {
|
|
944
|
-
x1: "14",
|
|
945
|
-
y1: "11",
|
|
946
|
-
x2: "14",
|
|
947
|
-
y2: "17"
|
|
948
|
-
})
|
|
949
|
-
]
|
|
950
|
-
})
|
|
1919
|
+
children: (0, react_jsx_runtime.jsx)(Trash2, { size: 13 })
|
|
951
1920
|
})
|
|
952
1921
|
]
|
|
953
1922
|
})
|
|
954
1923
|
]
|
|
955
1924
|
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
956
1925
|
className: "dsh-ig-gallery-card-meta",
|
|
957
|
-
children: [
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
})
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1926
|
+
children: [
|
|
1927
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1928
|
+
className: "dsh-ig-card-badge-row",
|
|
1929
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
1930
|
+
className: "dsh-ig-card-badge",
|
|
1931
|
+
title: badgeLabel,
|
|
1932
|
+
children: badgeLabel
|
|
1933
|
+
})
|
|
1934
|
+
}),
|
|
1935
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1936
|
+
className: "dsh-ig-gallery-card-prompt-line",
|
|
1937
|
+
title: item.prompt,
|
|
1938
|
+
children: item.prompt
|
|
1939
|
+
}),
|
|
1940
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1941
|
+
className: "dsh-ig-card-footer-row",
|
|
1942
|
+
children: [(0, react_jsx_runtime.jsxs)("span", {
|
|
1943
|
+
className: "dsh-ig-card-meta-text",
|
|
1944
|
+
children: [
|
|
1945
|
+
formatCardMeta(item),
|
|
1946
|
+
" | ",
|
|
1947
|
+
formatRelativeTime(item.createdAt, t)
|
|
1948
|
+
]
|
|
1949
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
1950
|
+
type: "button",
|
|
1951
|
+
className: `dsh-ig-card-fav-btn ${item.isFavorite ? "is-favorited" : ""}`,
|
|
1952
|
+
title: item.isFavorite ? t("favoriteRemoved") : t("favoriteAdded"),
|
|
1953
|
+
onClick: (e) => {
|
|
1954
|
+
toggleFavorite(e);
|
|
1955
|
+
},
|
|
1956
|
+
children: (0, react_jsx_runtime.jsx)(Heart, {
|
|
1957
|
+
size: 15,
|
|
1958
|
+
fill: item.isFavorite ? "currentColor" : "none"
|
|
1959
|
+
})
|
|
1960
|
+
})]
|
|
1961
|
+
})
|
|
1962
|
+
]
|
|
971
1963
|
})]
|
|
972
1964
|
});
|
|
973
1965
|
};
|
|
1966
|
+
const PlaceholderView = ({ icon, title, description, tip, badge }) => {
|
|
1967
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
1968
|
+
className: "dsh-ig-placeholder-view",
|
|
1969
|
+
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
1970
|
+
className: "dsh-ig-placeholder-card",
|
|
1971
|
+
children: [
|
|
1972
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1973
|
+
className: "dsh-ig-placeholder-icon",
|
|
1974
|
+
children: icon
|
|
1975
|
+
}),
|
|
1976
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1977
|
+
className: "dsh-ig-placeholder-header",
|
|
1978
|
+
children: [(0, react_jsx_runtime.jsx)("h2", {
|
|
1979
|
+
className: "dsh-ig-placeholder-title",
|
|
1980
|
+
children: title
|
|
1981
|
+
}), badge && (0, react_jsx_runtime.jsx)("span", {
|
|
1982
|
+
className: "dsh-ig-placeholder-badge",
|
|
1983
|
+
children: badge
|
|
1984
|
+
})]
|
|
1985
|
+
}),
|
|
1986
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
1987
|
+
className: "dsh-ig-placeholder-desc",
|
|
1988
|
+
children: description
|
|
1989
|
+
}),
|
|
1990
|
+
tip && (0, react_jsx_runtime.jsx)("div", {
|
|
1991
|
+
className: "dsh-ig-placeholder-tip",
|
|
1992
|
+
children: tip
|
|
1993
|
+
})
|
|
1994
|
+
]
|
|
1995
|
+
})
|
|
1996
|
+
});
|
|
1997
|
+
};
|
|
974
1998
|
async function copyImageBlob(blob) {
|
|
975
1999
|
try {
|
|
976
2000
|
if (blob.type === "image/png") {
|
|
@@ -1333,58 +2357,149 @@ window.__ModuleLoader__.load({
|
|
|
1333
2357
|
.dsh-ig-error{color:var(--dsw-alias-label-error,#d33);font-size:13px}
|
|
1334
2358
|
.dsh-ig-loading{color:var(--dsw-alias-label-tertiary,#7b818b);font-size:13px}
|
|
1335
2359
|
|
|
1336
|
-
/* Native Workspace Gallery View (Renders seamlessly inside DSH Session View) */
|
|
2360
|
+
/* Native Workspace Gallery & Studio View (Renders seamlessly inside DSH Session View) */
|
|
1337
2361
|
.dsh-ig-gallery-page{width:100%;height:100%;background:var(--dsw-alias-bg-layer-1,#ffffff);display:flex;flex-direction:column;overflow:hidden;flex:1}
|
|
1338
|
-
|
|
1339
|
-
.
|
|
1340
|
-
.dsh-ig-
|
|
1341
|
-
.dsh-ig-
|
|
1342
|
-
.dsh-ig-
|
|
1343
|
-
.dsh-ig-
|
|
1344
|
-
|
|
1345
|
-
.
|
|
1346
|
-
.dsh-ig-
|
|
1347
|
-
.dsh-ig-
|
|
1348
|
-
.dsh-ig-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
.dsh-ig-
|
|
1352
|
-
.dsh-ig-
|
|
2362
|
+
|
|
2363
|
+
/* 1. Top Navigation Tab Bar */
|
|
2364
|
+
.dsh-ig-studio-tabs-bar{display:flex;align-items:center;gap:6px;padding:6px 24px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#ffffff);flex-shrink:0}
|
|
2365
|
+
.dsh-ig-studio-tab-btn{appearance:none;-webkit-appearance:none;border:0;background:transparent;display:inline-flex;align-items:center;gap:7px;padding:7px 14px;font-size:13px;font-weight:500;color:var(--dsw-alias-label-secondary,#64748b);cursor:pointer;border-radius:6px;transition:color .15s ease,background-color .15s ease}
|
|
2366
|
+
.dsh-ig-studio-tab-btn:hover{color:var(--dsw-alias-label-primary,#0f172a);background:var(--dsw-alias-bg-layer-2,#f1f5f9)}
|
|
2367
|
+
.dsh-ig-studio-tab-btn.is-active{color:var(--dsw-alias-brand-primary,#2563eb);font-weight:600;background:rgba(37,99,235,0.08)}
|
|
2368
|
+
|
|
2369
|
+
/* 2. Secondary Filter & Search Toolbar */
|
|
2370
|
+
.dsh-ig-studio-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 24px;background:var(--dsw-alias-bg-layer-1,#ffffff);border-bottom:1px solid var(--dsw-alias-border-l2,#f1f5f9);flex-shrink:0;flex-wrap:wrap}
|
|
2371
|
+
.dsh-ig-studio-toolbar-left{display:flex;align-items:center;gap:10px;flex-wrap:wrap;flex:1;min-width:0}
|
|
2372
|
+
.dsh-ig-studio-toolbar-right{display:flex;align-items:center;gap:10px;flex-shrink:0}
|
|
2373
|
+
|
|
2374
|
+
/* Modern Custom Select (Removes OS default arrows & ugly borders) */
|
|
2375
|
+
.dsh-ig-studio-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;height:32px;line-height:30px;padding:0 28px 0 12px;font-size:12.5px;color:var(--dsw-alias-label-primary,#334155);background-color:var(--dsw-alias-bg-layer-2,#ffffff);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 9px center;border:1px solid var(--dsw-alias-border-l2,#e2e8f0);border-radius:6px;outline:none;cursor:pointer;box-sizing:border-box;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}
|
|
2376
|
+
.dsh-ig-studio-select:hover{border-color:var(--dsw-alias-border-l1,#cbd5e1);background-color:var(--dsw-alias-bg-layer-1,#f8fafc)}
|
|
2377
|
+
.dsh-ig-studio-select:focus{border-color:var(--dsw-alias-brand-primary,#3b82f6);box-shadow:0 0 0 2px rgba(59,130,246,0.15)}
|
|
2378
|
+
.dsh-ig-studio-select-sort{font-weight:500}
|
|
2379
|
+
|
|
2380
|
+
/* Unified Search Input */
|
|
2381
|
+
.dsh-ig-studio-search-wrap{position:relative;display:flex;align-items:center;min-width:190px;max-width:320px;flex:1}
|
|
2382
|
+
.dsh-ig-studio-search-icon{position:absolute;left:10px;color:var(--dsw-alias-label-tertiary,#94a3b8);pointer-events:none}
|
|
2383
|
+
.dsh-ig-studio-search-input{width:100%;height:32px;line-height:30px;padding:0 12px 0 32px;font-size:12.5px;border:1px solid var(--dsw-alias-border-l2,#e2e8f0);border-radius:6px;background-color:var(--dsw-alias-bg-layer-2,#ffffff);color:inherit;outline:none;box-sizing:border-box;transition:border-color .15s ease,box-shadow .15s ease}
|
|
2384
|
+
.dsh-ig-studio-search-input:hover{border-color:var(--dsw-alias-border-l1,#cbd5e1)}
|
|
2385
|
+
.dsh-ig-studio-search-input:focus{border-color:var(--dsw-alias-brand-primary,#3b82f6);box-shadow:0 0 0 2px rgba(59,130,246,0.15)}
|
|
2386
|
+
.dsh-ig-studio-search-input::placeholder{color:var(--dsw-alias-label-tertiary,#94a3b8)}
|
|
2387
|
+
|
|
2388
|
+
/* 3. Grid & Responsive Cards */
|
|
2389
|
+
.dsh-ig-gallery-page-body{flex:1;overflow-y:auto;padding:20px 24px}
|
|
2390
|
+
.dsh-ig-gallery-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:18px}
|
|
2391
|
+
.dsh-ig-gallery-card{background:var(--dsw-alias-bg-layer-2,#ffffff);border:1px solid var(--dsw-alias-border-l2,#e2e8f0);border-radius:10px;overflow:hidden;display:flex;flex-direction:column;cursor:pointer;transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease}
|
|
2392
|
+
.dsh-ig-gallery-card:hover{transform:translateY(-2px);box-shadow:0 10px 20px -5px rgba(0,0,0,0.06),0 4px 6px -2px rgba(0,0,0,0.03);border-color:var(--dsw-alias-border-l1,#cbd5e1)}
|
|
2393
|
+
.dsh-ig-gallery-card-media{position:relative;width:100%;aspect-ratio:1/1;background:#f1f5f9;overflow:hidden;display:flex;align-items:center;justify-content:center}
|
|
2394
|
+
.dsh-ig-gallery-card-img{width:100%;height:100%;object-fit:cover;transition:transform .2s ease}
|
|
1353
2395
|
.dsh-ig-gallery-card:hover .dsh-ig-gallery-card-img{transform:scale(1.03)}
|
|
1354
|
-
.dsh-ig-gallery-card-loading{font-size:12px;color:#
|
|
2396
|
+
.dsh-ig-gallery-card-loading{font-size:12px;color:#94a3b8}
|
|
1355
2397
|
.dsh-ig-gallery-card-error{font-size:12px;color:#ef4444;padding:8px;text-align:center}
|
|
1356
2398
|
|
|
1357
|
-
/* Floating Action Toolbar
|
|
2399
|
+
/* Floating Action Toolbar on Card Hover */
|
|
1358
2400
|
.dsh-ig-gallery-card:hover .dsh-ig-card-toolbar{opacity:1;pointer-events:auto}
|
|
1359
|
-
.dsh-ig-card-toolbar{position:absolute;top:
|
|
2401
|
+
.dsh-ig-card-toolbar{position:absolute;top:6px;left:6px;display:flex;align-items:center;gap:3px;padding:3px 5px;border-radius:6px;background:rgba(15,23,42,0.72);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);opacity:0;pointer-events:none;transition:opacity .18s ease;z-index:10;line-height:1}
|
|
1360
2402
|
|
|
1361
|
-
|
|
1362
|
-
.dsh-ig-gallery-card-
|
|
1363
|
-
.dsh-ig-
|
|
1364
|
-
.dsh-ig-
|
|
1365
|
-
.dsh-ig-gallery-card-prompt{
|
|
1366
|
-
.dsh-ig-
|
|
1367
|
-
.dsh-ig-
|
|
1368
|
-
.dsh-ig-
|
|
2403
|
+
/* Card Bottom Metadata */
|
|
2404
|
+
.dsh-ig-gallery-card-meta{padding:10px 12px;display:flex;flex-direction:column;gap:5px;background:var(--dsw-alias-bg-layer-2,#ffffff);flex:1}
|
|
2405
|
+
.dsh-ig-card-badge-row{display:flex;align-items:center}
|
|
2406
|
+
.dsh-ig-card-badge{display:inline-block;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-3,#f1f5f9);color:var(--dsw-alias-label-secondary,#475569);font-size:11px;font-weight:500;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2407
|
+
.dsh-ig-gallery-card-prompt-line{font-size:12.5px;font-weight:500;color:var(--dsw-alias-label-primary,#1e293b);line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2408
|
+
.dsh-ig-card-footer-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:2px}
|
|
2409
|
+
.dsh-ig-card-meta-text{font-size:11px;color:var(--dsw-alias-label-tertiary,#94a3b8);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2410
|
+
.dsh-ig-card-fav-btn{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-tertiary,#94a3b8);padding:2px;border-radius:4px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:color .15s,transform .15s;flex-shrink:0}
|
|
2411
|
+
.dsh-ig-card-fav-btn:hover{color:#ef4444;transform:scale(1.15)}
|
|
2412
|
+
.dsh-ig-card-fav-btn.is-favorited{color:#ef4444}
|
|
2413
|
+
|
|
2414
|
+
/* Placeholders for Upcoming Routes */
|
|
2415
|
+
.dsh-ig-placeholder-view{display:flex;align-items:center;justify-content:center;min-height:360px;height:100%;padding:24px}
|
|
2416
|
+
.dsh-ig-placeholder-card{max-width:500px;width:100%;text-align:center;padding:36px 28px;background:var(--dsw-alias-bg-layer-2,#ffffff);border:1px dashed var(--dsw-alias-border-l2,#e2e8f0);border-radius:14px;display:flex;flex-direction:column;align-items:center;gap:12px}
|
|
2417
|
+
.dsh-ig-placeholder-icon{font-size:40px;line-height:1}
|
|
2418
|
+
.dsh-ig-placeholder-header{display:flex;align-items:center;gap:8px;justify-content:center}
|
|
2419
|
+
.dsh-ig-placeholder-title{font-size:16px;font-weight:600;color:var(--dsw-alias-label-primary,inherit);margin:0}
|
|
2420
|
+
.dsh-ig-placeholder-badge{font-size:11px;font-weight:500;background:rgba(37,99,235,0.1);color:#2563eb;padding:2px 8px;border-radius:12px}
|
|
2421
|
+
.dsh-ig-placeholder-desc{font-size:13px;line-height:1.6;color:var(--dsw-alias-label-secondary,#64748b);margin:0}
|
|
2422
|
+
.dsh-ig-placeholder-tip{margin-top:6px;padding:8px 12px;font-size:12px;background:var(--dsw-alias-bg-layer-3,#f8fafc);border-radius:8px;color:var(--dsw-alias-label-tertiary,#64748b);text-align:left}
|
|
2423
|
+
|
|
2424
|
+
/* Empty State */
|
|
2425
|
+
.dsh-ig-gallery-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;min-height:360px;text-align:center;color:var(--dsw-alias-label-tertiary,#94a3b8)}
|
|
2426
|
+
.dsh-ig-gallery-empty-icon{font-size:44px;margin-bottom:10px}
|
|
2427
|
+
.dsh-ig-gallery-empty-title{font-size:15px;font-weight:600;color:var(--dsw-alias-label-primary,inherit);margin-bottom:4px}
|
|
1369
2428
|
.dsh-ig-gallery-empty-desc{font-size:13px;max-width:360px;line-height:1.5}
|
|
1370
2429
|
|
|
1371
2430
|
/* Pure Centered Lightbox */
|
|
1372
2431
|
.dsh-ig-lightbox-backdrop{position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,0.88);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;box-sizing:border-box;cursor:zoom-out;animation:dsh-ig-fade .15s ease-out}
|
|
1373
2432
|
.dsh-ig-lightbox-topbar{position:absolute;top:20px;left:24px;right:24px;display:flex;align-items:center;justify-content:space-between;z-index:10;pointer-events:none}
|
|
1374
2433
|
.dsh-ig-lightbox-meta{display:flex;align-items:center;gap:8px;pointer-events:auto}
|
|
2434
|
+
.dsh-ig-tag{display:inline-block;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-3,#edf0f3);color:var(--dsw-alias-label-secondary,inherit);font-weight:500;text-transform:uppercase;font-size:10px}
|
|
2435
|
+
.dsh-ig-tag-model{background:rgba(76,120,255,0.1);color:#4c78ff}
|
|
1375
2436
|
.dsh-ig-lightbox-close-btn{appearance:none;border:0;background:rgba(255,255,255,0.15);color:#fff;border-radius:50%;width:34px;height:34px;font-size:16px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .15s;pointer-events:auto}
|
|
1376
2437
|
.dsh-ig-lightbox-close-btn:hover{background:rgba(255,255,255,0.3)}
|
|
1377
2438
|
.dsh-ig-lightbox-img-wrap{max-width:86vw;max-height:78vh;display:flex;align-items:center;justify-content:center;cursor:default}
|
|
1378
2439
|
.dsh-ig-lightbox-img{max-width:100%;max-height:78vh;object-fit:contain;border-radius:8px;box-shadow:0 24px 60px rgba(0,0,0,0.7);user-select:none}
|
|
1379
2440
|
.dsh-ig-lightbox-bottombar{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);max-width:min(90vw,640px);background:rgba(20,22,26,0.85);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.15);border-radius:14px;padding:10px 16px;display:flex;flex-direction:column;gap:8px;color:#fff;box-shadow:0 16px 40px rgba(0,0,0,0.5);cursor:default}
|
|
1380
2441
|
.dsh-ig-lightbox-prompt-text{font-size:13px;line-height:1.4;color:rgba(255,255,255,0.92);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}
|
|
1381
|
-
.dsh-ig-lightbox-
|
|
2442
|
+
.dsh-ig-lightbox-counter{display:inline-flex;align-items:center;padding:2px 8px;border-radius:4px;background:rgba(255,255,255,0.15);color:#fff;font-size:11.5px;font-weight:500;font-variant-numeric:tabular-nums}
|
|
2443
|
+
.dsh-ig-lightbox-nav-btn{position:fixed;top:50%;transform:translateY(-50%);width:44px;height:44px;border-radius:50%;border:1px solid rgba(255,255,255,0.18);background:rgba(255,255,255,0.12);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:100;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);transition:background-color .15s ease,transform .15s ease,opacity .15s ease;outline:none}
|
|
2444
|
+
.dsh-ig-lightbox-nav-btn:hover:not(:disabled){background:rgba(255,255,255,0.28);transform:translateY(-50%) scale(1.08)}
|
|
2445
|
+
.dsh-ig-lightbox-nav-btn:disabled{opacity:0.2;cursor:not-allowed;pointer-events:none}
|
|
2446
|
+
.dsh-ig-lightbox-nav-prev{left:24px}
|
|
2447
|
+
.dsh-ig-lightbox-nav-next{right:24px}
|
|
2448
|
+
.dsh-ig-lightbox-loading{display:flex;align-items:center;justify-content:center;min-width:180px;min-height:180px}
|
|
2449
|
+
.dsh-ig-lightbox-spinner{width:36px;height:36px;border:3px solid rgba(255,255,255,0.2);border-top-color:#fff;border-radius:50%;animation:dsh-ig-spin .8s linear infinite}
|
|
2450
|
+
@keyframes dsh-ig-spin{to{transform:rotate(360deg)}}
|
|
1382
2451
|
.dsh-ig-lightbox-btn{appearance:none;border:1px solid rgba(255,255,255,0.18);background:rgba(255,255,255,0.08);color:#fff;border-radius:6px;padding:5px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:background .15s,border-color .15s,color .15s}
|
|
1383
2452
|
.dsh-ig-lightbox-btn:hover{background:rgba(255,255,255,0.22)}
|
|
1384
2453
|
.dsh-ig-lightbox-btn-danger{border-color:rgba(239,68,68,0.4);color:#fca5a5}
|
|
1385
2454
|
.dsh-ig-lightbox-btn-danger:hover{background:rgba(239,68,68,0.35)!important;color:#fff!important;border-color:rgba(239,68,68,0.7)!important}
|
|
1386
2455
|
.dsh-ig-gallery-page-toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.85);color:#fff;padding:6px 14px;border-radius:8px;font-size:13px;z-index:99999;animation:dsh-ig-fade .15s}
|
|
1387
2456
|
|
|
2457
|
+
/* Card selection and checkbox */
|
|
2458
|
+
.dsh-ig-gallery-card.is-selected{box-shadow:0 0 0 2px var(--dsw-alias-brand-primary,#2563eb);border-color:transparent}
|
|
2459
|
+
.dsh-ig-card-checkbox{position:absolute;top:8px;left:8px;width:22px;height:22px;border-radius:50%;border:1.5px solid rgba(255,255,255,0.7);background:rgba(0,0,0,0.35);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);color:#fff;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;z-index:5;opacity:0;transition:opacity .15s ease,background-color .15s ease,border-color .15s ease;padding:0;outline:none}
|
|
2460
|
+
.dsh-ig-gallery-card:hover .dsh-ig-card-checkbox,.dsh-ig-gallery-card.is-manage-mode .dsh-ig-card-checkbox,.dsh-ig-card-checkbox.is-checked{opacity:1}
|
|
2461
|
+
.dsh-ig-card-checkbox.is-checked{background:var(--dsw-alias-brand-primary,#2563eb);border-color:var(--dsw-alias-brand-primary,#2563eb)}
|
|
2462
|
+
|
|
2463
|
+
/* Studio button in toolbar */
|
|
2464
|
+
.dsh-ig-studio-btn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px;box-sizing:border-box;border-radius:6px;border:1px solid var(--dsw-alias-border-subtle,rgba(0,0,0,0.12));background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,inherit);font-size:12.5px;font-weight:500;cursor:pointer;transition:border-color .15s,background .15s,color .15s}
|
|
2465
|
+
.dsh-ig-studio-btn:hover{background:var(--dsw-alias-bg-layer-3,#f3f4f6);border-color:var(--dsw-alias-border-default,rgba(0,0,0,0.2))}
|
|
2466
|
+
.dsh-ig-studio-btn.is-active{background:var(--dsw-alias-brand-primary,#2563eb);border-color:var(--dsw-alias-brand-primary,#2563eb);color:#fff}
|
|
2467
|
+
.dsh-ig-studio-btn-danger{color:#ef4444;border-color:rgba(239,68,68,0.35);background:rgba(239,68,68,0.06)}
|
|
2468
|
+
.dsh-ig-studio-btn-danger:hover{background:rgba(239,68,68,0.14);border-color:rgba(239,68,68,0.6);color:#dc2626}
|
|
2469
|
+
.dsh-ig-studio-btn-danger.is-active{background:#dc2626;border-color:#dc2626;color:#fff}
|
|
2470
|
+
|
|
2471
|
+
/* Floating Batch Action Bar */
|
|
2472
|
+
.dsh-ig-batch-bar{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:16px;background:rgba(20,24,32,0.92);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(255,255,255,0.18);border-radius:40px;padding:8px 16px;box-shadow:0 16px 40px rgba(0,0,0,0.5);z-index:99990;animation:dsh-ig-slide-up .2s cubic-bezier(0.16,1,0.3,1);color:#fff}
|
|
2473
|
+
@keyframes dsh-ig-slide-up{from{transform:translate(-50%,20px);opacity:0}to{transform:translate(-50%,0);opacity:1}}
|
|
2474
|
+
.dsh-ig-batch-bar-left{display:flex;align-items:center;gap:10px}
|
|
2475
|
+
.dsh-ig-batch-bar-right{display:flex;align-items:center;gap:8px;border-left:1px solid rgba(255,255,255,0.15);padding-left:12px}
|
|
2476
|
+
.dsh-ig-batch-counter{font-size:13px;font-weight:600;color:rgba(255,255,255,0.95);margin-right:4px}
|
|
2477
|
+
.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}
|
|
2478
|
+
.dsh-ig-batch-btn:hover:not(:disabled){background:rgba(255,255,255,0.2)}
|
|
2479
|
+
.dsh-ig-batch-btn:disabled{opacity:0.4;cursor:not-allowed}
|
|
2480
|
+
.dsh-ig-batch-btn-danger{background:rgba(239,68,68,0.2);border-color:rgba(239,68,68,0.5);color:#fca5a5}
|
|
2481
|
+
.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}
|
|
2482
|
+
.dsh-ig-batch-btn-exit{border-color:transparent;background:transparent;color:rgba(255,255,255,0.7)}
|
|
2483
|
+
.dsh-ig-batch-btn-exit:hover{background:rgba(255,255,255,0.1);color:#fff}
|
|
2484
|
+
|
|
2485
|
+
/* Batch Delete Confirmation Modal */
|
|
2486
|
+
.dsh-ig-modal-backdrop{position:fixed;inset:0;background:rgba(0,0,0,0.65);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:99995;display:flex;align-items:center;justify-content:center;padding:16px;animation:dsh-ig-fade .15s ease-out}
|
|
2487
|
+
.dsh-ig-modal-box{width:100%;max-width:440px;background:var(--dsw-alias-bg-layer-1,#1c1e24);border:1px solid var(--dsw-alias-border-subtle,rgba(255,255,255,0.12));border-radius:12px;padding:22px;box-sizing:border-box;box-shadow:0 20px 50px rgba(0,0,0,0.45);color:var(--dsw-alias-label-primary,#fff);animation:dsh-ig-scale-up .15s ease-out}
|
|
2488
|
+
@keyframes dsh-ig-scale-up{from{transform:scale(0.95);opacity:0}to{transform:scale(1);opacity:1}}
|
|
2489
|
+
.dsh-ig-modal-header{display:flex;align-items:flex-start;gap:12px;margin-bottom:12px}
|
|
2490
|
+
.dsh-ig-modal-icon-danger{width:36px;height:36px;border-radius:50%;background:rgba(239,68,68,0.12);color:#ef4444;display:flex;align-items:center;justify-content:center;flex-shrink:0}
|
|
2491
|
+
.dsh-ig-modal-title{font-size:16px;font-weight:600;line-height:1.4}
|
|
2492
|
+
.dsh-ig-modal-body{margin-bottom:20px;padding-left:48px}
|
|
2493
|
+
.dsh-ig-modal-desc{font-size:13.5px;color:var(--dsw-alias-label-secondary,rgba(255,255,255,0.7));margin:0 0 14px 0;line-height:1.5}
|
|
2494
|
+
.dsh-ig-modal-checkbox-label{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--dsw-alias-label-primary,inherit);cursor:pointer;user-select:none}
|
|
2495
|
+
.dsh-ig-modal-checkbox-label input{margin:0;cursor:pointer;width:15px;height:15px}
|
|
2496
|
+
.dsh-ig-modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px}
|
|
2497
|
+
.dsh-ig-modal-btn{height:34px;padding:0 14px;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:background .15s,border-color .15s,color .15s;outline:none}
|
|
2498
|
+
.dsh-ig-modal-btn-cancel{background:transparent;border:1px solid var(--dsw-alias-border-subtle,rgba(255,255,255,0.2));color:var(--dsw-alias-label-primary,inherit)}
|
|
2499
|
+
.dsh-ig-modal-btn-cancel:hover{background:var(--dsw-alias-bg-layer-3,rgba(255,255,255,0.08))}
|
|
2500
|
+
.dsh-ig-modal-btn-danger{background:#dc2626;border:1px solid #dc2626;color:#fff}
|
|
2501
|
+
.dsh-ig-modal-btn-danger:hover{background:#b91c1c;border-color:#b91c1c}
|
|
2502
|
+
|
|
1388
2503
|
/* Hide floating chat composer when gallery page is active */
|
|
1389
2504
|
[data-conversation-scroll]:has(.dsh-ig-gallery-page) [data-composer-seat]{display:none!important}
|
|
1390
2505
|
`;
|
|
@@ -2034,6 +3149,7 @@ window.__ModuleLoader__.load({
|
|
|
2034
3149
|
provider: result.provider,
|
|
2035
3150
|
model: result.model,
|
|
2036
3151
|
output: result.output,
|
|
3152
|
+
...result.savedTo ? { savedTo: result.savedTo } : {},
|
|
2037
3153
|
...result.seed !== void 0 ? { seed: result.seed } : {}
|
|
2038
3154
|
});
|
|
2039
3155
|
}, [result?.attachment.attachmentId]);
|