@dickpy/dsh-imagegen 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +363 -196
  2. package/docs/images/ecommerce-mode.png +0 -0
  3. package/docs/images/image-generation-studio-three-column.png +0 -0
  4. package/docs/images/imagegen-overview.png +0 -0
  5. package/docs/images/multi-model-comparison.png +0 -0
  6. package/docs/videos/agent-chat-edit.gif +0 -0
  7. package/docs/videos/agent-chat-edit.mp4 +0 -0
  8. package/lib/client.js +2589 -884
  9. package/lib/client.js.map +1 -1
  10. package/lib/index.js +585 -240
  11. package/package.json +5 -2
  12. package/src/agent-image-tools.ts +131 -102
  13. package/src/client/ImageGenPanel.tsx +2679 -1594
  14. package/src/client/SettingsCard.tsx +6 -27
  15. package/src/client/api.ts +11 -1
  16. package/src/client/conversation-sync.ts +14 -0
  17. package/src/client/image-toolview.tsx +176 -165
  18. package/src/client/index.ts +25 -15
  19. package/src/client/locales.ts +746 -602
  20. package/src/client/mount.tsx +213 -124
  21. package/src/client/panel.module.css +2619 -1563
  22. package/src/client/sidebar-entry.ts +190 -144
  23. package/src/edit-image-command.ts +110 -0
  24. package/src/engine.ts +47 -5
  25. package/src/gallery-store.ts +20 -0
  26. package/src/generation-runtime.ts +11 -2
  27. package/src/history-store.ts +26 -0
  28. package/src/image-models.ts +1 -1
  29. package/src/index.ts +31 -12
  30. package/src/model-catalog.ts +19 -2
  31. package/src/presets.ts +11 -3
  32. package/src/prompt-enhancer.ts +63 -5
  33. package/src/protocol.ts +59 -5
  34. package/src/routes.ts +62 -4
  35. package/src/task-queue.ts +42 -32
  36. package/docs/images/agent-chat-edit.png +0 -0
  37. package/docs/images/agent-chat-generate.png +0 -0
  38. package/docs/images/agent-chat-poster-workflow.png +0 -0
