@dickpy/dsh-imagegen 1.0.20 → 1.2.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.
@@ -13,7 +13,20 @@ export const zh = {
13
13
  // prompt
14
14
  'prompt.placeholder': '描述你想要的画面,例如:一只戴着宇航员头盔的橘猫,在月球上举起望远镜,水彩风格,柔和光线…',
15
15
  'prompt.required': '请输入提示词',
16
- 'prompt.count': '{count}/2000',
16
+ 'prompt.count': '{count}',
17
+ 'prompt.enhance': '增强',
18
+ 'prompt.enhancing': '增强中…',
19
+ 'prompt.enhanceHint': '使用已配置的对话模型扩写提示词',
20
+ 'prompt.configTitle': '请先配置提示词增强模型',
21
+ 'prompt.configHint': '已打开设置。进入「插件 → AI 生图」,填写或复用 API 地址和密钥,选择对话模型并保存。',
22
+ 'tasks.title': '生成任务',
23
+ 'tasks.queued': '排队中',
24
+ 'tasks.running': '生成中',
25
+ 'tasks.completed': '已完成',
26
+ 'tasks.failed': '失败',
27
+ 'tasks.cancelled': '已取消',
28
+ 'tasks.cancel': '取消',
29
+ 'tasks.retry': '重试',
17
30
  // parameters
18
31
  'params.size': '尺寸',
19
32
  'params.quality': '清晰度',
@@ -42,6 +55,11 @@ export const zh = {
42
55
  'detail.hint': '透传参数,部分 gpt-image-2 网关支持;官方接口请保持「自动」',
43
56
  // footer
44
57
  'model.label': '模型',
58
+ 'compare.enable': '多模型对比',
59
+ 'compare.models': '参与对比的模型',
60
+ 'compare.title': '多模型结果对比',
61
+ 'compare.fullscreen': '全屏对比',
62
+ 'compare.selectRequired': '请至少选择一个对比模型',
45
63
  'generate': '开始生成',
46
64
  'generating': '生成中…',
47
65
  // edit reference image
@@ -67,6 +85,11 @@ export const zh = {
67
85
  'history.delete': '删除',
68
86
  'history.images': '张',
69
87
  'history.viewing': '历史 · {time}',
88
+ 'history.search': '搜索提示词或模型…',
89
+ 'history.model': '模型筛选',
90
+ 'history.ratio': '比例筛选',
91
+ 'history.allModels': '全部模型',
92
+ 'history.allRatios': '全部比例',
70
93
  // gallery
71
94
  'gallery.title': '画廊',
72
95
  'gallery.categories': '分类',
@@ -74,6 +97,7 @@ export const zh = {
74
97
  'gallery.gpt': 'gpt-image-2',
75
98
  'gallery.grok': 'grok-imagine-image',
76
99
  'gallery.ratio': '画面比例',
100
+ 'gallery.tags': '标签',
77
101
  'gallery.filterHint': '按生成模式、模型和比例筛选画廊',
78
102
  'gallery.count': '· 共 {count} 幅',
79
103
  'gallery.viewMode': '视图模式',
@@ -83,6 +107,19 @@ export const zh = {
83
107
  'gallery.newest': '最新发布',
84
108
  'gallery.oldest': '最早发布',
85
109
  'gallery.untitled': '未命名作品',
110
+ 'gallery.search': '搜索作品或模型…',
111
+ 'gallery.tagsPlaceholder': '标签,用逗号分隔',
112
+ 'gallery.tagsApply': '添加标签',
113
+ 'gallery.editTags': '编辑标签',
114
+ 'gallery.tagsEditShort': '编辑',
115
+ 'gallery.tagsSave': '保存',
116
+ 'gallery.tagsCancel': '取消',
117
+ 'gallery.selected': '已选 {count} 项',
118
+ 'gallery.selectionDone': '完成选择',
119
+ 'gallery.selectionClear': '取消选择',
120
+ 'gallery.downloadSelected': '下载所选',
121
+ 'gallery.exportJson': '导出 JSON',
122
+ 'gallery.select': '选择作品',
86
123
  'gallery.add': '加入画廊',
87
124
  'gallery.added': '已加入画廊',
88
125
  'gallery.already': '已在画廊中',
@@ -107,6 +144,12 @@ export const zh = {
107
144
  'preview.addToEdit': '添加到图生图',
108
145
  // config banner
109
146
  'config.missing': '尚未配置 API:请前往「设置 → 插件 → 可配置」为 AI 生图填写 api_url 与 api_key。',
147
+ 'config.generationTitle': '请先配置生图 API',
148
+ 'config.generationHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。填写生图 API 地址与 API 密钥,保存后即可开始生成。',
149
+ 'config.enhancementTitle': '请先配置提示词增强模型',
150
+ 'config.enhancementHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。填写或复用对话 API 地址和密钥,选择对话模型并保存。',
151
+ 'config.disabledTitle': 'AI 生图插件已停用',
152
+ 'config.disabledHint': '已自动打开 DSH「设置 → 插件 → AI 生图」。开启「启用插件」后即可生成图片。',
110
153
  'config.configured': '已连接 {url}',
111
154
  'config.disabled': '插件已停用,请在设置中重新启用。',
112
155
  'connection.connected': '已连接',
@@ -128,8 +171,39 @@ export const zh = {
128
171
  'settings.apiKeyHint': 'Bearer 密钥,明文存于本机设置文档;界面只显示是否已设置',
129
172
  'settings.apiKeySet': '已保存密钥;输入新值可更换,点击「清除」可删除',
130
173
  'settings.apiKeyClear': '清除',
174
+ 'settings.imageModelsTitle': '生图模型',
175
+ 'settings.imageModelsHint': '保存 API 地址和密钥后检测候选模型;请只选择实际支持生图的项。',
176
+ 'settings.imageModels': '允许使用的生图模型',
177
+ 'settings.imageModelsManualHint': '一行一个模型;API 未提供 /models 时可手动填写。面板和 Agent 只能使用此列表。',
178
+ 'settings.imageModelsFetch': '检测可用模型',
179
+ 'settings.imageModelsLoading': '检测中…',
180
+ 'settings.imageModelsCandidates': '检测到的候选模型(勾选后保存)',
181
+ 'settings.addModel': '+ 手动添加',
182
+ 'settings.cancelAddModel': '收起添加',
183
+ 'settings.addModelPlaceholder': '输入模型名称,例如 qwen-image',
184
+ 'settings.addModelConfirm': '添加',
185
+ 'settings.removeModel': '移除模型',
186
+ 'settings.optional': '可选',
187
+ 'settings.moreOptions': '更多设置',
188
+ 'settings.promptEnhanceTitle': '提示词增强模型',
189
+ 'settings.promptEnhanceHint': '用于将简短描述扩写为更完整的生图提示词;留空的地址和密钥会复用生图配置。',
190
+ 'settings.promptApiUrl': '对话 API 地址(可选)',
191
+ 'settings.promptApiUrlHint': 'OpenAI 兼容基址;留空则复用图像 API 地址。',
192
+ 'settings.promptApiKey': '对话 API 密钥(可选)',
193
+ 'settings.promptApiKeyHint': '留空则复用图像 API 密钥。',
194
+ 'settings.promptModel': '对话模型',
195
+ 'settings.promptModelHint': '选择或填写支持 /chat/completions 的模型。',
196
+ 'settings.promptModelDetectionHint': '默认复用生图 API;检测后点选一个模型即可。',
197
+ 'settings.promptModelsFetch': '获取可用模型',
198
+ 'settings.promptModelsLoading': '正在获取…',
199
+ 'settings.promptModelsSelect': '选择一个模型',
200
+ 'settings.promptModelsCandidates': '检测到的候选对话模型',
201
+ 'settings.addPromptModelPlaceholder': '输入对话模型名称,例如 gpt-4.1-mini',
202
+ 'settings.promptApiAdvanced': '使用独立对话 API(可选)',
131
203
  'settings.announceToAgent': '向 Agent 播报本插件',
132
204
  'settings.announceToAgentHint': '开启后,本插件的存在与能力会写入每个 Agent 的系统提示词',
205
+ 'settings.allowAgentImageGeneration': '允许 Agent 调用生图',
206
+ 'settings.allowAgentImageGenerationHint': '默认开启。关闭后,Agent 无法提交、查询或取消生图任务;侧边栏工作台不受影响。',
133
207
  'settings.enabled': '启用插件',
134
208
  'settings.enabledHint': '关闭后生图面板不可用(设置卡片始终可用)',
135
209
  'settings.save': '保存',
@@ -186,7 +260,20 @@ export const en: Record<keyof typeof zh, string> = {
186
260
  'mode.edit': 'Image to Image',
187
261
  '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…',
188
262
  'prompt.required': 'Enter a prompt first',
189
- 'prompt.count': '{count}/2000',
263
+ 'prompt.count': '{count}',
264
+ 'prompt.enhance': 'Enhance',
265
+ 'prompt.enhancing': 'Enhancing…',
266
+ 'prompt.enhanceHint': 'Expand the prompt with the configured chat model',
267
+ 'prompt.configTitle': 'Configure a prompt enhancement model first',
268
+ 'prompt.configHint': 'Settings has opened. Go to Plugins → AI Image, configure or reuse an API URL and key, choose a chat model, then save.',
269
+ 'tasks.title': 'Generation tasks',
270
+ 'tasks.queued': 'Queued',
271
+ 'tasks.running': 'Generating',
272
+ 'tasks.completed': 'Completed',
273
+ 'tasks.failed': 'Failed',
274
+ 'tasks.cancelled': 'Cancelled',
275
+ 'tasks.cancel': 'Cancel',
276
+ 'tasks.retry': 'Retry',
190
277
  'params.size': 'Size',
191
278
  'params.quality': 'Quality',
192
279
  'params.count': 'Count',
@@ -213,6 +300,11 @@ export const en: Record<keyof typeof zh, string> = {
213
300
  'detail.high': 'High',
214
301
  'detail.hint': 'Passthrough parameter supported by some gpt-image-2 gateways; keep "Auto" for official endpoints',
215
302
  'model.label': 'Model',
303
+ 'compare.enable': 'Multi-model comparison',
304
+ 'compare.models': 'Models to compare',
305
+ 'compare.title': 'Multi-model comparison',
306
+ 'compare.fullscreen': 'Fullscreen comparison',
307
+ 'compare.selectRequired': 'Select at least one model',
216
308
  'generate': 'Generate',
217
309
  'generating': 'Generating…',
218
310
  'edit.upload': 'Click or drag to upload a reference image',
@@ -235,12 +327,18 @@ export const en: Record<keyof typeof zh, string> = {
235
327
  'history.delete': 'Delete',
236
328
  'history.images': 'images',
237
329
  'history.viewing': 'History · {time}',
330
+ 'history.search': 'Search prompt or model…',
331
+ 'history.model': 'Model filter',
332
+ 'history.ratio': 'Ratio filter',
333
+ 'history.allModels': 'All models',
334
+ 'history.allRatios': 'All ratios',
238
335
  'gallery.title': 'Gallery',
239
336
  'gallery.categories': 'Categories',
240
337
  'gallery.all': 'All works',
241
338
  'gallery.gpt': 'gpt-image-2',
242
339
  'gallery.grok': 'grok-imagine-image',
243
340
  'gallery.ratio': 'Aspect ratio',
341
+ 'gallery.tags': 'Tags',
244
342
  'gallery.filterHint': 'Filter by mode, model, and aspect ratio',
245
343
  'gallery.count': '· {count} works',
246
344
  'gallery.viewMode': 'View mode',
@@ -250,6 +348,19 @@ export const en: Record<keyof typeof zh, string> = {
250
348
  'gallery.newest': 'Newest',
251
349
  'gallery.oldest': 'Oldest',
252
350
  'gallery.untitled': 'Untitled work',
351
+ 'gallery.search': 'Search works or models…',
352
+ 'gallery.tagsPlaceholder': 'Tags, comma separated',
353
+ 'gallery.tagsApply': 'Add tags',
354
+ 'gallery.editTags': 'Edit tags',
355
+ 'gallery.tagsEditShort': 'Edit',
356
+ 'gallery.tagsSave': 'Save',
357
+ 'gallery.tagsCancel': 'Cancel',
358
+ 'gallery.selected': '{count} selected',
359
+ 'gallery.selectionDone': 'Done selecting',
360
+ 'gallery.selectionClear': 'Clear selection',
361
+ 'gallery.downloadSelected': 'Download selected',
362
+ 'gallery.exportJson': 'Export JSON',
363
+ 'gallery.select': 'Select work',
253
364
  'gallery.add': 'Add to gallery',
254
365
  'gallery.added': 'Added to gallery',
255
366
  'gallery.already': 'Already in gallery',
@@ -272,6 +383,12 @@ export const en: Record<keyof typeof zh, string> = {
272
383
  'preview.copied': 'Copied',
273
384
  'preview.addToEdit': 'Add to image to image',
274
385
  'config.missing': 'API not configured: open "Settings → Plugins → Configurable" and fill in api_url and api_key for AI Image.',
386
+ 'config.generationTitle': 'Configure the image API first',
387
+ 'config.generationHint': 'DSH Settings → Plugins → AI Image has opened. Enter the image API URL and API key, then save before generating.',
388
+ 'config.enhancementTitle': 'Configure a prompt enhancement model first',
389
+ 'config.enhancementHint': 'DSH Settings → Plugins → AI Image has opened. Configure or reuse a chat API URL and key, choose a chat model, then save.',
390
+ 'config.disabledTitle': 'The AI Image plugin is disabled',
391
+ 'config.disabledHint': 'DSH Settings → Plugins → AI Image has opened. Enable the plugin, then generate images.',
275
392
  'config.configured': 'Connected to {url}',
276
393
  'config.disabled': 'The plugin is disabled — re-enable it in Settings.',
277
394
  'connection.connected': 'Connected',
@@ -291,8 +408,39 @@ export const en: Record<keyof typeof zh, string> = {
291
408
  'settings.apiKeyHint': 'Bearer key, stored in plaintext in the local settings document; the UI only shows whether it is set',
292
409
  'settings.apiKeySet': 'A key is stored; type a new value to replace it, or click "Clear" to remove it',
293
410
  'settings.apiKeyClear': 'Clear',
411
+ 'settings.imageModelsTitle': 'Image generation models',
412
+ 'settings.imageModelsHint': 'Save the API URL and key, then detect candidate models. Select only models that actually support image generation.',
413
+ 'settings.imageModels': 'Allowed image models',
414
+ 'settings.imageModelsManualHint': 'One model per line. Add models manually when the API does not provide /models. The panel and Agent can use only this list.',
415
+ 'settings.imageModelsFetch': 'Detect available models',
416
+ 'settings.imageModelsLoading': 'Detecting…',
417
+ 'settings.imageModelsCandidates': 'Detected candidate models (select, then save)',
418
+ 'settings.addModel': '+ Add manually',
419
+ 'settings.cancelAddModel': 'Hide add',
420
+ 'settings.addModelPlaceholder': 'Enter a model name, e.g. qwen-image',
421
+ 'settings.addModelConfirm': 'Add',
422
+ 'settings.removeModel': 'Remove model',
423
+ 'settings.optional': 'Optional',
424
+ 'settings.moreOptions': 'More settings',
425
+ 'settings.promptEnhanceTitle': 'Prompt enhancement model',
426
+ 'settings.promptEnhanceHint': 'Expands short requests into complete image prompts. Blank URL and key reuse the image API configuration.',
427
+ 'settings.promptApiUrl': 'Chat API URL (optional)',
428
+ 'settings.promptApiUrlHint': 'OpenAI-compatible base URL. Leave blank to reuse the image API URL.',
429
+ 'settings.promptApiKey': 'Chat API key (optional)',
430
+ 'settings.promptApiKeyHint': 'Leave blank to reuse the image API key.',
431
+ 'settings.promptModel': 'Chat model',
432
+ 'settings.promptModelHint': 'Select or enter a model supporting /chat/completions.',
433
+ 'settings.promptModelDetectionHint': 'Uses the image API by default. Detect and select one model.',
434
+ 'settings.promptModelsFetch': 'Fetch available models',
435
+ 'settings.promptModelsLoading': 'Fetching…',
436
+ 'settings.promptModelsSelect': 'Select a model',
437
+ 'settings.promptModelsCandidates': 'Detected chat model candidates',
438
+ 'settings.addPromptModelPlaceholder': 'Enter a chat model name, e.g. gpt-4.1-mini',
439
+ 'settings.promptApiAdvanced': 'Use a separate chat API (optional)',
294
440
  'settings.announceToAgent': 'Announce this plugin to agents',
295
441
  'settings.announceToAgentHint': 'When on, the plugin presence and capabilities are written into every agent system prompt',
442
+ 'settings.allowAgentImageGeneration': 'Allow agents to generate images',
443
+ 'settings.allowAgentImageGenerationHint': 'On by default. When off, agents cannot submit, query, or cancel image tasks; the sidebar studio remains available.',
296
444
  'settings.enabled': 'Enable plugin',
297
445
  'settings.enabledHint': 'When off, the generation studio is unavailable (this card stays available)',
298
446
  'settings.save': 'Save',
@@ -319,6 +319,35 @@ html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ss
319
319
  overflow: hidden;
320
320
  }
321
321
 
322
+ .taskTray {
323
+ position: absolute;
324
+ z-index: 6;
325
+ top: 12px;
326
+ right: 12px;
327
+ width: min(360px, calc(100% - 24px));
328
+ overflow: hidden;
329
+ border: 1px solid var(--dsw-alias-border-l2);
330
+ border-radius: 9px;
331
+ background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 92%, transparent);
332
+ box-shadow: 0 8px 24px rgb(0 0 0 / 12%);
333
+ backdrop-filter: blur(10px);
334
+ }
335
+
336
+ .taskTrayHeader { display: flex; justify-content: space-between; padding: 8px 10px; color: var(--dsw-alias-label-primary); font-size: 12px; font-weight: 600; border-bottom: 1px solid var(--dsw-alias-border-l1); }
337
+ .taskRow { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 7px; align-items: center; padding: 7px 10px; border-top: 1px solid var(--dsw-alias-border-l1); }
338
+ .taskRow:first-of-type { border-top: 0; }
339
+ .taskStatus { color: var(--dsw-alias-label-tertiary); font-size: 11px; white-space: nowrap; }
340
+ .taskRow[data-status='running'] .taskStatus { color: var(--dsw-alias-brand-primary); }
341
+ .taskRow[data-status='failed'] .taskStatus { color: var(--dsw-alias-label-error); }
342
+ .taskPrompt { overflow: hidden; color: var(--dsw-alias-label-secondary); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
343
+ .taskRow button { padding: 2px 7px; border: 0; border-radius: 5px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-2); cursor: pointer; font: inherit; font-size: 11px; }
344
+ .taskRow button:hover { color: var(--dsw-alias-brand-primary); }
345
+
346
+ .configGuide { position: fixed; inset: 0; z-index: 1100; display: grid; place-items: center; padding: 20px; background: rgb(0 0 0 / 35%); }
347
+ .configGuideBody { display: flex; width: min(360px, 100%); flex-direction: column; gap: 12px; padding: 18px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); box-shadow: 0 14px 40px rgb(0 0 0 / 20%); }
348
+ .configGuideBody span { color: var(--dsw-alias-label-secondary); font-size: 13px; line-height: 1.55; }
349
+ .configGuideBody button { align-self: flex-end; min-height: 30px; padding: 0 12px; border: 0; border-radius: 7px; color: var(--dsw-alias-bg-layer-1); background: var(--dsw-alias-brand-primary); cursor: pointer; font: inherit; font-size: 12px; }
350
+
322
351
  /* --- history column (far right) ------------------------------------------------ */
323
352
 
324
353
  .history {
@@ -345,6 +374,36 @@ html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ss
345
374
  flex: none;
346
375
  }
347
376
 
377
+ .historyFilters {
378
+ display: grid;
379
+ grid-template-columns: 1fr 1fr;
380
+ gap: 6px;
381
+ padding: 8px 10px;
382
+ border-bottom: 1px solid var(--dsw-alias-border-l1);
383
+ }
384
+
385
+ .historySearch,
386
+ .historyFilters select,
387
+ .gallerySearch {
388
+ min-width: 0;
389
+ min-height: 29px;
390
+ box-sizing: border-box;
391
+ padding: 0 8px;
392
+ border: 1px solid var(--dsw-alias-border-l2);
393
+ border-radius: 7px;
394
+ color: var(--dsw-alias-label-primary);
395
+ background: var(--dsw-alias-bg-layer-2);
396
+ font: inherit;
397
+ font-size: 11px;
398
+ }
399
+
400
+ .historySearch { grid-column: 1 / -1; }
401
+ .gallerySearch { width: 156px; }
402
+ .galleryTagInput { width: 130px; min-height: 29px; box-sizing: border-box; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 7px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); font: inherit; font-size: 11px; }
403
+ .galleryBulkButton { min-height: 29px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 7px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); cursor: pointer; font: inherit; font-size: 11px; }
404
+ .galleryBulkButton:hover:not(:disabled) { color: var(--dsw-alias-brand-primary); border-color: var(--dsw-alias-brand-primary); }
405
+ .galleryBulkButton:disabled { opacity: .45; cursor: default; }
406
+
348
407
  .historyTitle {
349
408
  font-size: 13px;
350
409
  font-weight: 600;
@@ -663,6 +722,26 @@ html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ss
663
722
  transform: translateY(0);
664
723
  }
665
724
 
725
+ .enhanceButton {
726
+ height: 26px;
727
+ margin-left: auto;
728
+ padding: 0 11px;
729
+ border: 1px solid var(--dsw-alias-border-l2);
730
+ border-radius: 999px;
731
+ color: var(--dsw-alias-label-secondary);
732
+ background: var(--dsw-alias-bg-layer-2);
733
+ font: inherit;
734
+ font-size: 12px;
735
+ cursor: pointer;
736
+ }
737
+
738
+ .enhanceButton:hover:not(:disabled) {
739
+ color: var(--dsw-alias-brand-primary);
740
+ border-color: var(--dsw-alias-brand-primary);
741
+ }
742
+
743
+ .enhanceButton:disabled { opacity: 0.5; cursor: default; }
744
+
666
745
  .promptCount {
667
746
  font-size: 11px;
668
747
  color: var(--dsw-alias-label-tertiary);
@@ -1376,22 +1455,62 @@ html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ss
1376
1455
  .galleryRatioList { display: flex; flex-wrap: wrap; gap: 6px; }
1377
1456
  .galleryRatio { padding: 6px 10px; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-2); cursor: pointer; font-size: 12px; }
1378
1457
  .galleryRatio[data-active] { color: var(--dsw-alias-brand-primary); background: color-mix(in srgb, var(--dsw-alias-brand-primary) 13%, transparent); }
1458
+ .galleryTagFilterList { display: flex; flex-wrap: wrap; gap: 6px; }
1459
+ .galleryTagFilter { display: inline-flex; align-items: center; gap: 5px; min-width: 0; max-width: 100%; min-height: 27px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-2); cursor: pointer; font: inherit; font-size: 11px; }
1460
+ .galleryTagFilter span:first-child { overflow: hidden; max-width: 112px; text-overflow: ellipsis; white-space: nowrap; }
1461
+ .galleryTagFilter span:last-child { color: var(--dsw-alias-label-tertiary); font-size: 10px; }
1462
+ .galleryTagFilter:hover, .galleryTagFilter[data-active] { color: var(--dsw-alias-brand-primary); background: color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent); }
1379
1463
  .galleryFilterNote { margin: 20px 4px 0; color: var(--dsw-alias-label-quaternary); font-size: 11px; line-height: 1.5; }
1380
1464
  .galleryWorkspace { position: absolute; inset: 0; display: flex; width: 100%; height: 100%; min-width: 0; min-height: 0; flex-direction: column; overflow: hidden; padding: 22px 24px 26px; box-sizing: border-box; }
1381
1465
  .galleryToolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex: none; min-width: 0; margin-bottom: 18px; }
1382
1466
  .galleryHeading { display: inline; margin: 0; color: var(--dsw-alias-label-primary); font-size: 20px; font-weight: 700; }
1383
1467
  .galleryCount { margin-left: 8px; color: var(--dsw-alias-label-tertiary); font-size: 13px; }
1384
1468
  .galleryToolbarActions { display: flex; align-items: center; gap: 10px; min-width: 0; flex-wrap: wrap; }
1469
+ .gallerySelectMode, .galleryBulkButton, .gallerySelectionClear { min-height: 30px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 7px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); cursor: pointer; font: inherit; font-size: 12px; }
1470
+ .gallerySelectMode:hover, .gallerySelectMode[data-active], .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); }
1471
+ .galleryBulkButton:disabled { cursor: not-allowed; opacity: .45; }
1472
+ .gallerySelectionBar { display: flex; align-items: center; gap: 10px; flex: none; min-width: 0; margin: -4px 0 16px; padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 35%, var(--dsw-alias-border-l1)); border-radius: 9px; background: color-mix(in srgb, var(--dsw-alias-brand-primary) 7%, var(--dsw-alias-bg-layer-1)); }
1473
+ .gallerySelectionBar strong { flex: none; color: var(--dsw-alias-brand-primary); font-size: 12px; }
1474
+ .gallerySelectionClear { margin-left: auto; }
1475
+ .gallerySelectionClear:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); }
1476
+ .galleryTagInput, .gallerySearch { min-width: 0; height: 30px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 7px; outline: none; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); font: inherit; font-size: 12px; }
1477
+ .galleryTagInput { flex: 1 1 190px; }
1478
+ .galleryTagInput:focus, .gallerySearch:focus { border-color: var(--dsw-alias-brand-primary); }
1385
1479
  .galleryViewToggle { display: flex; padding: 3px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 9px; background: var(--dsw-alias-bg-layer-1); }