package/lib/client.js CHANGED
@@ -29,6 +29,8 @@ window.__ModuleLoader__.load({
29
29
  const PRESETS_API = "/api/dsh-imagegen/presets";
30
30
  /** Loopback-only image reader for Agent tool-result previews. */
31
31
  const AGENT_IMAGE_API = "/api/dsh-imagegen/agent-image";
32
+ /** Store the current composer image for the direct edit_image command. */
33
+ const CONVERSATION_IMAGE_API = "/api/dsh-imagegen/conversation-image";
32
34
  /**
33
35
  * Host-computed per-channel usage counters (generation-count badges in the
34
36
  * settings card): entries are tallied from the persisted history and gallery
@@ -152,6 +154,18 @@ window.__ModuleLoader__.load({
152
154
  body: JSON.stringify({ prompt })
153
155
  }))).prompt;
154
156
  }
157
+ /** Stage a generated image as a durable reference for `/edit_image`. */
158
+ async attachConversationImage(sessionId, dataUrl, name) {
159
+ await readEnvelope(await fetch(CONVERSATION_IMAGE_API, {
160
+ method: "POST",
161
+ headers: { "content-type": "application/json" },
162
+ body: JSON.stringify({
163
+ sessionId,
164
+ dataUrl,
165
+ name
166
+ })
167
+ }));
168
+ }
155
169
  async taskSubmit(request) {
156
170
  return (await readEnvelope(await fetch(TASK_API.submit, {
157
171
  method: "POST",
@@ -291,11 +305,78 @@ window.__ModuleLoader__.load({
291
305
  */
292
306
  const zh = {
293
307
  "entry.label": "AI 生图",
294
- "entry.tooltip": "AI 生图面板(gpt-image-2 / grok-imagine-image / nanobanana / seedream 系列)",
308
+ "entry.tooltip": "AI 生图面板(gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream 系列)",
309
+ "entry.newSession": "新会话",
310
+ "entry.newSessionTooltip": "创建一个新的 DSH 会话",
311
+ "entry.image": "生图",
295
312
  "panel.title": "AI 生图",
296
313
  "panel.githubTip": "觉得好用或有建议?欢迎来 GitHub 提 issues、点个 star 支持一下!",
314
+ "conversation.add": "加入对话",
315
+ "conversation.adding": "加入中…",
316
+ "conversation.added": "图片已加入当前对话",
317
+ "conversation.addHint": "加入对话后可使用 /edit_image 修改;命令使用插件图片模型",
318
+ "conversation.noSession": "请先打开一个会话,再加入图片",
319
+ "conversation.unavailable": "当前会话暂不可用",
320
+ "conversation.busy": "当前对话正在发送,请稍后再试",
297
321
  "mode.text": "文生图",
298
322
  "mode.edit": "图生图",
323
+ "workspace.label": "创作模式",
324
+ "workspace.normal": "普通生图",
325
+ "workspace.ecommerce": "电商模式",
326
+ "chat.expand": "展开右侧对话",
327
+ "chat.collapse": "收起右侧对话",
328
+ "chat.toggle": "对话",
329
+ "config.resizeHint": "拖拽调整参数栏宽度",
330
+ "ecommerce.generation": "生成设置",
331
+ "ecommerce.title": "电商模式",
332
+ "ecommerce.badge": "预览",
333
+ "ecommerce.short": "电商",
334
+ "ecommerce.anchorPending": "已生成主图,其余图片正在自动参考主图生成,保证整套同款…",
335
+ "ecommerce.anchorFailed": "主图生成失败,其余图片已暂停。请重试主图(或整组重新生成)后再继续。",
336
+ "ecommerce.anchorNote": "锚定生成:先出主图,其余图片自动参考主图,保证整套同款",
337
+ "ecommerce.noAssetWarn": "未上传商品图:主图将按文字描述生成并作为锚定基准,建议先上传商品主图获得更高一致性",
338
+ "ecommerce.product": "商品信息",
339
+ "ecommerce.productName": "商品名称(必填)",
340
+ "ecommerce.projectName": "项目名称(可选)",
341
+ "ecommerce.constraints": "卖点与约束",
342
+ "ecommerce.sellingPoints": "商品卖点,例如:防水防污 / 大容量 / 便携",
343
+ "ecommerce.protectedFeatures": "必须保留的特征,例如:颜色、Logo、包装文字、结构",
344
+ "ecommerce.styleHint": "风格补充,例如:高级感 / 写实 / 自然光",
345
+ "ecommerce.setStructure": "套图结构",
346
+ "ecommerce.preview": "生成套图预览",
347
+ "ecommerce.planTitle": "预计生成 {count} 张图片",
348
+ "ecommerce.planSlot": "{count} 张 · {description}",
349
+ "ecommerce.confirm": "确认生成整套图片",
350
+ "ecommerce.uploadRef": "上传商品素材(可选,最多 4 张:主体 / 包装 / 细节 / 风格)",
351
+ "ecommerce.uploadShort": "上传素材",
352
+ "ecommerce.sellingTitle": "商品卖点",
353
+ "ecommerce.advanced": "高级选项(项目 / 平台 / 约束)",
354
+ "ecommerce.params": "参数选择",
355
+ "ecommerce.platformLabel": "平台",
356
+ "ecommerce.languageLabel": "文案语言",
357
+ "ecommerce.ratioLabel": "比例",
358
+ "ecommerce.categoryLabel": "类目",
359
+ "ecommerce.multiSelect": "可多选",
360
+ "ecommerce.countHint": "点击调整数量(1-4)",
361
+ "ecommerce.refSettings": "参考图设置(可选)",
362
+ "ecommerce.styleTitle": "风格 / 补充提示",
363
+ "ecommerce.protectedLabel": "必须保留的特征(可选)",
364
+ "ecommerce.assetsFull": "最多上传 4 张素材",
365
+ "ecommerce.refSelect": "该用途使用的参考图",
366
+ "ecommerce.refNone": "不使用参考图",
367
+ "ecommerce.role.product": "商品主体",
368
+ "ecommerce.role.packaging": "包装",
369
+ "ecommerce.role.detail": "细节/角度",
370
+ "ecommerce.role.style": "风格参考",
371
+ "ecommerce.footerReady": "将生成 {count} 张套图",
372
+ "ecommerce.footerEmpty": "请选择套图结构",
373
+ "ecommerce.results.title": "套图结果",
374
+ "ecommerce.results.progress": "{done}/{total} 已完成",
375
+ "ecommerce.results.failed": "{count} 张失败",
376
+ "ecommerce.results.empty": "确认生成后,每张图片的任务状态和结果会按用途显示在这里",
377
+ "ecommerce.results.regenerate": "重新生成",
378
+ "ecommerce.results.export": "导出清单",
379
+ "ecommerce.results.newProduct": "新商品",
299
380
  "prompt.placeholder": "描述你想要的画面,例如:一只戴着宇航员头盔的橘猫,在月球上举起望远镜,水彩风格,柔和光线…",
300
381
  "prompt.required": "请输入提示词",
301
382
  "prompt.count": "{count}",
@@ -338,6 +419,7 @@ window.__ModuleLoader__.load({
338
419
  "detail.high": "高清",
339
420
  "detail.hint": "透传参数,部分 gpt-image-2 网关支持;官方接口请保持「自动」",
340
421
  "model.label": "模型",
422
+ "model.noEditModels": "当前模式暂无可用模型",
341
423
  "compare.enable": "多模型对比",
342
424
  "compare.models": "参与对比的模型",
343
425
  "compare.title": "多模型结果对比",
@@ -352,6 +434,8 @@ window.__ModuleLoader__.load({
352
434
  "edit.required": "请先上传参考图片",
353
435
  "canvas.emptyTitle": "开始你的创作",
354
436
  "canvas.emptyHint": "在左侧输入提示词并点击「开始生成」,结果将显示在这里",
437
+ "canvas.new": "新建作图",
438
+ "canvas.newHint": "开始新的作图并清空预览",
355
439
  "canvas.error": "生成失败:{error}",
356
440
  "canvas.submitting": "正在加入生成队列…",
357
441
  "canvas.queued": "已加入生成队列",
@@ -364,6 +448,7 @@ window.__ModuleLoader__.load({
364
448
  "history.title": "历史记录",
365
449
  "history.empty": "暂无历史记录,生成图片后会保存在这里",
366
450
  "history.clear": "清空",
451
+ "history.clearConfirm": "确定要清空全部历史记录吗?此操作不可撤销。",
367
452
  "history.restore": "恢复",
368
453
  "history.delete": "删除",
369
454
  "history.images": "张",
@@ -407,6 +492,7 @@ window.__ModuleLoader__.load({
407
492
  "gallery.already": "已在画廊中",
408
493
  "gallery.delete": "移出画廊",
409
494
  "gallery.clear": "清空画廊",
495
+ "gallery.clearConfirm": "确定要清空整个画廊吗?此操作不可撤销。",
410
496
  "gallery.empty": "画廊还是空的,把喜欢的图片加入进来吧",
411
497
  "gallery.viewing": "画廊 · {time}",
412
498
  "preview.title": "图片预览",
@@ -434,6 +520,8 @@ window.__ModuleLoader__.load({
434
520
  "config.disabled": "插件已停用,请在设置中重新启用。",
435
521
  "connection.connected": "已连接",
436
522
  "connection.disconnected": "未连接",
523
+ "panel.collapseConfig": "收起参数栏",
524
+ "panel.expandConfig": "展开参数栏",
437
525
  "update.available": "检测到新版本:{version}",
438
526
  "update.install": "在线更新",
439
527
  "update.installing": "更新中…",
@@ -568,23 +656,88 @@ window.__ModuleLoader__.load({
568
656
  "channels.addModelConfirm": "添加",
569
657
  "channels.copyFrom": "从其他渠道复制…",
570
658
  "channels.copyApply": "复制",
571
- "channels.candidatesTitle": "候选模型(点击加入)",
572
- "channels.candidatesQuick": "只勾已收录协议",
659
+ "channels.candidatesTitle": "图片模型候选(点击加入)",
573
660
  "channels.deleteThisChannel": "删除此渠道",
574
661
  "channels.setDefault": "设为默认渠道",
575
662
  "channels.presetPickerTitle": "添加提供方",
576
663
  "channels.presetPickerHint": "选择一个内置提供方,仅需填写 API 密钥",
577
- "channels.presetModelsHint": "{n} 个预置模型",
578
664
  "channels.presetCustomHint": "自行填写 API 地址、密钥与模型目录",
579
665
  "channels.presetLoadFailed": "加载提供方失败:{error}"
580
666
  };
581
667
  const en = {
582
668
  "entry.label": "AI Image",
583
- "entry.tooltip": "AI image generation studio (gpt-image-2 / grok-imagine-image / nanobanana / seedream family)",
669
+ "entry.tooltip": "AI image generation studio (gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream family)",
670
+ "entry.newSession": "New session",
671
+ "entry.newSessionTooltip": "Create a new DSH session",
672
+ "entry.image": "Image",
584
673
  "panel.title": "AI Image",
585
674
  "panel.githubTip": "Like it or have suggestions? Head to GitHub to open issues and star us!",
675
+ "conversation.add": "Add to chat",
676
+ "conversation.adding": "Adding…",
677
+ "conversation.added": "Image added to the current chat",
678
+ "conversation.addHint": "After adding to chat, use /edit_image to modify it; the command uses the plugin image model",
679
+ "conversation.noSession": "Open a session before adding an image",
680
+ "conversation.unavailable": "The current session is unavailable",
681
+ "conversation.busy": "The chat is sending a message; try again shortly",
586
682
  "mode.text": "Text to Image",
587
683
  "mode.edit": "Image to Image",
684
+ "workspace.label": "Workspace",
685
+ "workspace.normal": "Standard",
686
+ "workspace.ecommerce": "E-commerce",
687
+ "chat.expand": "Expand the conversation pane",
688
+ "chat.collapse": "Collapse the conversation pane",
689
+ "chat.toggle": "Chat",
690
+ "config.resizeHint": "Drag to resize the parameter panel",
691
+ "ecommerce.generation": "Generation settings",
692
+ "ecommerce.title": "E-commerce Mode",
693
+ "ecommerce.badge": "Beta",
694
+ "ecommerce.short": "Store",
695
+ "ecommerce.anchorPending": "Main image generated — remaining images are being generated with the main image as reference to keep the set consistent…",
696
+ "ecommerce.anchorFailed": "Main image failed; remaining images are paused. Retry the main image or regenerate the set.",
697
+ "ecommerce.anchorNote": "Anchored: main image first, remaining images reference it automatically for a consistent set",
698
+ "ecommerce.noAssetWarn": "No product image uploaded: the main image will be generated from text as the anchor. Upload one for higher consistency.",
699
+ "ecommerce.product": "Product details",
700
+ "ecommerce.productName": "Product name (required)",
701
+ "ecommerce.projectName": "Project name (optional)",
702
+ "ecommerce.constraints": "Selling points & constraints",
703
+ "ecommerce.sellingPoints": "Selling points, e.g. waterproof / large capacity / portable",
704
+ "ecommerce.protectedFeatures": "Features to preserve: color, logo, packaging text, structure",
705
+ "ecommerce.styleHint": "Style hint, e.g. premium / realistic / natural light",
706
+ "ecommerce.setStructure": "Image set structure",
707
+ "ecommerce.preview": "Preview product set",
708
+ "ecommerce.planTitle": "Plan to generate {count} images",
709
+ "ecommerce.planSlot": "{count} images · {description}",
710
+ "ecommerce.confirm": "Confirm and generate set",
711
+ "ecommerce.uploadRef": "Upload product assets (optional, up to 4: product / packaging / detail / style)",
712
+ "ecommerce.uploadShort": "Upload",
713
+ "ecommerce.sellingTitle": "Selling points",
714
+ "ecommerce.advanced": "Advanced (project / platform / constraints)",
715
+ "ecommerce.params": "Parameters",
716
+ "ecommerce.platformLabel": "Platform",
717
+ "ecommerce.languageLabel": "Copy language",
718
+ "ecommerce.ratioLabel": "Aspect ratio",
719
+ "ecommerce.categoryLabel": "Category",
720
+ "ecommerce.multiSelect": "multi-select",
721
+ "ecommerce.countHint": "Click to adjust count (1-4)",
722
+ "ecommerce.refSettings": "Reference images (optional)",
723
+ "ecommerce.styleTitle": "Style / extra hints",
724
+ "ecommerce.protectedLabel": "Features to preserve (optional)",
725
+ "ecommerce.assetsFull": "Up to 4 assets",
726
+ "ecommerce.refSelect": "Reference image for this slot",
727
+ "ecommerce.refNone": "No reference",
728
+ "ecommerce.role.product": "Product",
729
+ "ecommerce.role.packaging": "Packaging",
730
+ "ecommerce.role.detail": "Detail / angle",
731
+ "ecommerce.role.style": "Style",
732
+ "ecommerce.footerReady": "{count} images in this set",
733
+ "ecommerce.footerEmpty": "Select the image set structure",
734
+ "ecommerce.results.title": "Product set results",
735
+ "ecommerce.results.progress": "{done}/{total} completed",
736
+ "ecommerce.results.failed": "{count} failed",
737
+ "ecommerce.results.empty": "After confirming, each image task and result will show here grouped by purpose",
738
+ "ecommerce.results.regenerate": "Regenerate",
739
+ "ecommerce.results.export": "Export manifest",
740
+ "ecommerce.results.newProduct": "New product",
588
741
  "prompt.placeholder": "Describe the picture you want, e.g. an orange cat in an astronaut helmet raising a telescope on the moon, watercolor style, soft light…",
589
742
  "prompt.required": "Enter a prompt first",
590
743
  "prompt.count": "{count}",
@@ -627,6 +780,7 @@ window.__ModuleLoader__.load({
627
780
  "detail.high": "High",
628
781
  "detail.hint": "Passthrough parameter supported by some gpt-image-2 gateways; keep \"Auto\" for official endpoints",
629
782
  "model.label": "Model",
783
+ "model.noEditModels": "No model is available for this mode",
630
784
  "compare.enable": "Multi-model comparison",
631
785
  "compare.models": "Models to compare",
632
786
  "compare.title": "Multi-model comparison",
@@ -641,6 +795,8 @@ window.__ModuleLoader__.load({
641
795
  "edit.required": "Upload a reference image first",
642
796
  "canvas.emptyTitle": "Start creating",
643
797
  "canvas.emptyHint": "Enter a prompt on the left and click \"Generate\"; results appear here",
798
+ "canvas.new": "New creation",
799
+ "canvas.newHint": "Start a new creation and clear the preview",
644
800
  "canvas.error": "Generation failed: {error}",
645
801
  "canvas.submitting": "Adding to the generation queue…",
646
802
  "canvas.queued": "Added to the generation queue",
@@ -653,6 +809,7 @@ window.__ModuleLoader__.load({
653
809
  "history.title": "History",
654
810
  "history.empty": "No history yet — generations will be saved here",
655
811
  "history.clear": "Clear",
812
+ "history.clearConfirm": "Clear all history? This action cannot be undone.",
656
813
  "history.restore": "Restore",
657
814
  "history.delete": "Delete",
658
815
  "history.images": "images",
@@ -696,6 +853,7 @@ window.__ModuleLoader__.load({
696
853
  "gallery.already": "Already in gallery",
697
854
  "gallery.delete": "Remove",
698
855
  "gallery.clear": "Clear gallery",
856
+ "gallery.clearConfirm": "Clear the entire gallery? This action cannot be undone.",
699
857
  "gallery.empty": "The gallery is empty — add images you like here",
700
858
  "gallery.viewing": "Gallery · {time}",
701
859
  "preview.title": "Image preview",
@@ -723,6 +881,8 @@ window.__ModuleLoader__.load({
723
881
  "config.disabled": "The plugin is disabled — re-enable it in Settings.",
724
882
  "connection.connected": "Connected",
725
883
  "connection.disconnected": "Disconnected",
884
+ "panel.collapseConfig": "Collapse settings panel",
885
+ "panel.expandConfig": "Expand settings panel",
726
886
  "update.available": "A new version is available: {version}",
727
887
  "update.install": "Update online",
728
888
  "update.installing": "Updating…",
@@ -857,13 +1017,11 @@ window.__ModuleLoader__.load({
857
1017
  "channels.addModelConfirm": "Add",
858
1018
  "channels.copyFrom": "Copy from another channel…",
859
1019
  "channels.copyApply": "Copy",
860
- "channels.candidatesTitle": "Candidates (click to add)",
861
- "channels.candidatesQuick": "Add only known protocols",
1020
+ "channels.candidatesTitle": "Image model candidates (click to add)",
862
1021
  "channels.deleteThisChannel": "Delete this channel",
863
1022
  "channels.setDefault": "Set as default channel",
864
1023
  "channels.presetPickerTitle": "Add provider",
865
1024
  "channels.presetPickerHint": "Pick a built-in provider — only the API key is needed",
866
- "channels.presetModelsHint": "{n} preset model(s)",
867
1025
  "channels.presetCustomHint": "Configure the API URL, key, and model catalog yourself",
868
1026
  "channels.presetLoadFailed": "Failed to load providers: {error}"
869
1027
  };
@@ -903,47 +1061,47 @@ window.__ModuleLoader__.load({
903
1061
  document.head.appendChild(tag);
904
1062
  }
905
1063
  var templates_module_css_default = {
906
- "detailMeta": "o0mAxG_detailMeta",
907
- "body": "o0mAxG_body",
908
- "cardSource": "o0mAxG_cardSource",
909
- "notice": "o0mAxG_notice",
910
- "cardCategory": "o0mAxG_cardCategory",
911
- "detailActions": "o0mAxG_detailActions",
912
- "overlay": "o0mAxG_overlay",
913
- "shell": "o0mAxG_shell",
1064
+ "dsh-imagegen-templates-spin": "o0mAxG_dsh-imagegen-templates-spin",
1065
+ "headerActions": "o0mAxG_headerActions",
914
1066
  "cardMeta": "o0mAxG_cardMeta",
1067
+ "detailPrompt": "o0mAxG_detailPrompt",
1068
+ "close": "o0mAxG_close",
1069
+ "detailLink": "o0mAxG_detailLink",
1070
+ "overlay": "o0mAxG_overlay",
915
1071
  "categoryPill": "o0mAxG_categoryPill",
1072
+ "notice": "o0mAxG_notice",
1073
+ "title": "o0mAxG_title",
1074
+ "grid": "o0mAxG_grid",
1075
+ "cardSource": "o0mAxG_cardSource",
1076
+ "heading": "o0mAxG_heading",
916
1077
  "thumbWrap": "o0mAxG_thumbWrap",
1078
+ "detail": "o0mAxG_detail",
917
1079
  "thumbPlaceholder": "o0mAxG_thumbPlaceholder",
918
- "cardBody": "o0mAxG_cardBody",
919
- "attribution": "o0mAxG_attribution",
920
- "sourceLink": "o0mAxG_sourceLink",
1080
+ "detailMedia": "o0mAxG_detailMedia",
921
1081
  "detailInfo": "o0mAxG_detailInfo",
922
- "detail": "o0mAxG_detail",
923
- "detailTitle": "o0mAxG_detailTitle",
924
- "footer": "o0mAxG_footer",
925
- "detailPrompt": "o0mAxG_detailPrompt",
926
1082
  "thumb": "o0mAxG_thumb",
927
- "detailLink": "o0mAxG_detailLink",
928
- "title": "o0mAxG_title",
1083
+ "footer": "o0mAxG_footer",
1084
+ "attribution": "o0mAxG_attribution",
1085
+ "cardCategory": "o0mAxG_cardCategory",
1086
+ "sourceLink": "o0mAxG_sourceLink",
929
1087
  "meta": "o0mAxG_meta",
930
1088
  "toolbar": "o0mAxG_toolbar",
931
- "detailOverlay": "o0mAxG_detailOverlay",
932
- "detailImage": "o0mAxG_detailImage",
933
- "categoryRow": "o0mAxG_categoryRow",
934
- "dsh-imagegen-templates-spin": "o0mAxG_dsh-imagegen-templates-spin",
935
- "headerActions": "o0mAxG_headerActions",
1089
+ "detailActions": "o0mAxG_detailActions",
1090
+ "cardTitle": "o0mAxG_cardTitle",
936
1091
  "search": "o0mAxG_search",
937
- "card": "o0mAxG_card",
938
- "heading": "o0mAxG_heading",
939
1092
  "spinner": "o0mAxG_spinner",
940
- "close": "o0mAxG_close",
941
- "header": "o0mAxG_header",
1093
+ "cardBody": "o0mAxG_cardBody",
942
1094
  "featuredBadge": "o0mAxG_featuredBadge",
943
- "cardTitle": "o0mAxG_cardTitle",
944
- "grid": "o0mAxG_grid",
1095
+ "body": "o0mAxG_body",
1096
+ "detailMeta": "o0mAxG_detailMeta",
1097
+ "header": "o0mAxG_header",
1098
+ "categoryRow": "o0mAxG_categoryRow",
1099
+ "detailImage": "o0mAxG_detailImage",
1100
+ "detailTitle": "o0mAxG_detailTitle",
945
1101
  "state": "o0mAxG_state",
946
- "detailMedia": "o0mAxG_detailMedia"
1102
+ "card": "o0mAxG_card",
1103
+ "shell": "o0mAxG_shell",
1104
+ "detailOverlay": "o0mAxG_detailOverlay"
947
1105
  };
948
1106
  //#endregion
949
1107
  //#region src/client/TemplateLibrary.tsx
@@ -1656,7 +1814,8 @@ window.__ModuleLoader__.load({
1656
1814
  "nanobanana2",
1657
1815
  "nanobanana2-lite",
1658
1816
  "nanobanana-pro",
1659
- "seedream-5.0-pro"
1817
+ "seedream-5.0-pro",
1818
+ "glm-image"
1660
1819
  ];
1661
1820
  /** Normalize user-entered model identifiers and retain a usable legacy default. */
1662
1821
  function normalizeImageModels(value) {
@@ -1670,8 +1829,118 @@ window.__ModuleLoader__.load({
1670
1829
  return unique.size > 0 ? [...unique] : [...DEFAULT_IMAGE_MODELS];
1671
1830
  }
1672
1831
  //#endregion
1832
+ //#region src/model-catalog.ts
1833
+ const ENTRIES = {
1834
+ "gpt-image": {
1835
+ label: "gpt-image",
1836
+ labelZh: "GPT 图像",
1837
+ known: true,
1838
+ supportsEdit: true,
1839
+ supportsAspectRatio: false,
1840
+ qualityTiers: [
1841
+ "1K",
1842
+ "2K",
1843
+ "4K"
1844
+ ]
1845
+ },
1846
+ "dall-e": {
1847
+ label: "DALL·E",
1848
+ labelZh: "DALL·E",
1849
+ known: true,
1850
+ supportsEdit: true,
1851
+ supportsAspectRatio: false,
1852
+ qualityTiers: ["auto"]
1853
+ },
1854
+ grok: {
1855
+ label: "grok",
1856
+ labelZh: "Grok",
1857
+ known: true,
1858
+ supportsEdit: true,
1859
+ supportsAspectRatio: true,
1860
+ qualityTiers: ["1K", "2K"]
1861
+ },
1862
+ nanobanana: {
1863
+ label: "nanobanana",
1864
+ labelZh: "Nano Banana",
1865
+ known: true,
1866
+ supportsEdit: true,
1867
+ supportsAspectRatio: true,
1868
+ qualityTiers: [
1869
+ "1K",
1870
+ "2K",
1871
+ "4K"
1872
+ ]
1873
+ },
1874
+ seedream: {
1875
+ label: "seedream",
1876
+ labelZh: "Seedream",
1877
+ known: true,
1878
+ supportsEdit: true,
1879
+ supportsAspectRatio: true,
1880
+ qualityTiers: ["1K", "2K"]
1881
+ },
1882
+ zhipu: {
1883
+ label: "GLM-Image",
1884
+ labelZh: "智谱图像",
1885
+ known: true,
1886
+ supportsEdit: false,
1887
+ supportsAspectRatio: false,
1888
+ qualityTiers: ["HD"]
1889
+ }
1890
+ };
1891
+ /** Official Gemini image ids served by Nano Banana gateways. */
1892
+ const NANOBANANA_GEMINI_IDS = /* @__PURE__ */ new Set([
1893
+ "gemini-3-pro-image",
1894
+ "gemini-3-pro-image-preview",
1895
+ "gemini-3.1-flash-image",
1896
+ "gemini-3.1-flash-image-preview",
1897
+ "gemini-3.1-flash-lite-image",
1898
+ "gemini-2.5-flash-image"
1899
+ ]);
1900
+ /** Classify one upstream model id into its request-shaping family. */
1901
+ function describeModel(model) {
1902
+ const id = model.trim();
1903
+ if (/^gpt-image/i.test(id)) return {
1904
+ family: "gpt-image",
1905
+ ...ENTRIES["gpt-image"]
1906
+ };
1907
+ if (/^dall-e/i.test(id)) return {
1908
+ family: "dall-e",
1909
+ ...ENTRIES["dall-e"]
1910
+ };
1911
+ if (/^grok-imagine(?:-|$)/.test(id)) return {
1912
+ family: "grok",
1913
+ ...ENTRIES.grok
1914
+ };
1915
+ if (/^nanobanana/i.test(id) || NANOBANANA_GEMINI_IDS.has(id)) return {
1916
+ family: "nanobanana",
1917
+ ...ENTRIES.nanobanana
1918
+ };
1919
+ if (/^(?:doubao-)?seedream/i.test(id)) return {
1920
+ family: "seedream",
1921
+ ...ENTRIES.seedream
1922
+ };
1923
+ if (/^(?:glm-image|cogview(?:-|$))/i.test(id)) return {
1924
+ family: "zhipu",
1925
+ ...ENTRIES.zhipu
1926
+ };
1927
+ return {
1928
+ family: "unknown",
1929
+ label: "unknown",
1930
+ labelZh: "未知协议",
1931
+ known: false,
1932
+ supportsEdit: true,
1933
+ supportsAspectRatio: false,
1934
+ qualityTiers: []
1935
+ };
1936
+ }
1937
+ //#endregion
1938
+ //#region src/client/conversation-sync.ts
1939
+ /** Document event used to bridge chat tool results into the image workspace. */
1940
+ const CHAT_IMAGE_EVENT = "dsh-imagegen:chat-images";
1941
+ //#endregion
1673
1942
  //#region \0dsh-css:E:\dsh-plugin\src\client\panel.module.css.mjs
1674
- const css$2 = "[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-imagegen-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-dsh-imagegen-view]{display:block}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane=conversation]>:not([data-dsh-imagegen-view]),html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*=centerCol]>:not([data-dsh-imagegen-view]){display:none!important}.Yvqh9W_entry{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:0 12px;font-size:13px;display:flex}.Yvqh9W_entry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}.Yvqh9W_entry[data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}.Yvqh9W_entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.Yvqh9W_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .Yvqh9W_entry{justify-content:center;width:100%;padding:0}[data-dsh-frame][data-sidebar-collapsed] .Yvqh9W_entryLabel{display:none}.Yvqh9W_view{overflow:hidden}.Yvqh9W_panel,.Yvqh9W_panel *,.Yvqh9W_panel :before,.Yvqh9W_panel :after{box-sizing:border-box}.Yvqh9W_panel{background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:10px;padding:14px 16px 16px;display:flex;position:relative;overflow:hidden}.Yvqh9W_panelHeader{flex:none;justify-content:space-between;align-items:center;gap:12px;display:flex}.Yvqh9W_panelHeading{align-items:baseline;gap:10px;min-width:0;display:flex}.Yvqh9W_panelTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:16px;font-weight:700}.Yvqh9W_githubLink{width:22px;height:22px;color:var(--dsw-alias-label-secondary);border-radius:6px;flex:none;justify-content:center;align-items:center;text-decoration:none;transition:color .12s,background .12s;display:inline-flex}.Yvqh9W_githubLink:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_connectionStatus{border:1px solid var(--dsw-alias-label-error);height:28px;color:var(--dsw-alias-label-error);font:inherit;white-space:nowrap;background:0 0;border-radius:8px;flex:none;align-items:center;gap:6px;padding:0 10px;font-size:12px;line-height:1;display:inline-flex}.Yvqh9W_connectionStatus[data-connected=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.Yvqh9W_connectionDot{background:currentColor;border-radius:50%;width:6px;height:6px}.Yvqh9W_updateBanner{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary);overflow-wrap:anywhere;border-radius:10px;flex:none;justify-content:space-between;align-items:center;gap:12px;padding:7px 10px 7px 12px;font-size:12px;line-height:1.5;display:flex}.Yvqh9W_updateBanner[data-kind=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.Yvqh9W_updateText{min-width:0}.Yvqh9W_updateActions{flex:none;align-items:center;gap:10px;display:inline-flex}.Yvqh9W_updateRelease{color:inherit;text-underline-offset:2px;white-space:nowrap;text-decoration:underline}@media (width<=700px){.Yvqh9W_panelHeader{align-items:flex-start}.Yvqh9W_panelHeading{flex-direction:column;align-items:flex-start;gap:2px}.Yvqh9W_updateBanner{flex-direction:column;align-items:flex-start}.Yvqh9W_updateActions{justify-content:space-between;width:100%}}.Yvqh9W_studio{flex:1;gap:14px;min-width:0;min-height:0;display:flex}.Yvqh9W_config{flex-direction:column;flex:none;gap:12px;width:300px;min-width:260px;max-width:340px;height:100%;min-height:0;display:flex;overflow:hidden}.Yvqh9W_configScroll{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex-direction:column;flex:1;gap:12px;min-height:0;padding-right:2px;display:flex;overflow-y:auto}.Yvqh9W_configScroll::-webkit-scrollbar{width:8px}.Yvqh9W_configScroll::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_canvas{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:12px;flex-direction:column;flex:1;min-width:0;min-height:0;display:flex;position:relative;overflow:hidden}.Yvqh9W_taskTray{z-index:6;border:1px solid var(--dsw-alias-border-l2);background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) 92%, transparent);backdrop-filter:blur(10px);border-radius:9px;flex-direction:column;width:min(360px,100% - 24px);max-height:calc(100% - 24px);display:flex;position:absolute;top:12px;right:12px;overflow:hidden;box-shadow:0 8px 24px #0000001f}.Yvqh9W_taskTray[data-open=false]{width:auto;max-width:calc(100% - 24px)}.Yvqh9W_taskTrayHeader{min-height:34px;color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:stretch;font-size:12px;font-weight:600;display:flex}.Yvqh9W_taskTray[data-open=false] .Yvqh9W_taskTrayHeader{border-bottom:0}.Yvqh9W_taskTrayToggle{min-width:0;color:inherit;cursor:pointer;font:inherit;font-size:inherit;font-weight:inherit;text-align:left;background:0 0;border:0;flex:1;align-items:center;gap:8px;padding:8px 10px;display:flex}.Yvqh9W_taskTrayToggle:hover{background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_taskTrayCount{min-width:18px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);text-align:center;border-radius:999px;padding:1px 5px;font-size:11px;font-weight:500}.Yvqh9W_taskTrayChevron{color:var(--dsw-alias-label-tertiary);margin-left:auto;font-size:12px;font-weight:400}.Yvqh9W_taskTrayClose{border:0;border-left:1px solid var(--dsw-alias-border-l1);width:32px;color:var(--dsw-alias-label-tertiary);cursor:pointer;font:inherit;background:0 0;font-size:17px}.Yvqh9W_taskTrayClose:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_taskTray[data-open=false] .Yvqh9W_taskTrayToggle{min-height:34px}.Yvqh9W_taskRows{min-height:0;overflow-y:auto}.Yvqh9W_taskTray[data-open=false] .Yvqh9W_taskRows{display:none}.Yvqh9W_taskRow{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:7px;padding:7px 10px;display:grid}.Yvqh9W_taskRow:first-of-type{border-top:0}.Yvqh9W_taskStatus{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:11px}.Yvqh9W_taskRow[data-status=running] .Yvqh9W_taskStatus{color:var(--dsw-alias-brand-primary)}.Yvqh9W_taskRow[data-status=failed] .Yvqh9W_taskStatus{color:var(--dsw-alias-label-error)}.Yvqh9W_taskPrompt{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.Yvqh9W_taskRow button{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;font:inherit;border:0;border-radius:5px;padding:2px 7px;font-size:11px}.Yvqh9W_taskRow button:hover{color:var(--dsw-alias-brand-primary)}.Yvqh9W_configGuide{z-index:1100;background:#00000059;place-items:center;padding:20px;display:grid;position:fixed;inset:0}.Yvqh9W_configGuideBody{border:1px solid var(--dsw-alias-border-l2);width:min(360px,100%);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border-radius:10px;flex-direction:column;gap:12px;padding:18px;display:flex;box-shadow:0 14px 40px #0003}.Yvqh9W_configGuideBody span{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55}.Yvqh9W_configGuideBody button{min-height:30px;color:var(--dsw-alias-bg-layer-1);background:var(--dsw-alias-brand-primary);cursor:pointer;font:inherit;border:0;border-radius:7px;align-self:flex-end;padding:0 12px;font-size:12px}.Yvqh9W_history{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:12px;flex-direction:column;flex:none;width:240px;min-width:200px;max-width:280px;min-height:0;display:flex;overflow:hidden}.Yvqh9W_historyHeader{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:10px 12px;display:flex}.Yvqh9W_historyFilters{border-bottom:1px solid var(--dsw-alias-border-l1);grid-template-columns:1fr 1fr;gap:6px;padding:8px 10px;display:grid}.Yvqh9W_historySearch,.Yvqh9W_historyFilters select,.Yvqh9W_gallerySearch{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);min-width:0;min-height:29px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;border-radius:7px;padding:0 8px;font-size:11px}.Yvqh9W_historySearch{grid-column:1/-1}.Yvqh9W_gallerySearch{width:156px}.Yvqh9W_galleryTagInput{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);width:130px;min-height:29px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;border-radius:7px;padding:0 8px;font-size:11px}.Yvqh9W_galleryBulkButton{border:1px solid var(--dsw-alias-border-l2);min-height:29px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;font:inherit;border-radius:7px;padding:0 8px;font-size:11px}.Yvqh9W_galleryBulkButton:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryBulkButton:disabled{opacity:.45;cursor:default}.Yvqh9W_historyTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.Yvqh9W_historyClear{font:inherit;color:var(--dsw-alias-label-tertiary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11.5px}.Yvqh9W_historyClear:hover{color:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}.Yvqh9W_historyList{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex-direction:column;flex:1;gap:8px;min-height:0;padding:10px;display:flex;overflow-y:auto}.Yvqh9W_historyList::-webkit-scrollbar{width:8px}.Yvqh9W_historyList::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_historyEmpty{text-align:center;color:var(--dsw-alias-label-tertiary);flex:1;justify-content:center;align-items:center;padding:20px;font-size:12px;line-height:1.6;display:flex}.Yvqh9W_historyItem{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:10px;flex-direction:column;flex:none;gap:6px;padding:8px;display:flex}.Yvqh9W_historyItem:hover{border-color:var(--dsw-alias-border-l2)}.Yvqh9W_historyItem[data-active]{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_historyMain{font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:flex-start;gap:8px;min-width:0;padding:0;display:flex}.Yvqh9W_historyThumb{object-fit:cover;background:var(--dsw-alias-bg-base);border-radius:8px;flex:none;width:52px;height:52px}.Yvqh9W_historyThumbPlaceholder{background:var(--dsw-alias-bg-layer-3);border-radius:8px;flex:none;width:52px;height:52px}.Yvqh9W_historyInfo{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Yvqh9W_historyPrompt{color:var(--dsw-alias-label-primary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}.Yvqh9W_historyMeta{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.Yvqh9W_historyActions{justify-content:flex-end;gap:6px;display:flex}.Yvqh9W_historyAction{font:inherit;color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11.5px}.Yvqh9W_historyAction:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Yvqh9W_historyAction[data-danger]:hover{color:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}.Yvqh9W_card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;flex:none;gap:10px;padding:12px;display:flex}.Yvqh9W_modeRow{align-items:center;gap:8px;display:flex}.Yvqh9W_modePill{flex:1;justify-content:center;height:28px;font-size:13px}.Yvqh9W_uploadBox{min-height:128px;color:var(--dsw-alias-label-secondary);border:1.5px dashed var(--dsw-alias-border-l2);cursor:pointer;font:inherit;text-align:center;background:0 0;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;gap:6px;padding:16px;font-size:12.5px;display:flex}.Yvqh9W_uploadBox:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-interactive-bg-hover)}.Yvqh9W_uploadBox:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Yvqh9W_uploadIcon{color:var(--dsw-alias-label-tertiary);display:inline-flex}.Yvqh9W_uploadHint{color:var(--dsw-alias-label-tertiary);font-size:11px}.Yvqh9W_reference{flex-direction:column;gap:8px;display:flex}.Yvqh9W_referenceImage{object-fit:contain;background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;width:100%;max-height:176px}.Yvqh9W_referenceActions{gap:8px;display:flex}.Yvqh9W_hiddenFile{display:none}.Yvqh9W_prompt{width:100%;min-height:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);resize:vertical;box-sizing:border-box;border-radius:10px;outline:none;padding:10px 12px;font-family:inherit;font-size:13px;line-height:1.6}.Yvqh9W_prompt:focus-visible{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_prompt::placeholder{color:var(--dsw-alias-label-tertiary)}.Yvqh9W_promptFooter{justify-content:space-between;align-items:center;gap:8px;margin-top:-6px;display:flex}.Yvqh9W_templatesButton{border:1px solid var(--dsw-alias-brand-primary);background:linear-gradient(135deg, color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent), color-mix(in srgb, var(--dsw-alias-brand-primary) 5%, transparent));height:26px;color:var(--dsw-alias-brand-primary);cursor:pointer;box-shadow:0 1px 0 color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent);border-radius:999px;align-items:center;gap:6px;padding:0 12px;font-family:inherit;font-size:12px;font-weight:600;transition:transform .12s,box-shadow .12s,background .12s;display:inline-flex}.Yvqh9W_templatesButton svg{flex:none}.Yvqh9W_templatesButton:hover{background:linear-gradient(135deg, color-mix(in srgb, var(--dsw-alias-brand-primary) 24%, transparent), color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent));color:var(--dsw-alias-brand-primary);box-shadow:0 2px 6px color-mix(in srgb, var(--dsw-alias-brand-primary) 30%, transparent);transform:translateY(-1px)}.Yvqh9W_templatesButton:active{transform:translateY(0)}.Yvqh9W_enhanceButton{border:1px solid var(--dsw-alias-border-l2);height:26px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:999px;margin-left:auto;padding:0 11px;font-size:12px}.Yvqh9W_enhanceButton:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_enhanceButton:disabled{opacity:.5;cursor:default}.Yvqh9W_promptCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px}.Yvqh9W_paramGroup{flex-direction:column;gap:8px;display:flex}.Yvqh9W_paramLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.Yvqh9W_optionRow{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_optionGrid{grid-template-columns:repeat(3,1fr);gap:6px;display:grid}.Yvqh9W_optionPill{justify-content:center}.Yvqh9W_paramHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45}.Yvqh9W_footer{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;flex:none;align-items:stretch;gap:8px;padding:10px 2px 0 0;display:flex}.Yvqh9W_modelWrap{flex-direction:column;gap:5px;min-width:0;display:flex}.Yvqh9W_modelLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.Yvqh9W_modelSelect{width:100%;height:36px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;text-align:left;border-radius:18px;outline:none;justify-content:space-between;align-items:center;gap:8px;padding:0 12px;font-family:inherit;font-size:13px;display:flex}.Yvqh9W_modelSelect:focus-visible{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_modelSelect:disabled{opacity:.55;cursor:default}.Yvqh9W_modelMenu{min-width:0;display:block;position:relative}.Yvqh9W_modelMenuList{z-index:40;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;padding:4px;display:flex;position:absolute;bottom:calc(100% + 6px);left:0;right:0;overflow:hidden;box-shadow:0 -8px 24px #0000002e}.Yvqh9W_modelMenuItem{width:100%;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;white-space:nowrap;text-overflow:ellipsis;background:0 0;border:none;border-radius:8px;padding:7px 10px;font-size:13px;display:block;overflow:hidden}.Yvqh9W_modelMenuItem:hover{background:var(--dsw-alias-bg-hover)}.Yvqh9W_modelMenuItem[data-selected]{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-1);font-weight:600}.Yvqh9W_generateButton{width:100%}.Yvqh9W_generateInner{align-items:center;gap:7px;display:inline-flex}.Yvqh9W_canvasState{text-align:center;color:var(--dsw-alias-label-tertiary);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px;display:flex}.Yvqh9W_canvasStateTitle{color:var(--dsw-alias-label-secondary);font-size:14px;font-weight:600}.Yvqh9W_canvasStateHint{max-width:380px;font-size:12px;line-height:1.6}.Yvqh9W_canvasEmptyIcon{color:var(--dsw-alias-label-tertiary);margin-bottom:4px;display:inline-flex}.Yvqh9W_canvasError{color:var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-label-error);overflow-wrap:anywhere;border-radius:10px;flex:none;margin:14px;padding:10px 14px;font-size:12.5px;line-height:1.6}.Yvqh9W_canvasBody{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex-direction:column;flex:1;gap:10px;min-height:0;padding:14px;display:flex;overflow-y:auto}.Yvqh9W_canvasBody::-webkit-scrollbar{width:8px}.Yvqh9W_canvasBody::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_canvasMeta{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:8px;font-size:12px;display:flex}.Yvqh9W_canvasHistoryTag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px}.Yvqh9W_grid{flex:1;grid-template-rows:repeat(2,minmax(0,1fr));grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;min-height:0;display:grid}.Yvqh9W_grid[data-count=\"1\"] .Yvqh9W_imageCard{grid-area:1/1/3/3}.Yvqh9W_imageCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);cursor:zoom-in;border-radius:12px;flex-direction:column;min-height:0;margin:0;display:flex;position:relative;overflow:hidden}.Yvqh9W_imageCard:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Yvqh9W_image{object-fit:cover;background:var(--dsw-alias-bg-base);flex:1;width:100%;min-height:0;display:block}.Yvqh9W_imageCaption{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;border-top:1px solid var(--dsw-alias-border-l1);padding:7px 10px;font-size:11px;line-height:1.5;overflow:hidden}.Yvqh9W_download{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-mask-1);border:1px solid var(--dsw-alias-border-l2);opacity:0;backdrop-filter:blur(4px);border-radius:999px;padding:2px 10px;font-size:12px;font-weight:500;line-height:20px;text-decoration:none;transition:opacity .12s;position:absolute;top:8px;right:8px}.Yvqh9W_imageCard:hover .Yvqh9W_download{opacity:1}.Yvqh9W_download:hover{background:var(--dsw-alias-bg-base)}.Yvqh9W_galleryAdd{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-mask-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;opacity:0;backdrop-filter:blur(4px);border-radius:999px;align-items:center;gap:5px;padding:2px 10px;font-size:12px;font-weight:500;line-height:20px;transition:opacity .12s;display:inline-flex;position:absolute;top:8px;left:8px}.Yvqh9W_imageCard:hover .Yvqh9W_galleryAdd{opacity:1}.Yvqh9W_galleryAdd:hover{background:var(--dsw-alias-bg-base)}.Yvqh9W_galleryAdd:disabled{opacity:.4;cursor:default}.Yvqh9W_zoomHint{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-mask-1);border:1px solid var(--dsw-alias-border-l2);opacity:0;backdrop-filter:blur(4px);pointer-events:none;border-radius:999px;align-items:center;gap:5px;padding:2px 10px;font-size:12px;font-weight:500;line-height:20px;transition:opacity .12s;display:inline-flex;position:absolute;bottom:8px;left:8px}.Yvqh9W_imageCard:hover .Yvqh9W_zoomHint{opacity:1}.Yvqh9W_spinner,.Yvqh9W_bigSpinner{border:2px solid;border-top-color:#0000;border-radius:50%;flex:none;animation:.8s linear infinite Yvqh9W_dshImageGenSpin;display:inline-block}.Yvqh9W_spinner{width:11px;height:11px}.Yvqh9W_bigSpinner{width:30px;height:30px;color:var(--dsw-alias-state-business-primary);border-width:3px;margin-bottom:6px}.Yvqh9W_lightbox{z-index:1000;backdrop-filter:blur(6px);background:#000000b8;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}.Yvqh9W_lightboxClose{color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;border-radius:50%;justify-content:center;align-items:center;width:38px;height:38px;display:inline-flex;position:absolute;top:16px;right:16px}.Yvqh9W_lightboxClose:hover{background:#ffffff42}.Yvqh9W_lightboxNav{color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;border-radius:50%;justify-content:center;align-items:center;width:42px;height:42px;display:inline-flex;position:absolute;top:50%;transform:translateY(-50%)}.Yvqh9W_lightboxNav:hover{background:#ffffff42}.Yvqh9W_lightboxNav[data-dir=prev]{left:max(20px,50% - 640px)}.Yvqh9W_lightboxNav[data-dir=next]{right:max(20px,50% - 640px)}.Yvqh9W_lightboxFigure{flex-direction:column;gap:10px;width:min(1100px,100vw - 160px);max-width:min(1100px,100vw - 160px);height:min(820px,100vh - 48px);min-height:0;margin:0;display:flex}.Yvqh9W_lightboxStage{background:#ffffff0a;border-radius:10px;flex:1;min-height:0;position:relative;overflow:auto}.Yvqh9W_lightboxScaleFrame{justify-content:center;align-items:center;min-width:100%;min-height:100%;display:flex}.Yvqh9W_lightboxImage{object-fit:contain;border-radius:10px;max-width:100%;max-height:100%;display:block;box-shadow:0 24px 80px #00000080}.Yvqh9W_lightboxTools{justify-content:center;align-items:center;gap:6px;display:flex}.Yvqh9W_lightboxTool,.Yvqh9W_lightboxZoomLevel,.Yvqh9W_lightboxCopy{color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;justify-content:center;align-items:center;display:inline-flex}.Yvqh9W_lightboxTool,.Yvqh9W_lightboxZoomLevel{height:32px}.Yvqh9W_lightboxTool{border-radius:50%;width:32px}.Yvqh9W_lightboxZoomLevel{min-width:58px;font:inherit;font-variant-numeric:tabular-nums;border-radius:999px;padding:0 9px;font-size:12px}.Yvqh9W_lightboxTool:hover,.Yvqh9W_lightboxZoomLevel:hover,.Yvqh9W_lightboxCopy:hover{background:#ffffff42}.Yvqh9W_lightboxCaptionRow{align-items:flex-start;gap:8px;min-width:0;display:flex}.Yvqh9W_lightboxCaption{color:#ffffffe6;-webkit-line-clamp:3;-webkit-box-orient:vertical;flex:1;min-width:0;font-size:12px;line-height:1.6;display:-webkit-box;overflow:hidden}.Yvqh9W_lightboxCopy{min-height:28px;font:inherit;white-space:nowrap;border-radius:999px;flex:none;gap:5px;padding:4px 9px;font-size:12px}.Yvqh9W_lightboxMeta{justify-content:space-between;align-items:center;gap:12px;display:flex}.Yvqh9W_lightboxIndex{color:#fffc;font-variant-numeric:tabular-nums;font-size:12px}.Yvqh9W_lightboxActions{align-items:center;gap:8px;display:inline-flex}.Yvqh9W_lightboxDownload,.Yvqh9W_lightboxEdit{font:inherit;color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;border-radius:999px;padding:4px 14px;font-size:12.5px;font-weight:500;text-decoration:none}.Yvqh9W_lightboxDownload:hover,.Yvqh9W_lightboxEdit:hover{background:#ffffff42}.Yvqh9W_lightboxEdit{color:#fff;background:#ffffff24;border:1px solid #ffffff47;border-radius:999px}@media (width<=720px){.Yvqh9W_lightbox{padding:16px}.Yvqh9W_lightboxFigure{width:calc(100vw - 32px);max-width:none}.Yvqh9W_lightboxNav[data-dir=prev]{left:20px}.Yvqh9W_lightboxNav[data-dir=next]{right:20px}.Yvqh9W_lightboxCaptionRow,.Yvqh9W_lightboxMeta{flex-direction:column;align-items:stretch}.Yvqh9W_lightboxCopy,.Yvqh9W_lightboxActions{align-self:flex-end}}@keyframes Yvqh9W_dshImageGenSpin{to{transform:rotate(360deg)}}.Yvqh9W_galleryToast{z-index:30;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-mask-1);border:1px solid var(--dsw-alias-border-l2);backdrop-filter:blur(6px);pointer-events:none;border-radius:999px;align-items:center;gap:7px;padding:6px 16px;font-size:13px;font-weight:500;animation:.16s ease-out Yvqh9W_dshImageGenToastIn;display:inline-flex;position:absolute;bottom:24px;left:50%;transform:translate(-50%);box-shadow:0 8px 24px #00000038}@keyframes Yvqh9W_dshImageGenToastIn{0%{opacity:0;transform:translate(-50%,6px)}to{opacity:1;transform:translate(-50%)}}@media (prefers-reduced-motion:reduce){.Yvqh9W_download,.Yvqh9W_spinner,.Yvqh9W_bigSpinner{transition:none;animation-duration:1.5s}}.Yvqh9W_config[data-gallery=true] .Yvqh9W_configScroll>:not(:first-child),.Yvqh9W_config[data-gallery=true] .Yvqh9W_footer,.Yvqh9W_canvas[data-gallery=true]>.Yvqh9W_canvasState,.Yvqh9W_canvas[data-gallery=true]>.Yvqh9W_canvasError,.Yvqh9W_canvas[data-gallery=true]>.Yvqh9W_canvasBody,.Yvqh9W_studio:has(.Yvqh9W_config[data-gallery=true])>.Yvqh9W_history{display:none}.Yvqh9W_config[data-gallery=true] .Yvqh9W_configScroll{flex:none;order:1;display:flex;overflow:visible}.Yvqh9W_config[data-gallery=true] .Yvqh9W_galleryFilters{flex:1;order:2;min-height:0}.Yvqh9W_galleryFilters{padding:18px 14px;overflow:hidden auto}.Yvqh9W_galleryFilterHeading{color:var(--dsw-alias-label-tertiary);margin:0 4px 10px;font-size:12px;font-weight:600}.Yvqh9W_galleryFilter{width:100%;min-height:34px;color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:left;background:0 0;border:0;border-radius:9px;justify-content:space-between;align-items:center;padding:0 10px;display:flex}.Yvqh9W_galleryFilter:hover,.Yvqh9W_galleryFilter[data-active]{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent)}.Yvqh9W_galleryFilterCount{min-width:20px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);text-align:center;border-radius:999px;padding:1px 6px;font-size:11px}.Yvqh9W_galleryFilterDivider{background:var(--dsw-alias-border-l1);height:1px;margin:18px 4px}.Yvqh9W_galleryRatioList{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_galleryRatio{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border:0;border-radius:999px;padding:6px 10px;font-size:12px}.Yvqh9W_galleryRatio[data-active]{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 13%, transparent)}.Yvqh9W_galleryTagFilterList{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_galleryTagFilter{border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:100%;min-height:27px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;font:inherit;border-radius:6px;align-items:center;gap:5px;padding:0 8px;font-size:11px;display:inline-flex}.Yvqh9W_galleryTagFilter span:first-child{text-overflow:ellipsis;white-space:nowrap;max-width:112px;overflow:hidden}.Yvqh9W_galleryTagFilter span:last-child{color:var(--dsw-alias-label-tertiary);font-size:10px}.Yvqh9W_galleryTagFilter:hover,.Yvqh9W_galleryTagFilter[data-active]{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent)}.Yvqh9W_galleryFilterNote{color:var(--dsw-alias-label-quaternary);margin:20px 4px 0;font-size:11px;line-height:1.5}.Yvqh9W_galleryWorkspace{box-sizing:border-box;flex-direction:column;width:100%;min-width:0;height:100%;min-height:0;padding:22px 24px 26px;display:flex;position:absolute;inset:0;overflow:hidden}.Yvqh9W_galleryToolbar{flex:none;justify-content:space-between;align-items:center;gap:16px;min-width:0;margin-bottom:18px;display:flex}.Yvqh9W_galleryHeading{color:var(--dsw-alias-label-primary);margin:0;font-size:20px;font-weight:700;display:inline}.Yvqh9W_galleryCount{color:var(--dsw-alias-label-tertiary);margin-left:8px;font-size:13px}.Yvqh9W_galleryToolbarActions{flex-wrap:wrap;align-items:center;gap:10px;min-width:0;display:flex}.Yvqh9W_gallerySelectMode,.Yvqh9W_galleryBulkButton,.Yvqh9W_gallerySelectionClear{border:1px solid var(--dsw-alias-border-l1);min-height:30px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;font:inherit;border-radius:7px;padding:0 10px;font-size:12px}.Yvqh9W_gallerySelectMode:hover,.Yvqh9W_gallerySelectMode[data-active],.Yvqh9W_galleryBulkButton:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 38%, var(--dsw-alias-border-l1));background:color-mix(in srgb, var(--dsw-alias-brand-primary) 11%, transparent)}.Yvqh9W_galleryBulkButton:disabled{cursor:not-allowed;opacity:.45}.Yvqh9W_gallerySelectionBar{border:1px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 35%, var(--dsw-alias-border-l1));background:color-mix(in srgb, var(--dsw-alias-brand-primary) 7%, var(--dsw-alias-bg-layer-1));border-radius:9px;flex:none;align-items:center;gap:10px;min-width:0;margin:-4px 0 16px;padding:10px 12px;display:flex}.Yvqh9W_gallerySelectionBar strong{color:var(--dsw-alias-brand-primary);flex:none;font-size:12px}.Yvqh9W_gallerySelectionClear{margin-left:auto}.Yvqh9W_gallerySelectionClear:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_galleryTagInput,.Yvqh9W_gallerySearch{border:1px solid var(--dsw-alias-border-l1);min-width:0;height:30px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);font:inherit;border-radius:7px;outline:none;padding:0 10px;font-size:12px}.Yvqh9W_galleryTagInput{flex:190px}.Yvqh9W_galleryTagInput:focus,.Yvqh9W_gallerySearch:focus{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryViewToggle{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:9px;padding:3px;display:flex}.Yvqh9W_galleryViewToggle button,.Yvqh9W_gallerySort,.Yvqh9W_galleryClear{min-height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:0;border-radius:7px;padding:0 10px;font-size:12px}.Yvqh9W_galleryViewToggle button[data-active],.Yvqh9W_galleryViewToggle button:hover,.Yvqh9W_gallerySort:hover,.Yvqh9W_galleryClear:hover{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 11%, transparent)}.Yvqh9W_gallerySort{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1)}.Yvqh9W_galleryClear{border:1px solid var(--dsw-alias-border-l1)}.Yvqh9W_compareControl{flex-direction:column;gap:6px;margin:0 0 10px;display:flex}.Yvqh9W_compareToggle,.Yvqh9W_compareModelChoices label{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}.Yvqh9W_compareToggle input,.Yvqh9W_compareModelChoices input{accent-color:var(--dsw-alias-brand-primary)}.Yvqh9W_compareModelChoices{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_compareModelChoices label{border:1px solid var(--dsw-alias-border-l1);border-radius:5px;padding:4px 6px;font-size:10px}.Yvqh9W_comparisonBoard{z-index:4;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;display:flex;position:absolute;inset:126px 14px 14px;overflow:hidden;box-shadow:0 8px 24px #0000001f}.Yvqh9W_comparisonBoard>header{border-bottom:1px solid var(--dsw-alias-border-l1);justify-content:space-between;align-items:center;padding:10px 12px;display:flex}.Yvqh9W_comparisonBoard>header div{align-items:baseline;gap:7px;display:flex}.Yvqh9W_comparisonBoard>header strong{color:var(--dsw-alias-label-primary);font-size:13px}.Yvqh9W_comparisonBoard>header span{color:var(--dsw-alias-label-tertiary);font-size:11px}.Yvqh9W_comparisonBoard>header button{border:1px solid var(--dsw-alias-border-l1);min-height:28px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;font:inherit;border-radius:5px;padding:0 9px;font-size:11px}.Yvqh9W_comparisonGrid{flex:1;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;padding:12px;display:grid;overflow:auto}.Yvqh9W_comparisonGrid article{flex-direction:column;gap:7px;min-width:0;display:flex}.Yvqh9W_comparisonGrid article>strong{color:var(--dsw-alias-label-primary);font-size:12px}.Yvqh9W_comparisonGrid article>span{min-height:160px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);place-items:center;font-size:12px;display:grid}.Yvqh9W_comparisonGrid img{object-fit:contain;background:var(--dsw-alias-bg-base);width:100%;min-height:160px;max-height:calc(100vh - 260px);display:block}.Yvqh9W_comparisonFullscreen{z-index:1200;background:#000000eb;padding:54px 24px 24px;position:fixed;inset:0;overflow:auto}.Yvqh9W_comparisonFullscreenGrid{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));align-items:start;gap:18px;min-height:100%;display:grid}.Yvqh9W_comparisonFullscreen figure{min-width:0;margin:0}.Yvqh9W_comparisonFullscreen figcaption{color:#fff;margin-bottom:8px;font-size:13px;font-weight:600}.Yvqh9W_comparisonFullscreen img{background:#111;width:100%;margin-bottom:10px;display:block}.Yvqh9W_galleryMasonry{box-sizing:border-box;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex:auto;grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:max-content;align-content:start;gap:16px;width:100%;min-width:0;max-width:100%;height:0;min-height:0;padding:2px 8px 16px 2px;display:grid;overflow:hidden scroll}.Yvqh9W_galleryMasonry::-webkit-scrollbar{width:8px}.Yvqh9W_galleryMasonry::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_galleryCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:14px;width:100%;margin:0;display:block;position:relative;overflow:hidden;box-shadow:0 5px 18px #18203612}.Yvqh9W_galleryCard[data-selected]{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent), 0 5px 18px #18203612}.Yvqh9W_gallerySelect{z-index:2;cursor:pointer;background:#00000094;border:1px solid #ffffffbf;border-radius:7px;place-items:center;width:26px;height:26px;display:grid;position:absolute;top:9px;right:9px}.Yvqh9W_gallerySelect input{width:16px;height:16px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer;margin:0}.Yvqh9W_galleryImageButton{background:var(--dsw-alias-bg-base);cursor:zoom-in;border:0;width:100%;padding:0;display:block;position:relative}.Yvqh9W_galleryImageButton[data-selecting]{cursor:pointer}.Yvqh9W_galleryImage{aspect-ratio:4/3;object-fit:cover;width:100%;display:block}.Yvqh9W_galleryMasonry[data-view=masonry] .Yvqh9W_galleryCard:nth-child(3n+1) .Yvqh9W_galleryImage{aspect-ratio:4/5}.Yvqh9W_galleryMasonry[data-view=masonry] .Yvqh9W_galleryCard:nth-child(3n+2) .Yvqh9W_galleryImage{aspect-ratio:4/3}.Yvqh9W_galleryMasonry[data-view=masonry] .Yvqh9W_galleryCard:nth-child(3n) .Yvqh9W_galleryImage{aspect-ratio:3/4}.Yvqh9W_galleryBadge{color:#fff;backdrop-filter:blur(4px);background:#121724c2;border-radius:999px;padding:4px 9px;font-size:11px;position:absolute;top:10px;left:10px}.Yvqh9W_galleryCardFooter{align-items:center;gap:9px;min-width:0;padding:10px 12px;display:flex}.Yvqh9W_galleryAvatar{color:#fff;background:var(--dsw-alias-brand-primary);border-radius:50%;flex:none;place-items:center;width:27px;height:27px;font-size:12px;font-weight:700;display:grid}.Yvqh9W_galleryCardInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.Yvqh9W_galleryCardInfo strong,.Yvqh9W_galleryCardInfo small{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.Yvqh9W_galleryCardInfo strong{color:var(--dsw-alias-label-primary);font-size:12px}.Yvqh9W_galleryCardInfo small{color:var(--dsw-alias-label-tertiary);font-size:10px}.Yvqh9W_galleryTags{flex-wrap:wrap;gap:4px;margin-top:4px;display:flex}.Yvqh9W_galleryTags button{max-width:96px;min-height:19px;color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);cursor:pointer;font:inherit;text-overflow:ellipsis;white-space:nowrap;border:0;border-radius:4px;padding:1px 6px;font-size:10px;line-height:1.35;overflow:hidden}.Yvqh9W_galleryTags button:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 17%, transparent)}.Yvqh9W_galleryTags .Yvqh9W_galleryTagEdit{color:var(--dsw-alias-label-tertiary);background:0 0;flex:none}.Yvqh9W_galleryTagEditor{align-items:center;gap:6px;padding:0 12px 10px 48px;display:flex}.Yvqh9W_galleryTagEditor input{border:1px solid var(--dsw-alias-border-l2);min-width:0;height:27px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;border-radius:6px;outline:none;flex:1;padding:0 8px;font-size:11px}.Yvqh9W_galleryTagEditor input:focus{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryTagEditor button{border:1px solid var(--dsw-alias-border-l2);height:27px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;font:inherit;border-radius:6px;padding:0 8px;font-size:11px}.Yvqh9W_galleryTagEditor button[type=submit]{color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryRemove{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:50%;flex:none;padding:0;font-size:18px}.Yvqh9W_galleryRemove:hover{color:var(--dsw-alias-state-error);background:var(--dsw-alias-bg-layer-2)}@media (width<=1100px){.Yvqh9W_galleryMasonry{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=760px){.Yvqh9W_studio{flex-direction:column;overflow:auto}.Yvqh9W_config{width:auto;max-width:none;height:auto;min-height:0}.Yvqh9W_config[data-gallery=true]{flex:none}.Yvqh9W_canvas{min-height:560px}.Yvqh9W_galleryToolbar{flex-direction:column;align-items:flex-start}.Yvqh9W_galleryToolbarActions{flex-wrap:wrap;width:100%}.Yvqh9W_galleryMasonry{grid-template-columns:1fr}}";
1943
+ const css$2 = "[data-pane=conversation],[class*=centerCol]{min-width:0;min-height:0;position:relative}[data-dsh-imagegen-view]{z-index:60;background:var(--dsw-alias-bg-base);grid-area:1/1;min-width:0;min-height:0;display:none;position:relative}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane=conversation],html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*=centerCol]{grid-template-columns:minmax(0, 1fr) 8px minmax(320px, clamp(320px, var(--dsh-imagegen-chat-width,36%), 70%));background:var(--dsw-alias-border-l1);grid-template-rows:minmax(0,1fr);gap:1px;overflow:hidden;display:grid!important}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-dsh-imagegen-view]{display:block}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-slot=conversation]{background:var(--dsw-alias-bg-base);grid-area:1/3;align-items:stretch;width:auto;min-width:0;min-height:0;overflow:hidden;display:flex!important}html[data-dsh-imagegen-active] [data-slot=conversation]>[data-phase]{flex:auto;align-self:stretch;width:100%;min-width:0;max-width:none}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-dsh-imagegen-chat-resizer]{grid-area:1/2}.Yvqh9W_chatResizer{z-index:70;width:8px;min-width:8px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-border-l1);cursor:col-resize;touch-action:none;justify-content:center;align-items:center;display:none;position:relative}.Yvqh9W_chatResizer:before{content:\"\";opacity:.55;background:currentColor;border-radius:3px;width:3px;height:36px}.Yvqh9W_chatResizer:hover,.Yvqh9W_chatResizer:focus-visible{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2);outline:none}html[data-dsh-imagegen-active] [data-dsh-imagegen-chat-resizer]{display:flex!important}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane=conversation]>:not([data-dsh-imagegen-view]):not([data-slot=conversation]):not([data-dsh-imagegen-chat-resizer]),html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*=centerCol]>:not([data-dsh-imagegen-view]):not([data-slot=conversation]):not([data-dsh-imagegen-chat-resizer]),html[data-dsh-imagegen-active] [data-dsh-imagegen-sidebar-root]>:not([data-dsh-imagegen-session-tabs]):not([class*=logoRow]):not([class*=regionArea]):not([class*=footArea]){display:none!important}@media (width<=760px){html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane=conversation],html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*=centerCol]{grid-template-rows:minmax(0,1fr) minmax(340px,.9fr);grid-template-columns:minmax(0,1fr);overflow-y:auto}html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-slot=conversation]{grid-area:2/1}html[data-dsh-imagegen-active] [data-dsh-imagegen-chat-resizer]{display:none!important}}.Yvqh9W_entry{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:0 12px;font-size:13px;display:flex}.Yvqh9W_entry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}.Yvqh9W_entry[data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}.Yvqh9W_entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.Yvqh9W_entryLabel{text-overflow:ellipsis;overflow:hidden}.Yvqh9W_sessionTabs{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:9px;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:2px;width:100%;min-width:0;padding:3px;display:grid}.Yvqh9W_sessionTab{min-width:0;height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;white-space:nowrap;background:0 0;border:0;border-radius:7px;justify-content:center;align-items:center;gap:6px;padding:0 8px;font-size:12px;display:inline-flex}[data-sidebar-collapsed] .Yvqh9W_sessionTabs{background:0 0;border:none;border-radius:0;flex-direction:column;gap:6px;width:auto;padding:0;display:flex}[data-sidebar-collapsed] .Yvqh9W_sessionTab{border-radius:9px;width:34px;height:34px;padding:0;font-size:0}[data-sidebar-collapsed] .Yvqh9W_sessionTabLabel{display:none}.Yvqh9W_sessionTab:hover,.Yvqh9W_sessionTab[data-active]{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-sidebar-nav-item-active)}.Yvqh9W_sessionTabIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.Yvqh9W_sessionTabLabel{text-overflow:ellipsis;min-width:0;overflow:hidden}[class*=regionArea]{min-height:0;position:relative}.Yvqh9W_sidebarHistoryHost{z-index:30;background:var(--dsw-alias-bg-base);min-width:0;min-height:0;padding:6px 4px;display:none;position:absolute;inset:0;overflow:hidden}html[data-dsh-imagegen-active] .Yvqh9W_sidebarHistoryHost{display:flex}.Yvqh9W_sidebarHistoryHost .Yvqh9W_history{background:0 0;border:0;border-radius:8px;width:100%;height:100%}[data-dsh-frame][data-sidebar-collapsed] .Yvqh9W_sidebarHistoryHost{display:none}[data-dsh-frame][data-sidebar-collapsed] .Yvqh9W_entry{justify-content:center;width:100%;padding:0}[data-dsh-frame][data-sidebar-collapsed] .Yvqh9W_entryLabel{display:none}.Yvqh9W_view{width:100%;min-width:0;min-height:0;overflow:hidden}.Yvqh9W_panel,.Yvqh9W_panel *,.Yvqh9W_panel :before,.Yvqh9W_panel :after{box-sizing:border-box}.Yvqh9W_panel{background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:10px;padding:14px 16px 16px;display:flex;position:relative;overflow:hidden}.Yvqh9W_panelHeader{flex:none;justify-content:space-between;align-items:center;gap:12px;display:flex;position:relative}.Yvqh9W_panelHeading{align-items:baseline;gap:10px;min-width:0;display:flex}.Yvqh9W_panelTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:16px;font-weight:700}.Yvqh9W_githubLink{width:22px;height:22px;color:var(--dsw-alias-label-secondary);border-radius:6px;flex:none;justify-content:center;align-items:center;text-decoration:none;transition:color .12s,background .12s;display:inline-flex}.Yvqh9W_githubLink:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_connectionStatus{border:1px solid var(--dsw-alias-label-error);height:28px;color:var(--dsw-alias-label-error);font:inherit;white-space:nowrap;background:0 0;border-radius:8px;flex:none;align-items:center;gap:6px;padding:0 10px;font-size:12px;line-height:1;display:inline-flex}.Yvqh9W_connectionStatus[data-connected=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.Yvqh9W_connectionDot{background:currentColor;border-radius:50%;width:6px;height:6px}.Yvqh9W_updateBanner{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary);overflow-wrap:anywhere;border-radius:10px;flex:none;justify-content:space-between;align-items:center;gap:12px;padding:7px 10px 7px 12px;font-size:12px;line-height:1.5;display:flex}.Yvqh9W_updateBanner[data-kind=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.Yvqh9W_updateText{min-width:0}.Yvqh9W_updateActions{flex:none;align-items:center;gap:10px;display:inline-flex}.Yvqh9W_updateRelease{color:inherit;text-underline-offset:2px;white-space:nowrap;text-decoration:underline}@media (width<=700px){.Yvqh9W_panelHeader{align-items:flex-start}.Yvqh9W_panelHeading{flex-direction:column;align-items:flex-start;gap:2px}.Yvqh9W_updateBanner{flex-direction:column;align-items:flex-start}.Yvqh9W_updateActions{justify-content:space-between;width:100%}}.Yvqh9W_studio{flex:1;min-width:0;min-height:0;display:flex}.Yvqh9W_generation{gap:14px;width:100%;min-width:0;min-height:0;display:flex}.Yvqh9W_config{width:clamp(240px, var(--dsh-imagegen-config-width,300px), 480px);flex-direction:column;flex:none;gap:12px;min-width:240px;max-width:480px;height:100%;min-height:0;display:flex;position:relative;overflow:hidden}.Yvqh9W_configHeader{flex:none;justify-content:flex-end;min-height:28px;display:flex}.Yvqh9W_configToggle{border:1px solid var(--dsw-alias-border-l1);width:28px;height:28px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;border-radius:7px;justify-content:center;align-items:center;padding:0;display:inline-flex}.Yvqh9W_configToggle:hover,.Yvqh9W_configToggle:focus-visible{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary);outline:none}.Yvqh9W_config[data-collapsed=true]{gap:0;width:38px;min-width:38px;max-width:38px}.Yvqh9W_config[data-collapsed=true] .Yvqh9W_configHeader{justify-content:center}.Yvqh9W_config[data-collapsed=true]>:not(.Yvqh9W_configHeader){display:none}.Yvqh9W_configScroll{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex-direction:column;flex:1;gap:12px;min-height:0;padding-right:2px;display:flex;overflow-y:auto}.Yvqh9W_configScroll::-webkit-scrollbar{width:8px}.Yvqh9W_configScroll::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_canvas{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:12px;flex-direction:column;flex:1;min-width:0;min-height:0;display:flex;position:relative;overflow:hidden}.Yvqh9W_taskTray{z-index:6;border:1px solid var(--dsw-alias-border-l2);background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) 92%, transparent);backdrop-filter:blur(10px);border-radius:9px;flex-direction:column;width:min(360px,100% - 24px);max-height:calc(100% - 24px);display:flex;position:absolute;top:12px;right:12px;overflow:hidden;box-shadow:0 8px 24px #0000001f}.Yvqh9W_taskTray[data-open=false]{width:auto;max-width:calc(100% - 24px)}.Yvqh9W_taskTrayHeader{min-height:34px;color:var(--dsw-alias-label-primary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:stretch;font-size:12px;font-weight:600;display:flex}.Yvqh9W_taskTray[data-open=false] .Yvqh9W_taskTrayHeader{border-bottom:0}.Yvqh9W_taskTrayToggle{min-width:0;color:inherit;cursor:pointer;font:inherit;font-size:inherit;font-weight:inherit;text-align:left;background:0 0;border:0;flex:1;align-items:center;gap:8px;padding:8px 10px;display:flex}.Yvqh9W_taskTrayToggle:hover{background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_taskTrayCount{min-width:18px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);text-align:center;border-radius:999px;padding:1px 5px;font-size:11px;font-weight:500}.Yvqh9W_taskTrayChevron{color:var(--dsw-alias-label-tertiary);margin-left:auto;font-size:12px;font-weight:400}.Yvqh9W_taskTrayClose{border:0;border-left:1px solid var(--dsw-alias-border-l1);width:32px;color:var(--dsw-alias-label-tertiary);cursor:pointer;font:inherit;background:0 0;font-size:17px}.Yvqh9W_taskTrayClose:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_taskTray[data-open=false] .Yvqh9W_taskTrayToggle{min-height:34px}.Yvqh9W_taskRows{min-height:0;overflow-y:auto}.Yvqh9W_taskTray[data-open=false] .Yvqh9W_taskRows{display:none}.Yvqh9W_taskRow{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:7px;padding:7px 10px;display:grid}.Yvqh9W_taskRow:first-of-type{border-top:0}.Yvqh9W_taskStatus{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:11px}.Yvqh9W_taskRow[data-status=running] .Yvqh9W_taskStatus{color:var(--dsw-alias-brand-primary)}.Yvqh9W_taskRow[data-status=failed] .Yvqh9W_taskStatus{color:var(--dsw-alias-label-error)}.Yvqh9W_taskPrompt{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.Yvqh9W_taskRow button{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;font:inherit;border:0;border-radius:5px;padding:2px 7px;font-size:11px}.Yvqh9W_taskRow button:hover{color:var(--dsw-alias-brand-primary)}.Yvqh9W_configGuide{z-index:1100;background:#00000059;place-items:center;padding:20px;display:grid;position:fixed;inset:0}.Yvqh9W_configGuideBody{border:1px solid var(--dsw-alias-border-l2);width:min(360px,100%);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border-radius:10px;flex-direction:column;gap:12px;padding:18px;display:flex;box-shadow:0 14px 40px #0003}.Yvqh9W_configGuideBody span{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55}.Yvqh9W_configGuideBody button{min-height:30px;color:var(--dsw-alias-bg-layer-1);background:var(--dsw-alias-brand-primary);cursor:pointer;font:inherit;border:0;border-radius:7px;align-self:flex-end;padding:0 12px;font-size:12px}.Yvqh9W_history{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:12px;flex-direction:column;width:auto;min-width:0;max-width:none;min-height:0;display:flex;overflow:hidden}.Yvqh9W_historyHeader{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;justify-content:space-between;align-items:center;gap:8px;padding:10px 12px;display:flex}.Yvqh9W_historyHeaderActions{flex:none;align-items:center;gap:6px;display:flex}.Yvqh9W_historyFilters{border-bottom:1px solid var(--dsw-alias-border-l1);grid-template-columns:1fr 1fr;gap:6px;padding:8px 10px;display:grid}.Yvqh9W_historySearch,.Yvqh9W_historyFilters select,.Yvqh9W_gallerySearch{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);min-width:0;min-height:29px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;border-radius:7px;padding:0 8px;font-size:11px}.Yvqh9W_historySearch{grid-column:1/-1}.Yvqh9W_gallerySearch{width:156px}.Yvqh9W_galleryTagInput{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);width:130px;min-height:29px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;border-radius:7px;padding:0 8px;font-size:11px}.Yvqh9W_galleryBulkButton{border:1px solid var(--dsw-alias-border-l2);min-height:29px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;font:inherit;border-radius:7px;padding:0 8px;font-size:11px}.Yvqh9W_galleryBulkButton:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryBulkButton:disabled{opacity:.45;cursor:default}.Yvqh9W_historyTitle{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.Yvqh9W_historyNew{width:26px;height:26px;color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:7px;justify-content:center;align-items:center;padding:0;display:inline-flex}.Yvqh9W_historyNew:hover{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_historyClear{font:inherit;color:var(--dsw-alias-label-tertiary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11.5px}.Yvqh9W_historyClear:hover{color:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}.Yvqh9W_historyList{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex-direction:column;flex:1;gap:8px;min-height:0;padding:10px;display:flex;overflow-y:auto}.Yvqh9W_historyList::-webkit-scrollbar{width:8px}.Yvqh9W_historyList::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_historyEmpty{text-align:center;color:var(--dsw-alias-label-tertiary);flex:1;justify-content:center;align-items:center;padding:20px;font-size:12px;line-height:1.6;display:flex}.Yvqh9W_historyItem{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:10px;flex-direction:column;flex:none;gap:6px;padding:8px;display:flex}.Yvqh9W_historyItem:hover{border-color:var(--dsw-alias-border-l2)}.Yvqh9W_historyItem[data-active]{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_historyMain{font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:flex-start;gap:8px;min-width:0;padding:0;display:flex}.Yvqh9W_historyThumb{object-fit:cover;background:var(--dsw-alias-bg-base);border-radius:8px;flex:none;width:52px;height:52px}.Yvqh9W_historyThumbPlaceholder{background:var(--dsw-alias-bg-layer-3);border-radius:8px;flex:none;width:52px;height:52px}.Yvqh9W_historyInfo{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Yvqh9W_historyPrompt{color:var(--dsw-alias-label-primary);-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;line-height:1.4;display:-webkit-box;overflow:hidden}.Yvqh9W_historyMeta{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.Yvqh9W_historyActions{justify-content:flex-end;gap:6px;display:flex}.Yvqh9W_historyAction{font:inherit;color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11.5px}.Yvqh9W_historyAction:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Yvqh9W_historyAction[data-danger]:hover{color:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}.Yvqh9W_card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;flex:none;gap:10px;padding:12px;display:flex}.Yvqh9W_ecommerceWorkspace{flex-direction:column;gap:16px;max-width:720px;margin:0 auto;padding:6px 2px 12px;display:flex}.Yvqh9W_ecommerceSection{background:0 0;border:none;border-radius:0;flex-direction:column;gap:8px;padding:0;display:flex}.Yvqh9W_ecommerceSection h3{align-items:baseline;gap:6px;margin:0;font-size:13.5px;font-weight:600;display:flex}.Yvqh9W_ecommerceSectionHint{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:400}.Yvqh9W_ecommerceSection input:not([type=checkbox]),.Yvqh9W_ecommerceSection select,.Yvqh9W_ecommerceSection textarea{box-sizing:border-box;width:100%;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);font:inherit;border-radius:8px;padding:8px 10px}.Yvqh9W_ecommerceSection textarea{resize:vertical;min-height:60px}.Yvqh9W_ecommerceField{flex-direction:column;gap:4px;min-width:0;display:flex}.Yvqh9W_ecommerceFieldLabel{color:var(--dsw-alias-label-secondary);font-size:11.5px}.Yvqh9W_ecommerceParamGrid{grid-template-columns:1fr 1fr;gap:10px 8px;display:grid}.Yvqh9W_ecommerceUploadHero{border:1.5px dashed var(--dsw-alias-border-l2);min-height:78px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;text-align:center;background:0 0;border-radius:10px;flex-direction:column;justify-content:center;align-items:center;gap:4px;padding:12px;display:flex}.Yvqh9W_ecommerceUploadHero:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-interactive-bg-hover)}.Yvqh9W_ecommerceUploadHero span{font-size:12px}.Yvqh9W_ecommerceUploadHero small{color:var(--dsw-alias-label-tertiary);font-size:10.5px}.Yvqh9W_ecommerceStructureGrid{grid-template-columns:repeat(3,1fr);gap:6px;display:grid}.Yvqh9W_ecommerceSlotCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);height:34px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;white-space:nowrap;border-radius:8px;justify-content:center;align-items:center;font-size:12px;display:flex;position:relative}.Yvqh9W_ecommerceSlotCard:hover{border-color:var(--dsw-alias-label-dimmed)}.Yvqh9W_ecommerceSlotCard[data-active]{background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base);font-weight:600}.Yvqh9W_ecommerceSlotCount{background:var(--dsw-alias-brand-primary);color:#fff;text-align:center;border-radius:8px;min-width:16px;height:16px;padding:0 4px;font-size:10px;font-weight:600;line-height:16px;position:absolute;top:-6px;right:-6px}.Yvqh9W_ecommerceRefRow{grid-template-columns:64px 1fr;align-items:center;gap:8px;display:grid}.Yvqh9W_ecommerceRefRow>span{color:var(--dsw-alias-label-secondary);font-size:12px}.Yvqh9W_ecommercePrimaryAction{border-radius:999px;width:100%;height:38px;font-size:13.5px}.Yvqh9W_previewBadge{background:var(--dsw-alias-brand-primary);color:#fff;letter-spacing:.02em;border-radius:4px;margin-left:5px;padding:0 4px;font-size:9px;font-weight:700;line-height:13px}.Yvqh9W_ecommercePlanNote{color:var(--dsw-alias-label-secondary);font-size:11.5px;line-height:1.5}.Yvqh9W_ecommercePlanWarn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-radius:6px;padding:6px 8px;font-size:11.5px;line-height:1.5}.Yvqh9W_ecommerceAssets{grid-template-columns:repeat(3,1fr);gap:6px;display:grid}.Yvqh9W_ecommerceAsset{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-3);border-radius:8px;flex-direction:column;gap:3px;padding:4px;display:flex;position:relative}.Yvqh9W_ecommerceAsset img{object-fit:cover;border-radius:5px;width:100%;height:44px}.Yvqh9W_ecommerceAsset select{width:100%;min-width:0;padding:2px;font-size:10px}.Yvqh9W_ecommerceAsset button{color:#fff;cursor:pointer;text-align:center;background:#0000008c;border:none;border-radius:50%;width:16px;height:16px;padding:0;font-size:11px;line-height:15px;position:absolute;top:7px;right:7px}.Yvqh9W_ecommerceAsset button:hover{background:#000c}.Yvqh9W_ecommerceAssetAdd{border:1.5px dashed var(--dsw-alias-border-l2);min-height:78px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border-radius:8px;flex-direction:column;justify-content:center;align-items:center;gap:2px;display:flex}.Yvqh9W_ecommerceAssetAdd:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Yvqh9W_ecommerceAssetAdd span{font-size:16px;line-height:1}.Yvqh9W_ecommerceAssetAdd small{font-size:10px}.Yvqh9W_ecommerceAdvancedToggle{width:100%;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:none;justify-content:space-between;align-items:center;padding:0;font-size:12.5px;display:flex}.Yvqh9W_ecommerceAdvancedToggle:hover{color:var(--dsw-alias-label-primary)}.Yvqh9W_ecommerceAdvancedChevron{font-size:11px}.Yvqh9W_ecommerceAdvancedBody{flex-direction:column;gap:8px;margin-top:8px;display:flex}.Yvqh9W_ecommerceFooterBody{flex-direction:column;gap:8px;display:flex}.Yvqh9W_ecommercePlanMini{border:1px solid var(--dsw-alias-brand-primary);border-radius:10px;flex-direction:column;gap:6px;padding:10px;display:flex}.Yvqh9W_ecommercePlanMini>strong{font-size:12.5px}.Yvqh9W_ecommercePlanList{flex-direction:column;gap:3px;max-height:116px;display:flex;overflow-y:auto}.Yvqh9W_ecommercePlanList>div{color:var(--dsw-alias-label-secondary);justify-content:space-between;gap:12px;font-size:11.5px;display:flex}.Yvqh9W_ecommercePlanBack{color:var(--dsw-alias-label-tertiary);cursor:pointer;font:inherit;background:0 0;border:none;padding:0;font-size:12px}.Yvqh9W_ecommercePlanBack:hover{color:var(--dsw-alias-label-primary)}.Yvqh9W_ecommerceFooterHint{color:var(--dsw-alias-label-secondary);font-size:12px}.Yvqh9W_ecommerceResults{flex-direction:column;gap:14px;width:100%;margin:0 auto;padding:16px 18px;display:flex;overflow-y:auto}.Yvqh9W_ecommerceResultsHeader{justify-content:space-between;align-items:center;gap:12px;display:flex}.Yvqh9W_ecommerceResultsHeader h3{margin:0;font-size:15px}.Yvqh9W_ecommerceResultsHeader>div:first-child{align-items:baseline;gap:10px;display:flex}.Yvqh9W_ecommerceResultsHeader span{color:var(--dsw-alias-label-secondary);font-size:12px}.Yvqh9W_ecommerceResultsActions{gap:8px;display:flex}.Yvqh9W_ecommerceResultsEmpty{color:var(--dsw-alias-label-tertiary);text-align:center;border:1.5px dashed var(--dsw-alias-border-l2);border-radius:12px;padding:32px 16px;font-size:13px}.Yvqh9W_ecommerceGroups{flex-direction:column;gap:16px;display:flex}.Yvqh9W_ecommerceGroup{background:0 0;border:none;border-radius:0;flex-direction:column;gap:8px;padding:0;display:flex}.Yvqh9W_ecommerceGroup>header{align-items:center;gap:10px;font-size:13px;display:flex}.Yvqh9W_ecommerceGroup>header strong{font-size:13px}.Yvqh9W_ecommerceGroup>header span{color:var(--dsw-alias-label-secondary);font-size:12px}.Yvqh9W_ecommerceGroup>header button{margin-left:auto}.Yvqh9W_ecommerceGroupGrid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.Yvqh9W_ecommerceTaskCard{aspect-ratio:1;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-3);border-radius:10px;justify-content:center;align-items:center;width:100%;min-height:0;padding:0;display:flex;position:relative;overflow:hidden}.Yvqh9W_ecommerceTaskCard[data-status=failed],.Yvqh9W_ecommerceTaskCard[data-status=cancelled]{color:var(--dsw-alias-label-tertiary);font-size:12px}.Yvqh9W_ecommerceTaskCard .Yvqh9W_imageCard{border:none;border-radius:0;width:100%;height:100%;margin:0}.Yvqh9W_ecommerceTaskCard .Yvqh9W_image{object-fit:cover;width:100%;height:100%}.Yvqh9W_ecommerceResultBadge{color:#fff;pointer-events:none;background:#0000008c;border-radius:999px;padding:0 7px;font-size:10px;line-height:16px;position:absolute;top:6px;left:6px}.Yvqh9W_ecommerceTaskActions{opacity:0;background:linear-gradient(#0000,#0000008c);justify-content:center;gap:4px;padding:10px 4px 5px;transition:opacity .12s;display:flex;position:absolute;bottom:0;left:0;right:0}.Yvqh9W_ecommerceTaskCard:hover .Yvqh9W_ecommerceTaskActions{opacity:1}.Yvqh9W_ecommerceActionChip{color:#111;cursor:pointer;white-space:nowrap;background:#ffffffeb;border:none;border-radius:6px;padding:0 6px;font-size:10px;font-weight:500;line-height:18px;text-decoration:none}.Yvqh9W_ecommerceActionChip:hover{background:#fff}.Yvqh9W_ecommerceActionChip:disabled{opacity:.5;cursor:default}.Yvqh9W_ecommerceTaskState{color:var(--dsw-alias-label-secondary);text-align:center;word-break:break-all;flex-direction:column;gap:3px;padding:10px 8px;font-size:11px;display:flex}.Yvqh9W_ecommerceTaskState b{color:var(--dsw-alias-label-primary);font-size:12px}.Yvqh9W_topNav{flex:none;justify-content:center;align-items:center;gap:2px;display:flex;position:absolute;left:50%;transform:translate(-50%)}.Yvqh9W_topNavItem{height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:none;border-radius:999px;align-items:center;gap:4px;padding:0 14px;font-size:13px;display:inline-flex}.Yvqh9W_topNavItem,.Yvqh9W_topNavItem span{white-space:nowrap}.Yvqh9W_topNavItem:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.Yvqh9W_topNavItem[data-active]{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base);font-weight:600}.Yvqh9W_topNavDivider{background:var(--dsw-alias-border-l2);width:1px;height:16px;margin:0 8px}.Yvqh9W_panelHeaderActions{align-items:center;gap:10px;display:inline-flex}.Yvqh9W_chatToggle{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;border-radius:999px;align-items:center;gap:5px;padding:0 10px;font-size:12px;display:inline-flex}.Yvqh9W_chatToggle:hover{color:var(--dsw-alias-label-primary)}.Yvqh9W_chatToggle[data-open=true]{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}html[data-dsh-imagegen-chat-collapsed][data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane=conversation],html[data-dsh-imagegen-chat-collapsed][data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*=centerCol]{grid-template-columns:minmax(0,1fr)}html[data-dsh-imagegen-chat-collapsed][data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-slot=conversation],html[data-dsh-imagegen-chat-collapsed] [data-dsh-imagegen-chat-resizer]{display:none!important}.Yvqh9W_configResizer{cursor:col-resize;z-index:6;width:6px;height:100%;position:absolute;top:0;right:0}.Yvqh9W_configResizer:hover{background:linear-gradient(90deg, transparent, var(--dsw-alias-border-l2))}.Yvqh9W_config[data-collapsed=true] .Yvqh9W_configResizer{display:none}.Yvqh9W_modeRow{align-items:center;gap:8px;display:flex}.Yvqh9W_modePill{flex:1;justify-content:center;height:28px;font-size:12.5px}.Yvqh9W_modePill,.Yvqh9W_modePill span,.Yvqh9W_modePill div{white-space:nowrap;min-width:0}.Yvqh9W_uploadBox{min-height:128px;color:var(--dsw-alias-label-secondary);border:1.5px dashed var(--dsw-alias-border-l2);cursor:pointer;font:inherit;text-align:center;background:0 0;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;gap:6px;padding:16px;font-size:12.5px;display:flex}.Yvqh9W_uploadBox:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-interactive-bg-hover)}.Yvqh9W_uploadBox:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Yvqh9W_uploadIcon{color:var(--dsw-alias-label-tertiary);display:inline-flex}.Yvqh9W_uploadHint{color:var(--dsw-alias-label-tertiary);font-size:11px}.Yvqh9W_reference{flex-direction:column;gap:8px;display:flex}.Yvqh9W_referenceImage{object-fit:contain;background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;width:100%;max-height:176px}.Yvqh9W_referenceActions{gap:8px;display:flex}.Yvqh9W_hiddenFile{display:none}.Yvqh9W_prompt{width:100%;min-height:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);resize:vertical;box-sizing:border-box;border-radius:10px;outline:none;padding:10px 12px;font-family:inherit;font-size:13px;line-height:1.6}.Yvqh9W_prompt:focus-visible{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_prompt::placeholder{color:var(--dsw-alias-label-tertiary)}.Yvqh9W_promptFooter{justify-content:space-between;align-items:center;gap:8px;margin-top:-6px;display:flex}.Yvqh9W_templatesButton{border:1px solid var(--dsw-alias-brand-primary);background:linear-gradient(135deg, color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent), color-mix(in srgb, var(--dsw-alias-brand-primary) 5%, transparent));height:26px;color:var(--dsw-alias-brand-primary);cursor:pointer;box-shadow:0 1px 0 color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent);border-radius:999px;align-items:center;gap:6px;padding:0 12px;font-family:inherit;font-size:12px;font-weight:600;transition:transform .12s,box-shadow .12s,background .12s;display:inline-flex}.Yvqh9W_templatesButton svg{flex:none}.Yvqh9W_templatesButton:hover{background:linear-gradient(135deg, color-mix(in srgb, var(--dsw-alias-brand-primary) 24%, transparent), color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent));color:var(--dsw-alias-brand-primary);box-shadow:0 2px 6px color-mix(in srgb, var(--dsw-alias-brand-primary) 30%, transparent);transform:translateY(-1px)}.Yvqh9W_templatesButton:active{transform:translateY(0)}.Yvqh9W_enhanceButton{border:1px solid var(--dsw-alias-border-l2);height:26px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:999px;margin-left:auto;padding:0 11px;font-size:12px}.Yvqh9W_enhanceButton:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_enhanceButton:disabled{opacity:.5;cursor:default}.Yvqh9W_promptCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px}.Yvqh9W_paramGroup{flex-direction:column;gap:8px;display:flex}.Yvqh9W_paramLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.Yvqh9W_optionRow{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_optionGrid{grid-template-columns:repeat(3,1fr);gap:6px;display:grid}.Yvqh9W_optionPill{justify-content:center}.Yvqh9W_paramHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45}.Yvqh9W_footer{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;flex:none;align-items:stretch;gap:8px;padding:10px 2px 0 0;display:flex}.Yvqh9W_modelWrap{flex-direction:column;gap:5px;min-width:0;display:flex}.Yvqh9W_modelLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.Yvqh9W_modelSelect{width:100%;height:36px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;text-align:left;border-radius:18px;outline:none;justify-content:space-between;align-items:center;gap:8px;padding:0 12px;font-family:inherit;font-size:13px;display:flex}.Yvqh9W_modelSelect:focus-visible{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_modelSelect:disabled{opacity:.55;cursor:default}.Yvqh9W_modelMenu{min-width:0;display:block;position:relative}.Yvqh9W_modelMenuList{z-index:40;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;padding:4px;display:flex;position:absolute;bottom:calc(100% + 6px);left:0;right:0;overflow:hidden;box-shadow:0 -8px 24px #0000002e}.Yvqh9W_modelMenuItem{width:100%;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;white-space:nowrap;text-overflow:ellipsis;background:0 0;border:none;border-radius:8px;padding:7px 10px;font-size:13px;display:block;overflow:hidden}.Yvqh9W_modelMenuItem:hover{background:var(--dsw-alias-bg-hover)}.Yvqh9W_modelMenuItem[data-selected]{color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-1);font-weight:600}.Yvqh9W_generateButton{width:100%}.Yvqh9W_generateInner{align-items:center;gap:7px;display:inline-flex}.Yvqh9W_canvasState{text-align:center;color:var(--dsw-alias-label-tertiary);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px;display:flex}.Yvqh9W_canvasStateTitle{color:var(--dsw-alias-label-secondary);font-size:14px;font-weight:600}.Yvqh9W_canvasStateHint{max-width:380px;font-size:12px;line-height:1.6}.Yvqh9W_canvasEmptyIcon{color:var(--dsw-alias-label-tertiary);margin-bottom:4px;display:inline-flex}.Yvqh9W_canvasError{color:var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-label-error);overflow-wrap:anywhere;border-radius:10px;flex:none;margin:14px;padding:10px 14px;font-size:12.5px;line-height:1.6}.Yvqh9W_canvasBody{scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex-direction:column;flex:1;gap:10px;min-height:0;padding:14px;display:flex;overflow-y:auto}.Yvqh9W_canvasBody::-webkit-scrollbar{width:8px}.Yvqh9W_canvasBody::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_canvasMeta{color:var(--dsw-alias-label-tertiary);flex:none;align-items:center;gap:8px;font-size:12px;display:flex}.Yvqh9W_canvasHistoryTag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px}.Yvqh9W_grid{flex:1;grid-template-rows:repeat(2,minmax(0,1fr));grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;min-height:0;display:grid}.Yvqh9W_grid[data-count=\"1\"] .Yvqh9W_imageCard{grid-area:1/1/3/3}.Yvqh9W_imageCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);cursor:zoom-in;border-radius:12px;flex-direction:column;min-height:0;margin:0;display:flex;position:relative;overflow:hidden}.Yvqh9W_imageCard:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Yvqh9W_image{object-fit:cover;background:var(--dsw-alias-bg-base);flex:1;width:100%;min-height:0;display:block}.Yvqh9W_imageCaption{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;border-top:1px solid var(--dsw-alias-border-l1);padding:7px 10px;font-size:11px;line-height:1.5;overflow:hidden}.Yvqh9W_download{color:#fff;opacity:0;backdrop-filter:blur(4px);background:#0f1218d1;border:1px solid #ffffff9e;border-radius:999px;padding:2px 10px;font-size:12px;font-weight:500;line-height:20px;text-decoration:none;transition:opacity .12s;position:absolute;top:8px;right:8px;box-shadow:0 2px 8px #00000047}.Yvqh9W_imageCard:hover .Yvqh9W_download{opacity:1}.Yvqh9W_download:hover{background:#0f1218f0}.Yvqh9W_download:focus-visible{opacity:1;outline-offset:2px;outline:2px solid #fff}.Yvqh9W_galleryAdd{font:inherit;color:#fff;cursor:pointer;opacity:0;backdrop-filter:blur(4px);background:#0f1218d1;border:1px solid #ffffff9e;border-radius:999px;align-items:center;gap:5px;padding:2px 10px;font-size:12px;font-weight:500;line-height:20px;transition:opacity .12s;display:inline-flex;position:absolute;top:8px;left:8px;box-shadow:0 2px 8px #00000047}.Yvqh9W_imageCard:hover .Yvqh9W_galleryAdd{opacity:1}.Yvqh9W_galleryAdd:hover{background:#0f1218f0}.Yvqh9W_galleryAdd:focus-visible{opacity:1;outline-offset:2px;outline:2px solid #fff}.Yvqh9W_galleryAdd:disabled{opacity:.4;cursor:default}.Yvqh9W_conversationAdd{color:#fff;cursor:pointer;font:inherit;opacity:0;backdrop-filter:blur(4px);background:#0f1218d1;border:1px solid #ffffff9e;border-radius:999px;align-items:center;gap:5px;padding:2px 10px;font-size:12px;font-weight:500;line-height:20px;transition:opacity .12s;display:inline-flex;position:absolute;top:38px;left:8px;box-shadow:0 2px 8px #00000047}.Yvqh9W_imageCard:hover .Yvqh9W_conversationAdd,.Yvqh9W_conversationAdd:focus-visible{opacity:1}.Yvqh9W_conversationAdd:hover{background:#0f1218f0}.Yvqh9W_conversationAdd:focus-visible{opacity:1;outline-offset:2px;outline:2px solid #fff}.Yvqh9W_conversationAdd:disabled{opacity:.4;cursor:default}.Yvqh9W_zoomHint{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-mask-1);border:1px solid var(--dsw-alias-border-l2);opacity:0;backdrop-filter:blur(4px);pointer-events:none;border-radius:999px;align-items:center;gap:5px;padding:2px 10px;font-size:12px;font-weight:500;line-height:20px;transition:opacity .12s;display:inline-flex;position:absolute;bottom:8px;left:8px}.Yvqh9W_imageCard:hover .Yvqh9W_zoomHint{opacity:1}.Yvqh9W_spinner,.Yvqh9W_bigSpinner{border:2px solid;border-top-color:#0000;border-radius:50%;flex:none;animation:.8s linear infinite Yvqh9W_dshImageGenSpin;display:inline-block}.Yvqh9W_spinner{width:11px;height:11px}.Yvqh9W_bigSpinner{width:30px;height:30px;color:var(--dsw-alias-state-business-primary);border-width:3px;margin-bottom:6px}.Yvqh9W_lightbox{z-index:1000;backdrop-filter:blur(6px);background:#000000b8;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}.Yvqh9W_lightboxClose{color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;border-radius:50%;justify-content:center;align-items:center;width:38px;height:38px;display:inline-flex;position:absolute;top:16px;right:16px}.Yvqh9W_lightboxClose:hover{background:#ffffff42}.Yvqh9W_lightboxNav{color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;border-radius:50%;justify-content:center;align-items:center;width:42px;height:42px;display:inline-flex;position:absolute;top:50%;transform:translateY(-50%)}.Yvqh9W_lightboxNav:hover{background:#ffffff42}.Yvqh9W_lightboxNav[data-dir=prev]{left:max(20px,50% - 640px)}.Yvqh9W_lightboxNav[data-dir=next]{right:max(20px,50% - 640px)}.Yvqh9W_lightboxFigure{flex-direction:column;gap:10px;width:min(1100px,100vw - 160px);max-width:min(1100px,100vw - 160px);height:min(820px,100vh - 48px);min-height:0;margin:0;display:flex}.Yvqh9W_lightboxStage{background:#ffffff0a;border-radius:10px;flex:1;min-height:0;position:relative;overflow:auto}.Yvqh9W_lightboxScaleFrame{justify-content:center;align-items:center;min-width:100%;min-height:100%;display:flex}.Yvqh9W_lightboxImage{object-fit:contain;border-radius:10px;max-width:100%;max-height:100%;display:block;box-shadow:0 24px 80px #00000080}.Yvqh9W_lightboxTools{justify-content:center;align-items:center;gap:6px;display:flex}.Yvqh9W_lightboxTool,.Yvqh9W_lightboxZoomLevel,.Yvqh9W_lightboxCopy{color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;justify-content:center;align-items:center;display:inline-flex}.Yvqh9W_lightboxTool,.Yvqh9W_lightboxZoomLevel{height:32px}.Yvqh9W_lightboxTool{border-radius:50%;width:32px}.Yvqh9W_lightboxZoomLevel{min-width:58px;font:inherit;font-variant-numeric:tabular-nums;border-radius:999px;padding:0 9px;font-size:12px}.Yvqh9W_lightboxTool:hover,.Yvqh9W_lightboxZoomLevel:hover,.Yvqh9W_lightboxCopy:hover{background:#ffffff42}.Yvqh9W_lightboxCaptionRow{align-items:flex-start;gap:8px;min-width:0;display:flex}.Yvqh9W_lightboxCaption{color:#ffffffe6;-webkit-line-clamp:3;-webkit-box-orient:vertical;flex:1;min-width:0;font-size:12px;line-height:1.6;display:-webkit-box;overflow:hidden}.Yvqh9W_lightboxCopy{min-height:28px;font:inherit;white-space:nowrap;border-radius:999px;flex:none;gap:5px;padding:4px 9px;font-size:12px}.Yvqh9W_lightboxMeta{justify-content:space-between;align-items:center;gap:12px;display:flex}.Yvqh9W_lightboxIndex{color:#fffc;font-variant-numeric:tabular-nums;font-size:12px}.Yvqh9W_lightboxActions{align-items:center;gap:8px;display:inline-flex}.Yvqh9W_lightboxDownload,.Yvqh9W_lightboxEdit{font:inherit;color:#fff;cursor:pointer;background:#ffffff24;border:1px solid #ffffff47;border-radius:999px;padding:4px 14px;font-size:12.5px;font-weight:500;text-decoration:none}.Yvqh9W_lightboxDownload:hover,.Yvqh9W_lightboxEdit:hover{background:#ffffff42}.Yvqh9W_lightboxEdit{color:#fff;background:#ffffff24;border:1px solid #ffffff47;border-radius:999px}@media (width<=720px){.Yvqh9W_lightbox{padding:16px}.Yvqh9W_lightboxFigure{width:calc(100vw - 32px);max-width:none}.Yvqh9W_lightboxNav[data-dir=prev]{left:20px}.Yvqh9W_lightboxNav[data-dir=next]{right:20px}.Yvqh9W_lightboxCaptionRow,.Yvqh9W_lightboxMeta{flex-direction:column;align-items:stretch}.Yvqh9W_lightboxCopy,.Yvqh9W_lightboxActions{align-self:flex-end}}@keyframes Yvqh9W_dshImageGenSpin{to{transform:rotate(360deg)}}.Yvqh9W_galleryToast{z-index:30;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-mask-1);border:1px solid var(--dsw-alias-border-l2);backdrop-filter:blur(6px);pointer-events:none;border-radius:999px;align-items:center;gap:7px;padding:6px 16px;font-size:13px;font-weight:500;animation:.16s ease-out Yvqh9W_dshImageGenToastIn;display:inline-flex;position:absolute;bottom:24px;left:50%;transform:translate(-50%);box-shadow:0 8px 24px #00000038}@keyframes Yvqh9W_dshImageGenToastIn{0%{opacity:0;transform:translate(-50%,6px)}to{opacity:1;transform:translate(-50%)}}.Yvqh9W_conversationToast{z-index:30;border:1px solid var(--dsw-alias-border-l2);max-width:calc(100% - 32px);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-mask-1);pointer-events:none;backdrop-filter:blur(6px);border-radius:999px;align-items:center;gap:7px;padding:6px 16px;font-size:13px;font-weight:500;animation:.16s ease-out Yvqh9W_dshImageGenToastIn;display:inline-flex;position:absolute;bottom:24px;left:50%;transform:translate(-50%);box-shadow:0 8px 24px #00000038}@media (prefers-reduced-motion:reduce){.Yvqh9W_download,.Yvqh9W_spinner,.Yvqh9W_bigSpinner{transition:none;animation-duration:1.5s}}.Yvqh9W_config[data-gallery=true] .Yvqh9W_configScroll>:not(:first-child),.Yvqh9W_config[data-gallery=true] .Yvqh9W_footer,.Yvqh9W_canvas[data-gallery=true]>.Yvqh9W_canvasState,.Yvqh9W_canvas[data-gallery=true]>.Yvqh9W_canvasError,.Yvqh9W_canvas[data-gallery=true]>.Yvqh9W_canvasBody,.Yvqh9W_studio:has(.Yvqh9W_config[data-gallery=true])>.Yvqh9W_history{display:none}.Yvqh9W_config[data-gallery=true] .Yvqh9W_configScroll{flex:none;order:1;display:flex;overflow:visible}.Yvqh9W_config[data-gallery=true] .Yvqh9W_galleryFilters{flex:1;order:2;min-height:0}.Yvqh9W_galleryFilters{padding:18px 14px;overflow:hidden auto}.Yvqh9W_galleryFilterHeading{color:var(--dsw-alias-label-tertiary);margin:0 4px 10px;font-size:12px;font-weight:600}.Yvqh9W_galleryFilter{width:100%;min-height:34px;color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:left;background:0 0;border:0;border-radius:9px;justify-content:space-between;align-items:center;padding:0 10px;display:flex}.Yvqh9W_galleryFilter:hover,.Yvqh9W_galleryFilter[data-active]{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent)}.Yvqh9W_galleryFilterCount{min-width:20px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);text-align:center;border-radius:999px;padding:1px 6px;font-size:11px}.Yvqh9W_galleryFilterDivider{background:var(--dsw-alias-border-l1);height:1px;margin:18px 4px}.Yvqh9W_galleryRatioList{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_galleryRatio{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border:0;border-radius:999px;padding:6px 10px;font-size:12px}.Yvqh9W_galleryRatio[data-active]{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 13%, transparent)}.Yvqh9W_galleryTagFilterList{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_galleryTagFilter{border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:100%;min-height:27px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;font:inherit;border-radius:6px;align-items:center;gap:5px;padding:0 8px;font-size:11px;display:inline-flex}.Yvqh9W_galleryTagFilter span:first-child{text-overflow:ellipsis;white-space:nowrap;max-width:112px;overflow:hidden}.Yvqh9W_galleryTagFilter span:last-child{color:var(--dsw-alias-label-tertiary);font-size:10px}.Yvqh9W_galleryTagFilter:hover,.Yvqh9W_galleryTagFilter[data-active]{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent)}.Yvqh9W_galleryFilterNote{color:var(--dsw-alias-label-quaternary);margin:20px 4px 0;font-size:11px;line-height:1.5}.Yvqh9W_galleryWorkspace{box-sizing:border-box;flex-direction:column;width:100%;min-width:0;height:100%;min-height:0;padding:22px 24px 26px;display:flex;position:absolute;inset:0;overflow:hidden}.Yvqh9W_galleryToolbar{flex:none;justify-content:space-between;align-items:center;gap:16px;min-width:0;margin-bottom:18px;display:flex}.Yvqh9W_galleryHeading{color:var(--dsw-alias-label-primary);margin:0;font-size:20px;font-weight:700;display:inline}.Yvqh9W_galleryCount{color:var(--dsw-alias-label-tertiary);margin-left:8px;font-size:13px}.Yvqh9W_galleryToolbarActions{flex-wrap:wrap;align-items:center;gap:10px;min-width:0;display:flex}.Yvqh9W_gallerySelectMode,.Yvqh9W_galleryBulkButton,.Yvqh9W_gallerySelectionClear{border:1px solid var(--dsw-alias-border-l1);min-height:30px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;font:inherit;border-radius:7px;padding:0 10px;font-size:12px}.Yvqh9W_gallerySelectMode:hover,.Yvqh9W_gallerySelectMode[data-active],.Yvqh9W_galleryBulkButton:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 38%, var(--dsw-alias-border-l1));background:color-mix(in srgb, var(--dsw-alias-brand-primary) 11%, transparent)}.Yvqh9W_galleryBulkButton:disabled{cursor:not-allowed;opacity:.45}.Yvqh9W_gallerySelectionBar{border:1px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 35%, var(--dsw-alias-border-l1));background:color-mix(in srgb, var(--dsw-alias-brand-primary) 7%, var(--dsw-alias-bg-layer-1));border-radius:9px;flex:none;align-items:center;gap:10px;min-width:0;margin:-4px 0 16px;padding:10px 12px;display:flex}.Yvqh9W_gallerySelectionBar strong{color:var(--dsw-alias-brand-primary);flex:none;font-size:12px}.Yvqh9W_gallerySelectionClear{margin-left:auto}.Yvqh9W_gallerySelectionClear:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.Yvqh9W_galleryTagInput,.Yvqh9W_gallerySearch{border:1px solid var(--dsw-alias-border-l1);min-width:0;height:30px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);font:inherit;border-radius:7px;outline:none;padding:0 10px;font-size:12px}.Yvqh9W_galleryTagInput{flex:190px}.Yvqh9W_galleryTagInput:focus,.Yvqh9W_gallerySearch:focus{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryViewToggle{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:9px;padding:3px;display:flex}.Yvqh9W_galleryViewToggle button,.Yvqh9W_gallerySort,.Yvqh9W_galleryClear{min-height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:0;border-radius:7px;padding:0 10px;font-size:12px}.Yvqh9W_galleryViewToggle button[data-active],.Yvqh9W_galleryViewToggle button:hover,.Yvqh9W_gallerySort:hover,.Yvqh9W_galleryClear:hover{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 11%, transparent)}.Yvqh9W_gallerySort{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1)}.Yvqh9W_galleryClear{border:1px solid var(--dsw-alias-border-l1)}.Yvqh9W_compareControl{flex-direction:column;gap:6px;margin:0 0 10px;display:flex}.Yvqh9W_compareToggle,.Yvqh9W_compareModelChoices label{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}.Yvqh9W_compareToggle input,.Yvqh9W_compareModelChoices input{accent-color:var(--dsw-alias-brand-primary)}.Yvqh9W_compareModelChoices{flex-wrap:wrap;gap:6px;display:flex}.Yvqh9W_compareModelChoices label{border:1px solid var(--dsw-alias-border-l1);border-radius:5px;padding:4px 6px;font-size:10px}.Yvqh9W_comparisonBoard{z-index:4;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;display:flex;position:absolute;inset:14px;overflow:hidden;box-shadow:0 8px 24px #0000001f}.Yvqh9W_comparisonBoard>header{border-bottom:1px solid var(--dsw-alias-border-l1);justify-content:space-between;align-items:center;padding:10px 12px;display:flex}.Yvqh9W_comparisonBoard>header div{align-items:baseline;gap:7px;display:flex}.Yvqh9W_comparisonBoard>header strong{color:var(--dsw-alias-label-primary);font-size:13px}.Yvqh9W_comparisonBoard>header span{color:var(--dsw-alias-label-tertiary);font-size:11px}.Yvqh9W_comparisonBoard>header button{border:1px solid var(--dsw-alias-border-l1);min-height:28px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;font:inherit;border-radius:5px;padding:0 9px;font-size:11px}.Yvqh9W_comparisonGrid{flex:1;grid-template-rows:minmax(0,1fr);grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;min-height:0;padding:12px;display:grid;overflow:auto}.Yvqh9W_comparisonGrid article{flex-direction:column;gap:7px;min-width:0;height:100%;min-height:0;display:flex}.Yvqh9W_comparisonGrid article>strong{color:var(--dsw-alias-label-primary);font-size:12px}.Yvqh9W_comparisonImageButton{cursor:zoom-in;background:var(--dsw-alias-bg-base);border:0;flex:1;width:100%;min-height:0;padding:0;display:flex;overflow:hidden}.Yvqh9W_comparisonImageButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Yvqh9W_comparisonGrid article>span{min-height:0;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);flex:1;place-items:center;font-size:12px;display:grid}.Yvqh9W_comparisonGrid img{object-fit:contain;background:var(--dsw-alias-bg-base);flex:1;width:100%;height:100%;min-height:0;max-height:none;display:block}.Yvqh9W_comparisonFullscreen{z-index:1200;background:#000000eb;padding:54px 24px 24px;position:fixed;inset:0;overflow:auto}.Yvqh9W_comparisonFullscreenGrid{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));align-items:start;gap:18px;min-height:100%;display:grid}.Yvqh9W_comparisonFullscreen figure{min-width:0;margin:0}.Yvqh9W_comparisonFullscreen figcaption{color:#fff;margin-bottom:8px;font-size:13px;font-weight:600}.Yvqh9W_comparisonFullscreen img{background:#111;width:100%;margin-bottom:10px;display:block}.Yvqh9W_historyItem[data-comparison]{border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, var(--dsw-alias-border-l1))}.Yvqh9W_galleryMasonry{box-sizing:border-box;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;flex:auto;grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:max-content;align-content:start;gap:16px;width:100%;min-width:0;max-width:100%;height:0;min-height:0;padding:2px 8px 16px 2px;display:grid;overflow:hidden scroll}.Yvqh9W_galleryMasonry::-webkit-scrollbar{width:8px}.Yvqh9W_galleryMasonry::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px}.Yvqh9W_galleryCard{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:14px;width:100%;margin:0;display:block;position:relative;overflow:hidden;box-shadow:0 5px 18px #18203612}.Yvqh9W_galleryCard[data-selected]{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent), 0 5px 18px #18203612}.Yvqh9W_gallerySelect{z-index:2;cursor:pointer;background:#00000094;border:1px solid #ffffffbf;border-radius:7px;place-items:center;width:26px;height:26px;display:grid;position:absolute;top:9px;right:9px}.Yvqh9W_gallerySelect input{width:16px;height:16px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer;margin:0}.Yvqh9W_galleryImageButton{background:var(--dsw-alias-bg-base);cursor:zoom-in;border:0;width:100%;padding:0;display:block;position:relative}.Yvqh9W_galleryImageButton[data-selecting]{cursor:pointer}.Yvqh9W_galleryImage{aspect-ratio:4/3;object-fit:cover;width:100%;display:block}.Yvqh9W_galleryMasonry[data-view=masonry] .Yvqh9W_galleryCard:nth-child(3n+1) .Yvqh9W_galleryImage{aspect-ratio:4/5}.Yvqh9W_galleryMasonry[data-view=masonry] .Yvqh9W_galleryCard:nth-child(3n+2) .Yvqh9W_galleryImage{aspect-ratio:4/3}.Yvqh9W_galleryMasonry[data-view=masonry] .Yvqh9W_galleryCard:nth-child(3n) .Yvqh9W_galleryImage{aspect-ratio:3/4}.Yvqh9W_galleryBadge{color:#fff;backdrop-filter:blur(4px);background:#121724c2;border-radius:999px;padding:4px 9px;font-size:11px;position:absolute;top:10px;left:10px}.Yvqh9W_galleryCardActions{padding:8px 12px 0;display:flex}.Yvqh9W_galleryCardAction{border:1px solid var(--dsw-alias-border-l2);width:100%;min-width:0;min-height:28px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);cursor:pointer;font:inherit;white-space:nowrap;border-radius:7px;justify-content:center;align-items:center;gap:4px;padding:0 4px;font-size:10px;font-weight:500;display:inline-flex}.Yvqh9W_galleryCardAction:hover,.Yvqh9W_galleryCardAction:focus-visible{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary);outline:none}.Yvqh9W_galleryCardAction:disabled{cursor:wait;opacity:.55}.Yvqh9W_galleryCardFooter{align-items:center;gap:9px;min-width:0;padding:10px 12px;display:flex}.Yvqh9W_galleryAvatar{color:#fff;background:var(--dsw-alias-brand-primary);border-radius:50%;flex:none;place-items:center;width:27px;height:27px;font-size:12px;font-weight:700;display:grid}.Yvqh9W_galleryCardInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.Yvqh9W_galleryCardInfo strong,.Yvqh9W_galleryCardInfo small{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.Yvqh9W_galleryCardInfo strong{color:var(--dsw-alias-label-primary);font-size:12px}.Yvqh9W_galleryCardInfo small{color:var(--dsw-alias-label-tertiary);font-size:10px}.Yvqh9W_galleryTags{flex-wrap:wrap;gap:4px;margin-top:4px;display:flex}.Yvqh9W_galleryTags button{max-width:96px;min-height:19px;color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);cursor:pointer;font:inherit;text-overflow:ellipsis;white-space:nowrap;border:0;border-radius:4px;padding:1px 6px;font-size:10px;line-height:1.35;overflow:hidden}.Yvqh9W_galleryTags button:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 17%, transparent)}.Yvqh9W_galleryTags .Yvqh9W_galleryTagEdit{color:var(--dsw-alias-label-tertiary);background:0 0;flex:none}.Yvqh9W_galleryTagEditor{align-items:center;gap:6px;padding:0 12px 10px 48px;display:flex}.Yvqh9W_galleryTagEditor input{border:1px solid var(--dsw-alias-border-l2);min-width:0;height:27px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;border-radius:6px;outline:none;flex:1;padding:0 8px;font-size:11px}.Yvqh9W_galleryTagEditor input:focus{border-color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryTagEditor button{border:1px solid var(--dsw-alias-border-l2);height:27px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);cursor:pointer;font:inherit;border-radius:6px;padding:0 8px;font-size:11px}.Yvqh9W_galleryTagEditor button[type=submit]{color:var(--dsw-alias-brand-primary)}.Yvqh9W_galleryRemove{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:50%;flex:none;padding:0;font-size:18px}.Yvqh9W_galleryRemove:hover{color:var(--dsw-alias-state-error);background:var(--dsw-alias-bg-layer-2)}@media (width<=1100px){.Yvqh9W_galleryMasonry{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=760px){.Yvqh9W_studio{grid-template-rows:minmax(220px,36%) minmax(0,1fr);grid-template-columns:minmax(0,1fr);overflow-y:auto}.Yvqh9W_history{grid-area:1/1;min-height:220px}.Yvqh9W_generation{flex-direction:column;grid-area:2/1;min-height:0}.Yvqh9W_config{width:auto;max-width:none;height:auto;min-height:0}.Yvqh9W_config[data-gallery=true]{flex:none}.Yvqh9W_canvas{min-height:560px}.Yvqh9W_galleryToolbar{flex-direction:column;align-items:flex-start}.Yvqh9W_galleryToolbarActions{flex-wrap:wrap;width:100%}.Yvqh9W_galleryMasonry{grid-template-columns:1fr}}";
1675
1944
  const tagId$2 = "@dickpy/dsh-imagegen/panel.module.css";
1676
1945
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1677
1946
  const tag = document.createElement("style");
@@ -1681,166 +1950,226 @@ window.__ModuleLoader__.load({
1681
1950
  document.head.appendChild(tag);
1682
1951
  }
1683
1952
  var panel_module_css_default = {
1684
- "image": "Yvqh9W_image",
1685
- "galleryTagInput": "Yvqh9W_galleryTagInput",
1686
- "lightboxCaptionRow": "Yvqh9W_lightboxCaptionRow",
1687
- "bigSpinner": "Yvqh9W_bigSpinner",
1688
- "galleryTagEdit": "Yvqh9W_galleryTagEdit",
1689
- "canvasState": "Yvqh9W_canvasState",
1690
- "compareControl": "Yvqh9W_compareControl",
1691
- "galleryTagFilter": "Yvqh9W_galleryTagFilter",
1692
- "taskTrayCount": "Yvqh9W_taskTrayCount",
1693
- "historyEmpty": "Yvqh9W_historyEmpty",
1694
- "gallerySelectionClear": "Yvqh9W_gallerySelectionClear",
1695
- "historyPrompt": "Yvqh9W_historyPrompt",
1953
+ "optionRow": "Yvqh9W_optionRow",
1696
1954
  "galleryFilterNote": "Yvqh9W_galleryFilterNote",
1955
+ "ecommerceAsset": "Yvqh9W_ecommerceAsset",
1956
+ "ecommerceFooterHint": "Yvqh9W_ecommerceFooterHint",
1957
+ "lightboxClose": "Yvqh9W_lightboxClose",
1697
1958
  "imageCaption": "Yvqh9W_imageCaption",
1698
- "compareModelChoices": "Yvqh9W_compareModelChoices",
1959
+ "taskTrayCount": "Yvqh9W_taskTrayCount",
1960
+ "lightboxActions": "Yvqh9W_lightboxActions",
1961
+ "ecommerceRefRow": "Yvqh9W_ecommerceRefRow",
1962
+ "historyHeader": "Yvqh9W_historyHeader",
1963
+ "paramGroup": "Yvqh9W_paramGroup",
1964
+ "gallerySelectMode": "Yvqh9W_gallerySelectMode",
1965
+ "lightboxCaptionRow": "Yvqh9W_lightboxCaptionRow",
1699
1966
  "entryLabel": "Yvqh9W_entryLabel",
1700
- "panelHeader": "Yvqh9W_panelHeader",
1701
- "comparisonGrid": "Yvqh9W_comparisonGrid",
1702
- "galleryCard": "Yvqh9W_galleryCard",
1703
- "galleryTagFilterList": "Yvqh9W_galleryTagFilterList",
1704
- "galleryCardInfo": "Yvqh9W_galleryCardInfo",
1705
- "galleryViewToggle": "Yvqh9W_galleryViewToggle",
1706
- "history": "Yvqh9W_history",
1707
- "comparisonFullscreenGrid": "Yvqh9W_comparisonFullscreenGrid",
1708
- "taskRow": "Yvqh9W_taskRow",
1709
- "prompt": "Yvqh9W_prompt",
1710
- "taskTrayHeader": "Yvqh9W_taskTrayHeader",
1711
- "optionGrid": "Yvqh9W_optionGrid",
1712
- "taskTray": "Yvqh9W_taskTray",
1713
- "canvasStateTitle": "Yvqh9W_canvasStateTitle",
1714
- "taskPrompt": "Yvqh9W_taskPrompt",
1715
- "connectionDot": "Yvqh9W_connectionDot",
1716
- "galleryFilter": "Yvqh9W_galleryFilter",
1717
- "dshImageGenSpin": "Yvqh9W_dshImageGenSpin",
1718
- "galleryRatioList": "Yvqh9W_galleryRatioList",
1719
- "gallerySort": "Yvqh9W_gallerySort",
1720
- "grid": "Yvqh9W_grid",
1721
- "gallerySelectionBar": "Yvqh9W_gallerySelectionBar",
1722
- "templatesButton": "Yvqh9W_templatesButton",
1723
- "updateActions": "Yvqh9W_updateActions",
1724
- "configGuide": "Yvqh9W_configGuide",
1725
- "modelLabel": "Yvqh9W_modelLabel",
1726
- "configGuideBody": "Yvqh9W_configGuideBody",
1727
- "paramLabel": "Yvqh9W_paramLabel",
1728
- "taskTrayToggle": "Yvqh9W_taskTrayToggle",
1729
- "canvas": "Yvqh9W_canvas",
1730
- "entryIcon": "Yvqh9W_entryIcon",
1967
+ "configToggle": "Yvqh9W_configToggle",
1968
+ "sessionTab": "Yvqh9W_sessionTab",
1969
+ "galleryBadge": "Yvqh9W_galleryBadge",
1970
+ "historyTitle": "Yvqh9W_historyTitle",
1971
+ "topNavDivider": "Yvqh9W_topNavDivider",
1972
+ "configResizer": "Yvqh9W_configResizer",
1973
+ "taskTrayChevron": "Yvqh9W_taskTrayChevron",
1731
1974
  "generateInner": "Yvqh9W_generateInner",
1732
- "historyInfo": "Yvqh9W_historyInfo",
1733
- "updateBanner": "Yvqh9W_updateBanner",
1975
+ "historyActions": "Yvqh9W_historyActions",
1734
1976
  "zoomHint": "Yvqh9W_zoomHint",
1735
- "lightboxActions": "Yvqh9W_lightboxActions",
1736
- "galleryAvatar": "Yvqh9W_galleryAvatar",
1977
+ "modelSelect": "Yvqh9W_modelSelect",
1978
+ "enhanceButton": "Yvqh9W_enhanceButton",
1979
+ "sidebarHistoryHost": "Yvqh9W_sidebarHistoryHost",
1980
+ "lightboxImage": "Yvqh9W_lightboxImage",
1981
+ "conversationToast": "Yvqh9W_conversationToast",
1982
+ "lightboxEdit": "Yvqh9W_lightboxEdit",
1983
+ "canvasStateTitle": "Yvqh9W_canvasStateTitle",
1737
1984
  "galleryHeading": "Yvqh9W_galleryHeading",
1738
- "download": "Yvqh9W_download",
1739
- "historyActions": "Yvqh9W_historyActions",
1740
- "panelTitle": "Yvqh9W_panelTitle",
1741
- "galleryCount": "Yvqh9W_galleryCount",
1742
- "reference": "Yvqh9W_reference",
1743
- "galleryMasonry": "Yvqh9W_galleryMasonry",
1744
- "galleryRemove": "Yvqh9W_galleryRemove",
1745
- "historyTitle": "Yvqh9W_historyTitle",
1746
- "connectionStatus": "Yvqh9W_connectionStatus",
1747
- "historyMain": "Yvqh9W_historyMain",
1748
- "galleryToolbar": "Yvqh9W_galleryToolbar",
1749
- "configScroll": "Yvqh9W_configScroll",
1750
- "comparisonFullscreen": "Yvqh9W_comparisonFullscreen",
1751
- "historyHeader": "Yvqh9W_historyHeader",
1752
- "imageCard": "Yvqh9W_imageCard",
1753
- "updateRelease": "Yvqh9W_updateRelease",
1754
- "canvasBody": "Yvqh9W_canvasBody",
1755
- "promptCount": "Yvqh9W_promptCount",
1756
- "paramGroup": "Yvqh9W_paramGroup",
1757
- "generateButton": "Yvqh9W_generateButton",
1758
- "lightboxCaption": "Yvqh9W_lightboxCaption",
1759
- "canvasHistoryTag": "Yvqh9W_canvasHistoryTag",
1760
- "uploadBox": "Yvqh9W_uploadBox",
1761
- "lightboxNav": "Yvqh9W_lightboxNav",
1762
- "updateText": "Yvqh9W_updateText",
1985
+ "ecommerceResults": "Yvqh9W_ecommerceResults",
1763
1986
  "galleryTags": "Yvqh9W_galleryTags",
1764
- "canvasEmptyIcon": "Yvqh9W_canvasEmptyIcon",
1765
- "gallerySelectMode": "Yvqh9W_gallerySelectMode",
1766
- "galleryWorkspace": "Yvqh9W_galleryWorkspace",
1767
- "galleryRatio": "Yvqh9W_galleryRatio",
1768
1987
  "spinner": "Yvqh9W_spinner",
1769
- "panel": "Yvqh9W_panel",
1770
- "historyClear": "Yvqh9W_historyClear",
1771
- "lightboxTools": "Yvqh9W_lightboxTools",
1772
- "card": "Yvqh9W_card",
1773
- "compareToggle": "Yvqh9W_compareToggle",
1774
- "lightboxEdit": "Yvqh9W_lightboxEdit",
1775
- "uploadHint": "Yvqh9W_uploadHint",
1776
- "enhanceButton": "Yvqh9W_enhanceButton",
1777
- "footer": "Yvqh9W_footer",
1778
- "galleryBadge": "Yvqh9W_galleryBadge",
1779
- "historyFilters": "Yvqh9W_historyFilters",
1780
- "gallerySelect": "Yvqh9W_gallerySelect",
1781
- "modePill": "Yvqh9W_modePill",
1782
- "view": "Yvqh9W_view",
1783
- "taskStatus": "Yvqh9W_taskStatus",
1988
+ "taskTray": "Yvqh9W_taskTray",
1784
1989
  "modeRow": "Yvqh9W_modeRow",
1785
- "comparisonBoard": "Yvqh9W_comparisonBoard",
1990
+ "historyThumbPlaceholder": "Yvqh9W_historyThumbPlaceholder",
1991
+ "historyEmpty": "Yvqh9W_historyEmpty",
1992
+ "ecommercePlanBack": "Yvqh9W_ecommercePlanBack",
1993
+ "conversationAdd": "Yvqh9W_conversationAdd",
1994
+ "ecommerceSlotCount": "Yvqh9W_ecommerceSlotCount",
1995
+ "ecommerceGroups": "Yvqh9W_ecommerceGroups",
1996
+ "bigSpinner": "Yvqh9W_bigSpinner",
1786
1997
  "taskRows": "Yvqh9W_taskRows",
1787
- "galleryImageButton": "Yvqh9W_galleryImageButton",
1788
- "uploadIcon": "Yvqh9W_uploadIcon",
1789
- "historyList": "Yvqh9W_historyList",
1790
- "promptFooter": "Yvqh9W_promptFooter",
1998
+ "configScroll": "Yvqh9W_configScroll",
1999
+ "galleryToast": "Yvqh9W_galleryToast",
2000
+ "lightboxNav": "Yvqh9W_lightboxNav",
2001
+ "ecommerceAssets": "Yvqh9W_ecommerceAssets",
1791
2002
  "panelHeading": "Yvqh9W_panelHeading",
1792
- "optionPill": "Yvqh9W_optionPill",
1793
- "modelMenuList": "Yvqh9W_modelMenuList",
1794
- "canvasError": "Yvqh9W_canvasError",
1795
- "canvasStateHint": "Yvqh9W_canvasStateHint",
1796
- "referenceActions": "Yvqh9W_referenceActions",
2003
+ "galleryTagFilter": "Yvqh9W_galleryTagFilter",
2004
+ "comparisonFullscreen": "Yvqh9W_comparisonFullscreen",
2005
+ "lightboxTools": "Yvqh9W_lightboxTools",
2006
+ "optionGrid": "Yvqh9W_optionGrid",
2007
+ "galleryCount": "Yvqh9W_galleryCount",
1797
2008
  "modelMenu": "Yvqh9W_modelMenu",
1798
- "modelMenuItem": "Yvqh9W_modelMenuItem",
2009
+ "paramHint": "Yvqh9W_paramHint",
2010
+ "ecommerceParamGrid": "Yvqh9W_ecommerceParamGrid",
2011
+ "lightboxCopy": "Yvqh9W_lightboxCopy",
2012
+ "templatesButton": "Yvqh9W_templatesButton",
2013
+ "canvasState": "Yvqh9W_canvasState",
2014
+ "ecommerceWorkspace": "Yvqh9W_ecommerceWorkspace",
2015
+ "promptFooter": "Yvqh9W_promptFooter",
2016
+ "galleryFilterDivider": "Yvqh9W_galleryFilterDivider",
2017
+ "lightboxCaption": "Yvqh9W_lightboxCaption",
2018
+ "entry": "Yvqh9W_entry",
2019
+ "canvas": "Yvqh9W_canvas",
2020
+ "configHeader": "Yvqh9W_configHeader",
2021
+ "chatResizer": "Yvqh9W_chatResizer",
2022
+ "ecommerceResultsHeader": "Yvqh9W_ecommerceResultsHeader",
2023
+ "updateBanner": "Yvqh9W_updateBanner",
2024
+ "topNavItem": "Yvqh9W_topNavItem",
2025
+ "galleryClear": "Yvqh9W_galleryClear",
2026
+ "compareModelChoices": "Yvqh9W_compareModelChoices",
2027
+ "referenceActions": "Yvqh9W_referenceActions",
2028
+ "comparisonBoard": "Yvqh9W_comparisonBoard",
2029
+ "galleryCardInfo": "Yvqh9W_galleryCardInfo",
2030
+ "galleryBulkButton": "Yvqh9W_galleryBulkButton",
2031
+ "ecommercePlanList": "Yvqh9W_ecommercePlanList",
2032
+ "entryIcon": "Yvqh9W_entryIcon",
2033
+ "ecommerceResultBadge": "Yvqh9W_ecommerceResultBadge",
2034
+ "ecommerceAdvancedChevron": "Yvqh9W_ecommerceAdvancedChevron",
2035
+ "historyThumb": "Yvqh9W_historyThumb",
2036
+ "lightboxScaleFrame": "Yvqh9W_lightboxScaleFrame",
2037
+ "ecommerceFieldLabel": "Yvqh9W_ecommerceFieldLabel",
2038
+ "comparisonImageButton": "Yvqh9W_comparisonImageButton",
2039
+ "card": "Yvqh9W_card",
2040
+ "configGuideBody": "Yvqh9W_configGuideBody",
2041
+ "ecommerceField": "Yvqh9W_ecommerceField",
2042
+ "ecommerceTaskState": "Yvqh9W_ecommerceTaskState",
2043
+ "galleryWorkspace": "Yvqh9W_galleryWorkspace",
1799
2044
  "lightbox": "Yvqh9W_lightbox",
2045
+ "generateButton": "Yvqh9W_generateButton",
2046
+ "galleryRatioList": "Yvqh9W_galleryRatioList",
2047
+ "taskPrompt": "Yvqh9W_taskPrompt",
2048
+ "lightboxZoomLevel": "Yvqh9W_lightboxZoomLevel",
2049
+ "historyPrompt": "Yvqh9W_historyPrompt",
2050
+ "ecommercePlanMini": "Yvqh9W_ecommercePlanMini",
2051
+ "sessionTabLabel": "Yvqh9W_sessionTabLabel",
2052
+ "prompt": "Yvqh9W_prompt",
1800
2053
  "lightboxMeta": "Yvqh9W_lightboxMeta",
1801
- "lightboxDownload": "Yvqh9W_lightboxDownload",
1802
- "historyMeta": "Yvqh9W_historyMeta",
2054
+ "modelLabel": "Yvqh9W_modelLabel",
2055
+ "gallerySelectionBar": "Yvqh9W_gallerySelectionBar",
2056
+ "galleryCardActions": "Yvqh9W_galleryCardActions",
2057
+ "galleryAvatar": "Yvqh9W_galleryAvatar",
2058
+ "githubLink": "Yvqh9W_githubLink",
2059
+ "galleryToolbar": "Yvqh9W_galleryToolbar",
2060
+ "galleryCardFooter": "Yvqh9W_galleryCardFooter",
2061
+ "compareToggle": "Yvqh9W_compareToggle",
2062
+ "historySearch": "Yvqh9W_historySearch",
2063
+ "lightboxFigure": "Yvqh9W_lightboxFigure",
2064
+ "uploadBox": "Yvqh9W_uploadBox",
2065
+ "galleryFilter": "Yvqh9W_galleryFilter",
2066
+ "galleryTagEditor": "Yvqh9W_galleryTagEditor",
2067
+ "download": "Yvqh9W_download",
2068
+ "galleryToolbarActions": "Yvqh9W_galleryToolbarActions",
2069
+ "imageCard": "Yvqh9W_imageCard",
2070
+ "comparisonFullscreenGrid": "Yvqh9W_comparisonFullscreenGrid",
2071
+ "gallerySelect": "Yvqh9W_gallerySelect",
2072
+ "taskTrayHeader": "Yvqh9W_taskTrayHeader",
2073
+ "ecommerceResultsActions": "Yvqh9W_ecommerceResultsActions",
2074
+ "topNav": "Yvqh9W_topNav",
2075
+ "connectionStatus": "Yvqh9W_connectionStatus",
2076
+ "ecommercePlanWarn": "Yvqh9W_ecommercePlanWarn",
2077
+ "paramLabel": "Yvqh9W_paramLabel",
2078
+ "ecommerceGroup": "Yvqh9W_ecommerceGroup",
2079
+ "canvasEmptyIcon": "Yvqh9W_canvasEmptyIcon",
2080
+ "sessionTabIcon": "Yvqh9W_sessionTabIcon",
2081
+ "historyAction": "Yvqh9W_historyAction",
2082
+ "ecommerceSlotCard": "Yvqh9W_ecommerceSlotCard",
2083
+ "panelHeaderActions": "Yvqh9W_panelHeaderActions",
2084
+ "modelMenuList": "Yvqh9W_modelMenuList",
2085
+ "taskTrayToggle": "Yvqh9W_taskTrayToggle",
2086
+ "updateActions": "Yvqh9W_updateActions",
2087
+ "uploadIcon": "Yvqh9W_uploadIcon",
1803
2088
  "canvasMeta": "Yvqh9W_canvasMeta",
1804
- "taskTrayChevron": "Yvqh9W_taskTrayChevron",
2089
+ "canvasHistoryTag": "Yvqh9W_canvasHistoryTag",
2090
+ "dshImageGenSpin": "Yvqh9W_dshImageGenSpin",
2091
+ "galleryImageButton": "Yvqh9W_galleryImageButton",
2092
+ "gallerySearch": "Yvqh9W_gallerySearch",
1805
2093
  "historyItem": "Yvqh9W_historyItem",
2094
+ "promptCount": "Yvqh9W_promptCount",
2095
+ "grid": "Yvqh9W_grid",
2096
+ "historyHeaderActions": "Yvqh9W_historyHeaderActions",
2097
+ "view": "Yvqh9W_view",
2098
+ "taskStatus": "Yvqh9W_taskStatus",
2099
+ "taskRow": "Yvqh9W_taskRow",
2100
+ "galleryCard": "Yvqh9W_galleryCard",
2101
+ "footer": "Yvqh9W_footer",
2102
+ "galleryImage": "Yvqh9W_galleryImage",
2103
+ "ecommerceGroupGrid": "Yvqh9W_ecommerceGroupGrid",
2104
+ "panelHeader": "Yvqh9W_panelHeader",
2105
+ "lightboxDownload": "Yvqh9W_lightboxDownload",
2106
+ "ecommerceSection": "Yvqh9W_ecommerceSection",
2107
+ "hiddenFile": "Yvqh9W_hiddenFile",
2108
+ "galleryRatio": "Yvqh9W_galleryRatio",
2109
+ "ecommerceResultsEmpty": "Yvqh9W_ecommerceResultsEmpty",
2110
+ "historyMain": "Yvqh9W_historyMain",
2111
+ "configGuide": "Yvqh9W_configGuide",
2112
+ "connectionDot": "Yvqh9W_connectionDot",
2113
+ "historyFilters": "Yvqh9W_historyFilters",
1806
2114
  "config": "Yvqh9W_config",
1807
- "optionRow": "Yvqh9W_optionRow",
2115
+ "ecommerceFooterBody": "Yvqh9W_ecommerceFooterBody",
2116
+ "modePill": "Yvqh9W_modePill",
2117
+ "ecommerceTaskActions": "Yvqh9W_ecommerceTaskActions",
2118
+ "reference": "Yvqh9W_reference",
2119
+ "galleryAdd": "Yvqh9W_galleryAdd",
2120
+ "gallerySort": "Yvqh9W_gallerySort",
2121
+ "galleryViewToggle": "Yvqh9W_galleryViewToggle",
2122
+ "galleryMasonry": "Yvqh9W_galleryMasonry",
2123
+ "galleryRemove": "Yvqh9W_galleryRemove",
2124
+ "galleryTagInput": "Yvqh9W_galleryTagInput",
2125
+ "updateText": "Yvqh9W_updateText",
2126
+ "generation": "Yvqh9W_generation",
2127
+ "ecommerceAdvancedToggle": "Yvqh9W_ecommerceAdvancedToggle",
2128
+ "image": "Yvqh9W_image",
2129
+ "historyMeta": "Yvqh9W_historyMeta",
1808
2130
  "modelWrap": "Yvqh9W_modelWrap",
1809
- "modelSelect": "Yvqh9W_modelSelect",
1810
- "lightboxTool": "Yvqh9W_lightboxTool",
1811
- "dshImageGenToastIn": "Yvqh9W_dshImageGenToastIn",
2131
+ "lightboxStage": "Yvqh9W_lightboxStage",
2132
+ "lightboxIndex": "Yvqh9W_lightboxIndex",
1812
2133
  "galleryFilterCount": "Yvqh9W_galleryFilterCount",
1813
- "galleryFilterDivider": "Yvqh9W_galleryFilterDivider",
1814
- "galleryClear": "Yvqh9W_galleryClear",
1815
- "galleryCardFooter": "Yvqh9W_galleryCardFooter",
1816
- "galleryTagEditor": "Yvqh9W_galleryTagEditor",
2134
+ "galleryTagFilterList": "Yvqh9W_galleryTagFilterList",
2135
+ "comparisonGrid": "Yvqh9W_comparisonGrid",
2136
+ "historyClear": "Yvqh9W_historyClear",
1817
2137
  "studio": "Yvqh9W_studio",
2138
+ "ecommercePrimaryAction": "Yvqh9W_ecommercePrimaryAction",
2139
+ "previewBadge": "Yvqh9W_previewBadge",
2140
+ "chatToggle": "Yvqh9W_chatToggle",
2141
+ "lightboxTool": "Yvqh9W_lightboxTool",
2142
+ "historyList": "Yvqh9W_historyList",
2143
+ "ecommerceAdvancedBody": "Yvqh9W_ecommerceAdvancedBody",
2144
+ "dshImageGenToastIn": "Yvqh9W_dshImageGenToastIn",
2145
+ "history": "Yvqh9W_history",
2146
+ "modelMenuItem": "Yvqh9W_modelMenuItem",
2147
+ "sessionTabs": "Yvqh9W_sessionTabs",
2148
+ "ecommerceStructureGrid": "Yvqh9W_ecommerceStructureGrid",
2149
+ "canvasStateHint": "Yvqh9W_canvasStateHint",
2150
+ "ecommerceSectionHint": "Yvqh9W_ecommerceSectionHint",
2151
+ "ecommerceUploadHero": "Yvqh9W_ecommerceUploadHero",
1818
2152
  "referenceImage": "Yvqh9W_referenceImage",
1819
- "entry": "Yvqh9W_entry",
1820
- "galleryAdd": "Yvqh9W_galleryAdd",
1821
- "lightboxClose": "Yvqh9W_lightboxClose",
1822
- "galleryToolbarActions": "Yvqh9W_galleryToolbarActions",
1823
- "lightboxIndex": "Yvqh9W_lightboxIndex",
1824
- "paramHint": "Yvqh9W_paramHint",
1825
- "historyAction": "Yvqh9W_historyAction",
1826
- "lightboxImage": "Yvqh9W_lightboxImage",
1827
- "githubLink": "Yvqh9W_githubLink",
1828
- "hiddenFile": "Yvqh9W_hiddenFile",
1829
- "lightboxZoomLevel": "Yvqh9W_lightboxZoomLevel",
1830
- "galleryImage": "Yvqh9W_galleryImage",
1831
- "galleryBulkButton": "Yvqh9W_galleryBulkButton",
1832
- "gallerySearch": "Yvqh9W_gallerySearch",
1833
- "historySearch": "Yvqh9W_historySearch",
1834
- "lightboxScaleFrame": "Yvqh9W_lightboxScaleFrame",
1835
- "historyThumb": "Yvqh9W_historyThumb",
1836
- "galleryFilters": "Yvqh9W_galleryFilters",
1837
- "lightboxCopy": "Yvqh9W_lightboxCopy",
1838
- "taskTrayClose": "Yvqh9W_taskTrayClose",
1839
- "galleryToast": "Yvqh9W_galleryToast",
2153
+ "galleryTagEdit": "Yvqh9W_galleryTagEdit",
2154
+ "ecommerceTaskCard": "Yvqh9W_ecommerceTaskCard",
2155
+ "ecommercePlanNote": "Yvqh9W_ecommercePlanNote",
2156
+ "ecommerceActionChip": "Yvqh9W_ecommerceActionChip",
2157
+ "canvasError": "Yvqh9W_canvasError",
1840
2158
  "galleryFilterHeading": "Yvqh9W_galleryFilterHeading",
1841
- "historyThumbPlaceholder": "Yvqh9W_historyThumbPlaceholder",
1842
- "lightboxFigure": "Yvqh9W_lightboxFigure",
1843
- "lightboxStage": "Yvqh9W_lightboxStage"
2159
+ "taskTrayClose": "Yvqh9W_taskTrayClose",
2160
+ "uploadHint": "Yvqh9W_uploadHint",
2161
+ "optionPill": "Yvqh9W_optionPill",
2162
+ "gallerySelectionClear": "Yvqh9W_gallerySelectionClear",
2163
+ "updateRelease": "Yvqh9W_updateRelease",
2164
+ "historyNew": "Yvqh9W_historyNew",
2165
+ "canvasBody": "Yvqh9W_canvasBody",
2166
+ "panel": "Yvqh9W_panel",
2167
+ "historyInfo": "Yvqh9W_historyInfo",
2168
+ "ecommerceAssetAdd": "Yvqh9W_ecommerceAssetAdd",
2169
+ "panelTitle": "Yvqh9W_panelTitle",
2170
+ "galleryFilters": "Yvqh9W_galleryFilters",
2171
+ "compareControl": "Yvqh9W_compareControl",
2172
+ "galleryCardAction": "Yvqh9W_galleryCardAction"
1844
2173
  };
1845
2174
  //#endregion
1846
2175
  //#region src/client/ImageGenPanel.tsx
@@ -1895,9 +2224,78 @@ window.__ModuleLoader__.load({
1895
2224
  "high"
1896
2225
  ];
1897
2226
  const REF_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
2227
+ const CONVERSATION_IMAGE_MAX_DIMENSION = 2e3;
2228
+ const CONVERSATION_IMAGE_JPEG_QUALITY = .9;
1898
2229
  const PREVIEW_SCALE_MIN = .5;
1899
2230
  const PREVIEW_SCALE_MAX = 3;
1900
2231
  const PREVIEW_SCALE_STEP = .25;
2232
+ const CONFIG_COLLAPSED_STORAGE_KEY = "dsh-imagegen-config-collapsed";
2233
+ const ECOMMERCE_DRAFT_STORAGE_KEY = "dsh-imagegen-ecommerce-draft";
2234
+ /** Reference roles an uploaded product asset can play (slot selections can
2235
+ * also pick 'none'). */
2236
+ const ECOMMERCE_ASSET_ROLES = [
2237
+ "product",
2238
+ "packaging",
2239
+ "detail",
2240
+ "style"
2241
+ ];
2242
+ const MAX_ECOMMERCE_ASSETS = 4;
2243
+ const ECOMMERCE_ROLE_PROMPT_LABELS = {
2244
+ product: "商品主体",
2245
+ packaging: "包装",
2246
+ detail: "细节/角度",
2247
+ style: "风格参考"
2248
+ };
2249
+ const PRODUCT_SET_SLOTS = [
2250
+ {
2251
+ key: "main",
2252
+ label: "主图",
2253
+ description: "干净背景,突出商品主体",
2254
+ count: 1,
2255
+ enabled: true,
2256
+ refRole: "product"
2257
+ },
2258
+ {
2259
+ key: "selling-point",
2260
+ label: "卖点图",
2261
+ description: "用画面展示商品核心卖点",
2262
+ count: 2,
2263
+ enabled: true,
2264
+ refRole: "product"
2265
+ },
2266
+ {
2267
+ key: "scene",
2268
+ label: "场景图",
2269
+ description: "真实生活或使用场景",
2270
+ count: 2,
2271
+ enabled: true,
2272
+ refRole: "product"
2273
+ },
2274
+ {
2275
+ key: "detail",
2276
+ label: "细节图",
2277
+ description: "材质、结构或工艺特写",
2278
+ count: 1,
2279
+ enabled: true,
2280
+ refRole: "detail"
2281
+ },
2282
+ {
2283
+ key: "spec",
2284
+ label: "规格图",
2285
+ description: "尺寸、容量或参数展示",
2286
+ count: 1,
2287
+ enabled: false,
2288
+ refRole: "product"
2289
+ },
2290
+ {
2291
+ key: "model",
2292
+ label: "使用图",
2293
+ description: "人物上手或穿戴效果",
2294
+ count: 1,
2295
+ enabled: false,
2296
+ refRole: "product"
2297
+ }
2298
+ ];
1901
2299
  /** Legacy pixel sizes saved by older versions, mapped onto the current
1902
2300
  * aspect-ratio vocabulary so restoring old history entries still works. */
1903
2301
  const LEGACY_SIZE_TO_RATIO = {
@@ -1927,6 +2325,37 @@ window.__ModuleLoader__.load({
1927
2325
  function clampPreviewScale(scale) {
1928
2326
  return Math.min(PREVIEW_SCALE_MAX, Math.max(PREVIEW_SCALE_MIN, scale));
1929
2327
  }
2328
+ /** Keep the image canvas preference across panel remounts without making it
2329
+ * part of the host settings document. */
2330
+ function readConfigCollapsed() {
2331
+ try {
2332
+ return window.localStorage.getItem(CONFIG_COLLAPSED_STORAGE_KEY) === "true";
2333
+ } catch {
2334
+ return false;
2335
+ }
2336
+ }
2337
+ const CHAT_COLLAPSED_STORAGE_KEY = "dsh-imagegen:chat-collapsed";
2338
+ const CONFIG_WIDTH_STORAGE_KEY = "dsh-imagegen:config-width";
2339
+ const CONFIG_WIDTH_MIN = 260;
2340
+ const CONFIG_WIDTH_MAX = 480;
2341
+ const CONFIG_WIDTH_DEFAULT = 300;
2342
+ /** The chat pane starts collapsed unless the user explicitly opened it. */
2343
+ function readChatOpen() {
2344
+ try {
2345
+ return window.localStorage.getItem(CHAT_COLLAPSED_STORAGE_KEY) === "open";
2346
+ } catch {
2347
+ return false;
2348
+ }
2349
+ }
2350
+ function readConfigWidth() {
2351
+ try {
2352
+ const raw = window.localStorage.getItem(CONFIG_WIDTH_STORAGE_KEY);
2353
+ if (raw === null) return CONFIG_WIDTH_DEFAULT;
2354
+ const value = Number(raw);
2355
+ if (Number.isFinite(value) && value > 0) return Math.min(CONFIG_WIDTH_MAX, Math.max(CONFIG_WIDTH_MIN, Math.round(value)));
2356
+ } catch {}
2357
+ return CONFIG_WIDTH_DEFAULT;
2358
+ }
1930
2359
  /** Read the current config from the settings scope snapshot. */
1931
2360
  function useConfig(scope) {
1932
2361
  const [value, setValue] = (0, react.useState)(scope.getSnapshot().value);
@@ -1964,6 +2393,127 @@ window.__ModuleLoader__.load({
1964
2393
  function srcOf(image) {
1965
2394
  return `data:${image.mime};base64,${image.b64}`;
1966
2395
  }
2396
+ /** Decode one durable conversation attachment into the panel's image shape. */
2397
+ async function attachmentToGenerated(ref) {
2398
+ const query = new URLSearchParams({
2399
+ attachment_id: String(ref.attachmentId),
2400
+ media_type: ref.mediaType,
2401
+ bytes: String(ref.bytes),
2402
+ width: String(ref.width),
2403
+ height: String(ref.height)
2404
+ });
2405
+ const response = await fetch(`${AGENT_IMAGE_API}?${query.toString()}`);
2406
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
2407
+ const blob = await response.blob();
2408
+ const dataUrl = await new Promise((resolve, reject) => {
2409
+ const reader = new FileReader();
2410
+ reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : "");
2411
+ reader.onerror = () => reject(/* @__PURE__ */ new Error("image read failed"));
2412
+ reader.readAsDataURL(blob);
2413
+ });
2414
+ const comma = dataUrl.indexOf(",");
2415
+ if (comma < 0) throw new Error("image decode failed");
2416
+ return {
2417
+ b64: dataUrl.slice(comma + 1),
2418
+ mime: ref.mediaType
2419
+ };
2420
+ }
2421
+ /** Convert a generated image into the browser-owned draft format. */
2422
+ function generatedImageToFile(image, index) {
2423
+ const binary = atob(image.b64);
2424
+ const bytes = new Uint8Array(binary.length);
2425
+ for (let offset = 0; offset < binary.length; offset += 1) bytes[offset] = binary.charCodeAt(offset);
2426
+ return new File([bytes], `dsh-image-${index + 1}.${extensionOf(image.mime)}`, { type: image.mime });
2427
+ }
2428
+ /** Decode a data URL into a browser File for the native composer. */
2429
+ function dataUrlToFile(dataUrl, name) {
2430
+ const match = /^data:(image\/(?:png|jpeg|webp|gif));base64,(.*)$/su.exec(dataUrl);
2431
+ if (match === null || match[1] === void 0 || match[2] === void 0) throw new Error("image processing returned an invalid data URL");
2432
+ const binary = atob(match[2]);
2433
+ const bytes = new Uint8Array(binary.length);
2434
+ for (let offset = 0; offset < binary.length; offset += 1) bytes[offset] = binary.charCodeAt(offset);
2435
+ return new File([bytes], name, { type: match[1] });
2436
+ }
2437
+ /** Read intrinsic dimensions without changing the original preview. */
2438
+ function imageDimensions(dataUrl) {
2439
+ return new Promise((resolve, reject) => {
2440
+ const image = new Image();
2441
+ image.onload = () => {
2442
+ const width = image.naturalWidth || image.width;
2443
+ const height = image.naturalHeight || image.height;
2444
+ if (width < 1 || height < 1) reject(/* @__PURE__ */ new Error("image dimensions are unavailable"));
2445
+ else resolve({
2446
+ width,
2447
+ height
2448
+ });
2449
+ };
2450
+ image.onerror = () => reject(/* @__PURE__ */ new Error("image decode failed"));
2451
+ image.src = dataUrl;
2452
+ });
2453
+ }
2454
+ /** Prepare the smaller conversation copy required by the host attachment policy. */
2455
+ async function prepareConversationImage(image, index) {
2456
+ const dataUrl = srcOf(image);
2457
+ const { width, height } = await imageDimensions(dataUrl);
2458
+ const longestSide = Math.max(width, height);
2459
+ if (longestSide <= CONVERSATION_IMAGE_MAX_DIMENSION) return {
2460
+ file: generatedImageToFile(image, index),
2461
+ dataUrl
2462
+ };
2463
+ const scale = CONVERSATION_IMAGE_MAX_DIMENSION / longestSide;
2464
+ const targetWidth = Math.max(1, Math.round(width * scale));
2465
+ const targetHeight = Math.max(1, Math.round(height * scale));
2466
+ const canvas = document.createElement("canvas");
2467
+ canvas.width = targetWidth;
2468
+ canvas.height = targetHeight;
2469
+ const context = canvas.getContext("2d");
2470
+ if (context === null) throw new Error("image resize is unavailable in this browser");
2471
+ const source = await new Promise((resolve, reject) => {
2472
+ const sourceImage = new Image();
2473
+ sourceImage.onload = () => resolve(sourceImage);
2474
+ sourceImage.onerror = () => reject(/* @__PURE__ */ new Error("image decode failed"));
2475
+ sourceImage.src = dataUrl;
2476
+ });
2477
+ context.drawImage(source, 0, 0, targetWidth, targetHeight);
2478
+ const resizedDataUrl = canvas.toDataURL("image/jpeg", CONVERSATION_IMAGE_JPEG_QUALITY);
2479
+ return {
2480
+ dataUrl: resizedDataUrl,
2481
+ file: dataUrlToFile(resizedDataUrl, `dsh-image-${index + 1}.jpg`)
2482
+ };
2483
+ }
2484
+ /** Follow the native session selection while the image panel stays mounted. */
2485
+ function useCurrentSessionId(sessions) {
2486
+ const [sessionId, setSessionId] = (0, react.useState)(() => sessions?.list.getSnapshot().current);
2487
+ (0, react.useEffect)(() => {
2488
+ if (sessions === void 0) {
2489
+ setSessionId(void 0);
2490
+ return;
2491
+ }
2492
+ const sync = () => {
2493
+ setSessionId(sessions.list.getSnapshot().current);
2494
+ };
2495
+ sync();
2496
+ return sessions.list.subscribe(sync);
2497
+ }, [sessions]);
2498
+ return sessionId;
2499
+ }
2500
+ /** Find the host mounted in the shell's left navigation region. */
2501
+ function useSidebarHistoryHost() {
2502
+ const [host, setHost] = (0, react.useState)(() => document.querySelector("[data-dsh-imagegen-history-host]"));
2503
+ (0, react.useEffect)(() => {
2504
+ const sync = () => {
2505
+ setHost(document.querySelector("[data-dsh-imagegen-history-host]"));
2506
+ };
2507
+ sync();
2508
+ const observer = new MutationObserver(sync);
2509
+ observer.observe(document.body, {
2510
+ childList: true,
2511
+ subtree: true
2512
+ });
2513
+ return () => observer.disconnect();
2514
+ }, []);
2515
+ return host;
2516
+ }
1967
2517
  /** Fetch persisted history image refs and decode them back to in-memory
1968
2518
  * GeneratedImage[] (base64), so the canvas/preview can reuse the same
1969
2519
  * rendering path as a fresh generation. */
@@ -1992,9 +2542,66 @@ window.__ModuleLoader__.load({
1992
2542
  const pad = (n) => String(n).padStart(2, "0");
1993
2543
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
1994
2544
  }
2545
+ function defaultEcommerceDraft() {
2546
+ return {
2547
+ projectId: "",
2548
+ projectName: "",
2549
+ category: "通用商品",
2550
+ platform: "通用",
2551
+ language: "中文",
2552
+ size: "1:1",
2553
+ productName: "",
2554
+ sellingPoints: "",
2555
+ protectedFeatures: "",
2556
+ styleHint: "",
2557
+ slots: PRODUCT_SET_SLOTS.map((slot) => ({ ...slot }))
2558
+ };
2559
+ }
2560
+ function ecommercePrompt(draft, slot) {
2561
+ const points = draft.sellingPoints.trim() || "突出商品真实材质、结构和核心价值";
2562
+ const protectedFeatures = draft.protectedFeatures.trim() || "保持商品颜色、形状、Logo、包装文字和结构真实,不添加不存在的配件";
2563
+ const refClause = slot.refRole !== void 0 && slot.refRole !== "none" ? `本图以上传的${ECOMMERCE_ROLE_PROMPT_LABELS[slot.refRole]}图片为参考,商品与风格必须与参考图保持一致;` : "";
2564
+ return `电商${slot.label}:为${draft.productName.trim() || "该商品"}制作${slot.description}。商品品类:${draft.category};平台:${draft.platform};语言:${draft.language}。商品卖点:${points}。必须遵守:${protectedFeatures}。${refClause}整体要求:商品主体清晰、比例真实、光线自然、画面干净、适合电商发布;${draft.styleHint.trim()}`;
2565
+ }
2566
+ /** Consistency prefix for slots generated after the main image exists. */
2567
+ function withAnchorNote(prompt) {
2568
+ return "商品套图一致性约束:附件是本套商品的主图,图中商品(外形、颜色、材质、Logo、包装文字)必须与附件完全一致,不得重新发明商品。" + prompt;
2569
+ }
2570
+ function modelsOfHistoryEntry(entry) {
2571
+ return entry.comparisonModels?.length !== void 0 && entry.comparisonModels.length > 1 ? entry.comparisonModels : [entry.model];
2572
+ }
2573
+ /** Comparison runs collapse by comparisonId, product sets by projectId. */
2574
+ function historyGroupKey(entry) {
2575
+ if (entry.comparisonId !== void 0) return entry.comparisonId;
2576
+ if (entry.workflow === "ecommerce" && entry.projectId !== void 0) return `project:${entry.projectId}`;
2577
+ return entry.id;
2578
+ }
2579
+ /** Collapse the per-model history rows that belong to one comparison run. */
2580
+ function groupHistoryEntries(entries) {
2581
+ const groups = /* @__PURE__ */ new Map();
2582
+ for (const entry of entries) {
2583
+ const key = historyGroupKey(entry);
2584
+ const existing = groups.get(key);
2585
+ if (existing === void 0) groups.set(key, {
2586
+ key,
2587
+ entries: [entry],
2588
+ models: modelsOfHistoryEntry(entry)
2589
+ });
2590
+ else {
2591
+ existing.entries.push(entry);
2592
+ existing.models = [.../* @__PURE__ */ new Set([...existing.models, ...modelsOfHistoryEntry(entry)])];
2593
+ }
2594
+ }
2595
+ return [...groups.values()];
2596
+ }
2597
+ function newComparisonId() {
2598
+ const cryptoApi = globalThis.crypto;
2599
+ if (typeof cryptoApi?.randomUUID === "function") return cryptoApi.randomUUID();
2600
+ return `comparison-${Date.now()}-${Math.random().toString(36).slice(2)}`;
2601
+ }
1995
2602
  /** Render the studio. */
1996
2603
  function ImageGenPanel(props) {
1997
- const { api, scope } = props;
2604
+ const { api, scope, sessions, conversation } = props;
1998
2605
  const config = useConfig(scope);
1999
2606
  const enabled = config?.enabled ?? true;
2000
2607
  const modelOptions = imageModelOptions(config);
@@ -2007,6 +2614,12 @@ window.__ModuleLoader__.load({
2007
2614
  const apiKeySet = (config?.channels ?? []).length > 0 ? channelKeySet : legacyKeySet;
2008
2615
  const connected = enabled && configured && apiKeySet;
2009
2616
  const [tab, setTab] = (0, react.useState)("text");
2617
+ const [workspace, setWorkspace] = (0, react.useState)("normal");
2618
+ /** Switch to a normal-generation tab, leaving any task workspace. */
2619
+ const openTab = (next) => {
2620
+ setWorkspace("normal");
2621
+ setTab(next);
2622
+ };
2010
2623
  const [prompt, setPrompt] = (0, react.useState)("");
2011
2624
  const [size, setSize] = (0, react.useState)("auto");
2012
2625
  const [quality, setQuality] = (0, react.useState)("auto");
@@ -2018,6 +2631,9 @@ window.__ModuleLoader__.load({
2018
2631
  const [modelOpen, setModelOpen] = (0, react.useState)(false);
2019
2632
  const [refImage, setRefImage] = (0, react.useState)(null);
2020
2633
  const [images, setImages] = (0, react.useState)([]);
2634
+ const [addingToConversation, setAddingToConversation] = (0, react.useState)(null);
2635
+ const [galleryConversationAddingId, setGalleryConversationAddingId] = (0, react.useState)(null);
2636
+ const [conversationMessage, setConversationMessage] = (0, react.useState)(null);
2021
2637
  const [error, setError] = (0, react.useState)(null);
2022
2638
  const [submitting, setSubmitting] = (0, react.useState)(false);
2023
2639
  const [enhancing, setEnhancing] = (0, react.useState)(false);
@@ -2051,9 +2667,45 @@ window.__ModuleLoader__.load({
2051
2667
  const [updateResult, setUpdateResult] = (0, react.useState)(null);
2052
2668
  const [libraryOpen, setLibraryOpen] = (0, react.useState)(false);
2053
2669
  const [tasks, setTasks] = (0, react.useState)([]);
2670
+ const tasksRef = (0, react.useRef)([]);
2054
2671
  const [taskTrayOpen, setTaskTrayOpen] = (0, react.useState)(false);
2055
2672
  const [comparison, setComparison] = (0, react.useState)(null);
2056
2673
  const [comparisonFullscreen, setComparisonFullscreen] = (0, react.useState)(false);
2674
+ const [ecommerce, setEcommerce] = (0, react.useState)(() => {
2675
+ try {
2676
+ const saved = window.localStorage.getItem(ECOMMERCE_DRAFT_STORAGE_KEY);
2677
+ if (saved !== null) {
2678
+ const merged = {
2679
+ ...defaultEcommerceDraft(),
2680
+ ...JSON.parse(saved)
2681
+ };
2682
+ if (Array.isArray(merged.slots)) merged.slots = merged.slots.map((slot) => ({
2683
+ ...slot,
2684
+ refRole: slot.refRole ?? "product"
2685
+ }));
2686
+ return merged;
2687
+ }
2688
+ } catch {}
2689
+ return defaultEcommerceDraft();
2690
+ });
2691
+ const [ecommercePreview, setEcommercePreview] = (0, react.useState)(false);
2692
+ const [ecommerceGenerating, setEcommerceGenerating] = (0, react.useState)(false);
2693
+ const [ecommerceProjectId, setEcommerceProjectId] = (0, react.useState)(null);
2694
+ const [ecommerceAssets, setEcommerceAssets] = (0, react.useState)([]);
2695
+ /** History-restored product set currently shown in the results canvas. */
2696
+ const [ecommerceRestored, setEcommerceRestored] = (0, react.useState)(null);
2697
+ /** Pending main-image anchor: the main image task is in flight; once it
2698
+ * completes, the remaining slots are resubmitted with it as their shared
2699
+ * reference so every image in the set shows the same product. */
2700
+ const [ecommerceAnchor, setEcommerceAnchor] = (0, react.useState)(null);
2701
+ const [ecommerceRefOpen, setEcommerceRefOpen] = (0, react.useState)(false);
2702
+ const [configCollapsed, setConfigCollapsed] = (0, react.useState)(readConfigCollapsed);
2703
+ const [chatOpen, setChatOpen] = (0, react.useState)(readChatOpen);
2704
+ const [configWidth, setConfigWidth] = (0, react.useState)(readConfigWidth);
2705
+ const configAsideRef = (0, react.useRef)(null);
2706
+ const currentSessionId = useCurrentSessionId(sessions);
2707
+ const sidebarHistoryHost = useSidebarHistoryHost();
2708
+ const modeModels = tab === "edit" ? imageModels.filter((candidate) => describeModel(candidate).supportsEdit) : imageModels;
2057
2709
  const fileInput = (0, react.useRef)(null);
2058
2710
  const previewStage = (0, react.useRef)(null);
2059
2711
  const activeTasks = tasks.filter((task) => task.status === "queued" || task.status === "running");
@@ -2061,12 +2713,63 @@ window.__ModuleLoader__.load({
2061
2713
  const generating = submitting || activeTasks.length > 0;
2062
2714
  const elapsed = useElapsed(generating, activeTask?.startedAt ?? activeTask?.createdAt ?? null);
2063
2715
  (0, react.useEffect)(() => {
2064
- setModel((previous) => imageModels.includes(previous) ? previous : imageModels[0]);
2716
+ try {
2717
+ window.localStorage.setItem(ECOMMERCE_DRAFT_STORAGE_KEY, JSON.stringify(ecommerce));
2718
+ } catch {}
2719
+ }, [ecommerce]);
2720
+ (0, react.useEffect)(() => {
2721
+ try {
2722
+ window.localStorage.setItem(CONFIG_COLLAPSED_STORAGE_KEY, String(configCollapsed));
2723
+ } catch {}
2724
+ }, [configCollapsed]);
2725
+ (0, react.useEffect)(() => {
2726
+ if (chatOpen) delete document.documentElement.dataset.dshImagegenChatCollapsed;
2727
+ else document.documentElement.dataset.dshImagegenChatCollapsed = "1";
2728
+ try {
2729
+ window.localStorage.setItem(CHAT_COLLAPSED_STORAGE_KEY, chatOpen ? "open" : "collapsed");
2730
+ } catch {}
2731
+ }, [chatOpen]);
2732
+ (0, react.useEffect)(() => () => {
2733
+ delete document.documentElement.dataset.dshImagegenChatCollapsed;
2734
+ }, []);
2735
+ (0, react.useEffect)(() => {
2736
+ if (ecommerceAnchor === null) return;
2737
+ const anchor = ecommerceAnchor;
2738
+ const mains = tasks.filter((task) => anchor.mainTaskIds.includes(task.id));
2739
+ if (mains.length === 0) return;
2740
+ if (mains.every((task) => task.status === "failed" || task.status === "cancelled")) {
2741
+ setEcommerceAnchor(null);
2742
+ setError(tt("ecommerce.anchorFailed"));
2743
+ return;
2744
+ }
2745
+ const done = mains.find((task) => task.status === "completed" && task.result !== void 0 && task.result.images.length > 0);
2746
+ if (done === void 0) return;
2747
+ setEcommerceAnchor(null);
2748
+ const dataUrl = srcOf(done.result.images[0]);
2749
+ const requests = anchor.remaining.map((request) => ({
2750
+ ...request,
2751
+ mode: "edit",
2752
+ image: dataUrl,
2753
+ refName: "set-main-anchor",
2754
+ prompt: withAnchorNote(request.prompt)
2755
+ }));
2756
+ Promise.all(requests.map((request) => api.taskSubmit(request))).then((submitted) => {
2757
+ setTasks((previous) => [...submitted, ...previous]);
2758
+ }).catch((caught) => {
2759
+ setError(errorMessage(caught));
2760
+ });
2761
+ }, [
2762
+ api,
2763
+ tasks,
2764
+ ecommerceAnchor
2765
+ ]);
2766
+ (0, react.useEffect)(() => {
2767
+ setModel((previous) => modeModels.includes(previous) ? previous : modeModels[0] ?? "");
2065
2768
  setCompareModels((previous) => {
2066
- const retained = previous.filter((candidate) => imageModels.includes(candidate));
2067
- return retained.length > 0 ? retained : [imageModels[0]];
2769
+ const retained = previous.filter((candidate) => modeModels.includes(candidate));
2770
+ return retained.length > 0 ? retained : modeModels[0] === void 0 ? [] : [modeModels[0]];
2068
2771
  });
2069
- }, [imageModels.join("\0")]);
2772
+ }, [modeModels.join("\0")]);
2070
2773
  const filteredGallery = gallery.filter((entry) => {
2071
2774
  if (galleryFilter === "all") return true;
2072
2775
  if (galleryFilter === "text" || galleryFilter === "edit") return entry.mode === galleryFilter;
@@ -2074,10 +2777,11 @@ window.__ModuleLoader__.load({
2074
2777
  }).filter((entry) => galleryRatio === "all" || normalizeSize(entry.size) === galleryRatio).filter((entry) => galleryTagFilter === null || (entry.tags ?? []).includes(galleryTagFilter)).filter((entry) => galleryQuery.trim() === "" || `${entry.prompt} ${entry.model} ${(entry.tags ?? []).join(" ")}`.toLocaleLowerCase().includes(galleryQuery.trim().toLocaleLowerCase())).slice().sort((a, b) => gallerySort === "newest" ? b.createdAt - a.createdAt : a.createdAt - b.createdAt);
2075
2778
  const galleryTagOptions = [...new Set(gallery.flatMap((entry) => entry.tags ?? []))].sort((a, b) => a.localeCompare(b));
2076
2779
  const galleryModels = [.../* @__PURE__ */ new Set([...imageModels, ...gallery.map((entry) => entry.model)])];
2077
- const filteredHistory = history.filter((entry) => {
2780
+ const filteredHistory = groupHistoryEntries(history).filter((group) => group.entries.some((entry) => {
2078
2781
  const query = historyQuery.trim().toLocaleLowerCase();
2079
- return (query === "" || `${entry.prompt} ${entry.model}`.toLocaleLowerCase().includes(query)) && (historyModelFilter === "all" || entry.model === historyModelFilter) && (historyRatioFilter === "all" || normalizeSize(entry.size) === historyRatioFilter);
2080
- });
2782
+ const models = modelsOfHistoryEntry(entry);
2783
+ return (query === "" || `${entry.prompt} ${models.join(" ")}`.toLocaleLowerCase().includes(query)) && (historyModelFilter === "all" || models.includes(historyModelFilter)) && (historyRatioFilter === "all" || normalizeSize(entry.size) === historyRatioFilter);
2784
+ }));
2081
2785
  (0, react.useEffect)(() => {
2082
2786
  let disposed = false;
2083
2787
  api.historyList().then((entries) => {
@@ -2090,11 +2794,31 @@ window.__ModuleLoader__.load({
2090
2794
  disposed = true;
2091
2795
  };
2092
2796
  }, [api]);
2797
+ (0, react.useEffect)(() => {
2798
+ const onChatImages = (event) => {
2799
+ const detail = event.detail;
2800
+ if (detail === void 0 || currentSessionId === void 0 || detail.sessionId !== currentSessionId) return;
2801
+ Promise.all(detail.refs.map(attachmentToGenerated)).then((next) => {
2802
+ openTab("text");
2803
+ setImages(next);
2804
+ setComparison(null);
2805
+ setViewingHistoryId(null);
2806
+ setGalleryViewingId(null);
2807
+ setError(null);
2808
+ }).catch((caught) => {
2809
+ setError(errorMessage(caught));
2810
+ });
2811
+ };
2812
+ document.addEventListener(CHAT_IMAGE_EVENT, onChatImages);
2813
+ return () => document.removeEventListener(CHAT_IMAGE_EVENT, onChatImages);
2814
+ }, [currentSessionId]);
2093
2815
  (0, react.useEffect)(() => {
2094
2816
  let disposed = false;
2095
2817
  const refresh = () => {
2096
2818
  api.taskList().then((next) => {
2097
2819
  if (disposed) return;
2820
+ const newlyCompleted = next.filter((task) => task.status === "completed" && task.result !== void 0 && !tasksRef.current.some((old) => old.id === task.id && old.status === "completed"));
2821
+ tasksRef.current = next;
2098
2822
  setTasks((previous) => {
2099
2823
  const completed = next.find((task) => task.status === "completed" && !previous.some((old) => old.id === task.id && old.status === "completed") && !comparison?.taskIds.includes(task.id));
2100
2824
  if (completed?.result !== void 0) {
@@ -2104,6 +2828,9 @@ window.__ModuleLoader__.load({
2104
2828
  }
2105
2829
  return next;
2106
2830
  });
2831
+ if (newlyCompleted.length > 0) api.historyList().then((entries) => {
2832
+ if (!disposed) setHistory(entries);
2833
+ }).catch(() => {});
2107
2834
  }).catch(() => {});
2108
2835
  };
2109
2836
  refresh();
@@ -2206,6 +2933,39 @@ window.__ModuleLoader__.load({
2206
2933
  };
2207
2934
  reader.readAsDataURL(file);
2208
2935
  };
2936
+ /** Read uploaded product assets into session-only data-URL chips, capped at
2937
+ * MAX_ECOMMERCE_ASSETS. Each starts as the product-role reference. */
2938
+ const acceptEcommerceFiles = (files) => {
2939
+ if (files === void 0) return;
2940
+ const incoming = Array.from(files).filter((file) => file.type.startsWith("image/") && file.size <= REF_IMAGE_MAX_BYTES);
2941
+ if (incoming.length === 0) {
2942
+ setError(tt("edit.uploadHint"));
2943
+ return;
2944
+ }
2945
+ for (const file of incoming) {
2946
+ const reader = new FileReader();
2947
+ reader.onload = () => {
2948
+ if (typeof reader.result !== "string") return;
2949
+ const dataUrl = reader.result;
2950
+ setEcommerceAssets((previous) => {
2951
+ if (previous.length >= MAX_ECOMMERCE_ASSETS) {
2952
+ setError(tt("ecommerce.assetsFull"));
2953
+ return previous;
2954
+ }
2955
+ return [...previous, {
2956
+ id: newComparisonId(),
2957
+ dataUrl,
2958
+ name: file.name,
2959
+ role: "product"
2960
+ }];
2961
+ });
2962
+ };
2963
+ reader.onerror = () => {
2964
+ setError(tt("edit.uploadHint"));
2965
+ };
2966
+ reader.readAsDataURL(file);
2967
+ }
2968
+ };
2209
2969
  /** Run one generation. */
2210
2970
  const handleGenerate = async () => {
2211
2971
  if (submitting) return;
@@ -2227,8 +2987,8 @@ window.__ModuleLoader__.load({
2227
2987
  return;
2228
2988
  }
2229
2989
  const request = {
2230
- mode: tab === "gallery" ? "text" : tab,
2231
- model: imageModels.includes(model) ? model : imageModels[0],
2990
+ mode: tab === "edit" ? "edit" : "text",
2991
+ model: modeModels.includes(model) ? model : modeModels[0] ?? "",
2232
2992
  prompt: promptText,
2233
2993
  size,
2234
2994
  quality,
@@ -2241,27 +3001,200 @@ window.__ModuleLoader__.load({
2241
3001
  setError(null);
2242
3002
  setSubmitting(true);
2243
3003
  try {
2244
- const targetModels = (compareEnabled ? compareModels : [request.model]).filter((candidate) => imageModels.includes(candidate));
3004
+ const targetModels = (compareEnabled ? compareModels : [request.model]).filter((candidate) => modeModels.includes(candidate));
2245
3005
  if (targetModels.length === 0) {
2246
3006
  setError(tt("compare.selectRequired"));
2247
3007
  return;
2248
3008
  }
3009
+ const comparisonId = targetModels.length > 1 ? newComparisonId() : void 0;
3010
+ const comparisonFields = comparisonId === void 0 ? {} : {
3011
+ comparisonId,
3012
+ comparisonModels: targetModels
3013
+ };
2249
3014
  const submitted = await Promise.all(targetModels.map((targetModel) => api.taskSubmit({
2250
3015
  ...request,
2251
- model: targetModel
3016
+ model: targetModel,
3017
+ ...comparisonFields
2252
3018
  })));
2253
3019
  setTasks((previous) => [...submitted, ...previous.filter((item) => !submitted.some((task) => task.id === item.id))]);
2254
- setComparison(targetModels.length > 1 ? {
3020
+ setComparison(comparisonId === void 0 ? null : {
2255
3021
  taskIds: submitted.map((task) => task.id),
2256
- prompt: promptText
2257
- } : null);
3022
+ prompt: promptText,
3023
+ comparisonId
3024
+ });
2258
3025
  } catch (caught) {
2259
3026
  setError(errorMessage(caught));
2260
3027
  } finally {
2261
3028
  setSubmitting(false);
2262
3029
  }
2263
3030
  };
2264
- /** Open the full-screen image preview at a given index. */
3031
+ const handleEcommerceGenerate = async () => {
3032
+ if (ecommerceGenerateDisabled) return;
3033
+ if (!enabled || !configured || !apiKeySet) {
3034
+ openSettingsGuide("generation");
3035
+ return;
3036
+ }
3037
+ const projectId = ecommerce.projectId || newComparisonId();
3038
+ const buildRequest = (slot, index) => {
3039
+ const refRole = slot.refRole ?? "product";
3040
+ const asset = refRole === "none" ? void 0 : ecommerceAssets.find((item) => item.role === refRole);
3041
+ return {
3042
+ mode: asset !== void 0 ? "edit" : "text",
3043
+ model: modeModels.includes(model) ? model : modeModels[0] ?? "",
3044
+ prompt: ecommercePrompt(ecommerce, slot),
3045
+ size: ecommerce.size,
3046
+ quality,
3047
+ n: 1,
3048
+ detail,
3049
+ ...defaultChannelId !== void 0 ? { channelId: defaultChannelId } : {},
3050
+ ...asset !== void 0 ? {
3051
+ image: asset.dataUrl,
3052
+ refName: asset.name
3053
+ } : {},
3054
+ workflow: "ecommerce",
3055
+ projectId,
3056
+ projectName: ecommerce.projectName.trim() || ecommerce.productName.trim(),
3057
+ slotKey: `${slot.key}-${index + 1}`,
3058
+ slotLabel: slot.label
3059
+ };
3060
+ };
3061
+ const mainSlots = ecommerceSlots.filter((slot) => slot.key === "main");
3062
+ const otherSlots = ecommerceSlots.filter((slot) => slot.key !== "main");
3063
+ const anchorChain = mainSlots.length > 0 && otherSlots.length > 0;
3064
+ const requests = (anchorChain ? mainSlots : ecommerceSlots).flatMap((slot) => Array.from({ length: slot.count }, (_, index) => buildRequest(slot, index)));
3065
+ const remaining = anchorChain ? otherSlots.flatMap((slot) => Array.from({ length: slot.count }, (_, index) => {
3066
+ const { image: _image, refName: _refName, ...rest } = buildRequest(slot, index);
3067
+ return rest;
3068
+ })) : [];
3069
+ setEcommerceGenerating(true);
3070
+ setSubmitting(true);
3071
+ setError(null);
3072
+ setEcommerceProjectId(projectId);
3073
+ setEcommerceRestored(null);
3074
+ setEcommerceAnchor(null);
3075
+ try {
3076
+ const submitted = await Promise.all(requests.map((request) => api.taskSubmit(request)));
3077
+ setTasks((previous) => [...submitted, ...previous]);
3078
+ setEcommercePreview(false);
3079
+ if (anchorChain) setEcommerceAnchor({
3080
+ projectId,
3081
+ mainTaskIds: submitted.map((task) => task.id),
3082
+ remaining
3083
+ });
3084
+ } catch (caught) {
3085
+ setError(errorMessage(caught));
3086
+ } finally {
3087
+ setSubmitting(false);
3088
+ setEcommerceGenerating(false);
3089
+ }
3090
+ };
3091
+ /** Start over with a fresh product draft (the old results stay in history). */
3092
+ const newEcommerceProduct = () => {
3093
+ setEcommerce(defaultEcommerceDraft());
3094
+ setEcommercePreview(false);
3095
+ setEcommerceProjectId(null);
3096
+ setEcommerceRestored(null);
3097
+ setEcommerceAnchor(null);
3098
+ setEcommerceAssets([]);
3099
+ setRefImage(null);
3100
+ setError(null);
3101
+ };
3102
+ /** Re-run every image of one slot with its original request. */
3103
+ const regenerateEcommerceSlot = async (label) => {
3104
+ if (ecommerceGenerating) return;
3105
+ const group = ecommerceMergedItems.filter((item) => item.label === label);
3106
+ if (group.length === 0) return;
3107
+ setEcommerceGenerating(true);
3108
+ setError(null);
3109
+ try {
3110
+ const submitted = await Promise.all(group.map((item) => api.taskSubmit({ ...item.source })));
3111
+ setTasks((previous) => [...submitted, ...previous]);
3112
+ } catch (caught) {
3113
+ setError(errorMessage(caught));
3114
+ } finally {
3115
+ setEcommerceGenerating(false);
3116
+ }
3117
+ };
3118
+ /** Open one persisted product set from history: rebuild the grouped results
3119
+ * canvas from its entries. Reference images are not persisted, so restored
3120
+ * edit-mode slots regenerate as text-to-image. */
3121
+ const viewEcommerceProject = async (group) => {
3122
+ const entry = group.entries[0];
3123
+ if (entry === void 0 || entry.projectId === void 0) return;
3124
+ try {
3125
+ const items = await Promise.all(group.entries.map(async (item) => ({
3126
+ id: item.id,
3127
+ label: item.slotLabel ?? "",
3128
+ slotKey: item.slotKey ?? "",
3129
+ status: "completed",
3130
+ model: item.model,
3131
+ prompt: item.prompt,
3132
+ images: await historyImagesToGenerated(item.images),
3133
+ source: {
3134
+ mode: item.mode === "edit" ? "text" : item.mode,
3135
+ model: item.model,
3136
+ prompt: item.prompt,
3137
+ size: item.size,
3138
+ quality: item.quality,
3139
+ detail: item.detail,
3140
+ n: 1,
3141
+ ...item.channelId !== void 0 ? { channelId: item.channelId } : {},
3142
+ workflow: "ecommerce",
3143
+ projectId: entry.projectId,
3144
+ projectName: entry.projectName ?? "",
3145
+ slotKey: item.slotKey ?? "",
3146
+ slotLabel: item.slotLabel ?? ""
3147
+ }
3148
+ })));
3149
+ setWorkspace("ecommerce");
3150
+ setEcommerceRestored({
3151
+ projectId: entry.projectId,
3152
+ projectName: entry.projectName ?? "",
3153
+ items
3154
+ });
3155
+ setEcommerceProjectId(entry.projectId);
3156
+ setEcommercePreview(false);
3157
+ setError(null);
3158
+ setViewingHistoryId(entry.id);
3159
+ setGalleryViewingId(null);
3160
+ } catch (caught) {
3161
+ setError(errorMessage(caught));
3162
+ }
3163
+ };
3164
+ /** Download a JSON manifest describing the whole product set (prompts,
3165
+ * slots and task outcomes) so results stay reproducible outside the panel. */
3166
+ const exportEcommerceManifest = () => {
3167
+ const manifest = {
3168
+ project: {
3169
+ id: ecommerceProjectId,
3170
+ name: ecommerce.projectName || ecommerce.productName,
3171
+ productName: ecommerce.productName,
3172
+ category: ecommerce.category,
3173
+ platform: ecommerce.platform,
3174
+ language: ecommerce.language,
3175
+ size: ecommerce.size,
3176
+ sellingPoints: ecommerce.sellingPoints,
3177
+ protectedFeatures: ecommerce.protectedFeatures,
3178
+ styleHint: ecommerce.styleHint
3179
+ },
3180
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3181
+ images: ecommerceMergedItems.map((item) => ({
3182
+ slotKey: item.slotKey,
3183
+ slotLabel: item.label,
3184
+ status: item.status,
3185
+ model: item.model,
3186
+ prompt: item.prompt,
3187
+ error: item.error
3188
+ }))
3189
+ };
3190
+ const blob = new Blob([JSON.stringify(manifest, null, 2)], { type: "application/json" });
3191
+ const url = URL.createObjectURL(blob);
3192
+ const anchor = document.createElement("a");
3193
+ anchor.href = url;
3194
+ anchor.download = `dsh-product-set-${(ecommerce.projectName || ecommerce.productName || "set").replace(/[^\w-]+/g, "-")}.json`;
3195
+ anchor.click();
3196
+ URL.revokeObjectURL(url);
3197
+ };
2265
3198
  const openPreview = (previewImages, index) => {
2266
3199
  setPreview({
2267
3200
  images: previewImages,
@@ -2311,10 +3244,21 @@ window.__ModuleLoader__.load({
2311
3244
  });
2312
3245
  return () => window.cancelAnimationFrame(frame);
2313
3246
  }, [preview, previewScale]);
2314
- /** Load a past generation's images into the canvas. */
2315
- const viewHistoryEntry = async (entry) => {
3247
+ const loadHistoryGroup = async (group) => {
3248
+ return (await Promise.all(group.entries.map((entry) => historyImagesToGenerated(entry.images)))).flat();
3249
+ };
3250
+ /** View every model result from one comparison as one canvas result set. */
3251
+ const viewHistoryGroup = async (group) => {
3252
+ const entry = group.entries[0];
3253
+ if (entry === void 0) return;
3254
+ if (entry.workflow === "ecommerce" && entry.projectId !== void 0) {
3255
+ await viewEcommerceProject(group);
3256
+ return;
3257
+ }
3258
+ openTab("text");
2316
3259
  try {
2317
- setImages(await historyImagesToGenerated(entry.images));
3260
+ setImages(await loadHistoryGroup(group));
3261
+ setComparison(null);
2318
3262
  setError(null);
2319
3263
  setViewingHistoryId(entry.id);
2320
3264
  setGalleryViewingId(null);
@@ -2322,19 +3266,28 @@ window.__ModuleLoader__.load({
2322
3266
  setError(errorMessage(caught));
2323
3267
  }
2324
3268
  };
2325
- /** Restore a past generation's parameters (and its images) into the form. */
2326
- const restoreHistoryEntry = async (entry) => {
3269
+ /** Restore one comparison group, including its selected model set. */
3270
+ const restoreHistoryGroup = async (group) => {
3271
+ const entry = group.entries[0];
3272
+ if (entry === void 0) return;
3273
+ if (entry.workflow === "ecommerce" && entry.projectId !== void 0) {
3274
+ await viewEcommerceProject(group);
3275
+ return;
3276
+ }
2327
3277
  try {
2328
- const restored = await historyImagesToGenerated(entry.images);
2329
- setTab(entry.mode);
3278
+ const restored = await loadHistoryGroup(group);
3279
+ openTab(entry.mode);
2330
3280
  setPrompt(entry.prompt);
2331
3281
  setSize(normalizeSize(entry.size));
2332
3282
  setQuality(normalizeQuality(entry.quality));
2333
3283
  setDetail(DETAILS.includes(entry.detail) ? entry.detail : "");
2334
3284
  setCount(entry.n >= 1 && entry.n <= 4 ? entry.n : 1);
2335
3285
  setModel(imageModels.includes(entry.model) ? entry.model : imageModels[0]);
3286
+ setCompareModels(group.models.filter((candidate) => imageModels.includes(candidate)));
3287
+ setCompareEnabled(group.models.filter((candidate) => imageModels.includes(candidate)).length > 1);
2336
3288
  setRefImage(null);
2337
3289
  setImages(restored);
3290
+ setComparison(null);
2338
3291
  setError(null);
2339
3292
  setViewingHistoryId(entry.id);
2340
3293
  setGalleryViewingId(null);
@@ -2342,16 +3295,45 @@ window.__ModuleLoader__.load({
2342
3295
  setError(errorMessage(caught));
2343
3296
  }
2344
3297
  };
2345
- /** Remove one history entry. */
2346
- const deleteHistoryEntry = async (id) => {
2347
- setHistory(history.filter((entry) => entry.id !== id));
2348
- if (viewingHistoryId === id) setViewingHistoryId(null);
3298
+ /** Remove every persisted row belonging to one comparison group. */
3299
+ const deleteHistoryGroup = async (group) => {
3300
+ const ids = new Set(group.entries.map((entry) => entry.id));
3301
+ setHistory((previous) => previous.filter((entry) => !ids.has(entry.id)));
3302
+ if (viewingHistoryId !== null && ids.has(viewingHistoryId)) setViewingHistoryId(null);
3303
+ if (ecommerceRestored !== null && group.entries.some((entry) => entry.projectId === ecommerceRestored.projectId)) {
3304
+ setEcommerceRestored(null);
3305
+ setEcommerceProjectId(null);
3306
+ setEcommerceAnchor(null);
3307
+ }
2349
3308
  try {
2350
- setHistory(await api.historyRemove(id));
3309
+ let next = history;
3310
+ for (const id of ids) next = await api.historyRemove(id);
3311
+ setHistory(next);
2351
3312
  } catch {}
2352
3313
  };
3314
+ /** Reset the workspace for a fresh image-generation run. */
3315
+ const startNewCreation = () => {
3316
+ openTab("text");
3317
+ setPrompt("");
3318
+ setRefImage(null);
3319
+ setImages([]);
3320
+ setPreview(null);
3321
+ setPreviewScale(1);
3322
+ setPromptCopied(false);
3323
+ setViewingHistoryId(null);
3324
+ setGalleryViewingId(null);
3325
+ setGallerySelecting(false);
3326
+ setSelectedGalleryIds(/* @__PURE__ */ new Set());
3327
+ setComparison(null);
3328
+ setComparisonFullscreen(false);
3329
+ setEcommerceRestored(null);
3330
+ setError(null);
3331
+ setConversationMessage(null);
3332
+ setGalleryMessage(null);
3333
+ };
2353
3334
  /** Remove all history entries. */
2354
3335
  const clearHistory = async () => {
3336
+ if (!window.confirm(tt("history.clearConfirm"))) return;
2355
3337
  setHistory([]);
2356
3338
  setViewingHistoryId(null);
2357
3339
  try {
@@ -2363,7 +3345,7 @@ window.__ModuleLoader__.load({
2363
3345
  * metadata + first image are saved); otherwise the current form state is
2364
3346
  * used. */
2365
3347
  const addToGallery = async (image, entry) => {
2366
- if (galleryAdding || tab === "gallery") return;
3348
+ if (galleryAdding || workspace === "normal" && tab === "gallery") return;
2367
3349
  const source = entry ?? viewingEntry ?? {
2368
3350
  mode: tab === "edit" ? "edit" : "text",
2369
3351
  model,
@@ -2399,6 +3381,47 @@ window.__ModuleLoader__.load({
2399
3381
  setGalleryAdding(false);
2400
3382
  }
2401
3383
  };
3384
+ /** Put a generated image into the native conversation composer. */
3385
+ const addImageToConversation = async (image, index, actionKey = index) => {
3386
+ if (addingToConversation !== null) return;
3387
+ if (conversation === void 0 || sessions === void 0 || currentSessionId === void 0) {
3388
+ setError(tt("conversation.noSession"));
3389
+ return;
3390
+ }
3391
+ const sessionScope = sessions.scope(currentSessionId);
3392
+ if (sessionScope === void 0) {
3393
+ setError(tt("conversation.unavailable"));
3394
+ return;
3395
+ }
3396
+ setAddingToConversation(actionKey);
3397
+ let attachments = [];
3398
+ let added = false;
3399
+ try {
3400
+ const prepared = await prepareConversationImage(image, index);
3401
+ const file = prepared.file;
3402
+ attachments = conversation.createDraftImages([file]);
3403
+ const input = conversation.input.for(sessionScope);
3404
+ if (!input.addImages(attachments.map((attachment) => attachment.id))) throw new Error(tt("conversation.busy"));
3405
+ added = true;
3406
+ try {
3407
+ await api.attachConversationImage(String(currentSessionId), prepared.dataUrl, file.name);
3408
+ } catch (caught) {
3409
+ for (const attachment of attachments) input.removeImage(attachment.id);
3410
+ added = false;
3411
+ throw caught;
3412
+ }
3413
+ if (input.state.getSnapshot().draft.trim() === "" && prompt.trim() !== "") input.setDraft(prompt.trim());
3414
+ setConversationMessage(tt("conversation.added"));
3415
+ window.setTimeout(() => {
3416
+ setConversationMessage(null);
3417
+ }, 2200);
3418
+ } catch (caught) {
3419
+ if (!added) conversation.releaseDraftImages(attachments);
3420
+ setError(errorMessage(caught));
3421
+ } finally {
3422
+ setAddingToConversation(null);
3423
+ }
3424
+ };
2402
3425
  /** Add one history entry's first image to the gallery (fetches it from the
2403
3426
  * history image route, then delegates to addToGallery). */
2404
3427
  const addHistoryEntryToGallery = async (entry) => {
@@ -2411,35 +3434,29 @@ window.__ModuleLoader__.load({
2411
3434
  setError(errorMessage(caught));
2412
3435
  }
2413
3436
  };
2414
- /** View a gallery image in the canvas. */
2415
- const viewGalleryEntry = async (entry) => {
3437
+ /** Load a persisted gallery image and add it to the current chat draft. */
3438
+ const addGalleryEntryToConversation = async (entry) => {
3439
+ if (galleryConversationAddingId !== null || addingToConversation !== null || entry.images.length === 0) return;
3440
+ setGalleryConversationAddingId(entry.id);
2416
3441
  try {
2417
- const restored = await historyImagesToGenerated(entry.images);
2418
- setImages(restored);
2419
- setError(null);
2420
- setViewingHistoryId(null);
2421
- setGalleryViewingId(entry.id);
2422
- if (restored.length > 0) openPreview(restored, 0);
3442
+ const [image] = await historyImagesToGenerated(entry.images.slice(0, 1));
3443
+ if (image === void 0) return;
3444
+ await addImageToConversation(image, 0, `gallery:${entry.id}`);
2423
3445
  } catch (caught) {
2424
3446
  setError(errorMessage(caught));
3447
+ } finally {
3448
+ setGalleryConversationAddingId(null);
2425
3449
  }
2426
3450
  };
2427
- /** Restore a gallery entry's parameters (and its images) into the form. */
2428
- const restoreGalleryEntry = async (entry) => {
3451
+ /** View a gallery image in the canvas. */
3452
+ const viewGalleryEntry = async (entry) => {
2429
3453
  try {
2430
3454
  const restored = await historyImagesToGenerated(entry.images);
2431
- setTab(entry.mode);
2432
- setPrompt(entry.prompt);
2433
- setSize(normalizeSize(entry.size));
2434
- setQuality(normalizeQuality(entry.quality));
2435
- setDetail(DETAILS.includes(entry.detail) ? entry.detail : "");
2436
- setCount(entry.n >= 1 && entry.n <= 4 ? entry.n : 1);
2437
- setModel(imageModels.includes(entry.model) ? entry.model : imageModels[0]);
2438
- setRefImage(null);
2439
3455
  setImages(restored);
2440
3456
  setError(null);
2441
3457
  setViewingHistoryId(null);
2442
- setGalleryViewingId(null);
3458
+ setGalleryViewingId(entry.id);
3459
+ if (restored.length > 0) openPreview(restored, 0);
2443
3460
  } catch (caught) {
2444
3461
  setError(errorMessage(caught));
2445
3462
  }
@@ -2454,6 +3471,7 @@ window.__ModuleLoader__.load({
2454
3471
  };
2455
3472
  /** Remove every gallery entry. */
2456
3473
  const clearGalleryAll = async () => {
3474
+ if (!window.confirm(tt("gallery.clearConfirm"))) return;
2457
3475
  setGallery([]);
2458
3476
  setGalleryViewingId(null);
2459
3477
  try {
@@ -2521,7 +3539,31 @@ window.__ModuleLoader__.load({
2521
3539
  anchor.click();
2522
3540
  });
2523
3541
  };
2524
- const generateDisabled = submitting;
3542
+ const generateDisabled = submitting || modeModels.length === 0;
3543
+ const ecommerceSlots = ecommerce.slots.filter((slot) => slot.enabled && slot.count > 0);
3544
+ const ecommerceTotal = ecommerceSlots.reduce((total, slot) => total + slot.count, 0);
3545
+ const ecommerceGenerateDisabled = submitting || ecommerceGenerating || ecommerceSlots.length === 0 || ecommerce.productName.trim() === "";
3546
+ const ecommerceFileInput = (0, react.useRef)(null);
3547
+ const ecommerceProjectTasks = ecommerceProjectId === null ? [] : tasks.filter((task) => task.request.workflow === "ecommerce" && task.request.projectId === ecommerceProjectId);
3548
+ const liveSlotKeys = new Set(ecommerceProjectTasks.map((task) => task.request.slotKey ?? task.id));
3549
+ const ecommerceMergedItems = [...ecommerceProjectTasks.map((task) => ({
3550
+ id: task.id,
3551
+ label: task.request.slotLabel ?? "",
3552
+ slotKey: task.request.slotKey ?? "",
3553
+ status: task.status,
3554
+ model: task.request.model,
3555
+ prompt: task.request.prompt,
3556
+ ...task.error !== void 0 ? { error: task.error } : {},
3557
+ images: task.result?.images ?? [],
3558
+ source: task.request
3559
+ })), ...ecommerceRestored !== null && ecommerceRestored.projectId === ecommerceProjectId ? ecommerceRestored.items.filter((item) => !liveSlotKeys.has(item.slotKey)) : []];
3560
+ const ecommerceDoneCount = ecommerceMergedItems.filter((item) => item.status === "completed").length;
3561
+ const ecommerceFailedCount = ecommerceMergedItems.filter((item) => item.status === "failed" || item.status === "cancelled").length;
3562
+ const ecommerceResultGroups = [...new Set(ecommerceMergedItems.map((item) => item.label))].filter((label) => label !== "").map((label) => ({
3563
+ label,
3564
+ items: ecommerceMergedItems.filter((item) => item.label === label)
3565
+ }));
3566
+ const conversationBusy = addingToConversation !== null || galleryConversationAddingId !== null;
2525
3567
  const viewingEntry = viewingHistoryId === null ? null : history.find((entry) => entry.id === viewingHistoryId) ?? null;
2526
3568
  const viewingGalleryEntry = galleryViewingId === null ? null : gallery.find((entry) => entry.id === galleryViewingId) ?? null;
2527
3569
  const previewImage = preview === null ? null : preview.images[preview.index] ?? null;
@@ -2529,6 +3571,30 @@ window.__ModuleLoader__.load({
2529
3571
  const comparisonResults = comparisonTasks.filter((task) => task.status === "completed" && task.result !== void 0);
2530
3572
  const previewFrameScale = Math.max(1, previewScale);
2531
3573
  const previewImageScale = previewScale / previewFrameScale;
3574
+ /** Drag the config panel's right edge to resize it (persisted per browser). */
3575
+ const onConfigResizeStart = (event) => {
3576
+ if (event.button !== 0) return;
3577
+ event.preventDefault();
3578
+ const aside = configAsideRef.current;
3579
+ if (aside === null) return;
3580
+ const left = aside.getBoundingClientRect().left;
3581
+ const onMove = (move) => {
3582
+ const width = Math.round(Math.min(CONFIG_WIDTH_MAX, Math.max(CONFIG_WIDTH_MIN, move.clientX - left)));
3583
+ setConfigWidth(width);
3584
+ try {
3585
+ window.localStorage.setItem(CONFIG_WIDTH_STORAGE_KEY, String(width));
3586
+ } catch {}
3587
+ };
3588
+ const onUp = () => {
3589
+ window.removeEventListener("pointermove", onMove);
3590
+ document.documentElement.style.removeProperty("cursor");
3591
+ document.documentElement.style.removeProperty("user-select");
3592
+ };
3593
+ window.addEventListener("pointermove", onMove);
3594
+ window.addEventListener("pointerup", onUp, { once: true });
3595
+ document.documentElement.style.setProperty("cursor", "col-resize");
3596
+ document.documentElement.style.setProperty("user-select", "none");
3597
+ };
2532
3598
  const copyPreviewPrompt = async (text) => {
2533
3599
  try {
2534
3600
  if (navigator.clipboard?.writeText !== void 0) await navigator.clipboard.writeText(text);
@@ -2553,7 +3619,7 @@ window.__ModuleLoader__.load({
2553
3619
  };
2554
3620
  const addPreviewToEdit = () => {
2555
3621
  if (previewImage === null || preview === null) return;
2556
- setTab("edit");
3622
+ openTab("edit");
2557
3623
  setRefImage({
2558
3624
  dataUrl: srcOf(previewImage),
2559
3625
  name: `dsh-image-${preview.index + 1}.${extensionOf(previewImage.mime)}`
@@ -2562,42 +3628,273 @@ window.__ModuleLoader__.load({
2562
3628
  setError(null);
2563
3629
  closePreview();
2564
3630
  };
3631
+ const historyPanel = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3632
+ className: panel_module_css_default.history,
3633
+ "data-dsh-imagegen-history": true,
3634
+ children: [
3635
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3636
+ className: panel_module_css_default.historyHeader,
3637
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3638
+ className: panel_module_css_default.historyTitle,
3639
+ children: tt("history.title")
3640
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3641
+ className: panel_module_css_default.historyHeaderActions,
3642
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3643
+ type: "button",
3644
+ className: panel_module_css_default.historyNew,
3645
+ "data-history-new": "",
3646
+ "aria-label": tt("canvas.new"),
3647
+ title: tt("canvas.newHint"),
3648
+ onClick: startNewCreation,
3649
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3650
+ viewBox: "0 0 16 16",
3651
+ width: "15",
3652
+ height: "15",
3653
+ fill: "none",
3654
+ stroke: "currentColor",
3655
+ strokeWidth: "1.7",
3656
+ strokeLinecap: "round",
3657
+ "aria-hidden": "true",
3658
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 3v10M3 8h10" })
3659
+ })
3660
+ }), history.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3661
+ type: "button",
3662
+ className: panel_module_css_default.historyClear,
3663
+ "data-history-clear": "",
3664
+ onClick: () => {
3665
+ clearHistory();
3666
+ },
3667
+ children: tt("history.clear")
3668
+ }) : null]
3669
+ })]
3670
+ }),
3671
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3672
+ className: panel_module_css_default.historyFilters,
3673
+ children: [
3674
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3675
+ className: panel_module_css_default.historySearch,
3676
+ value: historyQuery,
3677
+ onChange: (event) => {
3678
+ setHistoryQuery(event.target.value);
3679
+ },
3680
+ placeholder: tt("history.search"),
3681
+ "aria-label": tt("history.search")
3682
+ }),
3683
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3684
+ value: historyModelFilter,
3685
+ onChange: (event) => {
3686
+ setHistoryModelFilter(event.target.value);
3687
+ },
3688
+ "aria-label": tt("history.model"),
3689
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3690
+ value: "all",
3691
+ children: tt("history.allModels")
3692
+ }), [...new Set(history.flatMap((entry) => modelsOfHistoryEntry(entry)))].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3693
+ value: option,
3694
+ children: option
3695
+ }, option))]
3696
+ }),
3697
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3698
+ value: historyRatioFilter,
3699
+ onChange: (event) => {
3700
+ setHistoryRatioFilter(event.target.value);
3701
+ },
3702
+ "aria-label": tt("history.ratio"),
3703
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3704
+ value: "all",
3705
+ children: tt("history.allRatios")
3706
+ }), [...new Set(history.map((entry) => normalizeSize(entry.size)))].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3707
+ value: option,
3708
+ children: option
3709
+ }, option))]
3710
+ })
3711
+ ]
3712
+ }),
3713
+ filteredHistory.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3714
+ className: panel_module_css_default.historyEmpty,
3715
+ children: tt("history.empty")
3716
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3717
+ className: panel_module_css_default.historyList,
3718
+ children: filteredHistory.map((group) => {
3719
+ const entry = group.entries[0];
3720
+ const isComparison = group.models.length > 1;
3721
+ const imageCount = group.entries.reduce((total, item) => total + item.images.length, 0);
3722
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3723
+ className: panel_module_css_default.historyItem,
3724
+ "data-active": group.entries.some((item) => item.id === viewingHistoryId) ? "" : void 0,
3725
+ "data-comparison": isComparison ? "" : void 0,
3726
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3727
+ type: "button",
3728
+ className: panel_module_css_default.historyMain,
3729
+ "data-dsh-imagegen-history-main": "",
3730
+ onClick: () => {
3731
+ viewHistoryGroup(group);
3732
+ },
3733
+ children: [entry.images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
3734
+ className: panel_module_css_default.historyThumb,
3735
+ src: entry.images[0].url,
3736
+ alt: ""
3737
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.historyThumbPlaceholder }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3738
+ className: panel_module_css_default.historyInfo,
3739
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3740
+ className: panel_module_css_default.historyPrompt,
3741
+ children: entry.prompt
3742
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3743
+ className: panel_module_css_default.historyMeta,
3744
+ children: [
3745
+ isComparison ? tt("compare.title") : entry.workflow === "ecommerce" ? `${tt("ecommerce.short")}${entry.projectName !== void 0 && entry.projectName !== "" ? ` · ${entry.projectName}` : ""}` : tt(`mode.${entry.mode === "edit" ? "edit" : "text"}`),
3746
+ " · ",
3747
+ isComparison ? group.models.join(" · ") : entry.model,
3748
+ " · ",
3749
+ formatTime(entry.createdAt),
3750
+ " · ",
3751
+ imageCount,
3752
+ " ",
3753
+ tt("history.images")
3754
+ ]
3755
+ })]
3756
+ })]
3757
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3758
+ className: panel_module_css_default.historyActions,
3759
+ children: [
3760
+ entry.images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3761
+ type: "button",
3762
+ className: panel_module_css_default.historyAction,
3763
+ disabled: galleryAdding,
3764
+ title: tt("gallery.add"),
3765
+ onClick: () => {
3766
+ addHistoryEntryToGallery(entry);
3767
+ },
3768
+ children: tt("gallery.add")
3769
+ }) : null,
3770
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3771
+ type: "button",
3772
+ className: panel_module_css_default.historyAction,
3773
+ onClick: () => {
3774
+ restoreHistoryGroup(group);
3775
+ },
3776
+ children: tt("history.restore")
3777
+ }),
3778
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3779
+ type: "button",
3780
+ className: panel_module_css_default.historyAction,
3781
+ "data-danger": true,
3782
+ onClick: () => {
3783
+ deleteHistoryGroup(group);
3784
+ },
3785
+ children: tt("history.delete")
3786
+ })
3787
+ ]
3788
+ })]
3789
+ }, group.key);
3790
+ })
3791
+ })
3792
+ ]
3793
+ });
2565
3794
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2566
3795
  className: panel_module_css_default.panel,
2567
3796
  children: [
2568
3797
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
2569
3798
  className: panel_module_css_default.panelHeader,
2570
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2571
- className: panel_module_css_default.panelHeading,
2572
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
2573
- className: panel_module_css_default.panelTitle,
2574
- children: tt("panel.title")
2575
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
2576
- className: panel_module_css_default.githubLink,
2577
- href: "https://github.com/dickpy/dsh-imagegen",
2578
- target: "_blank",
2579
- rel: "noreferrer",
2580
- title: tt("panel.githubTip"),
2581
- "aria-label": tt("panel.githubTip"),
2582
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2583
- viewBox: "0 0 16 16",
2584
- width: "15",
2585
- height: "15",
2586
- fill: "currentColor",
2587
- "aria-hidden": "true",
2588
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z" })
2589
- })
2590
- })]
2591
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2592
- type: "button",
2593
- className: panel_module_css_default.connectionStatus,
2594
- "data-connected": connected ? "true" : "false",
2595
- "aria-label": tt(connected ? "connection.connected" : "connection.disconnected"),
2596
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2597
- className: panel_module_css_default.connectionDot,
2598
- "aria-hidden": "true"
2599
- }), tt(connected ? "connection.connected" : "connection.disconnected")]
2600
- })]
3799
+ children: [
3800
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3801
+ className: panel_module_css_default.panelHeading,
3802
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3803
+ className: panel_module_css_default.panelTitle,
3804
+ children: tt("panel.title")
3805
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3806
+ className: panel_module_css_default.githubLink,
3807
+ href: "https://github.com/dickpy/dsh-imagegen",
3808
+ target: "_blank",
3809
+ rel: "noreferrer",
3810
+ title: tt("panel.githubTip"),
3811
+ "aria-label": tt("panel.githubTip"),
3812
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3813
+ viewBox: "0 0 16 16",
3814
+ width: "15",
3815
+ height: "15",
3816
+ fill: "currentColor",
3817
+ "aria-hidden": "true",
3818
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z" })
3819
+ })
3820
+ })]
3821
+ }),
3822
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
3823
+ className: panel_module_css_default.topNav,
3824
+ role: "tablist",
3825
+ "aria-label": tt("workspace.label"),
3826
+ children: [
3827
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3828
+ type: "button",
3829
+ className: panel_module_css_default.topNavItem,
3830
+ "data-active": workspace === "normal" && tab !== "gallery" ? "" : void 0,
3831
+ onClick: () => {
3832
+ if (workspace !== "normal" || tab === "gallery") openTab("text");
3833
+ },
3834
+ children: tt("workspace.normal")
3835
+ }),
3836
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3837
+ type: "button",
3838
+ className: panel_module_css_default.topNavItem,
3839
+ "data-active": workspace === "normal" && tab === "gallery" ? "" : void 0,
3840
+ onClick: () => {
3841
+ openTab("gallery");
3842
+ },
3843
+ children: tt("gallery.title")
3844
+ }),
3845
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3846
+ className: panel_module_css_default.topNavDivider,
3847
+ "aria-hidden": "true"
3848
+ }),
3849
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3850
+ type: "button",
3851
+ className: panel_module_css_default.topNavItem,
3852
+ "data-active": workspace === "ecommerce" ? "" : void 0,
3853
+ onClick: () => {
3854
+ setWorkspace("ecommerce");
3855
+ },
3856
+ children: [tt("workspace.ecommerce"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3857
+ className: panel_module_css_default.previewBadge,
3858
+ children: tt("ecommerce.badge")
3859
+ })]
3860
+ })
3861
+ ]
3862
+ }),
3863
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3864
+ className: panel_module_css_default.panelHeaderActions,
3865
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3866
+ type: "button",
3867
+ className: panel_module_css_default.chatToggle,
3868
+ "data-open": chatOpen ? "true" : "false",
3869
+ "aria-pressed": chatOpen,
3870
+ title: chatOpen ? tt("chat.collapse") : tt("chat.expand"),
3871
+ onClick: () => {
3872
+ setChatOpen((open) => !open);
3873
+ },
3874
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3875
+ viewBox: "0 0 16 16",
3876
+ width: "13",
3877
+ height: "13",
3878
+ fill: "none",
3879
+ stroke: "currentColor",
3880
+ strokeWidth: "1.4",
3881
+ strokeLinecap: "round",
3882
+ strokeLinejoin: "round",
3883
+ "aria-hidden": "true",
3884
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.5 3.5h11v7.5H6.8L3.8 13.6v-2.6H2.5z" })
3885
+ }), tt("chat.toggle")]
3886
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3887
+ type: "button",
3888
+ className: panel_module_css_default.connectionStatus,
3889
+ "data-connected": connected ? "true" : "false",
3890
+ "aria-label": tt(connected ? "connection.connected" : "connection.disconnected"),
3891
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3892
+ className: panel_module_css_default.connectionDot,
3893
+ "aria-hidden": "true"
3894
+ }), tt(connected ? "connection.connected" : "connection.disconnected")]
3895
+ })]
3896
+ })
3897
+ ]
2601
3898
  }),
2602
3899
  update !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2603
3900
  className: panel_module_css_default.updateBanner,
@@ -2624,14 +3921,48 @@ window.__ModuleLoader__.load({
2624
3921
  })]
2625
3922
  })]
2626
3923
  }) : null,
2627
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3924
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2628
3925
  className: panel_module_css_default.studio,
2629
- children: [
2630
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3926
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3927
+ className: panel_module_css_default.generation,
3928
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3929
+ ref: configAsideRef,
2631
3930
  className: panel_module_css_default.config,
2632
- "data-gallery": tab === "gallery" ? "true" : void 0,
3931
+ style: { "--dsh-imagegen-config-width": `${configWidth}px` },
3932
+ "data-collapsed": configCollapsed ? "true" : "false",
3933
+ "data-gallery": workspace === "normal" && tab === "gallery" ? "true" : void 0,
2633
3934
  children: [
2634
- tab === "gallery" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3935
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3936
+ className: panel_module_css_default.configResizer,
3937
+ title: tt("config.resizeHint"),
3938
+ onPointerDown: onConfigResizeStart
3939
+ }),
3940
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3941
+ className: panel_module_css_default.configHeader,
3942
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3943
+ type: "button",
3944
+ className: panel_module_css_default.configToggle,
3945
+ "aria-expanded": !configCollapsed,
3946
+ "aria-label": tt(configCollapsed ? "panel.expandConfig" : "panel.collapseConfig"),
3947
+ title: tt(configCollapsed ? "panel.expandConfig" : "panel.collapseConfig"),
3948
+ onClick: () => {
3949
+ setConfigCollapsed((previous) => !previous);
3950
+ },
3951
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3952
+ viewBox: "0 0 16 16",
3953
+ width: "16",
3954
+ height: "16",
3955
+ fill: "none",
3956
+ stroke: "currentColor",
3957
+ strokeWidth: "1.6",
3958
+ strokeLinecap: "round",
3959
+ strokeLinejoin: "round",
3960
+ "aria-hidden": "true",
3961
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: configCollapsed ? "M6 3l5 5-5 5" : "M10 3L5 8l5 5" })
3962
+ })
3963
+ })
3964
+ }),
3965
+ workspace === "normal" && tab === "gallery" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2635
3966
  className: panel_module_css_default.galleryFilters,