1386
1480
  .galleryViewToggle button, .gallerySort, .galleryClear { min-height: 30px; padding: 0 10px; border: 0; border-radius: 7px; color: var(--dsw-alias-label-secondary); background: transparent; cursor: pointer; font: inherit; font-size: 12px; }
1387
1481
  .galleryViewToggle button[data-active], .galleryViewToggle button:hover, .gallerySort:hover, .galleryClear:hover { color: var(--dsw-alias-brand-primary); background: color-mix(in srgb, var(--dsw-alias-brand-primary) 11%, transparent); }
1388
1482
  .gallerySort { border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); }
1389
1483
  .galleryClear { border: 1px solid var(--dsw-alias-border-l1); }
1484
+ .compareControl { display: flex; flex-direction: column; gap: 6px; margin: 0 0 10px; }
1485
+ .compareToggle, .compareModelChoices label { display: flex; align-items: center; gap: 6px; color: var(--dsw-alias-label-secondary); font-size: 12px; cursor: pointer; }
1486
+ .compareToggle input, .compareModelChoices input { accent-color: var(--dsw-alias-brand-primary); }
1487
+ .compareModelChoices { display: flex; flex-wrap: wrap; gap: 6px; }
1488
+ .compareModelChoices label { padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 5px; font-size: 10px; }
1489
+ .comparisonBoard { position: absolute; z-index: 4; top: 126px; right: 14px; bottom: 14px; left: 14px; display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; background: var(--dsw-alias-bg-layer-1); box-shadow: 0 8px 24px rgb(0 0 0 / 12%); }
1490
+ .comparisonBoard > header { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
1491
+ .comparisonBoard > header div { display: flex; align-items: baseline; gap: 7px; }
1492
+ .comparisonBoard > header strong { color: var(--dsw-alias-label-primary); font-size: 13px; }
1493
+ .comparisonBoard > header span { color: var(--dsw-alias-label-tertiary); font-size: 11px; }
1494
+ .comparisonBoard > header button { min-height: 28px; padding: 0 9px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 5px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-2); cursor: pointer; font: inherit; font-size: 11px; }
1495
+ .comparisonGrid { display: grid; flex: 1; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; overflow: auto; padding: 12px; }
1496
+ .comparisonGrid article { display: flex; min-width: 0; flex-direction: column; gap: 7px; }
1497
+ .comparisonGrid article > strong { color: var(--dsw-alias-label-primary); font-size: 12px; }
1498
+ .comparisonGrid article > span { display: grid; min-height: 160px; place-items: center; color: var(--dsw-alias-label-tertiary); background: var(--dsw-alias-bg-layer-2); font-size: 12px; }
1499
+ .comparisonGrid img { display: block; width: 100%; min-height: 160px; max-height: calc(100vh - 260px); object-fit: contain; background: var(--dsw-alias-bg-base); }
1500
+ .comparisonFullscreen { position: fixed; z-index: 1200; inset: 0; overflow: auto; padding: 54px 24px 24px; background: rgb(0 0 0 / 92%); }
1501
+ .comparisonFullscreenGrid { display: grid; min-height: 100%; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); align-items: start; gap: 18px; }
1502
+ .comparisonFullscreen figure { min-width: 0; margin: 0; }
1503
+ .comparisonFullscreen figcaption { margin-bottom: 8px; color: #fff; font-size: 13px; font-weight: 600; }
1504
+ .comparisonFullscreen img { display: block; width: 100%; margin-bottom: 10px; background: #111; }
1390
1505
  .galleryMasonry { display: grid; flex: 1 1 auto; width: 100%; height: 0; max-width: 100%; min-width: 0; min-height: 0; box-sizing: border-box; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: max-content; align-content: start; gap: 16px; overflow-y: scroll; overflow-x: hidden; overscroll-behavior: contain; padding: 2px 8px 16px 2px; scrollbar-width: thin; scrollbar-color: var(--dsw-alias-border-l2) transparent; }
1391
1506
  .galleryMasonry::-webkit-scrollbar { width: 8px; }
1392
1507
  .galleryMasonry::-webkit-scrollbar-thumb { background: var(--dsw-alias-border-l2); border-radius: 999px; }
1393
- .galleryCard { display: block; width: 100%; margin: 0; overflow: hidden; border: 1px solid var(--dsw-alias-border-l1); border-radius: 14px; background: var(--dsw-alias-bg-layer-1); box-shadow: 0 5px 18px rgb(24 32 54 / 7%); }
1508
+ .galleryCard { position: relative; display: block; width: 100%; margin: 0; overflow: hidden; border: 1px solid var(--dsw-alias-border-l1); border-radius: 14px; background: var(--dsw-alias-bg-layer-1); box-shadow: 0 5px 18px rgb(24 32 54 / 7%); }
1509
+ .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 rgb(24 32 54 / 7%); }
1510
+ .gallerySelect { position: absolute; z-index: 2; top: 9px; right: 9px; display: grid; width: 26px; height: 26px; place-items: center; border: 1px solid rgb(255 255 255 / 75%); border-radius: 7px; background: rgb(0 0 0 / 58%); cursor: pointer; }
1511
+ .gallerySelect input { width: 16px; height: 16px; margin: 0; accent-color: var(--dsw-alias-brand-primary); cursor: pointer; }
1394
1512
  .galleryImageButton { position: relative; display: block; width: 100%; padding: 0; border: 0; background: var(--dsw-alias-bg-base); cursor: zoom-in; }
1513
+ .galleryImageButton[data-selecting] { cursor: pointer; }
1395
1514
  .galleryImage { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
1396
1515
  .galleryMasonry[data-view='masonry'] .galleryCard:nth-child(3n + 1) .galleryImage { aspect-ratio: 4 / 5; }
1397
1516
  .galleryMasonry[data-view='masonry'] .galleryCard:nth-child(3n + 2) .galleryImage { aspect-ratio: 4 / 3; }
@@ -1403,6 +1522,15 @@ html[data-dsh-imagegen-active]:not([data-dsh-taskboard-active]):not([data-dsh-ss
1403
1522
  .galleryCardInfo strong, .galleryCardInfo small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1404
1523
  .galleryCardInfo strong { color: var(--dsw-alias-label-primary); font-size: 12px; }
1405
1524
  .galleryCardInfo small { color: var(--dsw-alias-label-tertiary); font-size: 10px; }
1525
+ .galleryTags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
1526
+ .galleryTags button { overflow: hidden; max-width: 96px; min-height: 19px; padding: 1px 6px; border: 0; border-radius: 4px; color: var(--dsw-alias-brand-primary); background: color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent); cursor: pointer; font: inherit; font-size: 10px; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; }
1527
+ .galleryTags button:hover { background: color-mix(in srgb, var(--dsw-alias-brand-primary) 17%, transparent); }
1528
+ .galleryTags .galleryTagEdit { flex: none; color: var(--dsw-alias-label-tertiary); background: transparent; }
1529
+ .galleryTagEditor { display: flex; align-items: center; gap: 6px; padding: 0 12px 10px 48px; }
1530
+ .galleryTagEditor input { min-width: 0; flex: 1; height: 27px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; outline: none; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); font: inherit; font-size: 11px; }
1531
+ .galleryTagEditor input:focus { border-color: var(--dsw-alias-brand-primary); }
1532
+ .galleryTagEditor button { height: 27px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); cursor: pointer; font: inherit; font-size: 11px; }
1533
+ .galleryTagEditor button[type='submit'] { color: var(--dsw-alias-brand-primary); }
1406
1534
  .galleryRemove { flex: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%; color: var(--dsw-alias-label-tertiary); background: transparent; cursor: pointer; font-size: 18px; }
1407
1535
  .galleryRemove:hover { color: var(--dsw-alias-state-error); background: var(--dsw-alias-bg-layer-2); }
1408
1536
 
@@ -224,6 +224,25 @@
224
224
  outline: none;
225
225
  }