2636
3967
  children: [
2637
3968
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -2706,40 +4037,382 @@ window.__ModuleLoader__.load({
2706
4037
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2707
4038
  className: panel_module_css_default.configScroll,
2708
4039
  children: [
2709
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
4040
+ workspace === "normal" && tab !== "gallery" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
2710
4041
  className: panel_module_css_default.card,
2711
4042
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2712
4043
  className: panel_module_css_default.modeRow,
2713
4044
  role: "tablist",
2714
4045
  "aria-label": tt("panel.title"),
2715
- children: [
2716
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
2717
- active: tab === "text",
2718
- onClick: () => {
2719
- setTab("text");
2720
- },
2721
- className: panel_module_css_default.modePill,
2722
- children: tt("mode.text")
2723
- }),
2724
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
2725
- active: tab === "edit",
2726
- onClick: () => {
2727
- setTab("edit");
2728
- },
2729
- className: panel_module_css_default.modePill,
2730
- children: tt("mode.edit")
2731
- }),
2732
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
2733
- active: tab === "gallery",
2734
- onClick: () => {
2735
- setTab("gallery");
2736
- },
2737
- className: panel_module_css_default.modePill,
2738
- children: tt("gallery.title")
2739
- })
2740
- ]
4046
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
4047
+ active: tab === "text",
4048
+ onClick: () => {
4049
+ setTab("text");
4050
+ },
4051
+ className: panel_module_css_default.modePill,
4052
+ children: tt("mode.text")
4053
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
4054
+ active: tab === "edit",
4055
+ onClick: () => {
4056
+ setTab("edit");
4057
+ },
4058
+ className: panel_module_css_default.modePill,
4059
+ children: tt("mode.edit")
4060
+ })]
2741
4061
  })
2742
- }),
4062
+ }) : null,
4063
+ workspace === "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4064
+ className: panel_module_css_default.ecommerceWorkspace,
4065
+ "data-ecommerce-workspace": "",
4066
+ children: [
4067
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4068
+ className: panel_module_css_default.ecommerceSection,
4069
+ children: [
4070
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: tt("ecommerce.product") }),
4071
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4072
+ className: panel_module_css_default.ecommerceField,
4073
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4074
+ className: panel_module_css_default.ecommerceFieldLabel,
4075
+ children: tt("ecommerce.productName")
4076
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4077
+ value: ecommerce.productName,
4078
+ placeholder: tt("ecommerce.productName"),
4079
+ onChange: (event) => setEcommerce((previous) => ({
4080
+ ...previous,
4081
+ productName: event.target.value
4082
+ }))
4083
+ })]
4084
+ }),
4085
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4086
+ className: panel_module_css_default.ecommerceField,
4087
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4088
+ className: panel_module_css_default.ecommerceFieldLabel,
4089
+ children: tt("ecommerce.projectName")
4090
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4091
+ value: ecommerce.projectName,
4092
+ placeholder: tt("ecommerce.projectName"),
4093
+ onChange: (event) => setEcommerce((previous) => ({
4094
+ ...previous,
4095
+ projectName: event.target.value
4096
+ }))
4097
+ })]
4098
+ }),
4099
+ ecommerceAssets.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4100
+ type: "button",
4101
+ className: panel_module_css_default.ecommerceUploadHero,
4102
+ "data-ecommerce-upload": "",
4103
+ onClick: () => {
4104
+ ecommerceFileInput.current?.click();
4105
+ },
4106
+ onDragOver: (event) => {
4107
+ event.preventDefault();
4108
+ },
4109
+ onDrop: (event) => {
4110
+ event.preventDefault();
4111
+ acceptEcommerceFiles(event.dataTransfer.files ?? void 0);
4112
+ },
4113
+ children: [
4114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
4115
+ viewBox: "0 0 16 16",
4116
+ width: "15",
4117
+ height: "15",
4118
+ fill: "none",
4119
+ stroke: "currentColor",
4120
+ strokeWidth: "1.3",
4121
+ strokeLinecap: "round",
4122
+ strokeLinejoin: "round",
4123
+ "aria-hidden": "true",
4124
+ children: [
4125
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 10V3.5" }),
4126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.5 5.5L8 3l2.5 2.5" }),
4127
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 9.5V12a1.5 1.5 0 001.5 1.5h7A1.5 1.5 0 0013 12V9.5" })
4128
+ ]
4129
+ }),
4130
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: tt("ecommerce.uploadRef") }),
4131
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: tt("edit.uploadHint") })
4132
+ ]
4133
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4134
+ className: panel_module_css_default.ecommerceAssets,
4135
+ children: [ecommerceAssets.map((asset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4136
+ className: panel_module_css_default.ecommerceAsset,
4137
+ "data-ecommerce-asset": "",
4138
+ children: [
4139
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
4140
+ src: asset.dataUrl,
4141
+ alt: asset.name
4142
+ }),
4143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
4144
+ value: asset.role,
4145
+ "data-ecommerce-asset-role": "",
4146
+ "aria-label": tt("ecommerce.refSelect"),
4147
+ onChange: (event) => setEcommerceAssets((previous) => previous.map((item) => item.id === asset.id ? {
4148
+ ...item,
4149
+ role: event.target.value
4150
+ } : item)),
4151
+ children: ECOMMERCE_ASSET_ROLES.map((role) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4152
+ value: role,
4153
+ children: tt(`ecommerce.role.${role}`)
4154
+ }, role))
4155
+ }),
4156
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4157
+ type: "button",
4158
+ "aria-label": tt("edit.remove"),
4159
+ onClick: () => {
4160
+ setEcommerceAssets((previous) => previous.filter((item) => item.id !== asset.id));
4161
+ },
4162
+ children: "×"
4163
+ })
4164
+ ]
4165
+ }, asset.id)), ecommerceAssets.length < MAX_ECOMMERCE_ASSETS ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4166
+ type: "button",
4167
+ className: panel_module_css_default.ecommerceAssetAdd,
4168
+ "data-ecommerce-upload": "",
4169
+ title: tt("ecommerce.uploadRef"),
4170
+ onClick: () => {
4171
+ ecommerceFileInput.current?.click();
4172
+ },
4173
+ onDragOver: (event) => {
4174
+ event.preventDefault();
4175
+ },
4176
+ onDrop: (event) => {
4177
+ event.preventDefault();
4178
+ acceptEcommerceFiles(event.dataTransfer.files ?? void 0);
4179
+ },
4180
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4181
+ "aria-hidden": "true",
4182
+ children: "+"
4183
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: tt("ecommerce.uploadShort") })]
4184
+ }) : null]
4185
+ })
4186
+ ]
4187
+ }),
4188
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4189
+ className: panel_module_css_default.ecommerceSection,
4190
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: tt("ecommerce.params") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4191
+ className: panel_module_css_default.ecommerceParamGrid,
4192
+ children: [
4193
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4194
+ className: panel_module_css_default.ecommerceField,
4195
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4196
+ className: panel_module_css_default.ecommerceFieldLabel,
4197
+ children: tt("ecommerce.platformLabel")
4198
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4199
+ value: ecommerce.platform,
4200
+ onChange: (event) => setEcommerce((previous) => ({
4201
+ ...previous,
4202
+ platform: event.target.value
4203
+ })),
4204
+ children: [
4205
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "通用" }),
4206
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "淘宝 / 京东" }),
4207
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "Amazon" })
4208
+ ]
4209
+ })]
4210
+ }),
4211
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4212
+ className: panel_module_css_default.ecommerceField,
4213
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4214
+ className: panel_module_css_default.ecommerceFieldLabel,
4215
+ children: tt("ecommerce.languageLabel")
4216
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4217
+ value: ecommerce.language,
4218
+ onChange: (event) => setEcommerce((previous) => ({
4219
+ ...previous,
4220
+ language: event.target.value
4221
+ })),
4222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "中文" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "English" })]
4223
+ })]
4224
+ }),
4225
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4226
+ className: panel_module_css_default.ecommerceField,
4227
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4228
+ className: panel_module_css_default.ecommerceFieldLabel,
4229
+ children: tt("ecommerce.ratioLabel")
4230
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
4231
+ value: ecommerce.size,
4232
+ onChange: (event) => setEcommerce((previous) => ({
4233
+ ...previous,
4234
+ size: event.target.value
4235
+ })),
4236
+ children: SIZES.filter((size) => size !== "auto").map((size) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: size }, size))
4237
+ })]
4238
+ }),
4239
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4240
+ className: panel_module_css_default.ecommerceField,
4241
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4242
+ className: panel_module_css_default.ecommerceFieldLabel,
4243
+ children: tt("ecommerce.categoryLabel")
4244
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4245
+ value: ecommerce.category,
4246
+ onChange: (event) => setEcommerce((previous) => ({
4247
+ ...previous,
4248
+ category: event.target.value
4249
+ })),
4250
+ children: [
4251
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "通用商品" }),
4252
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "食品饮料" }),
4253
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "美妆个护" }),
4254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "服装配饰" }),
4255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "家居用品" }),
4256
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { children: "3C 数码" })
4257
+ ]
4258
+ })]
4259
+ })
4260
+ ]
4261
+ })]
4262
+ }),
4263
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4264
+ className: panel_module_css_default.ecommerceSection,
4265
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: tt("ecommerce.sellingTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
4266
+ value: ecommerce.sellingPoints,
4267
+ placeholder: tt("ecommerce.sellingPoints"),
4268
+ onChange: (event) => setEcommerce((previous) => ({
4269
+ ...previous,
4270
+ sellingPoints: event.target.value
4271
+ }))
4272
+ })]
4273
+ }),
4274
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4275
+ className: panel_module_css_default.ecommerceSection,
4276
+ children: [
4277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", { children: [tt("ecommerce.setStructure"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
4278
+ className: panel_module_css_default.ecommerceSectionHint,
4279
+ children: tt("ecommerce.multiSelect")
4280
+ })] }),
4281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4282
+ className: panel_module_css_default.ecommerceStructureGrid,
4283
+ children: ecommerce.slots.map((slot) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4284
+ type: "button",
4285
+ className: panel_module_css_default.ecommerceSlotCard,
4286
+ "data-active": slot.enabled ? "" : void 0,
4287
+ title: `${slot.label}:${slot.description}`,
4288
+ onClick: () => setEcommerce((previous) => ({
4289
+ ...previous,
4290
+ slots: previous.slots.map((item) => item.key === slot.key ? {
4291
+ ...item,
4292
+ enabled: !item.enabled
4293
+ } : item)
4294
+ })),
4295
+ children: [slot.label, slot.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4296
+ className: panel_module_css_default.ecommerceSlotCount,
4297
+ title: tt("ecommerce.countHint"),
4298
+ onClick: (event) => {
4299
+ event.stopPropagation();
4300
+ setEcommerce((previous) => ({
4301
+ ...previous,
4302
+ slots: previous.slots.map((item) => item.key === slot.key ? {
4303
+ ...item,
4304
+ count: item.count >= 4 ? 1 : item.count + 1
4305
+ } : item)
4306
+ }));
4307
+ },
4308
+ children: slot.count
4309
+ }) : null]
4310
+ }, slot.key))
4311
+ }),
4312
+ ecommerceSlots.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4313
+ type: "button",
4314
+ className: panel_module_css_default.ecommerceAdvancedToggle,
4315
+ "aria-expanded": ecommerceRefOpen,
4316
+ onClick: () => {
4317
+ setEcommerceRefOpen((open) => !open);
4318
+ },
4319
+ children: [tt("ecommerce.refSettings"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4320
+ className: panel_module_css_default.ecommerceAdvancedChevron,
4321
+ "aria-hidden": "true",
4322
+ children: ecommerceRefOpen ? "⌃" : "⌄"
4323
+ })]
4324
+ }), ecommerceRefOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4325
+ className: panel_module_css_default.ecommerceAdvancedBody,
4326
+ children: ecommerceSlots.map((slot) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4327
+ className: panel_module_css_default.ecommerceRefRow,
4328
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: slot.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
4329
+ value: slot.refRole ?? "product",
4330
+ "data-ecommerce-ref-select": "",
4331
+ "aria-label": `${tt("ecommerce.refSelect")} · ${slot.label}`,
4332
+ onChange: (event) => setEcommerce((previous) => ({
4333
+ ...previous,
4334
+ slots: previous.slots.map((item) => item.key === slot.key ? {
4335
+ ...item,
4336
+ refRole: event.target.value
4337
+ } : item)
4338
+ })),
4339
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4340
+ value: "none",
4341
+ children: tt("ecommerce.refNone")
4342
+ }), ECOMMERCE_ASSET_ROLES.map((role) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4343
+ value: role,
4344
+ children: tt(`ecommerce.role.${role}`)
4345
+ }, role))]
4346
+ })]
4347
+ }, slot.key))
4348
+ }) : null] }) : null
4349
+ ]
4350
+ }),
4351
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4352
+ className: panel_module_css_default.ecommerceSection,
4353
+ children: [
4354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: tt("ecommerce.generation") }),
4355
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
4356
+ value: modeModels.includes(model) ? model : modeModels[0] ?? "",
4357
+ "aria-label": tt("model.label"),
4358
+ onChange: (event) => setModel(event.target.value),
4359
+ children: modeModels.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
4360
+ value: option,
4361
+ children: option
4362
+ }, option))
4363
+ }),
4364
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4365
+ className: panel_module_css_default.optionRow,
4366
+ children: QUALITIES.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
4367
+ active: quality === option,
4368
+ onClick: () => {
4369
+ setQuality(option);
4370
+ },
4371
+ className: panel_module_css_default.optionPill,
4372
+ children: tt(`quality.${option}`)
4373
+ }, option))
4374
+ })
4375
+ ]
4376
+ }),
4377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4378
+ className: panel_module_css_default.ecommerceSection,
4379
+ children: [
4380
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: tt("ecommerce.styleTitle") }),
4381
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
4382
+ value: ecommerce.styleHint,
4383
+ placeholder: tt("ecommerce.styleHint"),
4384
+ onChange: (event) => setEcommerce((previous) => ({
4385
+ ...previous,
4386
+ styleHint: event.target.value
4387
+ }))
4388
+ }),
4389
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4390
+ className: panel_module_css_default.ecommerceFieldLabel,
4391
+ children: tt("ecommerce.protectedLabel")
4392
+ }),
4393
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
4394
+ value: ecommerce.protectedFeatures,
4395
+ placeholder: tt("ecommerce.protectedFeatures"),
4396
+ onChange: (event) => setEcommerce((previous) => ({
4397
+ ...previous,
4398
+ protectedFeatures: event.target.value
4399
+ }))
4400
+ })
4401
+ ]
4402
+ }),
4403
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4404
+ ref: ecommerceFileInput,
4405
+ type: "file",
4406
+ multiple: true,
4407
+ accept: "image/png,image/jpeg,image/webp,image/gif",
4408
+ className: panel_module_css_default.hiddenFile,
4409
+ onChange: (event) => {
4410
+ acceptEcommerceFiles(event.target.files ?? void 0);
4411
+ event.target.value = "";
4412
+ }
4413
+ })
4414
+ ]
4415
+ }) : null,
2743
4416
  tab === "edit" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2744