226
226
 
227
+ .textarea,
228
+ .textareaInvalid {
229
+ min-height: 70px;
230
+ resize: vertical;
231
+ padding: 8px 12px;
232
+ border-radius: 8px;
233
+ background: var(--dsw-alias-bg-layer-3);
234
+ font: inherit;
235
+ font-size: 13px;
236
+ line-height: 1.5;
237
+ color: var(--dsw-alias-label-primary);
238
+ outline: none;
239
+ }
240
+
241
+ .textarea { border: 1px solid var(--dsw-alias-border-l2); }
242
+ .textareaInvalid { border: 1px solid var(--dsw-alias-label-error); }
243
+ .textarea:focus-visible { border-color: var(--dsw-alias-brand-primary); }
244
+ .textarea:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
245
+
227
246
  .hint,
228
247
  .invalid {
229
248
  margin: 0;
@@ -247,6 +266,209 @@
247
266
  color: var(--dsw-alias-label-tertiary);
248
267
  }
249
268
 
269
+ .sectionDivider {
270
+ height: 1px;
271
+ margin: 18px 0 14px;
272
+ background: var(--dsw-alias-border-l2);
273
+ }
274
+
275
+ .sectionTitle {
276
+ margin: 0;
277
+ color: var(--dsw-alias-label-primary);
278
+ font-size: 14px;
279
+ line-height: 1.4;
280
+ }
281
+
282
+ .sectionHint {
283
+ margin: -4px 0 2px;
284
+ color: var(--dsw-alias-label-tertiary);
285
+ font-size: 12px;
286
+ line-height: 1.5;
287
+ }
288
+
289
+ .modelSection {
290
+ padding: 2px 0 14px;
291
+ }
292
+
293
+ .sectionHeader {
294
+ display: flex;
295
+ align-items: flex-start;
296
+ justify-content: space-between;
297
+ gap: 12px;
298
+ }
299
+
300
+ .sectionHeader .sectionHint {
301
+ max-width: 430px;
302
+ }
303
+
304
+ .modelSummary {
305
+ display: flex;
306
+ flex-wrap: wrap;
307
+ align-items: center;
308
+ gap: 6px;
309
+ margin-top: 12px;
310
+ }
311
+
312
+ .modelChip {
313
+ display: inline-flex;
314
+ align-items: center;
315
+ max-width: 100%;
316
+ gap: 5px;
317
+ padding: 3px 5px 3px 8px;
318
+ border: 1px solid var(--dsw-alias-border-l2);
319
+ border-radius: 6px;
320
+ background: var(--dsw-alias-bg-layer-3);
321
+ color: var(--dsw-alias-label-primary);
322
+ font-family: var(--dsw-font-family-mono, monospace);
323
+ font-size: 12px;
324
+ line-height: 1.5;
325
+ }
326
+
327
+ .modelChip > span {
328
+ overflow: hidden;
329
+ text-overflow: ellipsis;
330
+ white-space: nowrap;
331
+ }
332
+
333
+ .modelChip button {
334
+ appearance: none;
335
+ border: 0;
336
+ border-radius: 4px;
337
+ width: 18px;
338
+ height: 18px;
339
+ padding: 0;
340
+ background: transparent;
341
+ color: var(--dsw-alias-label-tertiary);
342
+ font: inherit;
343
+ line-height: 18px;
344
+ cursor: pointer;
345
+ }
346
+
347
+ .modelChip button:hover:not(:disabled) { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-module-platform); }
348
+ .modelChip button:disabled { cursor: default; opacity: 0.45; }
349
+
350
+ .addModel {
351
+ appearance: none;
352
+ min-height: 28px;
353
+ border: 1px dashed var(--dsw-alias-border-l2);
354
+ border-radius: 6px;
355
+ padding: 0 9px;
356
+ background: transparent;
357
+ color: var(--dsw-alias-label-secondary);
358
+ font: inherit;
359
+ font-size: 12px;
360
+ cursor: pointer;
361
+ }
362
+
363
+ .addModel:hover:not(:disabled) { color: var(--dsw-alias-brand-primary); border-color: var(--dsw-alias-brand-primary); }
364
+ .addModel:disabled { opacity: 0.45; cursor: default; }
365
+
366
+ .manualModelRow {
367
+ display: flex;
368
+ gap: 8px;
369
+ margin-top: 10px;
370
+ }
371
+
372
+ .manualModelRow .input { min-width: 0; flex: 1; }
373
+
374
+ .disclosure {
375
+ width: 100%;
376
+ appearance: none;
377
+ display: flex;
378
+ align-items: center;
379
+ gap: 8px;
380
+ border: 0;
381
+ border-top: 1px solid var(--dsw-alias-border-l2);
382
+ padding: 13px 0;
383
+ background: none;
384
+ color: var(--dsw-alias-label-primary);
385
+ font: inherit;
386
+ font-size: 13px;
387
+ font-weight: 500;
388
+ text-align: left;
389
+ cursor: pointer;
390
+ }
391
+
392
+ .disclosure > span:nth-child(2) { margin-left: auto; color: var(--dsw-alias-label-tertiary); font-size: 12px; font-weight: 400; }
393
+ .disclosure > span:last-child { color: var(--dsw-alias-label-tertiary); }
394
+ .disclosure:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 2px; }
395
+
396
+ .optionalContent {
397
+ padding: 0 0 8px;
398
+ }
399
+
400
+ .inlineDisclosure {
401
+ appearance: none;
402
+ display: inline-flex;
403
+ align-items: center;
404
+ gap: 6px;
405
+ margin-top: 12px;
406
+ padding: 0;
407
+ border: 0;
408
+ background: none;
409
+ color: var(--dsw-alias-label-secondary);
410
+ font: inherit;
411
+ font-size: 12px;
412
+ cursor: pointer;
413
+ }
414
+
415
+ .inlineDisclosure:hover { color: var(--dsw-alias-label-primary); }
416
+
417
+ .modelFetchRow {
418
+ display: flex;
419
+ gap: 8px;
420
+ align-items: center;
421
+ }
422
+
423
+ .modelFetch,
424
+ .modelChoices {
425
+ min-height: 32px;
426
+ border: 1px solid var(--dsw-alias-border-l2);
427
+ border-radius: 7px;
428
+ background: var(--dsw-alias-bg-layer-3);
429
+ color: var(--dsw-alias-label-secondary);
430
+ font: inherit;
431
+ font-size: 12px;
432
+ }
433
+
434
+ .modelFetch { padding: 0 10px; cursor: pointer; }
435
+ .modelFetch:hover:not(:disabled) { color: var(--dsw-alias-brand-primary); border-color: var(--dsw-alias-brand-primary); }
436
+ .modelFetch:disabled { opacity: 0.5; cursor: default; }
437
+ .modelChoices { min-width: 0; flex: 1; padding: 0 8px; }
438
+
439
+ .modelCandidateList {
440
+ display: flex;
441
+ flex-wrap: wrap;
442
+ gap: 6px 10px;
443
+ margin-top: 10px;
444
+ }
445
+
446
+ .modelCandidateLabel {
447
+ width: 100%;
448
+ color: var(--dsw-alias-label-tertiary);
449
+ font-size: 12px;
450
+ }
451
+
452
+ .modelCandidate {
453
+ appearance: none;
454
+ display: inline-flex;
455
+ align-items: center;
456
+ gap: 5px;
457
+ min-width: 0;
458
+ color: var(--dsw-alias-label-secondary);
459
+ font-size: 12px;
460
+ line-height: 1.5;
461
+ cursor: pointer;
462
+ padding: 3px 6px;
463
+ border: 1px solid transparent;
464
+ border-radius: 5px;
465
+ background: var(--dsw-alias-bg-layer-3);
466
+ font: inherit;
467
+ }
468
+
469
+ .modelCandidate input { margin: 0; }
470
+ .modelCandidate[data-selected] { border-color: var(--dsw-alias-border-l2); color: var(--dsw-alias-label-primary); }
471
+
250
472
  /* --- footer (mirror of the official card footer) ------------------------------ */
251
473
 
252
474
  .footer {