4417
  className: panel_module_css_default.card,
2745
4418
  children: [refImage === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
@@ -2816,7 +4489,7 @@ window.__ModuleLoader__.load({
2816
4489
  }
2817
4490
  })]
2818
4491
  }) : null,
2819
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4492
+ workspace === "normal" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2820
4493
  className: panel_module_css_default.card,
2821
4494
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2822
4495
  className: panel_module_css_default.prompt,
@@ -2864,8 +4537,7 @@ window.__ModuleLoader__.load({
2864
4537
  })
2865
4538
  ]
2866
4539
  })]
2867
- }),
2868
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4540
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2869
4541
  className: panel_module_css_default.card,
2870
4542
  children: [
2871
4543
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2949,13 +4621,64 @@ window.__ModuleLoader__.load({
2949
4621
  ]
2950
4622
  })
2951
4623
  ]
2952
- })
4624
+ })] }) : null
2953
4625
  ]
2954
4626
  }),
2955
4627
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2956
4628
  className: panel_module_css_default.footer,
2957
4629
  children: [
2958
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
4630
+ workspace === "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4631
+ className: panel_module_css_default.ecommerceFooterBody,
4632
+ children: ecommercePreview ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4633
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4634
+ className: panel_module_css_default.ecommercePlanMini,
4635
+ children: [
4636
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: tt("ecommerce.planTitle", { count: ecommerceTotal }) }),
4637
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4638
+ className: panel_module_css_default.ecommercePlanList,
4639
+ children: ecommerceSlots.map((slot) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: slot.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["×", slot.count] })] }, slot.key))
4640
+ }),
4641
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4642
+ className: panel_module_css_default.ecommercePlanNote,
4643
+ children: tt("ecommerce.anchorNote")
4644
+ }),
4645
+ ecommerceAssets.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4646
+ className: panel_module_css_default.ecommercePlanWarn,
4647
+ children: tt("ecommerce.noAssetWarn")
4648
+ }) : null
4649
+ ]
4650
+ }),
4651
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4652
+ variant: "primary",
4653
+ size: "md",
4654
+ className: panel_module_css_default.ecommercePrimaryAction,
4655
+ disabled: ecommerceGenerateDisabled,
4656
+ onClick: () => {
4657
+ handleEcommerceGenerate();
4658
+ },
4659
+ children: ecommerceGenerating ? tt("generating") : tt("ecommerce.confirm")
4660
+ }),
4661
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4662
+ type: "button",
4663
+ className: panel_module_css_default.ecommercePlanBack,
4664
+ onClick: () => {
4665
+ setEcommercePreview(false);
4666
+ },
4667
+ children: tt("gallery.tagsCancel")
4668
+ })
4669
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4670
+ className: panel_module_css_default.ecommerceFooterHint,
4671
+ children: ecommerceTotal > 0 ? tt("ecommerce.footerReady", { count: ecommerceTotal }) : tt("ecommerce.footerEmpty")
4672
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4673
+ variant: "primary",
4674
+ size: "md",
4675
+ className: panel_module_css_default.ecommercePrimaryAction,
4676
+ disabled: ecommerce.productName.trim() === "" || ecommerceTotal === 0,
4677
+ onClick: () => setEcommercePreview(true),
4678
+ children: tt("ecommerce.preview")
4679
+ })] })
4680
+ }) : null,
4681
+ workspace === "ecommerce" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2959
4682
  className: panel_module_css_default.modelWrap,
2960
4683
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2961
4684
  className: panel_module_css_default.modelLabel,
@@ -2973,7 +4696,7 @@ window.__ModuleLoader__.load({
2973
4696
  onClick: () => {
2974
4697
  setModelOpen((open) => !open);
2975
4698
  },
2976
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
4699
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model || tt("model.noEditModels") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2977
4700
  viewBox: "0 0 16 16",
2978
4701
  width: "12",
2979
4702
  height: "12",
@@ -2989,7 +4712,7 @@ window.__ModuleLoader__.load({
2989
4712
  className: panel_module_css_default.modelMenuList,
2990
4713
  role: "listbox",
2991
4714
  "aria-label": tt("model.label"),
2992
- children: imageModels.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4715
+ children: modeModels.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2993
4716
  type: "button",
2994
4717
  role: "option",
2995
4718
  "aria-selected": model === option,
@@ -3004,7 +4727,7 @@ window.__ModuleLoader__.load({
3004
4727
  }) : null]
3005
4728
  })]
3006
4729
  }),
3007
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4730
+ workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3008
4731
  className: panel_module_css_default.compareControl,
3009
4732
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
3010
4733
  className: panel_module_css_default.compareToggle,
@@ -3019,7 +4742,7 @@ window.__ModuleLoader__.load({
3019
4742
  className: panel_module_css_default.compareModelChoices,
3020
4743
  role: "group",
3021
4744
  "aria-label": tt("compare.models"),
3022
- children: imageModels.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4745
+ children: modeModels.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3023
4746
  type: "checkbox",
3024
4747
  checked: compareModels.includes(option),
3025
4748
  onChange: () => {
@@ -3027,8 +4750,8 @@ window.__ModuleLoader__.load({
3027
4750
  }
3028
4751
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: option })] }, option))
3029
4752
  }) : null]
3030
- }),
3031
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4753
+ }) : null,
4754
+ workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
3032
4755
  variant: "primary",
3033
4756
  size: "md",
3034
4757
  className: panel_module_css_default.generateButton,
@@ -3040,16 +4763,15 @@ window.__ModuleLoader__.load({
3040
4763
  className: panel_module_css_default.generateInner,
3041
4764
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.spinner }), tt("generating")]
3042
4765
  }) : tt("generate")
3043
- })
4766
+ }) : null
3044
4767
  ]
3045
4768
  })
3046
4769
  ]
3047
- }),
3048
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4770
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3049
4771
  className: panel_module_css_default.canvas,
3050
- "data-gallery": tab === "gallery" ? "true" : void 0,
4772
+ "data-gallery": workspace === "normal" && tab === "gallery" ? "true" : void 0,
3051
4773
  children: [
3052
- tab === "gallery" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4774
+ workspace === "normal" && tab === "gallery" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3053
4775
  className: panel_module_css_default.galleryWorkspace,
3054
4776
  children: [
3055
4777
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
@@ -3136,6 +4858,7 @@ window.__ModuleLoader__.load({
3136
4858
  gallery.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3137
4859
  type: "button",
3138
4860
  className: panel_module_css_default.galleryClear,
4861
+ "data-gallery-clear": "",
3139
4862
  onClick: () => {
3140
4863
  clearGalleryAll();
3141
4864
  },
@@ -3230,6 +4953,32 @@ window.__ModuleLoader__.load({
3230
4953
  children: entry.mode === "edit" ? tt("mode.edit") : tt("mode.text")
3231
4954
  })]
3232
4955
  }),
4956
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4957
+ className: panel_module_css_default.galleryCardActions,
4958
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4959
+ type: "button",
4960
+ className: panel_module_css_default.galleryCardAction,
4961
+ "data-gallery-add-conversation": "",
4962
+ disabled: conversationBusy,
4963
+ title: tt("conversation.addHint"),
4964
+ onClick: (event) => {
4965
+ event.stopPropagation();
4966
+ addGalleryEntryToConversation(entry);
4967
+ },
4968
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
4969
+ viewBox: "0 0 16 16",
4970
+ width: "13",
4971
+ height: "13",
4972
+ fill: "none",
4973
+ stroke: "currentColor",
4974
+ strokeWidth: "1.5",
4975
+ strokeLinecap: "round",
4976
+ strokeLinejoin: "round",
4977
+ "aria-hidden": "true",
4978
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 4.5h10v7H3z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.5 2.5h5M8 6v4M6 8h4" })]
4979
+ }), galleryConversationAddingId === entry.id || addingToConversation === `gallery:${entry.id}` ? tt("conversation.adding") : tt("conversation.add")]
4980
+ })
4981
+ }),
3233
4982
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3234
4983
  className: panel_module_css_default.galleryCardFooter,
3235
4984
  children: [
@@ -3315,7 +5064,130 @@ window.__ModuleLoader__.load({
3315
5064
  })
3316
5065
  ]
3317
5066
  }) : null,
3318
- tab !== "gallery" && tasks.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5067
+ workspace === "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5068
+ className: panel_module_css_default.ecommerceResults,
5069
+ "data-ecommerce-results": "",
5070
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
5071
+ className: panel_module_css_default.ecommerceResultsHeader,
5072
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
5073
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: tt("ecommerce.results.title") }),
5074
+ ecommerceRestored !== null && ecommerceRestored.projectId === ecommerceProjectId && ecommerceRestored.projectName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: ecommerceRestored.projectName }) : null,
5075
+ ecommerceMergedItems.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [tt("ecommerce.results.progress", {
5076
+ done: ecommerceDoneCount,
5077
+ total: ecommerceMergedItems.length
5078
+ }), ecommerceFailedCount > 0 ? ` · ${tt("ecommerce.results.failed", { count: ecommerceFailedCount })}` : ""] }) : null,
5079
+ ecommerceAnchor !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5080
+ "data-ecommerce-anchor": "",
5081
+ children: tt("ecommerce.anchorPending")
5082
+ }) : null
5083
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5084
+ className: panel_module_css_default.ecommerceResultsActions,
5085
+ children: [ecommerceMergedItems.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5086
+ type: "button",
5087
+ className: panel_module_css_default.galleryBulkButton,
5088
+ "data-ecommerce-export": "",
5089
+ onClick: exportEcommerceManifest,
5090
+ children: tt("ecommerce.results.export")
5091
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5092
+ type: "button",
5093
+ className: panel_module_css_default.galleryBulkButton,
5094
+ "data-ecommerce-new": "",
5095
+ onClick: newEcommerceProduct,
5096
+ children: tt("ecommerce.results.newProduct")
5097
+ })]
5098
+ })]
5099
+ }), ecommerceMergedItems.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5100
+ className: panel_module_css_default.ecommerceResultsEmpty,
5101
+ children: tt("ecommerce.results.empty")
5102
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5103
+ className: panel_module_css_default.ecommerceGroups,
5104
+ children: ecommerceResultGroups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5105
+ className: panel_module_css_default.ecommerceGroup,
5106
+ "data-ecommerce-group": group.label,
5107
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [
5108
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: group.label }),
5109
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
5110
+ group.items.filter((item) => item.status === "completed").length,
5111
+ "/",
5112
+ group.items.length
5113
+ ] }),
5114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5115
+ type: "button",
5116
+ className: panel_module_css_default.galleryBulkButton,
5117
+ disabled: ecommerceGenerating,
5118
+ onClick: () => {
5119
+ regenerateEcommerceSlot(group.label);
5120
+ },
5121
+ children: tt("ecommerce.results.regenerate")
5122
+ })
5123
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5124
+ className: panel_module_css_default.ecommerceGroupGrid,
5125
+ children: group.items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5126
+ className: panel_module_css_default.ecommerceTaskCard,
5127
+ "data-status": item.status,
5128
+ children: item.status === "completed" && item.images.length > 0 ? item.images.map((image, imageIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("figure", {
5129
+ className: panel_module_css_default.imageCard,
5130
+ role: "button",
5131
+ tabIndex: 0,
5132
+ title: tt("preview.open"),
5133
+ onClick: () => {
5134
+ openPreview(item.images, imageIndex);
5135
+ },
5136
+ children: [
5137
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
5138
+ className: panel_module_css_default.image,
5139
+ src: srcOf(image),
5140
+ alt: `${group.label} ${imageIndex + 1}`
5141
+ }),
5142
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5143
+ className: panel_module_css_default.ecommerceResultBadge,
5144
+ children: group.label
5145
+ }),
5146
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5147
+ className: panel_module_css_default.ecommerceTaskActions,
5148
+ onClick: (event) => event.stopPropagation(),
5149
+ children: [
5150
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
5151
+ className: panel_module_css_default.ecommerceActionChip,
5152
+ href: srcOf(image),
5153
+ download: `product-${item.slotKey || item.id}-${imageIndex + 1}.${extensionOf(image.mime)}`,
5154
+ children: tt("download")
5155
+ }),
5156
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5157
+ type: "button",
5158
+ className: panel_module_css_default.ecommerceActionChip,
5159
+ disabled: galleryAdding,
5160
+ onClick: () => {
5161
+ addToGallery(image);
5162
+ },
5163
+ children: tt("gallery.add")
5164
+ }),
5165
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5166
+ type: "button",
5167
+ className: panel_module_css_default.ecommerceActionChip,
5168
+ disabled: conversationBusy,
5169
+ onClick: () => {
5170
+ addImageToConversation(image, imageIndex, `${item.id}:${imageIndex}`);
5171
+ },
5172
+ children: addingToConversation === `${item.id}:${imageIndex}` ? tt("conversation.adding") : tt("conversation.add")
5173
+ })
5174
+ ]
5175
+ })
5176
+ ]
5177
+ }, imageIndex)) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5178
+ className: panel_module_css_default.ecommerceTaskState,
5179
+ children: [
5180
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: group.label }),
5181
+ tt(`tasks.${item.status}`),
5182
+ item.error !== void 0 ? ` · ${item.error}` : ""
5183
+ ]
5184
+ })
5185
+ }, item.id))
5186
+ })]
5187
+ }, group.label))
5188
+ })]
5189
+ }) : null,
5190
+ (workspace === "ecommerce" || tab !== "gallery") && tasks.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3319
5191
  className: panel_module_css_default.taskTray,
3320
5192
  "data-open": taskTrayOpen ? "true" : "false",
3321
5193
  "aria-label": tt("tasks.title"),
@@ -3381,13 +5253,14 @@ window.__ModuleLoader__.load({
3381
5253
  }, task.id))
3382
5254
  })]
3383
5255
  }) : null,
3384
- tab !== "gallery" && comparison !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5256
+ workspace === "normal" && tab !== "gallery" && comparison !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3385
5257
  className: panel_module_css_default.comparisonBoard,
3386
5258
  "aria-label": tt("compare.title"),
3387
5259
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: tt("compare.title") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
3388
5260
  comparisonResults.length,
3389
5261
  " / ",
3390
- comparisonTasks.length
5262
+ comparisonTasks.length,
5263
+ generating ? ` · ${tt("canvas.elapsed", { seconds: elapsed })}` : ""
3391
5264
  ] })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3392
5265
  type: "button",
3393
5266
  disabled: comparisonResults.length === 0,
@@ -3397,13 +5270,25 @@ window.__ModuleLoader__.load({
3397
5270
  children: tt("compare.fullscreen")
3398
5271
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3399
5272
  className: panel_module_css_default.comparisonGrid,
3400
- children: comparisonTasks.map((task) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: task.request.model }), task.result?.images[0] !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
3401
- src: srcOf(task.result.images[0]),
3402
- alt: task.request.model
3403
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: tt(`tasks.${task.status}`) })] }, task.id))
5273
+ children: comparisonTasks.map((task) => {
5274
+ const taskImages = task.result?.images ?? [];
5275
+ const image = taskImages[0];
5276
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: task.request.model }), image !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5277
+ type: "button",
5278
+ className: panel_module_css_default.comparisonImageButton,
5279
+ title: tt("preview.open"),
5280
+ onClick: () => {
5281
+ openPreview(taskImages, 0);
5282
+ },
5283
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
5284
+ src: srcOf(image),
5285
+ alt: task.request.model
5286
+ })
5287
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: tt(`tasks.${task.status}`) })] }, task.id);
5288
+ })
3404
5289
  })]
3405
5290
  }) : null,
3406
- generating ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5291
+ generating && comparison === null && workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3407
5292
  className: panel_module_css_default.canvasState,
3408
5293
  "data-generation-state": activeTask?.status ?? "submitting",
3409
5294
  role: "status",
@@ -3424,7 +5309,7 @@ window.__ModuleLoader__.load({
3424
5309
  role: "alert",
3425
5310
  children: tt("canvas.error", { error })
3426
5311
  }) : null,
3427
- !generating && !error && images.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5312
+ !generating && !error && images.length === 0 && workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3428
5313
  className: panel_module_css_default.canvasState,
3429
5314
  children: [
3430
5315
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -3466,7 +5351,7 @@ window.__ModuleLoader__.load({
3466
5351
  })
3467
5352
  ]
3468
5353
  }) : null,
3469
- !generating && images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5354
+ !generating && images.length > 0 && workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3470
5355
  className: panel_module_css_default.canvasBody,
3471
5356
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3472
5357
  className: panel_module_css_default.canvasMeta,
@@ -3553,6 +5438,28 @@ window.__ModuleLoader__.load({
3553
5438
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 5.8v4.4M5.8 8h4.4" })]
3554
5439
  }), tt("gallery.add")]
3555
5440
  }),
5441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
5442
+ type: "button",
5443
+ className: panel_module_css_default.conversationAdd,
5444
+ title: tt("conversation.add"),
5445
+ disabled: conversationBusy,
5446
+ onClick: (event) => {
5447
+ event.stopPropagation();
5448
+ addImageToConversation(image, index);
5449
+ },
5450
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
5451
+ viewBox: "0 0 16 16",
5452
+ width: "12",
5453
+ height: "12",
5454
+ fill: "none",
5455
+ stroke: "currentColor",
5456
+ strokeWidth: "1.5",
5457
+ strokeLinecap: "round",
5458
+ strokeLinejoin: "round",
5459
+ "aria-hidden": "true",
5460
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 4.5h10v7H3z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.5 2.5h5M8 6v4M6 8h4" })]
5461
+ }), addingToConversation === index ? tt("conversation.adding") : tt("conversation.add")]
5462
+ }),
3556
5463
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
3557
5464
  className: panel_module_css_default.download,
3558
5465
  href: srcOf(image),
@@ -3567,215 +5474,17 @@ window.__ModuleLoader__.load({
3567
5474
  })]
3568
5475
  }) : null
3569
5476
  ]
3570
- }),
3571
- tab === "gallery" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3572
- className: panel_module_css_default.history,
3573
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3574
- className: panel_module_css_default.historyHeader,
3575
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3576
- className: panel_module_css_default.historyTitle,
3577
- children: tt("gallery.title")
3578
- }), gallery.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3579
- type: "button",
3580
- className: panel_module_css_default.historyClear,
3581
- onClick: () => {
3582
- clearGalleryAll();
3583
- },
3584
- children: tt("gallery.clear")
3585
- }) : null]
3586
- }), gallery.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3587
- className: panel_module_css_default.historyEmpty,
3588
- children: tt("gallery.empty")
3589
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3590
- className: panel_module_css_default.historyList,
3591
- children: gallery.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3592
- className: panel_module_css_default.historyItem,
3593
- "data-active": entry.id === galleryViewingId ? "" : void 0,
3594
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3595
- type: "button",
3596
- className: panel_module_css_default.historyMain,
3597
- onClick: () => {
3598
- viewGalleryEntry(entry);
3599
- },
3600
- children: [entry.images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
3601
- className: panel_module_css_default.historyThumb,
3602
- src: entry.images[0].url,
3603
- alt: ""
3604
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.historyThumbPlaceholder }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3605
- className: panel_module_css_default.historyInfo,
3606
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3607
- className: panel_module_css_default.historyPrompt,
3608
- children: entry.prompt
3609
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3610
- className: panel_module_css_default.historyMeta,
3611
- children: [
3612
- tt(`mode.${entry.mode === "edit" ? "edit" : "text"}`),
3613
- " · ",
3614
- formatTime(entry.createdAt)
3615
- ]
3616
- })]
3617
- })]
3618
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3619
- className: panel_module_css_default.historyActions,
3620
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3621
- type: "button",
3622
- className: panel_module_css_default.historyAction,
3623
- onClick: () => {
3624
- restoreGalleryEntry(entry);
3625
- },
3626
- children: tt("history.restore")
3627
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3628
- type: "button",
3629
- className: panel_module_css_default.historyAction,
3630
- "data-danger": true,
3631
- onClick: () => {
3632
- deleteGalleryEntry(entry.id);
3633
- },
3634
- children: tt("gallery.delete")
3635
- })]
3636
- })]
3637
- }, entry.id))
3638
- })]
3639
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
3640
- className: panel_module_css_default.history,
3641
- children: [
3642
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
3643
- className: panel_module_css_default.historyHeader,
3644
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3645
- className: panel_module_css_default.historyTitle,
3646
- children: tt("history.title")
3647
- }), history.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3648
- type: "button",
3649
- className: panel_module_css_default.historyClear,
3650
- onClick: () => {
3651
- clearHistory();
3652
- },
3653
- children: tt("history.clear")
3654
- }) : null]
3655
- }),
3656
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3657
- className: panel_module_css_default.historyFilters,
3658
- children: [
3659
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3660
- className: panel_module_css_default.historySearch,
3661
- value: historyQuery,
3662
- onChange: (event) => {
3663
- setHistoryQuery(event.target.value);
3664
- },
3665
- placeholder: tt("history.search"),
3666
- "aria-label": tt("history.search")
3667
- }),
3668
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3669
- value: historyModelFilter,
3670
- onChange: (event) => {
3671
- setHistoryModelFilter(event.target.value);
3672
- },
3673
- "aria-label": tt("history.model"),
3674
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3675
- value: "all",
3676
- children: tt("history.allModels")
3677
- }), [...new Set(history.map((entry) => entry.model))].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3678
- value: option,
3679
- children: option
3680
- }, option))]
3681
- }),
3682
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3683
- value: historyRatioFilter,
3684
- onChange: (event) => {
3685
- setHistoryRatioFilter(event.target.value);
3686
- },
3687
- "aria-label": tt("history.ratio"),
3688
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3689
- value: "all",
3690
- children: tt("history.allRatios")
3691
- }), [...new Set(history.map((entry) => normalizeSize(entry.size)))].map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3692
- value: option,
3693
- children: option
3694
- }, option))]
3695
- })
3696
- ]
3697
- }),
3698
- filteredHistory.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3699
- className: panel_module_css_default.historyEmpty,
3700
- children: tt("history.empty")
3701
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3702
- className: panel_module_css_default.historyList,
3703
- children: filteredHistory.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3704
- className: panel_module_css_default.historyItem,
3705
- "data-active": entry.id === viewingHistoryId ? "" : void 0,
3706
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3707
- type: "button",
3708
- className: panel_module_css_default.historyMain,
3709
- onClick: () => {
3710
- viewHistoryEntry(entry);
3711
- },
3712
- children: [entry.images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
3713
- className: panel_module_css_default.historyThumb,
3714
- src: entry.images[0].url,
3715
- alt: ""
3716
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.historyThumbPlaceholder }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3717
- className: panel_module_css_default.historyInfo,
3718
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3719
- className: panel_module_css_default.historyPrompt,
3720
- children: entry.prompt
3721
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3722
- className: panel_module_css_default.historyMeta,
3723
- children: [
3724
- tt(`mode.${entry.mode === "edit" ? "edit" : "text"}`),
3725
- " · ",
3726
- formatTime(entry.createdAt),
3727
- " · ",
3728
- entry.images.length,
3729
- " ",
3730
- tt("history.images")
3731
- ]
3732
- })]
3733
- })]
3734
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3735
- className: panel_module_css_default.historyActions,
3736
- children: [
3737
- entry.images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3738
- type: "button",
3739
- className: panel_module_css_default.historyAction,
3740
- disabled: galleryAdding,
3741
- title: tt("gallery.add"),
3742
- onClick: () => {
3743
- addHistoryEntryToGallery(entry);
3744
- },
3745
- children: tt("gallery.add")
3746
- }) : null,
3747
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3748
- type: "button",
3749
- className: panel_module_css_default.historyAction,
3750
- onClick: () => {
3751
- restoreHistoryEntry(entry);
3752
- },
3753
- children: tt("history.restore")
3754
- }),
3755
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3756
- type: "button",
3757
- className: panel_module_css_default.historyAction,
3758
- "data-danger": true,
3759
- onClick: () => {
3760
- deleteHistoryEntry(entry.id);
3761
- },
3762
- children: tt("history.delete")
3763
- })
3764
- ]
3765
- })]
3766
- }, entry.id))
3767
- })
3768
- ]
3769
- })
3770
- ]
5477
+ })]
5478
+ })
3771
5479
  }),
5480
+ sidebarHistoryHost !== null && historyPanel !== null ? (0, react_dom.createPortal)(historyPanel, sidebarHistoryHost) : null,
3772
5481
  libraryOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TemplateLibrary, {
3773
5482
  api,
3774
5483
  onClose: () => {
3775
5484
  setLibraryOpen(false);
3776
5485
  },
3777
5486
  onUse: (text) => {
3778
- setTab("text");
5487
+ openTab("text");
3779
5488
  setPrompt(text);
3780
5489
  setError(null);
3781
5490
  setLibraryOpen(false);
@@ -4047,6 +5756,16 @@ window.__ModuleLoader__.load({
4047
5756
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4048
5757
  className: panel_module_css_default.lightboxActions,
4049
5758
  children: [
5759
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5760
+ type: "button",
5761
+ className: panel_module_css_default.lightboxEdit,
5762
+ disabled: conversationBusy,
5763
+ title: tt("conversation.addHint"),
5764
+ onClick: () => {
5765
+ addImageToConversation(previewImage, preview.index);
5766
+ },
5767
+ children: addingToConversation === preview.index ? tt("conversation.adding") : tt("conversation.add")
5768
+ }),
4050
5769
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4051
5770
  type: "button",
4052
5771
  className: panel_module_css_default.lightboxEdit,
@@ -4096,6 +5815,22 @@ window.__ModuleLoader__.load({
4096
5815
  rx: "1.5"
4097
5816
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 5.8v4.4M5.8 8h4.4" })]
4098
5817
  }), galleryMessage]
5818
+ }) : null,
5819
+ conversationMessage !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5820
+ className: panel_module_css_default.conversationToast,
5821
+ role: "status",
5822
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
5823
+ viewBox: "0 0 16 16",
5824
+ width: "14",
5825
+ height: "14",
5826
+ fill: "none",
5827
+ stroke: "currentColor",
5828
+ strokeWidth: "1.5",
5829
+ strokeLinecap: "round",
5830
+ strokeLinejoin: "round",
5831
+ "aria-hidden": "true",
5832
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 4.5h10v7H3z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.5 2.5h5M8 6v4M6 8h4" })]
5833
+ }), conversationMessage]
4099
5834
  }) : null
4100
5835
  ]
4101
5836
  });
@@ -4111,24 +5846,11 @@ window.__ModuleLoader__.load({
4111
5846
  }
4112
5847
  //#endregion
4113
5848
  //#region src/client/mount.tsx
4114
- /**
4115
- * Panel view mounting.
4116
- *
4117
- * The `conversation` slot is single-occupant (ui-conversation) and external
4118
- * plugins cannot declare slots, so the panel takes over the center column at
4119
- * the DOM level: a container is appended inside the conversation grid item
4120
- * (an extra trailing child React never manages), and a stylesheet rule hides
4121
- * the conversation content while the panel is active. Toggling is a data
4122
- * attribute on <html> — no React involvement, so the conversation subtree
4123
- * underneath stays mounted and stateful.
4124
- *
4125
- * Shell compatibility: the center column is `[data-pane="conversation"]` on
4126
- * legacy shells and `[class*="centerCol"]` on the rc.6+ AppFrame layout (the
4127
- * same dual selector the dsh-ssh / task-board panels use); both are queried
4128
- * and both get the `position: relative` base in panel.module.css.
4129
- */
4130
5849
  const CONVERSATION_COLUMN_SELECTOR = "[data-pane=\"conversation\"], [class*=\"centerCol\"]";
4131
5850
  const ACTIVE_ATTR = "data-dsh-imagegen-active";
5851
+ const RESIZER_SELECTOR = "[data-dsh-imagegen-chat-resizer]";
5852
+ const CHAT_WIDTH_STORAGE_KEY = "dsh-imagegen:chat-width";
5853
+ const CHAT_MIN_WIDTH = 320;
4132
5854
  /** Sibling panels' activation attributes, removed when this panel opens. */
4133
5855
  const OTHER_ACTIVE_ATTRS = ["data-dsh-taskboard-active", "data-dsh-ssh-active"];
4134
5856
  /** Cross-plugin activation event; detail is the activating panel name. */
@@ -4138,6 +5860,73 @@ window.__ModuleLoader__.load({
4138
5860
  function conversationColumn() {
4139
5861
  return document.querySelector(CONVERSATION_COLUMN_SELECTOR) ?? void 0;
4140
5862
  }
5863
+ function readChatWidth() {
5864
+ try {
5865
+ const raw = window.localStorage.getItem(CHAT_WIDTH_STORAGE_KEY);
5866
+ if (raw === null) return void 0;
5867
+ const value = Number(raw);
5868
+ return Number.isFinite(value) && value >= CHAT_MIN_WIDTH ? value : void 0;
5869
+ } catch {
5870
+ return;
5871
+ }
5872
+ }
5873
+ function writeChatWidth(value) {
5874
+ try {
5875
+ window.localStorage.setItem(CHAT_WIDTH_STORAGE_KEY, String(Math.round(value)));
5876
+ } catch {}
5877
+ }
5878
+ function applyChatWidth(column, clientX) {
5879
+ const bounds = column.getBoundingClientRect();
5880
+ const max = Math.max(CHAT_MIN_WIDTH, bounds.width * .7);
5881
+ const width = Math.min(max, Math.max(CHAT_MIN_WIDTH, bounds.right - clientX));
5882
+ column.style.setProperty("--dsh-imagegen-chat-width", `${Math.round(width)}px`);
5883
+ writeChatWidth(width);
5884
+ }
5885
+ /** Create the visible handle that separates the image workspace and chat. */
5886
+ function createChatResizer(column) {
5887
+ const resizer = document.createElement("div");
5888
+ resizer.dataset.dshImagegenChatResizer = "";
5889
+ resizer.className = panel_module_css_default.chatResizer;
5890
+ resizer.setAttribute("role", "separator");
5891
+ resizer.setAttribute("aria-orientation", "vertical");
5892
+ resizer.setAttribute("aria-label", "调整对话区域宽度");
5893
+ resizer.tabIndex = 0;
5894
+ const saved = readChatWidth();
5895
+ if (saved !== void 0) column.style.setProperty("--dsh-imagegen-chat-width", `${saved}px`);
5896
+ const onPointerDown = (event) => {
5897
+ if (event.button !== 0) return;
5898
+ event.preventDefault();
5899
+ resizer.setPointerCapture?.(event.pointerId);
5900
+ const onMove = (move) => {
5901
+ applyChatWidth(column, move.clientX);
5902
+ };
5903
+ const onUp = () => {
5904
+ window.removeEventListener("pointermove", onMove);
5905
+ window.removeEventListener("pointerup", onUp);
5906
+ window.removeEventListener("pointercancel", onUp);
5907
+ resizer.releasePointerCapture?.(event.pointerId);
5908
+ document.documentElement.style.removeProperty("cursor");
5909
+ document.documentElement.style.removeProperty("user-select");
5910
+ };
5911
+ window.addEventListener("pointermove", onMove);
5912
+ window.addEventListener("pointerup", onUp);
5913
+ window.addEventListener("pointercancel", onUp);
5914
+ document.documentElement.style.setProperty("cursor", "col-resize");
5915
+ document.documentElement.style.setProperty("user-select", "none");
5916
+ };
5917
+ const onKeyDown = (event) => {
5918
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
5919
+ event.preventDefault();
5920
+ const bounds = column.getBoundingClientRect();
5921
+ const current = column.style.getPropertyValue("--dsh-imagegen-chat-width");
5922
+ const currentWidth = Number.parseFloat(current) || bounds.width * .36;
5923
+ const delta = event.key === "ArrowLeft" ? -24 : 24;
5924
+ applyChatWidth(column, bounds.right - currentWidth - delta);
5925
+ };
5926
+ resizer.addEventListener("pointerdown", onPointerDown);
5927
+ resizer.addEventListener("keydown", onKeyDown);
5928
+ return resizer;
5929
+ }
4141
5930
  /**
4142
5931
  * Mount the panel React tree into the center column and bind its visibility
4143
5932
  * to the controller's panelOpen state.
@@ -4146,9 +5935,10 @@ window.__ModuleLoader__.load({
4146
5935
  * @param scope - the settings scope (config status banner).
4147
5936
  * @returns disposer unmounting the tree and restoring the column.
4148
5937
  */
4149
- function mountPanel(controller, api, scope) {
5938
+ function mountPanel(controller, api, scope, services = {}) {
4150
5939
  let root;
4151
5940
  let container;
5941
+ let resizer;
4152
5942
  const ensure = () => {
4153
5943
  if (container !== void 0) {
4154
5944
  if (container.isConnected) return;
@@ -4156,6 +5946,8 @@ window.__ModuleLoader__.load({
4156
5946
  root = void 0;
4157
5947
  container.remove();
4158
5948
  container = void 0;
5949
+ resizer?.remove();
5950
+ resizer = void 0;
4159
5951
  }
4160
5952
  const column = conversationColumn();
4161
5953
  if (column === void 0) return;
@@ -4166,8 +5958,11 @@ window.__ModuleLoader__.load({
4166
5958
  root = (0, react_dom_client.createRoot)(container);
4167
5959
  root.render(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageGenPanel, {
4168
5960
  api,
4169
- scope
5961
+ scope,
5962
+ ...services
4170
5963
  }));
5964
+ resizer = column.querySelector(RESIZER_SELECTOR) ?? createChatResizer(column);
5965
+ if (resizer.parentElement !== column) column.appendChild(resizer);
4171
5966
  };
4172
5967
  const waitObserver = new MutationObserver(() => {
4173
5968
  ensure();
@@ -4209,110 +6004,150 @@ window.__ModuleLoader__.load({
4209
6004
  root = void 0;
4210
6005
  container?.remove();
4211
6006
  container = void 0;
6007
+ resizer?.remove();
6008
+ resizer = void 0;
4212
6009
  };
4213
6010
  }
4214
6011
  //#endregion
4215
6012
  //#region src/client/sidebar-entry.ts
4216
- /** Family entry selectors of sibling plugins (relative placement anchor). */
4217
- const FAMILY_ENTRY_SELECTOR = "[data-dsh-taskboard-entry], [data-dsh-ssh-entry], [data-dsh-imagegen-entry]";
4218
- /** Find the sidebar shell root element, or undefined while not yet mounted. */
6013
+ /** Stable selector for the injected two-tab host. */
6014
+ const ENTRY_SELECTOR = "[data-dsh-imagegen-session-tabs]";
6015
+ /** Stable selector for the history surface in the shell region area. */
6016
+ const HISTORY_HOST_SELECTOR = "[data-dsh-imagegen-history-host]";
6017
+ /** Inline picture glyph kept deliberately small for the sidebar rail. */
6018
+ const IMAGE_ICON = "<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2.5\" width=\"12\" height=\"11\" rx=\"1.5\"/><circle cx=\"5.6\" cy=\"5.8\" r=\"1\"/><path d=\"M2.5 12.5l3.6-3.4 2.4 2.2 3-3 2 2.4\"/></svg>";
6019
+ /** Inline plus glyph for the new-session tab. */
6020
+ const NEW_SESSION_ICON = "<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M8 3v10M3 8h10\"/></svg>";
6021
+ /** Find the sidebar shell root, or undefined while it is not mounted. */
4219
6022
  function sidebarRoot() {
4220
6023
  const column = document.querySelector("[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]");
4221
6024
  if (column === null) return void 0;
4222
6025
  return column.querySelector("[class*=\"logoRow\"]")?.parentElement ?? column.firstElementChild;
4223
6026
  }
4224
- /** The New Session button: nested in the logo row on current shells, a direct child on legacy shells. */
6027
+ /** The shell-owned New Session button across current and legacy shells. */
4225
6028
  function newSessionButton(root) {
4226
- const nested = root.querySelector("button[class*=\"newSession\"]");
4227
- if (nested !== null) return nested;
4228
- for (const child of root.children) if (child.tagName === "BUTTON") return child;
6029
+ return root.querySelector("button[data-dsh-part=\"new-session\"], button[class*=\"newSession\"]") ?? Array.from(root.children).find((child) => child instanceof HTMLElement && child.tagName === "BUTTON");
4229
6030
  }
4230
- /** Build the entry row (a detached button; insert once the shell is up). */
4231
- function createEntry(controller, label, tooltip) {
4232
- const entry = document.createElement("button");
4233
- entry.type = "button";
4234
- entry.dataset.dshImagegenEntry = "";
4235
- entry.className = panel_module_css_default.entry;
4236
- entry.setAttribute("aria-label", label);
4237
- entry.setAttribute("title", tooltip);
4238
- entry.innerHTML = "<span class=\"" + panel_module_css_default.entryIcon + "\"><svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2.5\" width=\"12\" height=\"11\" rx=\"1.5\"/><circle cx=\"5.6\" cy=\"5.8\" r=\"1\"/><path d=\"M2.5 12.5l3.6-3.4 2.4 2.2 3-3 2 2.4\"/></svg></span><span class=\"" + panel_module_css_default.entryLabel + "\">" + label + "</span>";
4239
- entry.addEventListener("click", () => {
4240
- controller.toggle();
4241
- });
4242
- return entry;
6031
+ /** Locate the shell region that normally contains workspaces and sessions. */
6032
+ function regionArea(root) {
6033
+ return root.querySelector("[class*=\"regionArea\"]") ?? void 0;
4243
6034
  }
4244
- /** Re-insert the entry after the family block (task board ssh → imagegen). */
4245
- function placeEntry(root, entry) {
6035
+ function makeTab(label, tooltip, icon, onClick) {
6036
+ const tab = document.createElement("button");
6037
+ tab.type = "button";
6038
+ tab.className = panel_module_css_default.sessionTab;
6039
+ tab.setAttribute("aria-label", label);
6040
+ tab.setAttribute("title", tooltip);
6041
+ tab.innerHTML = `<span class="${panel_module_css_default.sessionTabIcon}">${icon}</span><span class="${panel_module_css_default.sessionTabLabel}">${label}</span>`;
6042
+ tab.addEventListener("click", onClick);
6043
+ return tab;
6044
+ }
6045
+ function hideShellButton(button) {
6046
+ button.dataset.dshImagegenOriginal = "";
6047
+ button.setAttribute("aria-hidden", "true");
6048
+ button.tabIndex = -1;
6049
+ button.style.display = "none";
6050
+ }
6051
+ function restoreShellButton(button) {
6052
+ button.style.removeProperty("display");
6053
+ button.removeAttribute("aria-hidden");
6054
+ button.removeAttribute("tabindex");
6055
+ delete button.dataset.dshImagegenOriginal;
6056
+ }
6057
+ /** Mount or repair the two-tab host at the shell's New Session position. */
6058
+ function placeTabs(root, controller, newSessionLabel, newSessionTooltip, imageLabel, imageTooltip) {
4246
6059
  const button = newSessionButton(root);
4247
- if (button === void 0) return false;
4248
- if (entry.parentElement !== root) {
4249
- const row = button.closest("[class*=\"logoRow\"]");
4250
- const base = row !== null && row.parentElement === root ? row : button;
4251
- const family = Array.from(root.children).filter((el) => el instanceof HTMLElement && el.matches(FAMILY_ENTRY_SELECTOR));
4252
- const anchor = family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling;
4253
- root.insertBefore(entry, anchor);
6060
+ if (button === void 0) return void 0;
6061
+ const existing = root.querySelector(ENTRY_SELECTOR);
6062
+ if (existing !== null && existing.parentElement === button.parentElement) {
6063
+ hideShellButton(button);
6064
+ return existing;
4254
6065
  }
4255
- return true;
6066
+ existing?.remove();
6067
+ const tabs = document.createElement("div");
6068
+ tabs.dataset.dshImagegenSessionTabs = "";
6069
+ tabs.className = panel_module_css_default.sessionTabs;
6070
+ tabs.setAttribute("role", "tablist");
6071
+ tabs.setAttribute("aria-label", imageTooltip);
6072
+ const newSessionTab = makeTab(newSessionLabel, newSessionTooltip, NEW_SESSION_ICON, () => {
6073
+ controller.close();
6074
+ button.click();
6075
+ });
6076
+ const imageTab = makeTab(imageLabel, imageTooltip, IMAGE_ICON, () => {
6077
+ controller.open();
6078
+ });
6079
+ newSessionTab.dataset.dshImagegenTab = "new-session";
6080
+ imageTab.dataset.dshImagegenTab = "image";
6081
+ tabs.append(newSessionTab, imageTab);
6082
+ button.parentElement?.insertBefore(tabs, button);
6083
+ hideShellButton(button);
6084
+ return tabs;
6085
+ }
6086
+ /** Mount an overlay host over the workspace/session tree for image history. */
6087
+ function placeHistoryHost(root) {
6088
+ const region = regionArea(root);
6089
+ if (region === void 0) return void 0;
6090
+ const existing = region.querySelector(HISTORY_HOST_SELECTOR);
6091
+ if (existing !== null) return existing;
6092
+ const host = document.createElement("div");
6093
+ host.dataset.dshImagegenHistoryHost = "";
6094
+ host.className = panel_module_css_default.sidebarHistoryHost;
6095
+ region.append(host);
6096
+ return host;
4256
6097
  }
4257
6098
  /**
4258
- * Mount the sidebar entry, waiting for the shell to render and self-healing
4259
- * on later React re-renders.
4260
- * @param controller - the panel controller the entry toggles.
4261
- * @param label - the entry label (localized).
4262
- * @param tooltip - the entry tooltip (localized).
4263
- * @returns disposer removing the entry and its observers.
6099
+ * Mount the two tabs and self-heal after React rebuilds the sidebar. The
6100
+ * shell-owned button is restored by the disposer so unloading the plugin
6101
+ * leaves the host unchanged.
4264
6102
  */
4265
- function mountSidebarEntry(controller, label, tooltip) {
4266
- const entry = createEntry(controller, label, tooltip);
6103
+ function mountSidebarEntry(controller, newSessionLabel, newSessionTooltip, imageLabel, imageTooltip) {
4267
6104
  let root;
4268
- let placed = false;
4269
- const tryPlace = () => {
4270
- if (root !== void 0 && !root.isConnected) {
4271
- rootObserver.disconnect();
4272
- root = void 0;
4273
- placed = false;
6105
+ let tabs;
6106
+ let historyHost;
6107
+ let originalButton;
6108
+ const syncActive = () => {
6109
+ if (tabs === void 0) return;
6110
+ const newTab = tabs.querySelector("[data-dsh-imagegen-tab=\"new-session\"]");
6111
+ const imageTab = tabs.querySelector("[data-dsh-imagegen-tab=\"image\"]");
6112
+ if (controller.getSnapshot().panelOpen) {
6113
+ if (newTab !== null) delete newTab.dataset.active;
6114
+ if (imageTab !== null) imageTab.dataset.active = "";
6115
+ } else {
6116
+ if (newTab !== null) newTab.dataset.active = "";
6117
+ if (imageTab !== null) delete imageTab.dataset.active;
4274
6118
  }
4275
- if (placed) {
4276
- if (document.body.contains(entry)) return;
4277
- rootObserver.disconnect();
6119
+ };
6120
+ const ensure = () => {
6121
+ if (root !== void 0 && !root.isConnected) {
4278
6122
  root = void 0;
4279
- placed = false;
6123
+ tabs = void 0;
6124
+ historyHost = void 0;
6125
+ originalButton = void 0;
4280
6126
  }
4281
6127
  root ??= sidebarRoot();
4282
6128
  if (root === void 0) return;
4283
- placed = placeEntry(root, entry);
4284
- if (placed) rootObserver.observe(root, {
4285
- childList: true,
4286
- subtree: true
4287
- });
6129
+ root.dataset.dshImagegenSidebarRoot = "";
6130
+ const button = newSessionButton(root);
6131
+ if (button === void 0) return;
6132
+ originalButton ??= button;
6133
+ tabs = placeTabs(root, controller, newSessionLabel, newSessionTooltip, imageLabel, imageTooltip);
6134
+ historyHost = placeHistoryHost(root);
6135
+ syncActive();
4288
6136
  };
4289
- const waitObserver = new MutationObserver(() => {
4290
- tryPlace();
4291
- });
4292
- waitObserver.observe(document.body, {
6137
+ const bodyObserver = new MutationObserver(ensure);
6138
+ bodyObserver.observe(document.body, {
4293
6139
  childList: true,
4294
6140
  subtree: true
4295
6141
  });
4296
- const rootObserver = new MutationObserver(() => {
4297
- if (root === void 0 || !root.isConnected) {
4298
- placed = false;
4299
- tryPlace();
4300
- return;
4301
- }
4302
- if (!root.contains(entry)) placed = placeEntry(root, entry);
4303
- });
4304
- const syncActive = () => {
4305
- if (controller.getSnapshot().panelOpen) entry.dataset.active = "true";
4306
- else delete entry.dataset.active;
4307
- };
4308
6142
  const unsubscribe = controller.subscribe(syncActive);
4309
- syncActive();
4310
- tryPlace();
6143
+ ensure();
4311
6144
  return () => {
4312
- waitObserver.disconnect();
4313
- rootObserver.disconnect();
6145
+ bodyObserver.disconnect();
4314
6146
  unsubscribe();
4315
- entry.remove();
6147
+ tabs?.remove();
6148
+ historyHost?.remove();
6149
+ if (originalButton !== void 0 && originalButton.isConnected) restoreShellButton(originalButton);
6150
+ if (root !== void 0) delete root.dataset.dshImagegenSidebarRoot;
4316
6151
  };
4317
6152
  }
4318
6153
  //#endregion
@@ -4771,132 +6606,38 @@ window.__ModuleLoader__.load({
4771
6606
  this.saving = true;
4772
6607
  this.failed = false;
4773
6608
  this.publish();
4774
- try {
4775
- await this.scope.mutateOps(ops);
4776
- this.stagedChannels = null;
4777
- this.stagedKeys.clear();
4778
- this.stagedDefault = null;
4779
- this.failed = false;
4780
- } catch {
4781
- this.failed = true;
4782
- } finally {
4783
- this.saving = false;
4784
- this.publish();
4785
- }
4786
- }
4787
- publish() {
4788
- for (const listener of [...this.listeners]) listener();
4789
- }
4790
- };
4791
- /** Project a stored channel into a draft (secrets never travel in channels). */
4792
- function toDraft(channel) {
4793
- return {
4794
- id: channel.id,
4795
- preset: channel.preset,
4796
- name: channel.name,
4797
- apiUrl: channel.apiUrl,
4798
- models: channel.models.map((model) => ({ ...model }))
4799
- };
4800
- }
4801
- /** The scope's current channels value (a plain array), for change detection. */
4802
- function scopeChannelsOf(scope) {
4803
- const view = scope.getSnapshot().value;
4804
- return Array.isArray(view?.channels) ? view.channels : [];
4805
- }
4806
- //#endregion
4807
- //#region src/model-catalog.ts
4808
- const ENTRIES = {
4809
- "gpt-image": {
4810
- label: "gpt-image",
4811
- labelZh: "GPT 图像",
4812
- known: true,
4813
- supportsEdit: true,
4814
- supportsAspectRatio: false,
4815
- qualityTiers: [
4816
- "1K",
4817
- "2K",
4818
- "4K"
4819
- ]
4820
- },
4821
- "dall-e": {
4822
- label: "DALL·E",
4823
- labelZh: "DALL·E",
4824
- known: true,
4825
- supportsEdit: true,
4826
- supportsAspectRatio: false,
4827
- qualityTiers: ["auto"]
4828
- },
4829
- grok: {
4830
- label: "grok",
4831
- labelZh: "Grok",
4832
- known: true,
4833
- supportsEdit: true,
4834
- supportsAspectRatio: true,
4835
- qualityTiers: ["1K", "2K"]
4836
- },
4837
- nanobanana: {
4838
- label: "nanobanana",
4839
- labelZh: "Nano Banana",
4840
- known: true,
4841
- supportsEdit: true,
4842
- supportsAspectRatio: true,
4843
- qualityTiers: [
4844
- "1K",
4845
- "2K",
4846
- "4K"
4847
- ]
4848
- },
4849
- seedream: {
4850
- label: "seedream",
4851
- labelZh: "Seedream",
4852
- known: true,
4853
- supportsEdit: true,
4854
- supportsAspectRatio: true,
4855
- qualityTiers: ["1K", "2K"]
6609
+ try {
6610
+ await this.scope.mutateOps(ops);
6611
+ this.stagedChannels = null;
6612
+ this.stagedKeys.clear();
6613
+ this.stagedDefault = null;
6614
+ this.failed = false;
6615
+ } catch {
6616
+ this.failed = true;
6617
+ } finally {
6618
+ this.saving = false;
6619
+ this.publish();
6620
+ }
6621
+ }
6622
+ publish() {
6623
+ for (const listener of [...this.listeners]) listener();
4856
6624
  }
4857
6625
  };
4858
- /** Official Gemini image ids served by Nano Banana gateways. */
4859
- const NANOBANANA_GEMINI_IDS = /* @__PURE__ */ new Set([
4860
- "gemini-3-pro-image",
4861
- "gemini-3-pro-image-preview",
4862
- "gemini-3.1-flash-image",
4863
- "gemini-3.1-flash-image-preview",
4864
- "gemini-3.1-flash-lite-image",
4865
- "gemini-2.5-flash-image"
4866
- ]);
4867
- /** Classify one upstream model id into its request-shaping family. */
4868
- function describeModel(model) {
4869
- const id = model.trim();
4870
- if (/^gpt-image/i.test(id)) return {
4871
- family: "gpt-image",
4872
- ...ENTRIES["gpt-image"]
4873
- };
4874
- if (/^dall-e/i.test(id)) return {
4875
- family: "dall-e",
4876
- ...ENTRIES["dall-e"]
4877
- };
4878
- if (/^grok-imagine(?:-|$)/.test(id)) return {
4879
- family: "grok",
4880
- ...ENTRIES.grok
4881
- };
4882
- if (/^nanobanana/i.test(id) || NANOBANANA_GEMINI_IDS.has(id)) return {
4883
- family: "nanobanana",
4884
- ...ENTRIES.nanobanana
4885
- };
4886
- if (/^(?:doubao-)?seedream/i.test(id)) return {
4887
- family: "seedream",
4888
- ...ENTRIES.seedream
4889
- };
6626
+ /** Project a stored channel into a draft (secrets never travel in channels). */
6627
+ function toDraft(channel) {
4890
6628
  return {
4891
- family: "unknown",
4892
- label: "unknown",
4893
- labelZh: "未知协议",
4894
- known: false,
4895
- supportsEdit: true,
4896
- supportsAspectRatio: false,
4897
- qualityTiers: []
6629
+ id: channel.id,
6630
+ preset: channel.preset,
6631
+ name: channel.name,
6632
+ apiUrl: channel.apiUrl,
6633
+ models: channel.models.map((model) => ({ ...model }))
4898
6634
  };
4899
6635
  }
6636
+ /** The scope's current channels value (a plain array), for change detection. */
6637
+ function scopeChannelsOf(scope) {
6638
+ const view = scope.getSnapshot().value;
6639
+ return Array.isArray(view?.channels) ? view.channels : [];
6640
+ }
4900
6641
  //#endregion
4901
6642
  //#region \0dsh-css:E:\dsh-plugin\src\client\settings-card.module.css.mjs
4902
6643
  const css$1 = ".i1cc5G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.i1cc5G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.i1cc5G_card:has(.i1cc5G_body){background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.i1cc5G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.i1cc5G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.i1cc5G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.i1cc5G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.i1cc5G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.i1cc5G_chevron,.i1cc5G_chevronOpen{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.i1cc5G_chevronOpen{transform:rotate(180deg)}.i1cc5G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.i1cc5G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.i1cc5G_versionRow{border-bottom:1px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:12px;padding:12px 0;display:flex}.i1cc5G_versionLabel{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.5}.i1cc5G_versionValue{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);font-family:var(--dsw-font-family-mono,monospace);border-radius:999px;padding:1px 8px;font-size:12px;line-height:1.5}.i1cc5G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.i1cc5G_field+.i1cc5G_field{border-top:1px solid var(--dsw-alias-border-l2)}.i1cc5G_head{align-items:center;gap:8px;display:flex}.i1cc5G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.i1cc5G_badges{align-items:center;gap:8px;display:inline-flex}.i1cc5G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.i1cc5G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.i1cc5G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.i1cc5G_reset:disabled{cursor:default;opacity:.5}.i1cc5G_input,.i1cc5G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;padding:0 12px;font-size:13px;line-height:1.5}.i1cc5G_input:focus-visible,.i1cc5G_select:focus-visible{border-color:var(--dsw-alias-brand-primary)}.i1cc5G_input:disabled,.i1cc5G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.i1cc5G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;padding:0 12px;font-size:13px;line-height:1.5}.i1cc5G_textarea,.i1cc5G_textareaInvalid{resize:vertical;background:var(--dsw-alias-bg-layer-3);min-height:70px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;padding:8px 12px;font-size:13px;line-height:1.5}.i1cc5G_textarea{border:1px solid var(--dsw-alias-border-l2)}.i1cc5G_textareaInvalid{border:1px solid var(--dsw-alias-label-error)}.i1cc5G_textarea:focus-visible{border-color:var(--dsw-alias-brand-primary)}.i1cc5G_textarea:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.i1cc5G_hint,.i1cc5G_invalid{margin:0;font-size:12px;line-height:1.5}.i1cc5G_hint{color:var(--dsw-alias-label-tertiary)}.i1cc5G_invalid{color:var(--dsw-alias-label-error)}.i1cc5G_readOnly,.i1cc5G_notExposed{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.i1cc5G_sectionDivider{background:var(--dsw-alias-border-l2);height:1px;margin:18px 0 14px}.i1cc5G_sectionTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:14px;line-height:1.4}.i1cc5G_sectionHint{color:var(--dsw-alias-label-tertiary);margin:-4px 0 2px;font-size:12px;line-height:1.5}.i1cc5G_modelSection{padding:2px 0 14px}.i1cc5G_sectionHeader{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.i1cc5G_sectionHeader .i1cc5G_sectionHint{max-width:430px}.i1cc5G_modelSummary{flex-wrap:wrap;align-items:center;gap:6px;margin-top:12px;display:flex}.i1cc5G_modelChip{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-width:100%;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family-mono,monospace);border-radius:6px;align-items:center;gap:5px;padding:3px 5px 3px 8px;font-size:12px;line-height:1.5;display:inline-flex}.i1cc5G_modelChip>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.i1cc5G_modelChip button{appearance:none;width:18px;height:18px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:4px;padding:0;line-height:18px}.i1cc5G_modelChip button:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}.i1cc5G_modelChip button:disabled{cursor:default;opacity:.45}.i1cc5G_addModel{appearance:none;border:1px dashed var(--dsw-alias-border-l2);min-height:28px;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:0 9px;font-size:12px}.i1cc5G_addModel:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.i1cc5G_addModel:disabled{opacity:.45;cursor:default}.i1cc5G_manualModelRow{gap:8px;margin-top:10px;display:flex}.i1cc5G_manualModelRow .i1cc5G_input{flex:1;min-width:0}.i1cc5G_disclosure{appearance:none;border:0;border-top:1px solid var(--dsw-alias-border-l2);width:100%;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;cursor:pointer;background:0 0;align-items:center;gap:8px;padding:13px 0;font-size:13px;font-weight:500;display:flex}.i1cc5G_disclosure>span:nth-child(2){color:var(--dsw-alias-label-tertiary);margin-left:auto;font-size:12px;font-weight:400}.i1cc5G_disclosure>span:last-child{color:var(--dsw-alias-label-tertiary)}.i1cc5G_disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.i1cc5G_optionalContent{padding:0 0 8px}.i1cc5G_inlineDisclosure{appearance:none;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;margin-top:12px;padding:0;font-size:12px;display:inline-flex}.i1cc5G_inlineDisclosure:hover{color:var(--dsw-alias-label-primary)}.i1cc5G_modelFetchRow{align-items:center;gap:8px;display:flex}.i1cc5G_modelFetch,.i1cc5G_modelChoices{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);min-height:32px;color:var(--dsw-alias-label-secondary);font:inherit;border-radius:7px;font-size:12px}.i1cc5G_modelFetch{cursor:pointer;padding:0 10px}.i1cc5G_modelFetch:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.i1cc5G_modelFetch:disabled{opacity:.5;cursor:default}.i1cc5G_modelChoices{flex:1;min-width:0;padding:0 8px}.i1cc5G_modelCandidateList{flex-wrap:wrap;gap:6px 10px;margin-top:10px;display:flex}.i1cc5G_modelCandidateLabel{width:100%;color:var(--dsw-alias-label-tertiary);font-size:12px}.i1cc5G_modelCandidate{appearance:none;min-width:0;color:var(--dsw-alias-label-secondary);cursor:pointer;background:var(--dsw-alias-bg-layer-3);font-size:12px;line-height:1.5;font:inherit;border:1px solid #0000;border-radius:5px;align-items:center;gap:5px;padding:3px 6px;display:inline-flex}.i1cc5G_modelCandidate input{margin:0}.i1cc5G_modelCandidate[data-selected]{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}.i1cc5G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.i1cc5G_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.i1cc5G_discard,.i1cc5G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.i1cc5G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.i1cc5G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.i1cc5G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.i1cc5G_discard:disabled,.i1cc5G_save:disabled{opacity:.4;cursor:default}.i1cc5G_discard:focus-visible,.i1cc5G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}@media (prefers-reduced-motion:reduce){.i1cc5G_card,.i1cc5G_chevron,.i1cc5G_chevronOpen{transition:none}}.i1cc5G_channelSection{padding:6px 0 2px}.i1cc5G_channelEmpty{color:var(--dsw-alias-label-tertiary);margin:10px 0 0;font-size:12px;line-height:1.5}.i1cc5G_channelList{flex-direction:column;gap:6px;margin:10px 0 0;padding:0;list-style:none;display:flex}.i1cc5G_channelRow{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;align-items:center;gap:10px;padding:8px 10px;display:flex}.i1cc5G_channelRow[data-action]{flex-wrap:wrap}.i1cc5G_channelDotReady,.i1cc5G_channelDotWarn{border-radius:50%;flex:none;width:9px;height:9px}.i1cc5G_channelDotReady{background:var(--dsw-color-success,#2fbf71)}.i1cc5G_channelDotWarn{background:var(--dsw-color-danger,#e5484d)}.i1cc5G_channelMain{appearance:none;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex-direction:column;flex:1;gap:3px;padding:0;display:flex}.i1cc5G_channelName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;line-height:1.4;overflow:hidden}.i1cc5G_channelMeta{flex-wrap:wrap;align-items:center;gap:4px 8px;display:flex}.i1cc5G_channelHost{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5;font-family:var(--dsw-font-family-mono,monospace);text-overflow:ellipsis;white-space:nowrap;max-width:180px;overflow:hidden}.i1cc5G_channelBadge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:0 7px;font-size:11px;font-weight:500;line-height:17px}.i1cc5G_channelBadge[data-warn]{color:var(--dsw-alias-label-error)}.i1cc5G_channelBadge[data-default]{background:var(--dsw-alias-bg-module-poped,var(--dsw-alias-bg-module-platform));color:var(--dsw-alias-brand-primary)}.i1cc5G_channelAction{appearance:none;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;padding:4px 8px;font-size:12px;line-height:1.5}.i1cc5G_channelAction:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}.i1cc5G_channelAction[data-danger]{color:var(--dsw-alias-label-error)}.i1cc5G_channelAction:disabled{opacity:.45;cursor:default}.i1cc5G_channelDanger{appearance:none;border:1px solid var(--dsw-alias-label-error);color:var(--dsw-alias-label-error);font:inherit;cursor:pointer;background:0 0;border-radius:6px;flex:none;padding:3px 10px;font-size:12px}.i1cc5G_channelDanger:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-label-error) 12%, transparent)}.i1cc5G_channelDanger:disabled{opacity:.45;cursor:default}.i1cc5G_deleteConfirmText{min-width:0;color:var(--dsw-alias-label-error);flex:1;font-size:12px;line-height:1.5}.i1cc5G_channelAddRow{gap:8px;margin-top:10px;display:flex}.i1cc5G_channelAdd{appearance:none;border:1px dashed var(--dsw-alias-border-l2);min-height:30px;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border-radius:8px;padding:0 12px;font-size:12px}.i1cc5G_channelAdd:hover:not(:disabled){color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.i1cc5G_channelAdd:disabled{opacity:.45;cursor:default}.i1cc5G_spacer{flex:1}.i1cc5G_editorBackdrop{z-index:120;background:color-mix(in srgb, var(--dsw-alias-bg-layer-1,#000) 45%, transparent);justify-content:center;align-items:flex-start;padding:9vh 16px 16px;display:flex;position:fixed;inset:0}.i1cc5G_editorPanel{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:14px;width:min(560px,100%);max-height:82vh;padding:16px;overflow-y:auto;box-shadow:0 18px 50px #00000059}.i1cc5G_editorHeader{border-bottom:1px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:flex-start;gap:12px;padding-bottom:10px;display:flex}.i1cc5G_editorClose{appearance:none;width:26px;height:26px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;font-size:16px;line-height:26px}.i1cc5G_editorClose:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform)}.i1cc5G_editorField{flex-direction:column;gap:6px;padding:12px 0 0;display:flex}.i1cc5G_editorDivider{background:var(--dsw-alias-border-l2);height:1px;margin:14px 0 4px}.i1cc5G_editorSectionHeader{justify-content:space-between;align-items:center;gap:12px;padding:10px 0 4px;display:flex}.i1cc5G_editorSectionHeader .i1cc5G_label{flex:1}.i1cc5G_editorTools{flex-direction:column;gap:8px;margin-top:10px;display:flex}.i1cc5G_editorFooter{border-top:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding-top:12px;display:flex}.i1cc5G_detectOk{color:var(--dsw-color-success,var(--dsw-alias-label-secondary));margin:0;font-size:12px;line-height:1.5}.i1cc5G_modelRows{flex-direction:column;gap:8px;margin:8px 0 0;padding:0;list-style:none;display:flex}.i1cc5G_modelRow{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;justify-content:space-between;align-items:center;gap:10px;padding:8px 10px;display:flex}.i1cc5G_modelRowInputs{flex:1;align-items:center;gap:6px;min-width:0;display:flex}.i1cc5G_modelRowInputs .i1cc5G_input{flex:1;min-width:0}.i1cc5G_modelArrow{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px}.i1cc5G_modelRowBadges{flex:none;align-items:center;gap:6px;display:flex}.i1cc5G_modelBadge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:0 7px;font-size:11px;line-height:17px}.i1cc5G_modelBadge[data-verified]{color:var(--dsw-color-success,var(--dsw-alias-label-secondary))}.i1cc5G_modelBadge[data-warn]{color:var(--dsw-alias-label-error)}.i1cc5G_modelRowRemove{appearance:none;width:20px;height:20px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;border-radius:4px;flex:none;padding:0;line-height:20px}.i1cc5G_modelRowRemove:hover:not(:disabled){color:var(--dsw-alias-label-error);background:var(--dsw-alias-bg-module-platform)}.i1cc5G_modelRowRemove:disabled{opacity:.45;cursor:default}.i1cc5G_modelCandidate>.i1cc5G_modelBadge{flex:none}.i1cc5G_channelControls{position:relative}.i1cc5G_presetInline{z-index:10;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;width:min(420px,100vw - 48px);max-height:min(440px,60vh);margin:0;padding:12px;position:absolute;bottom:calc(100% + 8px);left:0;overflow-y:auto;box-shadow:0 12px 30px #0000004d}.i1cc5G_presetInlineHeader{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.i1cc5G_presetList{flex-direction:column;gap:8px;margin-top:12px;display:flex}.i1cc5G_presetRow{appearance:none;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;border-radius:10px;flex-direction:column;align-items:flex-start;gap:3px;padding:10px 12px;display:flex}.i1cc5G_presetRow:hover:not(:disabled){border-color:var(--dsw-alias-brand-primary)}.i1cc5G_presetRow[data-custom]{border-style:dashed}.i1cc5G_presetRow:disabled{opacity:.5;cursor:default}.i1cc5G_presetName{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:1.4}.i1cc5G_presetMeta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5;font-family:var(--dsw-font-family-mono,monospace)}.i1cc5G_presetHint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5}";
@@ -4909,97 +6650,97 @@ window.__ModuleLoader__.load({
4909
6650
  document.head.appendChild(tag);
4910
6651
  }
4911
6652
  var settings_card_module_css_default = {
4912
- "optionalContent": "i1cc5G_optionalContent",
4913
- "editorSectionHeader": "i1cc5G_editorSectionHeader",
4914
- "channelDotWarn": "i1cc5G_channelDotWarn",
4915
- "modelSummary": "i1cc5G_modelSummary",
4916
- "editorField": "i1cc5G_editorField",
4917
- "modelCandidateLabel": "i1cc5G_modelCandidateLabel",
4918
- "reset": "i1cc5G_reset",
4919
- "body": "i1cc5G_body",
4920
- "field": "i1cc5G_field",
4921
- "badges": "i1cc5G_badges",
4922
- "addModel": "i1cc5G_addModel",
4923
- "editorClose": "i1cc5G_editorClose",
4924
- "presetInlineHeader": "i1cc5G_presetInlineHeader",
4925
- "versionLabel": "i1cc5G_versionLabel",
4926
- "header": "i1cc5G_header",
4927
- "head": "i1cc5G_head",
4928
- "presetInline": "i1cc5G_presetInline",
4929
- "detectOk": "i1cc5G_detectOk",
4930
- "modelCandidate": "i1cc5G_modelCandidate",
4931
6653
  "modelBadge": "i1cc5G_modelBadge",
4932
- "modelRowInputs": "i1cc5G_modelRowInputs",
6654
+ "headText": "i1cc5G_headText",
6655
+ "channelDotWarn": "i1cc5G_channelDotWarn",
4933
6656
  "channelName": "i1cc5G_channelName",
4934
- "channelSection": "i1cc5G_channelSection",
6657
+ "spacer": "i1cc5G_spacer",
6658
+ "modelRows": "i1cc5G_modelRows",
6659
+ "save": "i1cc5G_save",
4935
6660
  "input": "i1cc5G_input",
4936
- "channelList": "i1cc5G_channelList",
6661
+ "optionalContent": "i1cc5G_optionalContent",
6662
+ "modelSection": "i1cc5G_modelSection",
6663
+ "chevron": "i1cc5G_chevron",
6664
+ "presetList": "i1cc5G_presetList",
4937
6665
  "channelAddRow": "i1cc5G_channelAddRow",
4938
- "editorPanel": "i1cc5G_editorPanel",
4939
- "versionValue": "i1cc5G_versionValue",
4940
- "sectionHint": "i1cc5G_sectionHint",
4941
- "modelChip": "i1cc5G_modelChip",
4942
- "footer": "i1cc5G_footer",
4943
- "save": "i1cc5G_save",
6666
+ "channelRow": "i1cc5G_channelRow",
4944
6667
  "badge": "i1cc5G_badge",
4945
- "presetName": "i1cc5G_presetName",
4946
- "modelChoices": "i1cc5G_modelChoices",
4947
- "deleteConfirmText": "i1cc5G_deleteConfirmText",
4948
- "invalid": "i1cc5G_invalid",
4949
- "inlineDisclosure": "i1cc5G_inlineDisclosure",
4950
- "modelFetch": "i1cc5G_modelFetch",
4951
- "pending": "i1cc5G_pending",
4952
- "textarea": "i1cc5G_textarea",
4953
6668
  "select": "i1cc5G_select",
4954
- "channelRow": "i1cc5G_channelRow",
4955
- "label": "i1cc5G_label",
6669
+ "modelRowRemove": "i1cc5G_modelRowRemove",
6670
+ "presetMeta": "i1cc5G_presetMeta",
6671
+ "textareaInvalid": "i1cc5G_textareaInvalid",
4956
6672
  "failed": "i1cc5G_failed",
4957
- "channelHost": "i1cc5G_channelHost",
4958
- "channelDanger": "i1cc5G_channelDanger",
4959
- "sectionDivider": "i1cc5G_sectionDivider",
4960
- "editorBackdrop": "i1cc5G_editorBackdrop",
4961
- "versionRow": "i1cc5G_versionRow",
6673
+ "editorPanel": "i1cc5G_editorPanel",
6674
+ "presetHint": "i1cc5G_presetHint",
6675
+ "modelChip": "i1cc5G_modelChip",
6676
+ "modelChoices": "i1cc5G_modelChoices",
6677
+ "reset": "i1cc5G_reset",
6678
+ "detectOk": "i1cc5G_detectOk",
6679
+ "channelAction": "i1cc5G_channelAction",
4962
6680
  "hint": "i1cc5G_hint",
4963
- "modelFetchRow": "i1cc5G_modelFetchRow",
4964
- "editorTools": "i1cc5G_editorTools",
4965
- "editorFooter": "i1cc5G_editorFooter",
6681
+ "modelSummary": "i1cc5G_modelSummary",
6682
+ "presetName": "i1cc5G_presetName",
6683
+ "sectionDivider": "i1cc5G_sectionDivider",
4966
6684
  "discard": "i1cc5G_discard",
4967
- "channelEmpty": "i1cc5G_channelEmpty",
4968
- "modelRows": "i1cc5G_modelRows",
4969
- "modelRow": "i1cc5G_modelRow",
4970
- "modelArrow": "i1cc5G_modelArrow",
4971
- "modelRowBadges": "i1cc5G_modelRowBadges",
4972
- "presetList": "i1cc5G_presetList",
4973
- "presetHint": "i1cc5G_presetHint",
4974
- "manualModelRow": "i1cc5G_manualModelRow",
4975
- "chevron": "i1cc5G_chevron",
4976
- "textareaInvalid": "i1cc5G_textareaInvalid",
4977
- "editorHeader": "i1cc5G_editorHeader",
4978
- "readOnly": "i1cc5G_readOnly",
4979
- "sectionHeader": "i1cc5G_sectionHeader",
4980
- "channelMeta": "i1cc5G_channelMeta",
4981
- "presetMeta": "i1cc5G_presetMeta",
6685
+ "textarea": "i1cc5G_textarea",
6686
+ "channelAdd": "i1cc5G_channelAdd",
4982
6687
  "editorDivider": "i1cc5G_editorDivider",
4983
- "card": "i1cc5G_card",
4984
- "modelSection": "i1cc5G_modelSection",
6688
+ "channelList": "i1cc5G_channelList",
6689
+ "editorSectionHeader": "i1cc5G_editorSectionHeader",
6690
+ "channelBadge": "i1cc5G_channelBadge",
4985
6691
  "presetRow": "i1cc5G_presetRow",
4986
- "spacer": "i1cc5G_spacer",
4987
- "channelAction": "i1cc5G_channelAction",
4988
- "inputInvalid": "i1cc5G_inputInvalid",
4989
- "channelControls": "i1cc5G_channelControls",
6692
+ "invalid": "i1cc5G_invalid",
6693
+ "presetInlineHeader": "i1cc5G_presetInlineHeader",
6694
+ "sectionHint": "i1cc5G_sectionHint",
6695
+ "modelFetch": "i1cc5G_modelFetch",
6696
+ "addModel": "i1cc5G_addModel",
6697
+ "modelArrow": "i1cc5G_modelArrow",
6698
+ "editorClose": "i1cc5G_editorClose",
4990
6699
  "sectionTitle": "i1cc5G_sectionTitle",
4991
- "channelAdd": "i1cc5G_channelAdd",
6700
+ "badges": "i1cc5G_badges",
6701
+ "disclosure": "i1cc5G_disclosure",
6702
+ "modelCandidateList": "i1cc5G_modelCandidateList",
6703
+ "modelCandidateLabel": "i1cc5G_modelCandidateLabel",
6704
+ "inlineDisclosure": "i1cc5G_inlineDisclosure",
6705
+ "versionLabel": "i1cc5G_versionLabel",
6706
+ "channelEmpty": "i1cc5G_channelEmpty",
6707
+ "channelMain": "i1cc5G_channelMain",
6708
+ "deleteConfirmText": "i1cc5G_deleteConfirmText",
6709
+ "versionRow": "i1cc5G_versionRow",
4992
6710
  "name": "i1cc5G_name",
6711
+ "editorHeader": "i1cc5G_editorHeader",
6712
+ "editorFooter": "i1cc5G_editorFooter",
6713
+ "channelDotReady": "i1cc5G_channelDotReady",
6714
+ "editorBackdrop": "i1cc5G_editorBackdrop",
6715
+ "footer": "i1cc5G_footer",
4993
6716
  "notExposed": "i1cc5G_notExposed",
4994
- "modelCandidateList": "i1cc5G_modelCandidateList",
4995
- "channelBadge": "i1cc5G_channelBadge",
6717
+ "head": "i1cc5G_head",
6718
+ "versionValue": "i1cc5G_versionValue",
6719
+ "body": "i1cc5G_body",
6720
+ "inputInvalid": "i1cc5G_inputInvalid",
6721
+ "modelRow": "i1cc5G_modelRow",
6722
+ "sectionHeader": "i1cc5G_sectionHeader",
6723
+ "modelRowBadges": "i1cc5G_modelRowBadges",
6724
+ "channelDanger": "i1cc5G_channelDanger",
6725
+ "field": "i1cc5G_field",
4996
6726
  "description": "i1cc5G_description",
4997
- "channelMain": "i1cc5G_channelMain",
6727
+ "modelCandidate": "i1cc5G_modelCandidate",
6728
+ "channelSection": "i1cc5G_channelSection",
6729
+ "channelControls": "i1cc5G_channelControls",
6730
+ "channelMeta": "i1cc5G_channelMeta",
6731
+ "card": "i1cc5G_card",
6732
+ "editorField": "i1cc5G_editorField",
6733
+ "presetInline": "i1cc5G_presetInline",
6734
+ "modelFetchRow": "i1cc5G_modelFetchRow",
6735
+ "header": "i1cc5G_header",
6736
+ "pending": "i1cc5G_pending",
6737
+ "editorTools": "i1cc5G_editorTools",
6738
+ "readOnly": "i1cc5G_readOnly",
6739
+ "modelRowInputs": "i1cc5G_modelRowInputs",
4998
6740
  "chevronOpen": "i1cc5G_chevronOpen",
4999
- "disclosure": "i1cc5G_disclosure",
5000
- "channelDotReady": "i1cc5G_channelDotReady",
5001
- "headText": "i1cc5G_headText",
5002
- "modelRowRemove": "i1cc5G_modelRowRemove"
6741
+ "label": "i1cc5G_label",
6742
+ "manualModelRow": "i1cc5G_manualModelRow",
6743
+ "channelHost": "i1cc5G_channelHost"
5003
6744
  };
5004
6745
  //#endregion
5005
6746
  //#region src/client/SettingsCard.tsx
@@ -5257,29 +6998,17 @@ window.__ModuleLoader__.load({
5257
6998
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5258
6999
  className: settings_card_module_css_default.channelName,
5259
7000
  children: isDefault ? `★ ${channel.name || t("channels.untitled")}` : channel.name || t("channels.untitled")
5260
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7001
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5261
7002
  className: settings_card_module_css_default.channelMeta,
5262
- children: [
5263
- channel.apiUrl !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5264
- className: settings_card_module_css_default.channelHost,
5265
- children: hostOf(channel.apiUrl)
5266
- }) : null,
5267
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5268
- className: settings_card_module_css_default.channelBadge,
5269
- "data-warn": keyHeld ? void 0 : "",
5270
- children: keyHeld ? t("channels.keySet") : t("channels.keyMissing")
5271
- }),
5272
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5273
- className: settings_card_module_css_default.channelBadge,
5274
- "data-warn": channel.models.length > 0 ? void 0 : "",
5275
- children: channel.models.length > 0 ? t("channels.modelCount", { n: channel.models.length }) : t("channels.noModels")
5276
- }),
5277
- isDefault ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5278
- className: settings_card_module_css_default.channelBadge,
5279
- "data-default": true,
5280
- children: t("channels.defaultLabel")
5281
- }) : null
5282
- ]
7003
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7004
+ className: settings_card_module_css_default.channelBadge,
7005
+ "data-warn": !keyHeld || channel.models.length === 0 ? "" : void 0,
7006
+ children: [
7007
+ keyHeld ? t("channels.keySet") : t("channels.keyMissing"),
7008
+ " · ",
7009
+ channel.models.length > 0 ? t("channels.modelCount", { n: channel.models.length }) : t("channels.noModels")
7010
+ ]
7011
+ })
5283
7012
  })]
5284
7013
  }),
5285
7014
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -5684,13 +7413,6 @@ window.__ModuleLoader__.load({
5684
7413
  ...patch
5685
7414
  } : channel));
5686
7415
  }
5687
- function hostOf(apiUrl) {
5688
- try {
5689
- return new URL(apiUrl).hostname;
5690
- } catch {
5691
- return apiUrl;
5692
- }
5693
- }
5694
7416
  function clientId() {
5695
7417
  return (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : void 0) ?? `ch-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
5696
7418
  }
@@ -5731,24 +7453,13 @@ window.__ModuleLoader__.load({
5731
7453
  onClick: () => {
5732
7454
  props.onPick(preset);
5733
7455
  },
5734
- children: [
5735
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5736
- className: settings_card_module_css_default.presetName,
5737
- children: preset.name
5738
- }),
5739
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5740
- className: settings_card_module_css_default.presetMeta,
5741
- children: [
5742
- preset.apiUrl,
5743
- " · ",
5744
- t("channels.presetModelsHint", { n: preset.models.length })
5745
- ]
5746
- }),
5747
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5748
- className: settings_card_module_css_default.presetHint,
5749
- children: preset.hint
5750
- })
5751
- ]
7456
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7457
+ className: settings_card_module_css_default.presetName,
7458
+ children: preset.name
7459
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7460
+ className: settings_card_module_css_default.presetMeta,
7461
+ children: preset.models.map((model) => model.alias).join(" · ")
7462
+ })]
5752
7463
  }, preset.id)),
5753
7464
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
5754
7465
  type: "button",
@@ -5842,19 +7553,6 @@ window.__ModuleLoader__.load({
5842
7553
  props.onSetModels(merged);
5843
7554
  setCopyFrom("");
5844
7555
  };
5845
- const onlyKnown = () => {
5846
- const known = (candidates ?? []).filter((id) => describeModel(id).known);
5847
- const merged = [...channel.models];
5848
- for (const id of known) {
5849
- const alias = id;
5850
- if (!merged.some((model) => model.alias === alias)) merged.push({
5851
- alias,
5852
- id
5853
- });
5854
- }
5855
- props.onSetModels(merged);
5856
- };
5857
- const knownCount = (candidates ?? []).filter((id) => describeModel(id).known).length;
5858
7556
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5859
7557
  className: settings_card_module_css_default.editorBackdrop,
5860
7558
  role: "dialog",
@@ -6104,15 +7802,9 @@ window.__ModuleLoader__.load({
6104
7802
  }),
6105
7803
  candidates !== null && candidates.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6106
7804
  className: settings_card_module_css_default.modelCandidateList,
6107
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7805
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6108
7806
  className: settings_card_module_css_default.modelCandidateLabel,
6109
- children: [t("channels.candidatesTitle"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6110
- type: "button",
6111
- className: settings_card_module_css_default.inlineDisclosure,
6112
- disabled: !props.writable || knownCount === 0,
6113
- onClick: onlyKnown,
6114
- children: t("channels.candidatesQuick")
6115
- })]
7807
+ children: t("channels.candidatesTitle")
6116
7808
  }), candidates.map((candidate) => {
6117
7809
  const selected = channel.models.some((model) => model.alias === candidate);
6118
7810
  const entry = describeModel(candidate);
@@ -6311,16 +8003,16 @@ window.__ModuleLoader__.load({
6311
8003
  document.head.appendChild(tag);
6312
8004
  }
6313
8005
  var image_toolview_module_css_default = {
6314
- "header": "_3eGrYG_header",
6315
- "message": "_3eGrYG_message",
6316
- "status": "_3eGrYG_status",
6317
8006
  "loading": "_3eGrYG_loading",
6318
- "image": "_3eGrYG_image",
8007
+ "icon": "_3eGrYG_icon",
6319
8008
  "imageLink": "_3eGrYG_imageLink",
6320
8009
  "images": "_3eGrYG_images",
6321
- "icon": "_3eGrYG_icon",
8010
+ "status": "_3eGrYG_status",
8011
+ "header": "_3eGrYG_header",
8012
+ "root": "_3eGrYG_root",
8013
+ "image": "_3eGrYG_image",
6322
8014
  "error": "_3eGrYG_error",
6323
- "root": "_3eGrYG_root"
8015
+ "message": "_3eGrYG_message"
6324
8016
  };
6325
8017
  //#endregion
6326
8018
  //#region src/client/image-toolview.tsx
@@ -6422,6 +8114,13 @@ window.__ModuleLoader__.load({
6422
8114
  const refs = (0, react.useMemo)(() => imageRefsOf(props.block), [props.block]);
6423
8115
  const { status, message } = resultInfo(props.block);
6424
8116
  const { images, error } = useAttachmentImages(props.sessionId, refs, load);
8117
+ (0, react.useEffect)(() => {
8118
+ if (images.length === 0) return;
8119
+ document.dispatchEvent(new CustomEvent(CHAT_IMAGE_EVENT, { detail: {
8120
+ sessionId: props.sessionId,
8121
+ refs: images.map((image) => image.ref)
8122
+ } }));
8123
+ }, [images, props.sessionId]);
6425
8124
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6426
8125
  className: image_toolview_module_css_default.root,
6427
8126
  "data-state": status,
@@ -6492,7 +8191,8 @@ window.__ModuleLoader__.load({
6492
8191
  "slots",
6493
8192
  "locale",
6494
8193
  "connection",
6495
- "sessions"
8194
+ "sessions",
8195
+ "conversation"
6496
8196
  ];
6497
8197
  /**
6498
8198
  * Mount the studio, its sidebar entry, and the settings card.
@@ -6527,10 +8227,15 @@ window.__ModuleLoader__.load({
6527
8227
  if (uiDisposer !== void 0) return;
6528
8228
  const controller = new ImageGenController();
6529
8229
  const api = new ImageGenApi();
8230
+ const sessions = ctx.get("sessions");
8231
+ const conversation = ctx.get("conversation");
6530
8232
  const disposers = [];
6531
8233
  try {
6532
- disposers.push(mountSidebarEntry(controller, tt("entry.label"), tt("entry.tooltip")));
6533
- disposers.push(mountPanel(controller, api, scope));
8234
+ disposers.push(mountSidebarEntry(controller, tt("entry.newSession"), tt("entry.newSessionTooltip"), tt("entry.image"), tt("entry.tooltip")));
8235
+ disposers.push(mountPanel(controller, api, scope, {
8236
+ sessions,
8237
+ conversation
8238
+ }));
6534
8239
  } catch (error) {
6535
8240
  console.warn("[dsh-imagegen] mount failed:", error);
6536
8241
  }