@dickpy/dsh-imagegen 1.5.1 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -76,16 +76,43 @@ window.__ModuleLoader__.load({
76
76
  image: "/api/dsh-imagegen/gallery/image"
77
77
  };
78
78
  /**
79
- * Same-origin route family for the bundled prompt-template library
80
- * (awesome-gpt-image-2 mirror). The case list ships inside the package and is
81
- * served by the host; reference images are proxied through the `image` prefix
82
- * route and cached on disk so repeated views never hit the network again.
79
+ * Same-origin route family for the prompt-template libraries. The library is
80
+ * multi-source: every request names a source id from {@link TEMPLATE_SOURCES},
81
+ * each source keeps an independent snapshot/image cache host-side, and
82
+ * reference images are proxied through the source-scoped `image` prefix route
83
+ * (`…/image/<sourceId>/<file>`) and cached on disk so repeated views never hit
84
+ * the network again.
83
85
  */
84
86
  const TEMPLATES_API = {
85
87
  list: "/api/dsh-imagegen/templates/list",
86
88
  refresh: "/api/dsh-imagegen/templates/refresh",
89
+ sample: "/api/dsh-imagegen/templates/sample",
87
90
  image: "/api/dsh-imagegen/templates/image"
88
91
  };
92
+ /** Same-origin route family for the user's saved (favorited) templates. */
93
+ const TEMPLATE_FAVORITES_API = {
94
+ list: "/api/dsh-imagegen/templates/favorites/list",
95
+ add: "/api/dsh-imagegen/templates/favorites/add",
96
+ remove: "/api/dsh-imagegen/templates/favorites/remove"
97
+ };
98
+ /**
99
+ * The template-library source registry. Each entry is fully independent (own
100
+ * upstream JSON, own image pool, own refresh state) and renders as its own
101
+ * tab; adding a source later means appending an entry here plus a host-side
102
+ * fetch definition in templates-store.ts and an optional bundled snapshot.
103
+ */
104
+ const TEMPLATE_SOURCES = [{
105
+ id: "vibeui",
106
+ label: "精选案例库",
107
+ homepage: "https://vibeui.top/",
108
+ description: "awesome-gpt-image-2 精选提示词案例(vibeui.top 镜像)"
109
+ }, {
110
+ id: "canghe",
111
+ label: "沧河案例库",
112
+ homepage: "https://gpt-image2.canghe.ai/",
113
+ description: "GPT-Image2 Prompt Gallery(gpt-image2.canghe.ai,定期更新)"
114
+ }];
115
+ TEMPLATE_SOURCES[0].id;
89
116
  //#endregion
90
117
  //#region src/client/api.ts
91
118
  /**
@@ -245,10 +272,15 @@ window.__ModuleLoader__.load({
245
272
  })
246
273
  }))).entries;
247
274
  }
248
- /** Fetch the prompt-template library (bundled snapshot or refreshed copy). */
249
- async templatesList() {
250
- const body = await readEnvelope(await fetch(TEMPLATES_API.list, { method: "POST" }));
275
+ /** Fetch one template source's list (bundled snapshot or refreshed copy). */
276
+ async templatesList(sourceId) {
277
+ const body = await readEnvelope(await fetch(TEMPLATES_API.list, {
278
+ method: "POST",
279
+ headers: { "content-type": "application/json" },
280
+ body: JSON.stringify({ source: sourceId })
281
+ }));
251
282
  return {
283
+ sourceId: body.sourceId,
252
284
  cases: body.cases,
253
285
  total: body.total,
254
286
  origin: body.origin,
@@ -256,14 +288,50 @@ window.__ModuleLoader__.load({
256
288
  fetchedAt: body.fetchedAt
257
289
  };
258
290
  }
259
- /** Re-download the template library from the upstream mirror (host-side). */
260
- async templatesRefresh() {
261
- const body = await readEnvelope(await fetch(TEMPLATES_API.refresh, { method: "POST" }));
291
+ /** Re-download one template source's list from its upstream mirror (host-side). */
292
+ async templatesRefresh(sourceId) {
293
+ const body = await readEnvelope(await fetch(TEMPLATES_API.refresh, {
294
+ method: "POST",
295
+ headers: { "content-type": "application/json" },
296
+ body: JSON.stringify({ source: sourceId })
297
+ }));
262
298
  return {
299
+ sourceId: body.sourceId,
263
300
  total: body.total,
264
301
  fetchedAt: body.fetchedAt
265
302
  };
266
303
  }
304
+ /** Draw random cases across every source (studio inspiration wall). */
305
+ async templatesSample(count) {
306
+ return (await readEnvelope(await fetch(TEMPLATES_API.sample, {
307
+ method: "POST",
308
+ headers: { "content-type": "application/json" },
309
+ body: JSON.stringify({ count })
310
+ }))).samples;
311
+ }
312
+ /** List the host-persisted template favorites. */
313
+ async favoritesList() {
314
+ return (await readEnvelope(await fetch(TEMPLATE_FAVORITES_API.list, { method: "POST" }))).favorites;
315
+ }
316
+ /** Star one template (the host keeps a full case snapshot). */
317
+ async favoritesAdd(sourceId, item) {
318
+ return (await readEnvelope(await fetch(TEMPLATE_FAVORITES_API.add, {
319
+ method: "POST",
320
+ headers: { "content-type": "application/json" },
321
+ body: JSON.stringify({
322
+ source: sourceId,
323
+ case: item
324
+ })
325
+ }))).favorites;
326
+ }
327
+ /** Unstar one template by its favorites key. */
328
+ async favoritesRemove(key) {
329
+ return (await readEnvelope(await fetch(TEMPLATE_FAVORITES_API.remove, {
330
+ method: "POST",
331
+ headers: { "content-type": "application/json" },
332
+ body: JSON.stringify({ key })
333
+ }))).favorites;
334
+ }
267
335
  };
268
336
  //#endregion
269
337
  //#region src/client/controller.ts
@@ -589,6 +657,7 @@ window.__ModuleLoader__.load({
589
657
  "settings.invalidNumber": "请输入有效数字",
590
658
  "templates.open": "模板库",
591
659
  "templates.title": "提示词模板库",
660
+ "templates.sources": "模板库来源",
592
661
  "templates.meta": "共 {count} 个模板 · {origin}",
593
662
  "templates.origin.bundled": "内置快照",
594
663
  "templates.origin.refreshed": "在线刷新",
@@ -599,21 +668,32 @@ window.__ModuleLoader__.load({
599
668
  "templates.use": "使用此提示词",
600
669
  "templates.copy": "复制提示词",
601
670
  "templates.copied": "已复制",
602
- "templates.refresh": "刷新模板库",
671
+ "templates.refresh": "刷新本库",
603
672
  "templates.refreshing": "刷新中…",
604
673
  "templates.refreshed": "已刷新,共 {count} 个模板",
605
674
  "templates.refreshFailed": "刷新失败:{error}",
675
+ "templates.favorites": "收藏",
676
+ "templates.favoritesHint": "只看已收藏的模板",
677
+ "templates.favoritesEmpty": "还没有收藏,点击模板卡片右上角的星标即可收藏",
678
+ "templates.favoriteAdd": "收藏此模板",
679
+ "templates.favoriteRemove": "取消收藏",
680
+ "templates.favorite": "收藏",
681
+ "templates.unfavorite": "已收藏",
606
682
  "templates.cacheAll": "缓存全部图片",
607
- "templates.cacheAllHint": "通过本机代理把全部参考图缓存到本地磁盘,之后离线也能浏览",
683
+ "templates.cacheAllHint": "通过本机代理把当前模板库的全部参考图缓存到本地磁盘,之后离线也能浏览",
608
684
  "templates.caching": "缓存中 {done}/{total}…",
609
685
  "templates.cached": "图片已全部缓存",
610
686
  "templates.empty": "没有匹配的模板",
611
687
  "templates.loading": "正在加载模板库…",
612
688
  "templates.loadFailed": "模板库加载失败:{error}",
613
689
  "templates.retry": "重试",
614
- "templates.attribution": "模板与图片来自 awesome-gpt-image-2 项目,作者链接见各模板详情",
615
- "templates.source": "来源:vibeui.top",
690
+ "templates.attribution": "模板与图片来自各来源站点,作者链接见模板详情",
691
+ "templates.source": "来源:{label}",
616
692
  "templates.featured": "精选",
693
+ "inspiration.title": "灵感案例",
694
+ "inspiration.shuffle": "随机",
695
+ "inspiration.shuffling": "换一批…",
696
+ "inspiration.useHint": "点击使用该提示词",
617
697
  "channels.title": "渠道",
618
698
  "channels.hint": "填写各渠道的 API 地址与密钥即可使用对应模型",
619
699
  "channels.empty": "尚无渠道,点击下方按钮添加",
@@ -950,6 +1030,7 @@ window.__ModuleLoader__.load({
950
1030
  "settings.invalidNumber": "Enter a valid number",
951
1031
  "templates.open": "Templates",
952
1032
  "templates.title": "Prompt Template Library",
1033
+ "templates.sources": "Template sources",
953
1034
  "templates.meta": "{count} templates · {origin}",
954
1035
  "templates.origin.bundled": "bundled snapshot",
955
1036
  "templates.origin.refreshed": "refreshed online",
@@ -960,21 +1041,32 @@ window.__ModuleLoader__.load({
960
1041
  "templates.use": "Use this prompt",
961
1042
  "templates.copy": "Copy prompt",
962
1043
  "templates.copied": "Copied",
963
- "templates.refresh": "Refresh library",
1044
+ "templates.refresh": "Refresh this library",
964
1045
  "templates.refreshing": "Refreshing…",
965
1046
  "templates.refreshed": "Refreshed — {count} templates",
966
1047
  "templates.refreshFailed": "Refresh failed: {error}",
1048
+ "templates.favorites": "Favorites",
1049
+ "templates.favoritesHint": "Show favorited templates only",
1050
+ "templates.favoritesEmpty": "No favorites yet — tap the star in a card's top-right corner to save one",
1051
+ "templates.favoriteAdd": "Favorite this template",
1052
+ "templates.favoriteRemove": "Remove from favorites",
1053
+ "templates.favorite": "Favorite",
1054
+ "templates.unfavorite": "Favorited",
967
1055
  "templates.cacheAll": "Cache all images",
968
- "templates.cacheAllHint": "Mirror every reference image to local disk through the host proxy, for offline browsing",
1056
+ "templates.cacheAllHint": "Mirror every reference image of the current library to local disk through the host proxy, for offline browsing",
969
1057
  "templates.caching": "Caching {done}/{total}…",
970
1058
  "templates.cached": "All images cached",
971
1059
  "templates.empty": "No matching templates",
972
1060
  "templates.loading": "Loading the template library…",
973
1061
  "templates.loadFailed": "Failed to load the library: {error}",
974
1062
  "templates.retry": "Retry",
975
- "templates.attribution": "Templates and images come from the awesome-gpt-image-2 project; author links are on each template",
976
- "templates.source": "Source: vibeui.top",
1063
+ "templates.attribution": "Templates and images come from each source site; author links are on each template",
1064
+ "templates.source": "Source: {label}",
977
1065
  "templates.featured": "Featured",
1066
+ "inspiration.title": "Inspiration",
1067
+ "inspiration.shuffle": "Shuffle",
1068
+ "inspiration.shuffling": "Shuffling…",
1069
+ "inspiration.useHint": "Click to use this prompt",
978
1070
  "channels.title": "Channels",
979
1071
  "channels.hint": "Fill in each channel's API URL and key to use its models",
980
1072
  "channels.empty": "No channels yet — add one below",
@@ -1025,16 +1117,425 @@ window.__ModuleLoader__.load({
1025
1117
  "channels.presetCustomHint": "Configure the API URL, key, and model catalog yourself",
1026
1118
  "channels.presetLoadFailed": "Failed to load providers: {error}"
1027
1119
  };
1120
+ /** Russian mirror of every key (полный словарь интерфейса). */
1121
+ const ru = {
1122
+ "entry.label": "ИИ-генерация",
1123
+ "entry.tooltip": "Панель ИИ-генерации изображений (gpt-image-2 / glm-image / grok-imagine-image / nanobanana / seedream)",
1124
+ "entry.newSession": "Новая сессия",
1125
+ "entry.newSessionTooltip": "Создать новую сессию DSH",
1126
+ "entry.image": "Генерация",
1127
+ "panel.title": "ИИ-генерация изображений",
1128
+ "panel.githubTip": "Понравился плагин или есть идеи? Поставьте звезду или откройте issue на GitHub!",
1129
+ "conversation.add": "В диалог",
1130
+ "conversation.adding": "Добавляем…",
1131
+ "conversation.added": "Изображение добавлено в текущий диалог",
1132
+ "conversation.addHint": "После добавления можно править через /edit_image; команда использует модель плагина",
1133
+ "conversation.noSession": "Сначала откройте сессию, затем добавьте изображение",
1134
+ "conversation.unavailable": "Текущая сессия недоступна",
1135
+ "conversation.busy": "Диалог отправляет сообщение, попробуйте позже",
1136
+ "mode.text": "Из текста",
1137
+ "mode.edit": "Из изображения",
1138
+ "workspace.label": "Режим работы",
1139
+ "workspace.normal": "Генерация",
1140
+ "workspace.ecommerce": "E-commerce",
1141
+ "chat.expand": "Показать диалог",
1142
+ "chat.collapse": "Скрыть диалог",
1143
+ "chat.toggle": "Диалог",
1144
+ "config.resizeHint": "Потяните, чтобы изменить ширину панели параметров",
1145
+ "ecommerce.generation": "Параметры генерации",
1146
+ "ecommerce.title": "Режим e-commerce",
1147
+ "ecommerce.badge": "Превью",
1148
+ "ecommerce.short": "E-com",
1149
+ "ecommerce.anchorPending": "Главное изображение готово, остальные генерируются по нему для единого стиля…",
1150
+ "ecommerce.anchorFailed": "Не удалось создать главное изображение, остальные приостановлены. Повторите главное (или всю группу) и продолжите.",
1151
+ "ecommerce.anchorNote": "Якорная генерация: сначала главное изображение, остальные создаются по нему для единого стиля",
1152
+ "ecommerce.noAssetWarn": "Товар не загружен: главное изображение будет создано по описанию и станет якорем; загрузите фото товара для большей точности",
1153
+ "ecommerce.product": "Товар",
1154
+ "ecommerce.productName": "Название товара (обязательно)",
1155
+ "ecommerce.projectName": "Название проекта (необязательно)",
1156
+ "ecommerce.constraints": "Преимущества и ограничения",
1157
+ "ecommerce.sellingPoints": "Преимущества товара, например: водостойкость / большая вместимость / компактность",
1158
+ "ecommerce.protectedFeatures": "Что обязательно сохранить: цвет, логотип, текст упаковки, конструкцию",
1159
+ "ecommerce.styleHint": "Стиль, например: премиальный / реалистичный / естественный свет",
1160
+ "ecommerce.setStructure": "Структура комплекта",
1161
+ "ecommerce.preview": "Создать превью комплекта",
1162
+ "ecommerce.planTitle": "Будет сгенерировано изображений: {count}",
1163
+ "ecommerce.planSlot": "{count} шт. · {description}",
1164
+ "ecommerce.confirm": "Сгенерировать весь комплект",
1165
+ "ecommerce.uploadRef": "Материалы товара (необязательно, до 4 шт.: товар / упаковка / детали / стиль)",
1166
+ "ecommerce.uploadShort": "Загрузить материалы",
1167
+ "ecommerce.sellingTitle": "Преимущества товара",
1168
+ "ecommerce.advanced": "Дополнительно (проект / площадка / ограничения)",
1169
+ "ecommerce.params": "Параметры",
1170
+ "ecommerce.platformLabel": "Площадка",
1171
+ "ecommerce.languageLabel": "Язык текста",
1172
+ "ecommerce.ratioLabel": "Пропорции",
1173
+ "ecommerce.categoryLabel": "Категория",
1174
+ "ecommerce.multiSelect": "Можно выбрать несколько",
1175
+ "ecommerce.countHint": "Нажмите, чтобы изменить количество (1-4)",
1176
+ "ecommerce.refSettings": "Настройки референсов (необязательно)",
1177
+ "ecommerce.styleTitle": "Стиль / дополнения",
1178
+ "ecommerce.protectedLabel": "Обязательно сохранить (необязательно)",
1179
+ "ecommerce.assetsFull": "Не более 4 материалов",
1180
+ "ecommerce.refSelect": "Референс для этого назначения",
1181
+ "ecommerce.refNone": "Без референса",
1182
+ "ecommerce.role.product": "Товар",
1183
+ "ecommerce.role.packaging": "Упаковка",
1184
+ "ecommerce.role.detail": "Детали/ракурс",
1185
+ "ecommerce.role.style": "Стиль",
1186
+ "ecommerce.footerReady": "Будет сгенерировано {count} изображений",
1187
+ "ecommerce.footerEmpty": "Выберите структуру комплекта",
1188
+ "ecommerce.results.title": "Результаты комплекта",
1189
+ "ecommerce.results.progress": "{done}/{total} готово",
1190
+ "ecommerce.results.failed": "{count} с ошибкой",
1191
+ "ecommerce.results.empty": "После запуска генерации здесь появятся статус и результат каждого изображения",
1192
+ "ecommerce.results.regenerate": "Сгенерировать заново",
1193
+ "ecommerce.results.export": "Экспорт списка",
1194
+ "ecommerce.results.newProduct": "Новый товар",
1195
+ "prompt.placeholder": "Опишите желаемое изображение, например: рыжий кот в шлеме космонавта смотрит в телескоп на Луне, акварель, мягкий свет…",
1196
+ "prompt.required": "Введите промпт",
1197
+ "prompt.count": "{count}",
1198
+ "prompt.enhance": "Улучш.",
1199
+ "prompt.enhancing": "Улучшаем…",
1200
+ "prompt.enhanceHint": "Развернуть краткий промпт через настроенную диалоговую модель",
1201
+ "prompt.configTitle": "Сначала настройте модель улучшения промптов",
1202
+ "prompt.configHint": "Настройки открыты. Перейдите в «Плагины → ИИ-генерация», укажите или переиспользуйте API-адрес и ключ, выберите диалоговую модель и сохраните.",
1203
+ "tasks.title": "Задачи генерации",
1204
+ "tasks.queued": "В очереди",
1205
+ "tasks.running": "Генерация",
1206
+ "tasks.completed": "Готово",
1207
+ "tasks.failed": "Ошибка",
1208
+ "tasks.cancelled": "Отменено",
1209
+ "tasks.cancel": "Отменить",
1210
+ "tasks.retry": "Повторить",
1211
+ "params.size": "Размер",
1212
+ "params.quality": "Чёткость",
1213
+ "params.count": "Количество",
1214
+ "params.detail": "Детализация",
1215
+ "size.auto": "Авто",
1216
+ "size.square": "1:1 квадрат",
1217
+ "size.portrait34": "3:4 верт.",
1218
+ "size.landscape43": "4:3 гориз.",
1219
+ "size.portrait916": "9:16 верт.",
1220
+ "size.portrait23": "2:3 верт.",
1221
+ "size.landscape32": "3:2 гориз.",
1222
+ "size.wide169": "16:9 шир.",
1223
+ "size.ultrawide21": "21:9 ультрашир.",
1224
+ "quality.auto": "Авто",
1225
+ "quality.1k": "1K",
1226
+ "quality.2k": "2K",
1227
+ "quality.4k": "4K",
1228
+ "count.one": "1 шт.",
1229
+ "count.two": "2 шт.",
1230
+ "count.three": "3 шт.",
1231
+ "count.four": "4 шт.",
1232
+ "detail.auto": "Авто",
1233
+ "detail.standard": "Стандарт",
1234
+ "detail.high": "Высокая",
1235
+ "detail.hint": "Сквозной параметр, поддерживают некоторые шлюзы gpt-image-2; для официального API оставьте «Авто»",
1236
+ "model.label": "Модель",
1237
+ "model.noEditModels": "В текущем режиме нет доступных моделей",
1238
+ "compare.enable": "Сравнение моделей",
1239
+ "compare.models": "Модели для сравнения",
1240
+ "compare.title": "Сравнение результатов моделей",
1241
+ "compare.fullscreen": "Полноэкранное сравнение",
1242
+ "compare.selectRequired": "Выберите хотя бы одну модель для сравнения",
1243
+ "generate": "Сгенерировать",
1244
+ "generating": "Генерация…",
1245
+ "edit.upload": "Нажмите или перетащите референс",
1246
+ "edit.uploadHint": "PNG / JPG / WEBP, до 10 МБ",
1247
+ "edit.change": "Заменить изображение",
1248
+ "edit.remove": "Убрать",
1249
+ "edit.required": "Сначала загрузите референс",
1250
+ "canvas.emptyTitle": "Начните творить",
1251
+ "canvas.emptyHint": "Введите промпт слева и нажмите «Сгенерировать» — результат появится здесь",
1252
+ "canvas.new": "Новая генерация",
1253
+ "canvas.newHint": "Начать новую генерацию и очистить просмотр",
1254
+ "canvas.error": "Ошибка генерации: {error}",
1255
+ "canvas.submitting": "Ставим в очередь…",
1256
+ "canvas.queued": "Задача в очереди",
1257
+ "canvas.queueHint": "Сейчас ожидают {count} задач",
1258
+ "canvas.generating": "Генерируем изображение…",
1259
+ "canvas.elapsed": "Прошло {seconds} с",
1260
+ "canvas.images": "Сгенерировано: {count}",
1261
+ "download": "Скачать",
1262
+ "revisedPrompt": "Улучшенный промпт: {prompt}",
1263
+ "history.title": "История",
1264
+ "history.empty": "Истории пока нет — созданные изображения появятся здесь",
1265
+ "history.clear": "Очистить",
1266
+ "history.clearConfirm": "Очистить всю историю? Действие необратимо.",
1267
+ "history.restore": "Восстановить",
1268
+ "history.delete": "Удалить",
1269
+ "history.images": "шт.",
1270
+ "history.viewing": "История · {time}",
1271
+ "history.search": "Поиск по промптам и моделям…",
1272
+ "history.model": "Фильтр по модели",
1273
+ "history.ratio": "Фильтр по пропорциям",
1274
+ "history.allModels": "Все модели",
1275
+ "history.allRatios": "Все пропорции",
1276
+ "gallery.title": "Галерея",
1277
+ "gallery.categories": "Категории",
1278
+ "gallery.all": "Все работы",
1279
+ "gallery.gpt": "gpt-image-2",
1280
+ "gallery.grok": "grok-imagine-image",
1281
+ "gallery.ratio": "Пропорции",
1282
+ "gallery.tags": "Метки",
1283
+ "gallery.filterHint": "Фильтр галереи по режиму, модели и пропорциям",
1284
+ "gallery.count": "· всего {count}",
1285
+ "gallery.viewMode": "Вид",
1286
+ "gallery.masonry": "Плитка",
1287
+ "gallery.grid": "Сетка",
1288
+ "gallery.sort": "Сортировка",
1289
+ "gallery.newest": "Сначала новые",
1290
+ "gallery.oldest": "Сначала старые",
1291
+ "gallery.untitled": "Без названия",
1292
+ "gallery.search": "Поиск по работам и моделям…",
1293
+ "gallery.tagsPlaceholder": "Метки через запятую",
1294
+ "gallery.tagsApply": "Добавить метки",
1295
+ "gallery.editTags": "Править метки",
1296
+ "gallery.tagsEditShort": "Правка",
1297
+ "gallery.tagsSave": "Сохранить",
1298
+ "gallery.tagsCancel": "Отмена",
1299
+ "gallery.selected": "Выбрано: {count}",
1300
+ "gallery.selectionDone": "Завершить выбор",
1301
+ "gallery.selectionClear": "Снять выбор",
1302
+ "gallery.downloadSelected": "Скачать выбранные",
1303
+ "gallery.exportJson": "Экспорт JSON",
1304
+ "gallery.select": "Выбрать работы",
1305
+ "gallery.add": "В галерею",
1306
+ "gallery.added": "Добавлено в галерею",
1307
+ "gallery.already": "Уже в галерее",
1308
+ "gallery.delete": "Убрать из галереи",
1309
+ "gallery.clear": "Очистить галерею",
1310
+ "gallery.clearConfirm": "Очистить всю галерею? Действие необратимо.",
1311
+ "gallery.empty": "Галерея пуста — добавьте понравившиеся изображения",
1312
+ "gallery.viewing": "Галерея · {time}",
1313
+ "preview.title": "Просмотр изображения",
1314
+ "preview.open": "Открыть просмотр",
1315
+ "preview.close": "Закрыть",
1316
+ "preview.prev": "Назад",
1317
+ "preview.next": "Вперёд",
1318
+ "preview.index": "{index} / {total}",
1319
+ "preview.zoomControls": "Управление масштабом",
1320
+ "preview.zoomIn": "Увеличить",
1321
+ "preview.zoomOut": "Уменьшить",
1322
+ "preview.zoomReset": "Сбросить масштаб",
1323
+ "preview.zoomLevel": "{percent}%",
1324
+ "preview.copyPrompt": "Копировать промпт",
1325
+ "preview.copied": "Скопировано",
1326
+ "preview.addToEdit": "В режим img2img",
1327
+ "config.missing": "API не настроен: откройте «Настройки → Плагины → Настраиваемые» и заполните api_url и api_key для ИИ-генерации.",
1328
+ "config.generationTitle": "Сначала настройте API генерации",
1329
+ "config.generationHint": "Открыты «Настройки → Плагины → ИИ-генерация». Укажите адрес и ключ API генерации, сохраните — и можно творить.",
1330
+ "config.enhancementTitle": "Сначала настройте модель улучшения промптов",
1331
+ "config.enhancementHint": "Открыты «Настройки → Плагины → ИИ-генерация». Укажите или переиспользуйте адрес и ключ диалогового API, выберите модель и сохраните.",
1332
+ "config.disabledTitle": "Плагин ИИ-генерации отключён",
1333
+ "config.disabledHint": "Открыты «Настройки → Плагины → ИИ-генерация». Включите плагин — и генерация заработает.",
1334
+ "config.configured": "Подключено: {url}",
1335
+ "config.disabled": "Плагин отключён — включите его в настройках.",
1336
+ "connection.connected": "Подключено",
1337
+ "connection.disconnected": "Нет подключения",
1338
+ "panel.collapseConfig": "Свернуть параметры",
1339
+ "panel.expandConfig": "Развернуть параметры",
1340
+ "update.available": "Доступна новая версия: {version}",
1341
+ "update.install": "Обновить онлайн",
1342
+ "update.installing": "Обновляем…",
1343
+ "update.success": "Обновлено до {version}, перезапустите DSH",
1344
+ "update.failed": "Не удалось обновить, попробуйте ещё раз",
1345
+ "update.release": "Открыть релиз",
1346
+ "settings.title": "ИИ-генерация (dsh-imagegen)",
1347
+ "settings.description": "Адрес и ключ API генерации изображений",
1348
+ "settings.currentVersion": "Текущая версия",
1349
+ "settings.apiUrl": "Адрес API (api_url)",
1350
+ "settings.apiUrlHint": "База OpenAI-совместимого API, например https://api.openai.com/v1; /images/generations и /images/edits дописываются автоматически",
1351
+ "settings.apiKey": "Ключ API (api_key)",
1352
+ "settings.apiKeyHint": "Bearer-ключ хранится открыто в локальном документе настроек; интерфейс показывает лишь факт его наличия",
1353
+ "settings.apiKeySet": "Ключ сохранён; введите новый, чтобы заменить, или нажмите «Очистить»",
1354
+ "settings.apiKeyClear": "Очистить",
1355
+ "settings.imageModelsTitle": "Модели генерации",
1356
+ "settings.imageModelsHint": "После сохранения адреса и ключа будут найдены кандидаты; выбирайте только те, что реально умеют генерировать изображения.",
1357
+ "settings.imageModels": "Разрешённые модели генерации",
1358
+ "settings.imageModelsManualHint": "По одной модели в строке; можно заполнить вручную, если API не отдаёт /models. Панель и агент используют только этот список.",
1359
+ "settings.imageModelsFetch": "Найти модели",
1360
+ "settings.imageModelsLoading": "Ищем…",
1361
+ "settings.imageModelsCandidates": "Найденные кандидаты (отметьте и сохраните)",
1362
+ "settings.addModel": "+ Добавить вручную",
1363
+ "settings.cancelAddModel": "Скрыть добавление",
1364
+ "settings.addModelPlaceholder": "Имя модели, например qwen-image",
1365
+ "settings.addModelConfirm": "Добавить",
1366
+ "settings.removeModel": "Удалить модель",
1367
+ "settings.optional": "необязательно",
1368
+ "settings.moreOptions": "Дополнительно",
1369
+ "settings.promptEnhanceTitle": "Модель улучшения промптов",
1370
+ "settings.promptEnhanceHint": "Разворачивает краткое описание в полноценный промпт; пустые адрес и ключ наследуют настройки генерации.",
1371
+ "settings.promptApiUrl": "Адрес диалогового API (необязательно)",
1372
+ "settings.promptApiUrlHint": "OpenAI-совместимая база; если пусто — используется адрес API генерации.",
1373
+ "settings.promptApiKey": "Ключ диалогового API (необязательно)",
1374
+ "settings.promptApiKeyHint": "Если пусто — используется ключ API генерации.",
1375
+ "settings.promptModel": "Диалоговая модель",
1376
+ "settings.promptModelHint": "Выберите или впишите модель с поддержкой /chat/completions.",
1377
+ "settings.promptModelDetectionHint": "По умолчанию используется API генерации; после поиска просто выберите модель.",
1378
+ "settings.promptModelsFetch": "Получить модели",
1379
+ "settings.promptModelsLoading": "Получаем…",
1380
+ "settings.promptModelsSelect": "Выберите модель",
1381
+ "settings.promptModelsCandidates": "Найденные диалоговые модели",
1382
+ "settings.addPromptModelPlaceholder": "Имя диалоговой модели, например gpt-4.1-mini",
1383
+ "settings.promptApiAdvanced": "Отдельный диалоговый API (необязательно)",
1384
+ "settings.announceToAgent": "Оповещать агентов о плагине",
1385
+ "settings.announceToAgentHint": "Включено — возможности плагина добавляются в системный промпт каждого агента",
1386
+ "settings.allowAgentImageGeneration": "Разрешить агентам генерацию",
1387
+ "settings.allowAgentImageGenerationHint": "Включено по умолчанию. Выключено — агенты не могут создавать, смотреть или отменять задачи; боковая панель работает.",
1388
+ "settings.enabled": "Включить плагин",
1389
+ "settings.enabledHint": "Выключено — панель генерации недоступна (карточка настроек работает всегда)",
1390
+ "settings.save": "Сохранить",
1391
+ "settings.saving": "Сохраняем…",
1392
+ "settings.discard": "Отменить правки",
1393
+ "settings.unsaved": "Есть несохранённые правки",
1394
+ "settings.saveFailed": "Не удалось сохранить, попробуйте ещё раз",
1395
+ "settings.readOnly": "Документ настроек сейчас только для чтения, сохранить нельзя.",
1396
+ "settings.notExposed": "Пространство настроек недоступно: этот деплой не предоставляет сервис настроек плагина.",
1397
+ "settings.expand": "Развернуть",
1398
+ "settings.collapse": "Свернуть",
1399
+ "settings.inherit": "Наследовать",
1400
+ "settings.on": "Вкл",
1401
+ "settings.off": "Выкл",
1402
+ "settings.overridden": "Переопределено",
1403
+ "settings.reset": "Сбросить",
1404
+ "settings.invalidNumber": "Введите корректное число",
1405
+ "templates.open": "Шаблоны",
1406
+ "templates.title": "Библиотека промпт-шаблонов",
1407
+ "templates.sources": "Источники шаблонов",
1408
+ "templates.meta": "Всего шаблонов: {count} · {origin}",
1409
+ "templates.origin.bundled": "встроенный снимок",
1410
+ "templates.origin.refreshed": "обновлено онлайн",
1411
+ "templates.search": "Поиск по названиям и промптам…",
1412
+ "templates.all": "Все",
1413
+ "templates.close": "Закрыть",
1414
+ "templates.back": "К списку",
1415
+ "templates.use": "Использовать промпт",
1416
+ "templates.copy": "Копировать промпт",
1417
+ "templates.copied": "Скопировано",
1418
+ "templates.refresh": "Обновить библиотеку",
1419
+ "templates.refreshing": "Обновляем…",
1420
+ "templates.refreshed": "Обновлено, шаблонов: {count}",
1421
+ "templates.refreshFailed": "Ошибка обновления: {error}",
1422
+ "templates.favorites": "Избранное",
1423
+ "templates.favoritesHint": "Показать только избранные шаблоны",
1424
+ "templates.favoritesEmpty": "Пока пусто — нажмите звёздочку в углу карточки, чтобы сохранить шаблон",
1425
+ "templates.favoriteAdd": "Добавить в избранное",
1426
+ "templates.favoriteRemove": "Убрать из избранного",
1427
+ "templates.favorite": "В избранное",
1428
+ "templates.unfavorite": "В избранном",
1429
+ "templates.cacheAll": "Кэшировать все картинки",
1430
+ "templates.cacheAllHint": "Через локальный прокси сохранит все референсы этой библиотеки на диск — дальше доступно офлайн",
1431
+ "templates.caching": "Кэшируем {done}/{total}…",
1432
+ "templates.cached": "Все картинки закэшированы",
1433
+ "templates.empty": "Нет подходящих шаблонов",
1434
+ "templates.loading": "Загружаем библиотеку…",
1435
+ "templates.loadFailed": "Не удалось загрузить библиотеку: {error}",
1436
+ "templates.retry": "Повторить",
1437
+ "templates.attribution": "Шаблоны и изображения принадлежат сайтам-источникам; ссылки на авторов — в карточках",
1438
+ "templates.source": "Источник: {label}",
1439
+ "templates.featured": "Выбор редакции",
1440
+ "inspiration.title": "Вдохновение",
1441
+ "inspiration.shuffle": "Другие",
1442
+ "inspiration.shuffling": "Подбираем…",
1443
+ "inspiration.useHint": "Нажмите, чтобы использовать промпт",
1444
+ "channels.title": "Каналы",
1445
+ "channels.hint": "Укажите адрес и ключ API каждого канала — и модели станут доступны",
1446
+ "channels.empty": "Каналов пока нет — добавьте кнопкой ниже",
1447
+ "channels.addProvider": "Добавить провайдера",
1448
+ "channels.addCustom": "Добавить свой канал",
1449
+ "channels.untitled": "Канал без названия",
1450
+ "channels.keySet": "Ключ задан",
1451
+ "channels.keyMissing": "Ключ не задан",
1452
+ "channels.modelCount": "Моделей: {n}",
1453
+ "channels.noModels": "Модели не настроены",
1454
+ "channels.defaultLabel": "По умолчанию",
1455
+ "channels.statusReady": "Готов",
1456
+ "channels.statusIncomplete": "Настройка не завершена",
1457
+ "channels.edit": "Править",
1458
+ "channels.delete": "Удалить",
1459
+ "channels.deleteConfirmTitle": "Удалить канал «{name}»? Ключ канала будет удалён; история и галерея сохранятся.",
1460
+ "channels.confirm": "Удалить",
1461
+ "channels.cancel": "Отмена",
1462
+ "channels.editorTitle": "Канал",
1463
+ "channels.editorSaveNote": "Изменения вступят в силу после «Сохранить» внизу карточки",
1464
+ "channels.displayName": "Название",
1465
+ "channels.apiUrl": "Адрес API",
1466
+ "channels.apiKey": "Ключ API",
1467
+ "channels.keyReplaceHint": "Задан — введите новый, чтобы заменить",
1468
+ "channels.keyMissingHint": "Введите ключ",
1469
+ "channels.keyClear": "Очистить ключ",
1470
+ "channels.modelCatalogTitle": "Каталог моделей",
1471
+ "channels.noModelsHint": "Моделей пока нет: нажмите «Найти» для списка кандидатов или добавьте вручную; псевдоним по умолчанию равен id модели и его можно менять.",
1472
+ "channels.detect": "Найти снова",
1473
+ "channels.detecting": "Ищем…",
1474
+ "channels.detectSuccess": "Связь есть ✓ · кандидатов: {n}",
1475
+ "channels.detectFailed": "Ошибка поиска: {error}",
1476
+ "channels.detectOk": "API отвечает",
1477
+ "channels.modelAliasLabel": "Псевдоним (отображается)",
1478
+ "channels.modelIdLabel": "id модели у провайдера",
1479
+ "channels.generated": "Сгенерировано: {n}",
1480
+ "channels.unknownProtocol": "Протокол неизвестен — пробуем универсальный OpenAI",
1481
+ "channels.removeModel": "Удалить модель",
1482
+ "channels.manualAddPlaceholder": "id модели, например qwen-image",
1483
+ "channels.addModelConfirm": "Добавить",
1484
+ "channels.copyFrom": "Скопировать из канала…",
1485
+ "channels.copyApply": "Копировать",
1486
+ "channels.candidatesTitle": "Кандидаты (нажмите, чтобы добавить)",
1487
+ "channels.deleteThisChannel": "Удалить этот канал",
1488
+ "channels.setDefault": "Сделать каналом по умолчанию",
1489
+ "channels.presetPickerTitle": "Добавить провайдера",
1490
+ "channels.presetPickerHint": "Выберите встроенного провайдера — останется ввести только ключ API",
1491
+ "channels.presetCustomHint": "Свой адрес, ключ и каталог моделей",
1492
+ "channels.presetLoadFailed": "Не удалось загрузить провайдеров: {error}"
1493
+ };
1028
1494
  //#endregion
1029
1495
  //#region src/client/helpers.ts
1030
1496
  /**
1031
- * Shared panel helpers: the active-dictionary pick (document-language based,
1032
- * dsh-ssh precedent) bound to the dsh-imagegen interpolator, plus a small
1497
+ * Shared panel helpers: the active-dictionary pick bound to the dsh-imagegen
1498
+ * interpolator, the plugin locale that follows the DSH interface language
1499
+ * (bridged in client/index.ts from ctx.locale — the plugin ships zh / en / ru
1500
+ * and registers itself as a DSH language pack for Русский), plus a small
1033
1501
  * error-message extractor. All copy stays in the locale dictionaries.
1034
1502
  */
1035
- /** Active dictionary, picked by the document language at call time. */
1503
+ const DICTIONARIES = {
1504
+ zh,
1505
+ en,
1506
+ ru
1507
+ };
1508
+ /** The active DSH locale mapped onto our dictionary (module-level, one value per app). */
1509
+ let activeLocale = "zh";
1510
+ /** Bumped on every locale change; useSyncExternalStore version. */
1511
+ let languageVersion = 0;
1512
+ const languageListeners = /* @__PURE__ */ new Set();
1513
+ /**
1514
+ * Adopt the DSH interface language. Unknown ids (future language packs)
1515
+ * resolve to English — the same per-key fallback convention the host locale
1516
+ * chain uses.
1517
+ */
1518
+ function applyHostLocale(id) {
1519
+ const next = id === "zh" || id === "ru" ? id : "en";
1520
+ if (next === activeLocale) return;
1521
+ activeLocale = next;
1522
+ languageVersion += 1;
1523
+ for (const listener of [...languageListeners]) listener();
1524
+ }
1525
+ /** Monotonic version of the active locale (external-store snapshot). */
1526
+ function getImageGenLanguageVersion() {
1527
+ return languageVersion;
1528
+ }
1529
+ /** Observe locale changes; returns the unsubscriber. */
1530
+ function subscribeImageGenLanguage(listener) {
1531
+ languageListeners.add(listener);
1532
+ return () => {
1533
+ languageListeners.delete(listener);
1534
+ };
1535
+ }
1536
+ /** Active dictionary for the current DSH language. */
1036
1537
  function dictionary() {
1037
- return (typeof document !== "undefined" ? document.documentElement.lang : "zh").toLowerCase().startsWith("en") ? { ...en } : { ...zh };
1538
+ return DICTIONARIES[activeLocale];
1038
1539
  }
1039
1540
  /** Translate a key with optional {name} template params (current language). */
1040
1541
  function tt(key, values) {
@@ -1051,73 +1552,84 @@ window.__ModuleLoader__.load({
1051
1552
  }
1052
1553
  //#endregion
1053
1554
  //#region \0dsh-css:E:\dsh-plugin\src\client\templates.module.css.mjs
1054
- const css$3 = ".o0mAxG_overlay,.o0mAxG_overlay *,.o0mAxG_overlay :before,.o0mAxG_overlay :after{box-sizing:border-box}.o0mAxG_overlay{z-index:130;background:var(--dsw-alias-bg-mask-1);color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);justify-content:center;align-items:center;padding:28px;display:flex;position:fixed;inset:0}.o0mAxG_shell{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:14px;flex-direction:column;width:min(1180px,100%);height:100%;max-height:100%;display:flex;overflow:hidden;box-shadow:0 18px 60px #00000047}.o0mAxG_header{flex:none;justify-content:space-between;align-items:center;gap:12px;padding:14px 18px 10px;display:flex}.o0mAxG_heading{align-items:baseline;gap:10px;min-width:0;display:flex}.o0mAxG_title{margin:0;font-size:15px;font-weight:650}.o0mAxG_meta{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px}.o0mAxG_headerActions{flex:none;align-items:center;gap:8px;display:inline-flex}.o0mAxG_close{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;display:inline-flex}.o0mAxG_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.o0mAxG_toolbar{flex-direction:column;flex:none;gap:10px;padding:0 18px 12px;display:flex}.o0mAxG_search{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);width:100%;height:34px;color:var(--dsw-alias-label-primary);border-radius:9px;outline:none;padding:0 12px;font-family:inherit;font-size:13px}.o0mAxG_search:focus{border-color:var(--dsw-alias-brand-primary)}.o0mAxG_search::placeholder{color:var(--dsw-alias-label-dimmed)}.o0mAxG_categoryRow{flex-wrap:wrap;gap:6px;max-height:64px;display:flex;overflow-y:auto}.o0mAxG_categoryPill{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;border-radius:999px;padding:0 11px;font-family:inherit;font-size:12px}.o0mAxG_categoryPill:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.o0mAxG_categoryPill[data-active]{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-base)}.o0mAxG_notice{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);border-radius:9px;flex:none;margin:0 18px 10px;padding:8px 12px;font-size:12px}.o0mAxG_body{flex:1;min-height:0;padding:2px 18px 14px;overflow-y:auto}.o0mAxG_state{height:100%;min-height:220px;color:var(--dsw-alias-label-tertiary);flex-direction:column;justify-content:center;align-items:center;gap:12px;font-size:13px;display:flex}.o0mAxG_spinner{border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-brand-primary);border-radius:50%;width:26px;height:26px;animation:.9s linear infinite o0mAxG_dsh-imagegen-templates-spin}@keyframes o0mAxG_dsh-imagegen-templates-spin{to{transform:rotate(360deg)}}.o0mAxG_grid{grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px;display:grid}.o0mAxG_card{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);cursor:pointer;text-align:left;color:var(--dsw-alias-label-primary);border-radius:11px;flex-direction:column;gap:0;padding:0;font-family:inherit;transition:border-color .15s,transform .15s;display:flex;overflow:hidden}.o0mAxG_card:hover{border-color:var(--dsw-alias-brand-primary);transform:translateY(-1px)}.o0mAxG_thumbWrap{aspect-ratio:1;background:var(--dsw-alias-bg-layer-2);width:100%;display:block;position:relative}.o0mAxG_thumb{object-fit:cover;width:100%;height:100%;display:block}.o0mAxG_thumbPlaceholder{width:100%;height:100%;color:var(--dsw-alias-label-dimmed);justify-content:center;align-items:center;display:flex}.o0mAxG_featuredBadge{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-base);border-radius:999px;padding:2px 8px;font-size:11px;font-weight:600;position:absolute;top:8px;left:8px}.o0mAxG_cardBody{flex-direction:column;gap:4px;min-width:0;padding:9px 11px 10px;display:flex}.o0mAxG_cardTitle{-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12.5px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.o0mAxG_cardMeta{min-width:0;color:var(--dsw-alias-label-tertiary);align-items:center;gap:6px;font-size:11px;display:flex}.o0mAxG_cardCategory{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 7px}.o0mAxG_cardSource{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.o0mAxG_footer{border-top:1px solid var(--dsw-alias-border-l1);flex:none;justify-content:space-between;align-items:center;gap:10px;padding:9px 18px;display:flex}.o0mAxG_attribution{color:var(--dsw-alias-label-dimmed);min-width:0;font-size:11.5px}.o0mAxG_sourceLink{color:var(--dsw-alias-brand-primary);white-space:nowrap;flex:none;font-size:11.5px;font-weight:600;text-decoration:none}.o0mAxG_sourceLink:hover{text-decoration:underline}.o0mAxG_detailOverlay{background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;padding:34px;display:flex;position:absolute;inset:0}.o0mAxG_detail{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:14px;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,5fr) minmax(0,4fr);gap:0;width:min(980px,100%);max-height:100%;display:grid;overflow:hidden;box-shadow:0 18px 60px #00000052}.o0mAxG_detailMedia{background:var(--dsw-alias-bg-layer-2);justify-content:center;align-items:center;min-height:0;display:flex;overflow:hidden}.o0mAxG_detailImage{object-fit:contain;width:100%;height:100%;display:block}.o0mAxG_detailInfo{flex-direction:column;gap:10px;min-height:0;padding:18px;display:flex;overflow-y:auto}.o0mAxG_detailTitle{margin:0;font-size:15px;font-weight:650;line-height:1.4}.o0mAxG_detailMeta{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;display:flex}.o0mAxG_detailLink{color:var(--dsw-alias-brand-primary);text-overflow:ellipsis;white-space:nowrap;text-decoration:none;overflow:hidden}.o0mAxG_detailLink:hover{text-decoration:underline}.o0mAxG_detailPrompt{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);min-height:0;color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;user-select:text;border-radius:10px;flex:1;margin:0;padding:12px;font-family:inherit;font-size:12.5px;line-height:1.6;overflow-y:auto}.o0mAxG_detailActions{background:var(--dsw-alias-bg-base);border-top:1px solid var(--dsw-alias-border-l1);flex:none;gap:8px;margin:0 -18px -18px;padding:12px 18px;display:flex;position:sticky;bottom:0}@media (width<=760px){.o0mAxG_detail{grid-template-rows:minmax(0,3fr) minmax(0,4fr);grid-template-columns:1fr}}";
1055
- const tagId$3 = "@dickpy/dsh-imagegen/templates.module.css";
1056
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1555
+ const css$4 = ".o0mAxG_overlay,.o0mAxG_overlay *,.o0mAxG_overlay :before,.o0mAxG_overlay :after{box-sizing:border-box}.o0mAxG_overlay{z-index:130;background:var(--dsw-alias-bg-mask-1);color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);justify-content:center;align-items:center;padding:28px;display:flex;position:fixed;inset:0}.o0mAxG_shell{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:14px;flex-direction:column;width:min(1180px,100%);height:100%;max-height:100%;display:flex;overflow:hidden;box-shadow:0 18px 60px #00000047}.o0mAxG_header{flex:none;justify-content:space-between;align-items:center;gap:12px;padding:14px 18px 10px;display:flex}.o0mAxG_heading{align-items:baseline;gap:10px;min-width:0;display:flex}.o0mAxG_title{margin:0;font-size:15px;font-weight:650}.o0mAxG_meta{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px}.o0mAxG_headerActions{flex:none;align-items:center;gap:8px;display:inline-flex}.o0mAxG_close{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;display:inline-flex}.o0mAxG_close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.o0mAxG_sourceTabs{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;align-items:center;gap:6px;padding:0 18px 10px;display:flex}.o0mAxG_sourceTab{height:30px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:1px solid #0000;border-bottom:none;border-radius:9px 9px 0 0;align-items:center;gap:6px;padding:0 14px;font-family:inherit;font-size:13px;font-weight:600;display:inline-flex}.o0mAxG_sourceTab:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.o0mAxG_sourceTab[data-active]{border-color:var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-brand-primary);border-radius:9px}.o0mAxG_sourceTabCount{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:0 6px;font-size:11px;font-weight:500}.o0mAxG_sourceTab[data-active] .o0mAxG_sourceTabCount{color:var(--dsw-alias-label-secondary)}.o0mAxG_toolbar{flex-direction:column;flex:none;gap:10px;padding:0 18px 12px;display:flex}.o0mAxG_search{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);width:100%;height:34px;color:var(--dsw-alias-label-primary);border-radius:9px;outline:none;padding:0 12px;font-family:inherit;font-size:13px}.o0mAxG_search:focus{border-color:var(--dsw-alias-brand-primary)}.o0mAxG_search::placeholder{color:var(--dsw-alias-label-dimmed)}.o0mAxG_categoryRow{flex-wrap:wrap;gap:6px;max-height:64px;display:flex;overflow-y:auto}.o0mAxG_categoryPill{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;border-radius:999px;padding:0 11px;font-family:inherit;font-size:12px}.o0mAxG_categoryPill:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.o0mAxG_categoryPill[data-active]{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-base)}.o0mAxG_notice{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);border-radius:9px;flex:none;margin:0 18px 10px;padding:8px 12px;font-size:12px}.o0mAxG_body{flex:1;min-height:0;padding:2px 18px 14px;overflow-y:auto}.o0mAxG_state{height:100%;min-height:220px;color:var(--dsw-alias-label-tertiary);flex-direction:column;justify-content:center;align-items:center;gap:12px;font-size:13px;display:flex}.o0mAxG_spinner{border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-brand-primary);border-radius:50%;width:26px;height:26px;animation:.9s linear infinite o0mAxG_dsh-imagegen-templates-spin}@keyframes o0mAxG_dsh-imagegen-templates-spin{to{transform:rotate(360deg)}}.o0mAxG_grid{grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px;display:grid}.o0mAxG_card{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);cursor:pointer;text-align:left;color:var(--dsw-alias-label-primary);border-radius:11px;flex-direction:column;gap:0;padding:0;font-family:inherit;transition:border-color .15s,transform .15s;display:flex;overflow:hidden}.o0mAxG_card:hover{border-color:var(--dsw-alias-brand-primary);transform:translateY(-1px)}.o0mAxG_thumbWrap{aspect-ratio:1;background:var(--dsw-alias-bg-layer-2);width:100%;display:block;position:relative}.o0mAxG_thumb{object-fit:cover;width:100%;height:100%;display:block}.o0mAxG_thumbPlaceholder{width:100%;height:100%;color:var(--dsw-alias-label-dimmed);justify-content:center;align-items:center;display:flex}.o0mAxG_featuredBadge{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-base);border-radius:999px;padding:2px 8px;font-size:11px;font-weight:600;position:absolute;top:8px;left:8px}.o0mAxG_favStar{color:#ffffffd9;cursor:pointer;opacity:0;background:#00000059;border:none;border-radius:8px;justify-content:center;align-items:center;width:26px;height:26px;padding:0;transition:opacity .15s;display:inline-flex;position:absolute;top:6px;right:6px}.o0mAxG_card:hover .o0mAxG_favStar,.o0mAxG_favStar:focus-visible,.o0mAxG_favStar[data-active]{opacity:1}.o0mAxG_favStar:hover{color:#fff;background:#0000008c}.o0mAxG_favStar[data-active]{color:#ffb020}@media (hover:none){.o0mAxG_favStar{opacity:1}}.o0mAxG_cardBody{flex-direction:column;gap:4px;min-width:0;padding:9px 11px 10px;display:flex}.o0mAxG_cardTitle{-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12.5px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.o0mAxG_cardMeta{min-width:0;color:var(--dsw-alias-label-tertiary);align-items:center;gap:6px;font-size:11px;display:flex}.o0mAxG_cardCategory{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 7px}.o0mAxG_cardSource{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.o0mAxG_footer{border-top:1px solid var(--dsw-alias-border-l1);flex:none;justify-content:space-between;align-items:center;gap:10px;padding:9px 18px;display:flex}.o0mAxG_attribution{color:var(--dsw-alias-label-dimmed);min-width:0;font-size:11.5px}.o0mAxG_sourceLink{color:var(--dsw-alias-brand-primary);white-space:nowrap;flex:none;font-size:11.5px;font-weight:600;text-decoration:none}.o0mAxG_sourceLink:hover{text-decoration:underline}.o0mAxG_detailOverlay{background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;padding:34px;display:flex;position:absolute;inset:0}.o0mAxG_detail{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:14px;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,5fr) minmax(0,4fr);gap:0;width:min(980px,100%);max-height:100%;display:grid;overflow:hidden;box-shadow:0 18px 60px #00000052}.o0mAxG_detailMedia{background:var(--dsw-alias-bg-layer-2);justify-content:center;align-items:center;min-height:0;display:flex;overflow:hidden}.o0mAxG_detailImage{object-fit:contain;width:100%;height:100%;display:block}.o0mAxG_detailInfo{flex-direction:column;gap:10px;min-height:0;padding:18px;display:flex;overflow-y:auto}.o0mAxG_detailTitle{margin:0;font-size:15px;font-weight:650;line-height:1.4}.o0mAxG_detailMeta{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;display:flex}.o0mAxG_detailLink{color:var(--dsw-alias-brand-primary);text-overflow:ellipsis;white-space:nowrap;text-decoration:none;overflow:hidden}.o0mAxG_detailLink:hover{text-decoration:underline}.o0mAxG_detailPrompt{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);min-height:0;color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;user-select:text;border-radius:10px;flex:1;margin:0;padding:12px;font-family:inherit;font-size:12.5px;line-height:1.6;overflow-y:auto}.o0mAxG_detailActions{background:var(--dsw-alias-bg-base);border-top:1px solid var(--dsw-alias-border-l1);flex:none;gap:8px;margin:0 -18px -18px;padding:12px 18px;display:flex;position:sticky;bottom:0}@media (width<=760px){.o0mAxG_detail{grid-template-rows:minmax(0,3fr) minmax(0,4fr);grid-template-columns:1fr}}";
1556
+ const tagId$4 = "@dickpy/dsh-imagegen/templates.module.css";
1557
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
1057
1558
  const tag = document.createElement("style");
1058
1559
  tag.dataset.plugin = "@dickpy/dsh-imagegen";
1059
- tag.dataset.pluginCss = tagId$3;
1060
- tag.textContent = css$3;
1560
+ tag.dataset.pluginCss = tagId$4;
1561
+ tag.textContent = css$4;
1061
1562
  document.head.appendChild(tag);
1062
1563
  }
1063
1564
  var templates_module_css_default = {
1064
- "categoryRow": "o0mAxG_categoryRow",
1065
- "headerActions": "o0mAxG_headerActions",
1066
- "state": "o0mAxG_state",
1067
- "detailLink": "o0mAxG_detailLink",
1068
- "toolbar": "o0mAxG_toolbar",
1069
- "card": "o0mAxG_card",
1070
- "cardCategory": "o0mAxG_cardCategory",
1071
- "categoryPill": "o0mAxG_categoryPill",
1072
- "thumbPlaceholder": "o0mAxG_thumbPlaceholder",
1565
+ "detailActions": "o0mAxG_detailActions",
1073
1566
  "spinner": "o0mAxG_spinner",
1074
1567
  "thumbWrap": "o0mAxG_thumbWrap",
1075
- "sourceLink": "o0mAxG_sourceLink",
1076
- "featuredBadge": "o0mAxG_featuredBadge",
1077
- "footer": "o0mAxG_footer",
1568
+ "cardCategory": "o0mAxG_cardCategory",
1078
1569
  "search": "o0mAxG_search",
1079
- "cardBody": "o0mAxG_cardBody",
1080
- "cardSource": "o0mAxG_cardSource",
1570
+ "favStar": "o0mAxG_favStar",
1081
1571
  "detailOverlay": "o0mAxG_detailOverlay",
1082
- "detailInfo": "o0mAxG_detailInfo",
1083
- "detailTitle": "o0mAxG_detailTitle",
1084
- "detailActions": "o0mAxG_detailActions",
1085
- "body": "o0mAxG_body",
1086
- "attribution": "o0mAxG_attribution",
1087
- "cardMeta": "o0mAxG_cardMeta",
1088
- "heading": "o0mAxG_heading",
1089
- "thumb": "o0mAxG_thumb",
1572
+ "close": "o0mAxG_close",
1573
+ "featuredBadge": "o0mAxG_featuredBadge",
1090
1574
  "detailPrompt": "o0mAxG_detailPrompt",
1091
- "grid": "o0mAxG_grid",
1092
- "shell": "o0mAxG_shell",
1575
+ "cardMeta": "o0mAxG_cardMeta",
1576
+ "detailMeta": "o0mAxG_detailMeta",
1577
+ "card": "o0mAxG_card",
1578
+ "dsh-imagegen-templates-spin": "o0mAxG_dsh-imagegen-templates-spin",
1579
+ "toolbar": "o0mAxG_toolbar",
1580
+ "categoryRow": "o0mAxG_categoryRow",
1581
+ "categoryPill": "o0mAxG_categoryPill",
1093
1582
  "overlay": "o0mAxG_overlay",
1094
- "header": "o0mAxG_header",
1095
1583
  "meta": "o0mAxG_meta",
1096
- "close": "o0mAxG_close",
1584
+ "detailMedia": "o0mAxG_detailMedia",
1585
+ "body": "o0mAxG_body",
1586
+ "detailImage": "o0mAxG_detailImage",
1587
+ "header": "o0mAxG_header",
1588
+ "detailLink": "o0mAxG_detailLink",
1589
+ "heading": "o0mAxG_heading",
1590
+ "headerActions": "o0mAxG_headerActions",
1591
+ "sourceTabs": "o0mAxG_sourceTabs",
1592
+ "sourceTab": "o0mAxG_sourceTab",
1593
+ "state": "o0mAxG_state",
1594
+ "shell": "o0mAxG_shell",
1595
+ "thumb": "o0mAxG_thumb",
1596
+ "detailInfo": "o0mAxG_detailInfo",
1097
1597
  "cardTitle": "o0mAxG_cardTitle",
1598
+ "sourceTabCount": "o0mAxG_sourceTabCount",
1599
+ "grid": "o0mAxG_grid",
1600
+ "thumbPlaceholder": "o0mAxG_thumbPlaceholder",
1601
+ "sourceLink": "o0mAxG_sourceLink",
1098
1602
  "notice": "o0mAxG_notice",
1099
- "detailImage": "o0mAxG_detailImage",
1100
- "title": "o0mAxG_title",
1101
- "dsh-imagegen-templates-spin": "o0mAxG_dsh-imagegen-templates-spin",
1102
- "detailMedia": "o0mAxG_detailMedia",
1603
+ "cardBody": "o0mAxG_cardBody",
1604
+ "attribution": "o0mAxG_attribution",
1103
1605
  "detail": "o0mAxG_detail",
1104
- "detailMeta": "o0mAxG_detailMeta"
1606
+ "detailTitle": "o0mAxG_detailTitle",
1607
+ "title": "o0mAxG_title",
1608
+ "cardSource": "o0mAxG_cardSource",
1609
+ "footer": "o0mAxG_footer"
1105
1610
  };
1106
1611
  //#endregion
1107
1612
  //#region src/client/TemplateLibrary.tsx
1108
1613
  /**
1109
- * Prompt-template library overlay: a searchable, category-filtered gallery of
1110
- * the bundled awesome-gpt-image-2 cases. The case list is served by the host
1111
- * (bundled snapshot, optionally refreshed online); reference images load
1112
- * lazily through the host's caching proxy, so browsing progressively mirrors
1113
- * the gallery onto the local disk. Picking a template hands its prompt back
1114
- * to the studio form.
1614
+ * Prompt-template library overlay: a multi-source, searchable, category-
1615
+ * filtered gallery. Each registered source (TEMPLATE_SOURCES) renders as its
1616
+ * own tab with an independent list, refresh state, and image pool; case lists
1617
+ * are served by the host (bundled snapshot, optionally refreshed online or
1618
+ * auto-synced in the background) and reference images load lazily through the
1619
+ * host's caching proxy, so browsing progressively mirrors the gallery onto the
1620
+ * local disk. Templates can be starred; favorites persist host-side as full
1621
+ * case snapshots and are reachable through the ★ filter pill per tab. Picking
1622
+ * a template hands its prompt back to the studio form.
1115
1623
  */
1116
1624
  /** Concurrent image downloads while caching the whole gallery offline. */
1117
1625
  const CACHE_ALL_CONCURRENCY = 4;
1626
+ /** Stable favorites key of one case within a source. */
1627
+ function favoriteKeyOf(sourceId, item) {
1628
+ return `${sourceId}:${item.id}`;
1629
+ }
1118
1630
  /** Same-origin URL of one case's reference image (host caching proxy). */
1119
- function imageUrlOf(item) {
1120
- return `${TEMPLATES_API.image}/${encodeURIComponent(item.image)}`;
1631
+ function imageUrlOf$1(sourceId, item) {
1632
+ return `${TEMPLATES_API.image}/${encodeURIComponent(sourceId)}/${encodeURIComponent(item.image)}`;
1121
1633
  }
1122
1634
  /** A card thumbnail that falls back to a placeholder when the proxy 404s. */
1123
1635
  function TemplateThumb(props) {
@@ -1153,7 +1665,7 @@ window.__ModuleLoader__.load({
1153
1665
  });
1154
1666
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
1155
1667
  className: templates_module_css_default.thumb,
1156
- src: imageUrlOf(props.item),
1668
+ src: imageUrlOf$1(props.sourceId, props.item),
1157
1669
  alt: props.item.title,
1158
1670
  loading: "lazy",
1159
1671
  onError: () => {
@@ -1161,13 +1673,50 @@ window.__ModuleLoader__.load({
1161
1673
  }
1162
1674
  });
1163
1675
  }
1676
+ /** Card-corner star toggle; the click must not open the detail view. Rendered
1677
+ * as a span (a button cannot nest inside the card button). */
1678
+ function FavoriteStar(props) {
1679
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1680
+ role: "button",
1681
+ tabIndex: 0,
1682
+ className: templates_module_css_default.favStar,
1683
+ "data-active": props.active ? "" : void 0,
1684
+ "aria-label": props.title,
1685
+ title: props.title,
1686
+ onClick: (event) => {
1687
+ event.stopPropagation();
1688
+ props.onToggle();
1689
+ },
1690
+ onKeyDown: (event) => {
1691
+ if (event.key !== "Enter" && event.key !== " ") return;
1692
+ event.stopPropagation();
1693
+ event.preventDefault();
1694
+ props.onToggle();
1695
+ },
1696
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1697
+ viewBox: "0 0 24 24",
1698
+ width: "15",
1699
+ height: "15",
1700
+ fill: props.active ? "currentColor" : "none",
1701
+ stroke: "currentColor",
1702
+ strokeWidth: "1.6",
1703
+ strokeLinecap: "round",
1704
+ strokeLinejoin: "round",
1705
+ "aria-hidden": "true",
1706
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 3.6l2.6 5.3 5.8.8-4.2 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8L3.6 9.7l5.8-.8z" })
1707
+ })
1708
+ });
1709
+ }
1164
1710
  /** The template-library modal. Rendered through a portal above the studio. */
1165
1711
  function TemplateLibrary(props) {
1166
1712
  const { api, onUse, onClose } = props;
1167
- const [list, setList] = (0, react.useState)(null);
1168
- const [loadError, setLoadError] = (0, react.useState)(null);
1713
+ const [activeSource, setActiveSource] = (0, react.useState)(TEMPLATE_SOURCES[0].id);
1714
+ const [lists, setLists] = (0, react.useState)({});
1715
+ const [loadErrors, setLoadErrors] = (0, react.useState)({});
1716
+ const [favorites, setFavorites] = (0, react.useState)([]);
1169
1717
  const [query, setQuery] = (0, react.useState)("");
1170
1718
  const [category, setCategory] = (0, react.useState)("");
1719
+ const [favoritesOnly, setFavoritesOnly] = (0, react.useState)(false);
1171
1720
  const [selected, setSelected] = (0, react.useState)(null);
1172
1721
  const [copied, setCopied] = (0, react.useState)(false);
1173
1722
  const [refreshing, setRefreshing] = (0, react.useState)(false);
@@ -1178,20 +1727,42 @@ window.__ModuleLoader__.load({
1178
1727
  total: 0
1179
1728
  });
1180
1729
  const searchRef = (0, react.useRef)(null);
1181
- const load = () => {
1182
- api.templatesList().then((result) => {
1183
- setList(result);
1184
- setLoadError(null);
1730
+ const list = lists[activeSource];
1731
+ const loadError = loadErrors[activeSource] || null;
1732
+ /** Fetch one source's list into the per-source cache. */
1733
+ const loadSource = (sourceId) => {
1734
+ api.templatesList(sourceId).then((result) => {
1735
+ setLists((current) => ({
1736
+ ...current,
1737
+ [sourceId]: result
1738
+ }));
1739
+ setLoadErrors((current) => ({
1740
+ ...current,
1741
+ [sourceId]: ""
1742
+ }));
1185
1743
  }).catch((caught) => {
1186
- setLoadError(errorMessage(caught));
1744
+ setLoadErrors((current) => ({
1745
+ ...current,
1746
+ [sourceId]: errorMessage(caught)
1747
+ }));
1187
1748
  });
1188
1749
  };
1189
1750
  (0, react.useEffect)(() => {
1190
- load();
1751
+ loadSource(TEMPLATE_SOURCES[0].id);
1752
+ api.favoritesList().then(setFavorites).catch(() => {});
1191
1753
  searchRef.current?.focus();
1192
1754
  }, []);
1755
+ const switchSource = (sourceId) => {
1756
+ if (sourceId === activeSource) return;
1757
+ setActiveSource(sourceId);
1758
+ setCategory("");
1759
+ setFavoritesOnly(false);
1760
+ setSelected(null);
1761
+ setNotice(null);
1762
+ if (lists[sourceId] === void 0) loadSource(sourceId);
1763
+ };
1193
1764
  const categories = (0, react.useMemo)(() => {
1194
- if (list === null) return [];
1765
+ if (list === void 0) return [];
1195
1766
  const counts = /* @__PURE__ */ new Map();
1196
1767
  for (const item of list.cases) {
1197
1768
  const entry = counts.get(item.category) ?? {
@@ -1207,16 +1778,21 @@ window.__ModuleLoader__.load({
1207
1778
  count: value.count
1208
1779
  }));
1209
1780
  }, [list]);
1781
+ /** Favorites of the active source, as standalone case snapshots. */
1782
+ const activeFavorites = (0, react.useMemo)(() => favorites.filter((entry) => entry.sourceId === activeSource).map((entry) => entry.case), [favorites, activeSource]);
1783
+ const favKeys = (0, react.useMemo)(() => new Set(favorites.map((entry) => entry.key)), [favorites]);
1210
1784
  const filtered = (0, react.useMemo)(() => {
1211
- if (list === null) return [];
1785
+ const pool = favoritesOnly ? activeFavorites : list?.cases ?? [];
1212
1786
  const needle = query.trim().toLowerCase();
1213
- return list.cases.filter((item) => {
1787
+ return pool.filter((item) => {
1214
1788
  if (category !== "" && item.category !== category) return false;
1215
1789
  if (needle === "") return true;
1216
1790
  return item.title.toLowerCase().includes(needle) || item.prompt.toLowerCase().includes(needle) || item.sourceLabel.toLowerCase().includes(needle);
1217
1791
  });
1218
1792
  }, [
1219
1793
  list,
1794
+ activeFavorites,
1795
+ favoritesOnly,
1220
1796
  query,
1221
1797
  category
1222
1798
  ]);
@@ -1235,10 +1811,12 @@ window.__ModuleLoader__.load({
1235
1811
  setRefreshing(true);
1236
1812
  setNotice(null);
1237
1813
  try {
1238
- const result = await api.templatesRefresh();
1239
- const reloaded = await api.templatesList();
1240
- setList(reloaded);
1241
- setLoadError(null);
1814
+ const result = await api.templatesRefresh(activeSource);
1815
+ const reloaded = await api.templatesList(activeSource);
1816
+ setLists((current) => ({
1817
+ ...current,
1818
+ [activeSource]: reloaded
1819
+ }));
1242
1820
  setNotice(tt("templates.refreshed", { count: result.total }));
1243
1821
  } catch (caught) {
1244
1822
  setNotice(tt("templates.refreshFailed", { error: errorMessage(caught) }));
@@ -1246,9 +1824,14 @@ window.__ModuleLoader__.load({
1246
1824
  setRefreshing(false);
1247
1825
  }
1248
1826
  };
1249
- /** Mirror every reference image through the host cache (offline browsing). */
1827
+ /** Star / unstar one template of the active source. */
1828
+ const toggleFavorite = (item) => {
1829
+ const key = favoriteKeyOf(activeSource, item);
1830
+ (favKeys.has(key) ? api.favoritesRemove(key) : api.favoritesAdd(activeSource, item)).then(setFavorites).catch(() => {});
1831
+ };
1832
+ /** Mirror every reference image of the active source through the host cache. */
1250
1833
  const cacheAllImages = async () => {
1251
- if (cacheAll.running || list === null) return;
1834
+ if (cacheAll.running || list === void 0) return;
1252
1835
  const files = [...new Set(list.cases.map((item) => item.image).filter((name) => name !== ""))];
1253
1836
  setCacheAll({
1254
1837
  running: true,
@@ -1261,7 +1844,7 @@ window.__ModuleLoader__.load({
1261
1844
  const file = files[index];
1262
1845
  index += 1;
1263
1846
  try {
1264
- await fetch(`${TEMPLATES_API.image}/${encodeURIComponent(file)}`);
1847
+ await fetch(`${TEMPLATES_API.image}/${encodeURIComponent(activeSource)}/${encodeURIComponent(file)}`);
1265
1848
  } catch {}
1266
1849
  setCacheAll((current) => ({
1267
1850
  ...current,
@@ -1298,7 +1881,8 @@ window.__ModuleLoader__.load({
1298
1881
  setCopied(false);
1299
1882
  }
1300
1883
  };
1301
- const originLabel = list === null ? "" : tt(list.origin === "refreshed" ? "templates.origin.refreshed" : "templates.origin.bundled");
1884
+ const originLabel = list === void 0 ? "" : tt(list.origin === "refreshed" ? "templates.origin.refreshed" : "templates.origin.bundled");
1885
+ const activeMeta = TEMPLATE_SOURCES.find((source) => source.id === activeSource);
1302
1886
  return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1303
1887
  className: templates_module_css_default.overlay,
1304
1888
  role: "dialog",
@@ -1318,7 +1902,7 @@ window.__ModuleLoader__.load({
1318
1902
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1319
1903
  className: templates_module_css_default.title,
1320
1904
  children: tt("templates.title")
1321
- }), list !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1905
+ }), list !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1322
1906
  className: templates_module_css_default.meta,
1323
1907
  children: tt("templates.meta", {
1324
1908
  count: list.total,
@@ -1340,7 +1924,7 @@ window.__ModuleLoader__.load({
1340
1924
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1341
1925
  variant: "outline",
1342
1926
  size: "sm",
1343
- disabled: list === null || cacheAll.running,
1927
+ disabled: list === void 0 || cacheAll.running,
1344
1928
  title: tt("templates.cacheAllHint"),
1345
1929
  onClick: () => {
1346
1930
  cacheAllImages();
@@ -1371,6 +1955,26 @@ window.__ModuleLoader__.load({
1371
1955
  ]
1372
1956
  })]
1373
1957
  }),
1958
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1959
+ className: templates_module_css_default.sourceTabs,
1960
+ role: "tablist",
1961
+ "aria-label": tt("templates.sources"),
1962
+ children: TEMPLATE_SOURCES.map((source) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1963
+ type: "button",
1964
+ role: "tab",
1965
+ "aria-selected": source.id === activeSource,
1966
+ className: templates_module_css_default.sourceTab,
1967
+ "data-active": source.id === activeSource ? "" : void 0,
1968
+ title: source.description,
1969
+ onClick: () => {
1970
+ switchSource(source.id);
1971
+ },
1972
+ children: [source.label, lists[source.id] !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1973
+ className: templates_module_css_default.sourceTabCount,
1974
+ children: lists[source.id].total
1975
+ }) : null]
1976
+ }, source.id))
1977
+ }),
1374
1978
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1375
1979
  className: templates_module_css_default.toolbar,
1376
1980
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -1384,27 +1988,46 @@ window.__ModuleLoader__.load({
1384
1988
  }
1385
1989
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1386
1990
  className: templates_module_css_default.categoryRow,
1387
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1388
- type: "button",
1389
- className: templates_module_css_default.categoryPill,
1390
- "data-active": category === "" ? "" : void 0,
1391
- onClick: () => {
1392
- setCategory("");
1393
- },
1394
- children: [tt("templates.all"), list !== null ? ` ${list.total}` : ""]
1395
- }), categories.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1396
- type: "button",
1397
- className: templates_module_css_default.categoryPill,
1398
- "data-active": category === entry.key ? "" : void 0,
1399
- onClick: () => {
1400
- setCategory(entry.key);
1401
- },
1402
- children: [
1403
- entry.label,
1404
- " ",
1405
- entry.count
1406
- ]
1407
- }, entry.key))]
1991
+ children: [
1992
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1993
+ type: "button",
1994
+ className: templates_module_css_default.categoryPill,
1995
+ "data-active": favoritesOnly ? "" : void 0,
1996
+ title: tt("templates.favoritesHint"),
1997
+ onClick: () => {
1998
+ setFavoritesOnly((value) => !value);
1999
+ },
2000
+ children: [
2001
+ "★ ",
2002
+ tt("templates.favorites"),
2003
+ activeFavorites.length > 0 ? ` ${activeFavorites.length}` : ""
2004
+ ]
2005
+ }),
2006
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2007
+ type: "button",
2008
+ className: templates_module_css_default.categoryPill,
2009
+ "data-active": !favoritesOnly && category === "" ? "" : void 0,
2010
+ onClick: () => {
2011
+ setFavoritesOnly(false);
2012
+ setCategory("");
2013
+ },
2014
+ children: [tt("templates.all"), list !== void 0 ? ` ${list.total}` : ""]
2015
+ }),
2016
+ categories.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2017
+ type: "button",
2018
+ className: templates_module_css_default.categoryPill,
2019
+ "data-active": !favoritesOnly && category === entry.key ? "" : void 0,
2020
+ onClick: () => {
2021
+ setFavoritesOnly(false);
2022
+ setCategory(entry.key);
2023
+ },
2024
+ children: [
2025
+ entry.label,
2026
+ " ",
2027
+ entry.count
2028
+ ]
2029
+ }, entry.key))
2030
+ ]
1408
2031
  })]
1409
2032
  }),
1410
2033
  notice !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1415,7 +2038,7 @@ window.__ModuleLoader__.load({
1415
2038
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1416
2039
  className: templates_module_css_default.body,
1417
2040
  children: [
1418
- list === null && loadError === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2041
+ list === void 0 && loadError === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1419
2042
  className: templates_module_css_default.state,
1420
2043
  role: "status",
1421
2044
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: templates_module_css_default.spinner }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: tt("templates.loading") })]
@@ -1427,18 +2050,20 @@ window.__ModuleLoader__.load({
1427
2050
  variant: "outline",
1428
2051
  size: "sm",
1429
2052
  onClick: () => {
1430
- setLoadError(null);
1431
- setList(null);
1432
- load();
2053
+ setLoadErrors((current) => ({
2054
+ ...current,
2055
+ [activeSource]: ""
2056
+ }));
2057
+ loadSource(activeSource);
1433
2058
  },
1434
2059
  children: tt("templates.retry")
1435
2060
  })]
1436
2061
  }) : null,
1437
- list !== null && filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2062
+ loadError === null && (list !== void 0 || favoritesOnly) && filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1438
2063
  className: templates_module_css_default.state,
1439
- children: tt("templates.empty")
2064
+ children: favoritesOnly && activeFavorites.length === 0 ? tt("templates.favoritesEmpty") : tt("templates.empty")
1440
2065
  }) : null,
1441
- list !== null && filtered.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2066
+ filtered.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1442
2067
  className: templates_module_css_default.grid,
1443
2068
  children: filtered.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1444
2069
  type: "button",
@@ -1449,10 +2074,23 @@ window.__ModuleLoader__.load({
1449
2074
  },
1450
2075
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1451
2076
  className: templates_module_css_default.thumbWrap,
1452
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TemplateThumb, { item }), item.featured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1453
- className: templates_module_css_default.featuredBadge,
1454
- children: tt("templates.featured")
1455
- }) : null]
2077
+ children: [
2078
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TemplateThumb, {
2079
+ sourceId: activeSource,
2080
+ item
2081
+ }),
2082
+ item.featured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2083
+ className: templates_module_css_default.featuredBadge,
2084
+ children: tt("templates.featured")
2085
+ }) : null,
2086
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FavoriteStar, {
2087
+ active: favKeys.has(favoriteKeyOf(activeSource, item)),
2088
+ title: favKeys.has(favoriteKeyOf(activeSource, item)) ? tt("templates.favoriteRemove") : tt("templates.favoriteAdd"),
2089
+ onToggle: () => {
2090
+ toggleFavorite(item);
2091
+ }
2092
+ })
2093
+ ]
1456
2094
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1457
2095
  className: templates_module_css_default.cardBody,
1458
2096
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -1469,7 +2107,7 @@ window.__ModuleLoader__.load({
1469
2107
  }) : null]
1470
2108
  })]
1471
2109
  })]
1472
- }, item.id))
2110
+ }, `${activeSource}:${item.id}`))
1473
2111
  }) : null
1474
2112
  ]
1475
2113
  }),
@@ -1480,10 +2118,10 @@ window.__ModuleLoader__.load({
1480
2118
  children: tt("templates.attribution")
1481
2119
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1482
2120
  className: templates_module_css_default.sourceLink,
1483
- href: "https://vibeui.top/",
2121
+ href: activeMeta.homepage,
1484
2122
  target: "_blank",
1485
2123
  rel: "noreferrer",
1486
- children: tt("templates.source")
2124
+ children: tt("templates.source", { label: activeMeta.label })
1487
2125
  })]
1488
2126
  })
1489
2127
  ]
@@ -1501,7 +2139,7 @@ window.__ModuleLoader__.load({
1501
2139
  className: templates_module_css_default.detailMedia,
1502
2140
  children: selected.image !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
1503
2141
  className: templates_module_css_default.detailImage,
1504
- src: imageUrlOf(selected),
2142
+ src: imageUrlOf$1(activeSource, selected),
1505
2143
  alt: selected.title
1506
2144
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1507
2145
  className: templates_module_css_default.thumbPlaceholder,
@@ -1560,6 +2198,14 @@ window.__ModuleLoader__.load({
1560
2198
  },
1561
2199
  children: copied ? tt("templates.copied") : tt("templates.copy")
1562
2200
  }),
2201
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2202
+ variant: "outline",
2203
+ size: "md",
2204
+ onClick: () => {
2205
+ toggleFavorite(selected);
2206
+ },
2207
+ children: favKeys.has(favoriteKeyOf(activeSource, selected)) ? tt("templates.unfavorite") : tt("templates.favorite")
2208
+ }),
1563
2209
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1564
2210
  variant: "outline",
1565
2211
  size: "md",
@@ -1577,6 +2223,188 @@ window.__ModuleLoader__.load({
1577
2223
  }), document.body);
1578
2224
  }
1579
2225
  //#endregion
2226
+ //#region \0dsh-css:E:\dsh-plugin\src\client\inspiration.module.css.mjs
2227
+ const css$3 = ".IgRnJG_wrap{width:min(880px,100%);font-family:var(--dsw-font-family);flex-direction:column;align-items:center;gap:16px;margin:auto;padding:24px;display:flex}.IgRnJG_title{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:650}.IgRnJG_emptyIcon{color:var(--dsw-alias-label-dimmed);display:inline-flex}.IgRnJG_emptyTitle{color:var(--dsw-alias-label-primary);margin-top:-6px;font-size:15px;font-weight:650}.IgRnJG_emptyHint{color:var(--dsw-alias-label-tertiary);font-size:12.5px}.IgRnJG_grid{grid-template-columns:repeat(4,1fr);gap:12px;width:100%;display:grid}@media (width<=900px){.IgRnJG_grid{grid-template-columns:repeat(3,1fr)}}@media (width<=560px){.IgRnJG_grid{grid-template-columns:repeat(2,1fr)}}.IgRnJG_tile{aspect-ratio:1;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);cursor:pointer;border-radius:12px;width:100%;padding:0;transition:transform .15s,border-color .15s,box-shadow .15s;display:block;position:relative;overflow:hidden}.IgRnJG_tile:hover{border-color:var(--dsw-alias-brand-primary);transform:translateY(-2px);box-shadow:0 6px 18px #0000001f}.IgRnJG_thumbWrap{display:block;position:absolute;inset:0}.IgRnJG_thumb{object-fit:cover;width:100%;height:100%;display:block}.IgRnJG_thumbFallback{background:var(--dsw-alias-bg-layer-2);width:100%;height:100%;color:var(--dsw-alias-label-tertiary);text-align:center;justify-content:center;align-items:center;padding:6px;font-size:11px;line-height:1.4;display:flex;overflow:hidden}.IgRnJG_thumbTitle{text-overflow:ellipsis;white-space:nowrap;color:#fff;text-align:center;opacity:0;pointer-events:none;background:linear-gradient(#0000,#0000008c);padding:14px 8px 6px;font-size:10.5px;transition:opacity .15s;position:absolute;inset:auto 0 0;overflow:hidden}.IgRnJG_tile:hover .IgRnJG_thumbTitle{opacity:1}.IgRnJG_spinner{border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-brand-primary);border-radius:50%;width:20px;height:20px;animation:.9s linear infinite IgRnJG_dsh-imagegen-inspiration-spin}@keyframes IgRnJG_dsh-imagegen-inspiration-spin{to{transform:rotate(360deg)}}@media (hover:none){.IgRnJG_thumbTitle{opacity:1}}";
2228
+ const tagId$3 = "@dickpy/dsh-imagegen/inspiration.module.css";
2229
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
2230
+ const tag = document.createElement("style");
2231
+ tag.dataset.plugin = "@dickpy/dsh-imagegen";
2232
+ tag.dataset.pluginCss = tagId$3;
2233
+ tag.textContent = css$3;
2234
+ document.head.appendChild(tag);
2235
+ }
2236
+ var inspiration_module_css_default = {
2237
+ "emptyIcon": "IgRnJG_emptyIcon",
2238
+ "wrap": "IgRnJG_wrap",
2239
+ "spinner": "IgRnJG_spinner",
2240
+ "thumbWrap": "IgRnJG_thumbWrap",
2241
+ "grid": "IgRnJG_grid",
2242
+ "thumb": "IgRnJG_thumb",
2243
+ "title": "IgRnJG_title",
2244
+ "emptyTitle": "IgRnJG_emptyTitle",
2245
+ "emptyHint": "IgRnJG_emptyHint",
2246
+ "tile": "IgRnJG_tile",
2247
+ "thumbFallback": "IgRnJG_thumbFallback",
2248
+ "thumbTitle": "IgRnJG_thumbTitle",
2249
+ "dsh-imagegen-inspiration-spin": "IgRnJG_dsh-imagegen-inspiration-spin"
2250
+ };
2251
+ //#endregion
2252
+ //#region src/client/InspirationGallery.tsx
2253
+ /**
2254
+ * Inspiration wall for the studio's empty canvas: a small grid of random
2255
+ * template cases sampled host-side across every library source. Clicking a
2256
+ * card hands its prompt to the form; the 随机 button re-rolls the pick. On
2257
+ * failure the wall collapses to nothing (the panel falls back to the plain
2258
+ * empty-state hint).
2259
+ */
2260
+ /** Card count per deal — a 4×3 wall that uses the canvas's spare width. */
2261
+ const SAMPLE_COUNT = 12;
2262
+ /** Same-origin proxy URL of one sampled case's reference image. */
2263
+ function imageUrlOf(sample) {
2264
+ return `${TEMPLATES_API.image}/${encodeURIComponent(sample.sourceId)}/${encodeURIComponent(sample.case.image)}`;
2265
+ }
2266
+ /** One thumbnail that degrades to a text tile when the image 404s. */
2267
+ function SampleThumb(props) {
2268
+ const [failed, setFailed] = (0, react.useState)(false);
2269
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2270
+ className: inspiration_module_css_default.thumbWrap,
2271
+ children: [props.sample.case.image !== "" && !failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
2272
+ className: inspiration_module_css_default.thumb,
2273
+ src: imageUrlOf(props.sample),
2274
+ alt: props.sample.case.title,
2275
+ loading: "lazy",
2276
+ onError: () => {
2277
+ setFailed(true);
2278
+ }
2279
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2280
+ className: inspiration_module_css_default.thumbFallback,
2281
+ "aria-hidden": "true",
2282
+ children: props.sample.case.title
2283
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2284
+ className: inspiration_module_css_default.thumbTitle,
2285
+ children: props.sample.case.title
2286
+ })]
2287
+ });
2288
+ }
2289
+ /** The random-case wall shown while the canvas has no results. */
2290
+ function InspirationGallery(props) {
2291
+ const { api, onUse } = props;
2292
+ const [samples, setSamples] = (0, react.useState)(null);
2293
+ const [loading, setLoading] = (0, react.useState)(false);
2294
+ const deal = () => {
2295
+ if (loading) return;
2296
+ setLoading(true);
2297
+ api.templatesSample(SAMPLE_COUNT).then(setSamples).catch(() => {
2298
+ setSamples((current) => current ?? []);
2299
+ }).finally(() => {
2300
+ setLoading(false);
2301
+ });
2302
+ };
2303
+ (0, react.useEffect)(() => {
2304
+ deal();
2305
+ }, []);
2306
+ if (samples !== null && samples.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2307
+ className: inspiration_module_css_default.wrap,
2308
+ "aria-label": tt("inspiration.title"),
2309
+ children: [
2310
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2311
+ className: inspiration_module_css_default.emptyIcon,
2312
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
2313
+ viewBox: "0 0 24 24",
2314
+ width: "34",
2315
+ height: "34",
2316
+ fill: "none",
2317
+ stroke: "currentColor",
2318
+ strokeWidth: "1.2",
2319
+ strokeLinecap: "round",
2320
+ strokeLinejoin: "round",
2321
+ "aria-hidden": "true",
2322
+ children: [
2323
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
2324
+ x: "3",
2325
+ y: "3",
2326
+ width: "18",
2327
+ height: "18",
2328
+ rx: "3"
2329
+ }),
2330
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
2331
+ cx: "8.5",
2332
+ cy: "8.5",
2333
+ r: "1.5"
2334
+ }),
2335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M21 15l-5-5L5 21" })
2336
+ ]
2337
+ })
2338
+ }),
2339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2340
+ className: inspiration_module_css_default.emptyTitle,
2341
+ children: tt("canvas.emptyTitle")
2342
+ }),
2343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2344
+ className: inspiration_module_css_default.emptyHint,
2345
+ children: tt("canvas.emptyHint")
2346
+ })
2347
+ ]
2348
+ });
2349
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2350
+ className: inspiration_module_css_default.wrap,
2351
+ "aria-label": tt("inspiration.title"),
2352
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2353
+ className: inspiration_module_css_default.title,
2354
+ children: tt("inspiration.title")
2355
+ }), samples === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2356
+ className: inspiration_module_css_default.spinner,
2357
+ "aria-hidden": "true"
2358
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2359
+ className: inspiration_module_css_default.grid,
2360
+ children: samples.map((sample) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2361
+ type: "button",
2362
+ className: inspiration_module_css_default.tile,
2363
+ title: tt("inspiration.useHint"),
2364
+ onClick: () => {
2365
+ onUse(sample.case.prompt);
2366
+ },
2367
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SampleThumb, { sample })
2368
+ }, `${sample.sourceId}:${sample.case.id}`))
2369
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2370
+ variant: "outline",
2371
+ size: "sm",
2372
+ disabled: loading,
2373
+ onClick: deal,
2374
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
2375
+ viewBox: "0 0 24 24",
2376
+ width: "14",
2377
+ height: "14",
2378
+ fill: "none",
2379
+ stroke: "currentColor",
2380
+ strokeWidth: "1.8",
2381
+ strokeLinecap: "round",
2382
+ strokeLinejoin: "round",
2383
+ "aria-hidden": "true",
2384
+ children: [
2385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2 18h1.4c1.3 0 2.5-.6 3.3-1.7l6.1-8.6c.8-1.1 2-1.7 3.3-1.7H22" }),
2386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m18 2 4 4-4 4" }),
2387
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2 6h1.9c1.5 0 2.9.9 3.6 2.2" }),
2388
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M22 18h-5.9c-1.3 0-2.6-.7-3.3-1.8l-.5-.8" }),
2389
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m18 14 4 4-4 4" })
2390
+ ]
2391
+ }), loading ? tt("inspiration.shuffling") : tt("inspiration.shuffle")]
2392
+ })] })]
2393
+ });
2394
+ }
2395
+ //#endregion
2396
+ //#region src/client/use-language.ts
2397
+ /**
2398
+ * React binding for the plugin locale, which follows the DSH interface
2399
+ * language (bridged from ctx.locale in client/index.ts). Surfaces that render
2400
+ * tt() output subscribe to this tick so a DSH language switch re-renders them
2401
+ * immediately.
2402
+ */
2403
+ /** Re-render the calling component whenever the DSH language changes. */
2404
+ function useImageGenLanguageTick() {
2405
+ return (0, react.useSyncExternalStore)(subscribeImageGenLanguage, getImageGenLanguageVersion);
2406
+ }
2407
+ //#endregion
1580
2408
  //#region src/client/settings-scope.ts
1581
2409
  /**
1582
2410
  * Browser-side settings scope for the dsh-imagegen namespace, served by the
@@ -1889,6 +2717,14 @@ window.__ModuleLoader__.load({
1889
2717
  supportsEdit: false,
1890
2718
  supportsAspectRatio: false,
1891
2719
  qualityTiers: ["HD"]
2720
+ },
2721
+ qwen: {
2722
+ label: "qwen-image",
2723
+ labelZh: "千问图像",
2724
+ known: true,
2725
+ supportsEdit: true,
2726
+ supportsAspectRatio: true,
2727
+ qualityTiers: ["auto"]
1892
2728
  }
1893
2729
  };
1894
2730
  /** Official Gemini image ids served by Nano Banana gateways. */
@@ -1927,6 +2763,10 @@ window.__ModuleLoader__.load({
1927
2763
  family: "zhipu",
1928
2764
  ...ENTRIES.zhipu
1929
2765
  };
2766
+ if (/^qwen-image(?:[-_.]|$)/i.test(id)) return {
2767
+ family: "qwen",
2768
+ ...ENTRIES.qwen
2769
+ };
1930
2770
  return {
1931
2771
  family: "unknown",
1932
2772
  label: "unknown",
@@ -1953,226 +2793,226 @@ window.__ModuleLoader__.load({
1953
2793
  document.head.appendChild(tag);
1954
2794
  }
1955
2795
  var panel_module_css_default = {
2796
+ "galleryRatio": "Yvqh9W_galleryRatio",
2797
+ "lightboxMeta": "Yvqh9W_lightboxMeta",
2798
+ "ecommerceAsset": "Yvqh9W_ecommerceAsset",
2799
+ "configGuideBody": "Yvqh9W_configGuideBody",
2800
+ "enhanceButton": "Yvqh9W_enhanceButton",
2801
+ "imageCaption": "Yvqh9W_imageCaption",
2802
+ "ecommercePlanNote": "Yvqh9W_ecommercePlanNote",
2803
+ "zoomHint": "Yvqh9W_zoomHint",
2804
+ "historyTitle": "Yvqh9W_historyTitle",
2805
+ "comparisonImageButton": "Yvqh9W_comparisonImageButton",
2806
+ "topNavDivider": "Yvqh9W_topNavDivider",
2807
+ "modelWrap": "Yvqh9W_modelWrap",
1956
2808
  "galleryAdd": "Yvqh9W_galleryAdd",
1957
- "ecommerceResultsHeader": "Yvqh9W_ecommerceResultsHeader",
1958
- "lightboxCopy": "Yvqh9W_lightboxCopy",
1959
- "galleryToolbar": "Yvqh9W_galleryToolbar",
1960
- "ecommerceAdvancedChevron": "Yvqh9W_ecommerceAdvancedChevron",
1961
- "bigSpinner": "Yvqh9W_bigSpinner",
1962
- "lightboxScaleFrame": "Yvqh9W_lightboxScaleFrame",
1963
- "galleryViewToggle": "Yvqh9W_galleryViewToggle",
1964
- "compareControl": "Yvqh9W_compareControl",
1965
- "galleryCardInfo": "Yvqh9W_galleryCardInfo",
1966
- "studio": "Yvqh9W_studio",
1967
- "ecommerceFooterHint": "Yvqh9W_ecommerceFooterHint",
1968
- "download": "Yvqh9W_download",
2809
+ "historyHeaderActions": "Yvqh9W_historyHeaderActions",
2810
+ "historyFilters": "Yvqh9W_historyFilters",
2811
+ "configGuide": "Yvqh9W_configGuide",
2812
+ "previewBadge": "Yvqh9W_previewBadge",
2813
+ "updateBanner": "Yvqh9W_updateBanner",
2814
+ "ecommerceAssetAdd": "Yvqh9W_ecommerceAssetAdd",
2815
+ "panelHeaderActions": "Yvqh9W_panelHeaderActions",
2816
+ "modelLabel": "Yvqh9W_modelLabel",
1969
2817
  "galleryFilterHeading": "Yvqh9W_galleryFilterHeading",
1970
- "dshImageGenSpin": "Yvqh9W_dshImageGenSpin",
1971
- "galleryRatioList": "Yvqh9W_galleryRatioList",
1972
- "configHeader": "Yvqh9W_configHeader",
1973
- "configGuideBody": "Yvqh9W_configGuideBody",
1974
- "view": "Yvqh9W_view",
1975
- "historyPrompt": "Yvqh9W_historyPrompt",
1976
- "ecommerceGroups": "Yvqh9W_ecommerceGroups",
1977
- "galleryTags": "Yvqh9W_galleryTags",
1978
- "modelMenuList": "Yvqh9W_modelMenuList",
1979
- "topNav": "Yvqh9W_topNav",
1980
- "galleryWorkspace": "Yvqh9W_galleryWorkspace",
1981
2818
  "gallerySelectMode": "Yvqh9W_gallerySelectMode",
1982
- "referenceImage": "Yvqh9W_referenceImage",
1983
- "ecommerceSlotCard": "Yvqh9W_ecommerceSlotCard",
1984
- "configScroll": "Yvqh9W_configScroll",
1985
- "galleryTagInput": "Yvqh9W_galleryTagInput",
1986
- "ecommercePlanBack": "Yvqh9W_ecommercePlanBack",
1987
- "modelMenu": "Yvqh9W_modelMenu",
1988
- "ecommerceTaskCard": "Yvqh9W_ecommerceTaskCard",
1989
- "chatResizer": "Yvqh9W_chatResizer",
1990
- "canvasMeta": "Yvqh9W_canvasMeta",
1991
- "card": "Yvqh9W_card",
1992
- "ecommerceAdvancedToggle": "Yvqh9W_ecommerceAdvancedToggle",
1993
- "optionPill": "Yvqh9W_optionPill",
1994
- "lightbox": "Yvqh9W_lightbox",
1995
- "galleryFilter": "Yvqh9W_galleryFilter",
1996
- "spinner": "Yvqh9W_spinner",
2819
+ "gallerySort": "Yvqh9W_gallerySort",
1997
2820
  "historyMeta": "Yvqh9W_historyMeta",
1998
- "canvasError": "Yvqh9W_canvasError",
1999
- "ecommerceUploadHero": "Yvqh9W_ecommerceUploadHero",
2000
- "canvasHistoryTag": "Yvqh9W_canvasHistoryTag",
2001
- "lightboxClose": "Yvqh9W_lightboxClose",
2002
- "galleryFilterDivider": "Yvqh9W_galleryFilterDivider",
2003
- "galleryHeading": "Yvqh9W_galleryHeading",
2004
- "galleryAvatar": "Yvqh9W_galleryAvatar",
2005
- "taskStatus": "Yvqh9W_taskStatus",
2006
- "taskTrayToggle": "Yvqh9W_taskTrayToggle",
2007
- "taskRows": "Yvqh9W_taskRows",
2008
- "panelHeaderActions": "Yvqh9W_panelHeaderActions",
2009
- "sessionTabLabel": "Yvqh9W_sessionTabLabel",
2010
- "ecommerceFooterBody": "Yvqh9W_ecommerceFooterBody",
2821
+ "grid": "Yvqh9W_grid",
2822
+ "download": "Yvqh9W_download",
2823
+ "galleryTagEditor": "Yvqh9W_galleryTagEditor",
2824
+ "ecommerceField": "Yvqh9W_ecommerceField",
2825
+ "canvas": "Yvqh9W_canvas",
2011
2826
  "panelHeading": "Yvqh9W_panelHeading",
2012
- "entryIcon": "Yvqh9W_entryIcon",
2013
- "configToggle": "Yvqh9W_configToggle",
2014
- "historyEmpty": "Yvqh9W_historyEmpty",
2015
- "ecommercePlanWarn": "Yvqh9W_ecommercePlanWarn",
2827
+ "taskRow": "Yvqh9W_taskRow",
2828
+ "historyList": "Yvqh9W_historyList",
2829
+ "historySearch": "Yvqh9W_historySearch",
2830
+ "ecommerceAdvancedBody": "Yvqh9W_ecommerceAdvancedBody",
2831
+ "galleryToolbarActions": "Yvqh9W_galleryToolbarActions",
2832
+ "galleryBulkButton": "Yvqh9W_galleryBulkButton",
2833
+ "galleryFilter": "Yvqh9W_galleryFilter",
2834
+ "connectionStatus": "Yvqh9W_connectionStatus",
2835
+ "panel": "Yvqh9W_panel",
2836
+ "reference": "Yvqh9W_reference",
2837
+ "galleryToast": "Yvqh9W_galleryToast",
2838
+ "comparisonFullscreenGrid": "Yvqh9W_comparisonFullscreenGrid",
2839
+ "modelMenuItem": "Yvqh9W_modelMenuItem",
2840
+ "panelTitle": "Yvqh9W_panelTitle",
2841
+ "githubLink": "Yvqh9W_githubLink",
2842
+ "galleryTagInput": "Yvqh9W_galleryTagInput",
2843
+ "topNavItem": "Yvqh9W_topNavItem",
2844
+ "optionGrid": "Yvqh9W_optionGrid",
2845
+ "lightboxDownload": "Yvqh9W_lightboxDownload",
2846
+ "configResizer": "Yvqh9W_configResizer",
2016
2847
  "ecommerceResultBadge": "Yvqh9W_ecommerceResultBadge",
2017
- "chatToggle": "Yvqh9W_chatToggle",
2018
- "promptFooter": "Yvqh9W_promptFooter",
2019
- "ecommerceFieldLabel": "Yvqh9W_ecommerceFieldLabel",
2020
- "enhanceButton": "Yvqh9W_enhanceButton",
2021
- "footer": "Yvqh9W_footer",
2848
+ "configHeader": "Yvqh9W_configHeader",
2849
+ "gallerySearch": "Yvqh9W_gallerySearch",
2850
+ "ecommerceSlotCard": "Yvqh9W_ecommerceSlotCard",
2851
+ "ecommerceSlotCount": "Yvqh9W_ecommerceSlotCount",
2852
+ "uploadBox": "Yvqh9W_uploadBox",
2853
+ "lightboxZoomLevel": "Yvqh9W_lightboxZoomLevel",
2854
+ "optionPill": "Yvqh9W_optionPill",
2855
+ "lightboxStage": "Yvqh9W_lightboxStage",
2856
+ "galleryFilterCount": "Yvqh9W_galleryFilterCount",
2857
+ "imageCard": "Yvqh9W_imageCard",
2858
+ "galleryCardActions": "Yvqh9W_galleryCardActions",
2859
+ "galleryRatioList": "Yvqh9W_galleryRatioList",
2860
+ "connectionDot": "Yvqh9W_connectionDot",
2861
+ "ecommerceSectionHint": "Yvqh9W_ecommerceSectionHint",
2862
+ "comparisonFullscreen": "Yvqh9W_comparisonFullscreen",
2863
+ "galleryRemove": "Yvqh9W_galleryRemove",
2864
+ "ecommerceRefRow": "Yvqh9W_ecommerceRefRow",
2865
+ "gallerySelectionBar": "Yvqh9W_gallerySelectionBar",
2866
+ "galleryWorkspace": "Yvqh9W_galleryWorkspace",
2867
+ "galleryImage": "Yvqh9W_galleryImage",
2868
+ "historyEmpty": "Yvqh9W_historyEmpty",
2869
+ "galleryTagEdit": "Yvqh9W_galleryTagEdit",
2022
2870
  "taskTrayChevron": "Yvqh9W_taskTrayChevron",
2023
- "lightboxCaptionRow": "Yvqh9W_lightboxCaptionRow",
2024
- "lightboxCaption": "Yvqh9W_lightboxCaption",
2025
2871
  "taskTrayClose": "Yvqh9W_taskTrayClose",
2026
- "lightboxDownload": "Yvqh9W_lightboxDownload",
2027
- "reference": "Yvqh9W_reference",
2028
- "taskTray": "Yvqh9W_taskTray",
2029
- "ecommerceGroup": "Yvqh9W_ecommerceGroup",
2030
- "ecommerceAssets": "Yvqh9W_ecommerceAssets",
2031
- "lightboxEdit": "Yvqh9W_lightboxEdit",
2032
- "ecommerceAssetAdd": "Yvqh9W_ecommerceAssetAdd",
2872
+ "historyThumb": "Yvqh9W_historyThumb",
2873
+ "ecommerceAdvancedToggle": "Yvqh9W_ecommerceAdvancedToggle",
2874
+ "galleryHeading": "Yvqh9W_galleryHeading",
2875
+ "galleryCard": "Yvqh9W_galleryCard",
2876
+ "uploadHint": "Yvqh9W_uploadHint",
2877
+ "promptCount": "Yvqh9W_promptCount",
2878
+ "modelSelect": "Yvqh9W_modelSelect",
2879
+ "lightboxIndex": "Yvqh9W_lightboxIndex",
2880
+ "ecommerceStructureGrid": "Yvqh9W_ecommerceStructureGrid",
2881
+ "lightbox": "Yvqh9W_lightbox",
2882
+ "comparisonGrid": "Yvqh9W_comparisonGrid",
2883
+ "entryIcon": "Yvqh9W_entryIcon",
2884
+ "paramGroup": "Yvqh9W_paramGroup",
2033
2885
  "historyHeader": "Yvqh9W_historyHeader",
2034
- "ecommerceResultsEmpty": "Yvqh9W_ecommerceResultsEmpty",
2035
- "sessionTab": "Yvqh9W_sessionTab",
2036
- "generateInner": "Yvqh9W_generateInner",
2037
- "dshImageGenToastIn": "Yvqh9W_dshImageGenToastIn",
2038
- "galleryTagFilterList": "Yvqh9W_galleryTagFilterList",
2039
- "generateButton": "Yvqh9W_generateButton",
2040
- "imageCaption": "Yvqh9W_imageCaption",
2041
- "ecommerceAdvancedBody": "Yvqh9W_ecommerceAdvancedBody",
2042
- "ecommerceWorkspace": "Yvqh9W_ecommerceWorkspace",
2043
- "ecommerceTaskActions": "Yvqh9W_ecommerceTaskActions",
2886
+ "gallerySelectionClear": "Yvqh9W_gallerySelectionClear",
2887
+ "galleryCardAction": "Yvqh9W_galleryCardAction",
2044
2888
  "gallerySelect": "Yvqh9W_gallerySelect",
2045
- "galleryTagEdit": "Yvqh9W_galleryTagEdit",
2046
- "ecommerceGroupGrid": "Yvqh9W_ecommerceGroupGrid",
2889
+ "updateText": "Yvqh9W_updateText",
2890
+ "lightboxCaption": "Yvqh9W_lightboxCaption",
2891
+ "ecommercePrimaryAction": "Yvqh9W_ecommercePrimaryAction",
2892
+ "canvasEmptyIcon": "Yvqh9W_canvasEmptyIcon",
2893
+ "panelHeader": "Yvqh9W_panelHeader",
2894
+ "historyPrompt": "Yvqh9W_historyPrompt",
2895
+ "generateInner": "Yvqh9W_generateInner",
2896
+ "lightboxTools": "Yvqh9W_lightboxTools",
2897
+ "compareModelChoices": "Yvqh9W_compareModelChoices",
2898
+ "configScroll": "Yvqh9W_configScroll",
2047
2899
  "historyClear": "Yvqh9W_historyClear",
2048
- "galleryTagEditor": "Yvqh9W_galleryTagEditor",
2049
- "uploadHint": "Yvqh9W_uploadHint",
2900
+ "configToggle": "Yvqh9W_configToggle",
2901
+ "historyInfo": "Yvqh9W_historyInfo",
2050
2902
  "referenceActions": "Yvqh9W_referenceActions",
2051
- "gallerySort": "Yvqh9W_gallerySort",
2052
- "compareToggle": "Yvqh9W_compareToggle",
2053
- "sidebarHistoryHost": "Yvqh9W_sidebarHistoryHost",
2054
- "ecommercePlanList": "Yvqh9W_ecommercePlanList",
2055
- "ecommercePrimaryAction": "Yvqh9W_ecommercePrimaryAction",
2056
- "updateActions": "Yvqh9W_updateActions",
2057
- "configGuide": "Yvqh9W_configGuide",
2058
- "galleryFilterCount": "Yvqh9W_galleryFilterCount",
2059
- "ecommerceField": "Yvqh9W_ecommerceField",
2060
- "lightboxFigure": "Yvqh9W_lightboxFigure",
2061
- "gallerySelectionClear": "Yvqh9W_gallerySelectionClear",
2062
- "modeRow": "Yvqh9W_modeRow",
2063
- "galleryCardFooter": "Yvqh9W_galleryCardFooter",
2064
- "topNavDivider": "Yvqh9W_topNavDivider",
2065
- "comparisonFullscreen": "Yvqh9W_comparisonFullscreen",
2066
- "galleryRatio": "Yvqh9W_galleryRatio",
2067
- "ecommercePlanMini": "Yvqh9W_ecommercePlanMini",
2068
- "ecommerceResults": "Yvqh9W_ecommerceResults",
2069
- "paramLabel": "Yvqh9W_paramLabel",
2070
- "lightboxMeta": "Yvqh9W_lightboxMeta",
2071
- "comparisonImageButton": "Yvqh9W_comparisonImageButton",
2072
- "taskPrompt": "Yvqh9W_taskPrompt",
2073
- "gallerySearch": "Yvqh9W_gallerySearch",
2074
- "ecommerceActionChip": "Yvqh9W_ecommerceActionChip",
2075
- "historyFilters": "Yvqh9W_historyFilters",
2076
- "canvasBody": "Yvqh9W_canvasBody",
2077
2903
  "lightboxNav": "Yvqh9W_lightboxNav",
2078
- "historyNew": "Yvqh9W_historyNew",
2079
- "imageCard": "Yvqh9W_imageCard",
2080
- "updateBanner": "Yvqh9W_updateBanner",
2081
- "updateText": "Yvqh9W_updateText",
2904
+ "galleryFilterNote": "Yvqh9W_galleryFilterNote",
2905
+ "comparisonBoard": "Yvqh9W_comparisonBoard",
2906
+ "galleryTagFilter": "Yvqh9W_galleryTagFilter",
2907
+ "historyMain": "Yvqh9W_historyMain",
2908
+ "galleryCount": "Yvqh9W_galleryCount",
2909
+ "lightboxActions": "Yvqh9W_lightboxActions",
2082
2910
  "ecommerceParamGrid": "Yvqh9W_ecommerceParamGrid",
2083
- "historyThumbPlaceholder": "Yvqh9W_historyThumbPlaceholder",
2084
- "modelMenuItem": "Yvqh9W_modelMenuItem",
2085
- "connectionDot": "Yvqh9W_connectionDot",
2086
- "lightboxImage": "Yvqh9W_lightboxImage",
2087
- "galleryClear": "Yvqh9W_galleryClear",
2088
- "updateRelease": "Yvqh9W_updateRelease",
2089
- "historyItem": "Yvqh9W_historyItem",
2090
- "uploadIcon": "Yvqh9W_uploadIcon",
2091
- "modelWrap": "Yvqh9W_modelWrap",
2092
- "galleryToast": "Yvqh9W_galleryToast",
2093
- "grid": "Yvqh9W_grid",
2094
- "lightboxStage": "Yvqh9W_lightboxStage",
2095
- "galleryImageButton": "Yvqh9W_galleryImageButton",
2096
- "galleryRemove": "Yvqh9W_galleryRemove",
2911
+ "ecommercePlanBack": "Yvqh9W_ecommercePlanBack",
2912
+ "canvasBody": "Yvqh9W_canvasBody",
2097
2913
  "galleryBadge": "Yvqh9W_galleryBadge",
2098
- "optionRow": "Yvqh9W_optionRow",
2099
- "modePill": "Yvqh9W_modePill",
2100
- "historySearch": "Yvqh9W_historySearch",
2101
- "compareModelChoices": "Yvqh9W_compareModelChoices",
2102
- "topNavItem": "Yvqh9W_topNavItem",
2103
- "sessionTabIcon": "Yvqh9W_sessionTabIcon",
2104
- "ecommercePlanNote": "Yvqh9W_ecommercePlanNote",
2105
- "galleryCardAction": "Yvqh9W_galleryCardAction",
2106
- "prompt": "Yvqh9W_prompt",
2107
- "ecommerceSection": "Yvqh9W_ecommerceSection",
2108
- "historyMain": "Yvqh9W_historyMain",
2109
- "optionGrid": "Yvqh9W_optionGrid",
2110
- "conversationToast": "Yvqh9W_conversationToast",
2111
- "galleryCard": "Yvqh9W_galleryCard",
2112
- "conversationAdd": "Yvqh9W_conversationAdd",
2113
- "ecommerceSectionHint": "Yvqh9W_ecommerceSectionHint",
2114
- "entryLabel": "Yvqh9W_entryLabel",
2115
- "taskTrayCount": "Yvqh9W_taskTrayCount",
2116
- "connectionStatus": "Yvqh9W_connectionStatus",
2117
- "historyThumb": "Yvqh9W_historyThumb",
2118
- "ecommerceRefRow": "Yvqh9W_ecommerceRefRow",
2119
- "lightboxTool": "Yvqh9W_lightboxTool",
2120
- "galleryToolbarActions": "Yvqh9W_galleryToolbarActions",
2121
- "comparisonGrid": "Yvqh9W_comparisonGrid",
2122
- "hiddenFile": "Yvqh9W_hiddenFile",
2914
+ "card": "Yvqh9W_card",
2123
2915
  "canvasState": "Yvqh9W_canvasState",
2124
- "previewBadge": "Yvqh9W_previewBadge",
2125
- "ecommerceTaskState": "Yvqh9W_ecommerceTaskState",
2126
- "canvasStateTitle": "Yvqh9W_canvasStateTitle",
2127
- "canvasStateHint": "Yvqh9W_canvasStateHint",
2128
- "githubLink": "Yvqh9W_githubLink",
2129
- "lightboxTools": "Yvqh9W_lightboxTools",
2130
- "galleryTagFilter": "Yvqh9W_galleryTagFilter",
2131
- "galleryFilterNote": "Yvqh9W_galleryFilterNote",
2132
- "galleryImage": "Yvqh9W_galleryImage",
2133
- "taskTrayHeader": "Yvqh9W_taskTrayHeader",
2134
- "galleryCount": "Yvqh9W_galleryCount",
2135
- "galleryCardActions": "Yvqh9W_galleryCardActions",
2916
+ "galleryFilterDivider": "Yvqh9W_galleryFilterDivider",
2136
2917
  "historyActions": "Yvqh9W_historyActions",
2137
- "sessionTabs": "Yvqh9W_sessionTabs",
2138
- "entry": "Yvqh9W_entry",
2139
- "lightboxIndex": "Yvqh9W_lightboxIndex",
2140
- "lightboxActions": "Yvqh9W_lightboxActions",
2918
+ "view": "Yvqh9W_view",
2919
+ "galleryImageButton": "Yvqh9W_galleryImageButton",
2920
+ "sessionTab": "Yvqh9W_sessionTab",
2141
2921
  "history": "Yvqh9W_history",
2142
- "comparisonBoard": "Yvqh9W_comparisonBoard",
2143
- "galleryBulkButton": "Yvqh9W_galleryBulkButton",
2144
- "historyList": "Yvqh9W_historyList",
2145
- "config": "Yvqh9W_config",
2146
- "ecommerceStructureGrid": "Yvqh9W_ecommerceStructureGrid",
2147
- "ecommerceSlotCount": "Yvqh9W_ecommerceSlotCount",
2148
- "ecommerceAsset": "Yvqh9W_ecommerceAsset",
2149
- "historyInfo": "Yvqh9W_historyInfo",
2150
- "promptCount": "Yvqh9W_promptCount",
2922
+ "sessionTabIcon": "Yvqh9W_sessionTabIcon",
2923
+ "ecommerceFooterHint": "Yvqh9W_ecommerceFooterHint",
2924
+ "modeRow": "Yvqh9W_modeRow",
2925
+ "lightboxCopy": "Yvqh9W_lightboxCopy",
2926
+ "entryLabel": "Yvqh9W_entryLabel",
2927
+ "ecommercePlanWarn": "Yvqh9W_ecommercePlanWarn",
2928
+ "promptFooter": "Yvqh9W_promptFooter",
2929
+ "ecommerceResults": "Yvqh9W_ecommerceResults",
2930
+ "ecommerceResultsHeader": "Yvqh9W_ecommerceResultsHeader",
2931
+ "historyThumbPlaceholder": "Yvqh9W_historyThumbPlaceholder",
2932
+ "ecommercePlanMini": "Yvqh9W_ecommercePlanMini",
2933
+ "referenceImage": "Yvqh9W_referenceImage",
2151
2934
  "templatesButton": "Yvqh9W_templatesButton",
2935
+ "uploadIcon": "Yvqh9W_uploadIcon",
2936
+ "ecommerceGroup": "Yvqh9W_ecommerceGroup",
2937
+ "modelMenu": "Yvqh9W_modelMenu",
2938
+ "canvasMeta": "Yvqh9W_canvasMeta",
2939
+ "bigSpinner": "Yvqh9W_bigSpinner",
2940
+ "image": "Yvqh9W_image",
2941
+ "dshImageGenSpin": "Yvqh9W_dshImageGenSpin",
2942
+ "sessionTabLabel": "Yvqh9W_sessionTabLabel",
2943
+ "chatToggle": "Yvqh9W_chatToggle",
2944
+ "modePill": "Yvqh9W_modePill",
2945
+ "lightboxScaleFrame": "Yvqh9W_lightboxScaleFrame",
2946
+ "lightboxCaptionRow": "Yvqh9W_lightboxCaptionRow",
2947
+ "galleryMasonry": "Yvqh9W_galleryMasonry",
2948
+ "ecommerceWorkspace": "Yvqh9W_ecommerceWorkspace",
2949
+ "ecommerceTaskActions": "Yvqh9W_ecommerceTaskActions",
2950
+ "lightboxImage": "Yvqh9W_lightboxImage",
2951
+ "ecommerceGroups": "Yvqh9W_ecommerceGroups",
2952
+ "ecommerceActionChip": "Yvqh9W_ecommerceActionChip",
2953
+ "ecommerceAdvancedChevron": "Yvqh9W_ecommerceAdvancedChevron",
2954
+ "sidebarHistoryHost": "Yvqh9W_sidebarHistoryHost",
2955
+ "taskRows": "Yvqh9W_taskRows",
2956
+ "taskTray": "Yvqh9W_taskTray",
2957
+ "ecommerceResultsActions": "Yvqh9W_ecommerceResultsActions",
2958
+ "paramLabel": "Yvqh9W_paramLabel",
2959
+ "galleryFilters": "Yvqh9W_galleryFilters",
2960
+ "chatResizer": "Yvqh9W_chatResizer",
2961
+ "galleryClear": "Yvqh9W_galleryClear",
2962
+ "config": "Yvqh9W_config",
2963
+ "taskTrayHeader": "Yvqh9W_taskTrayHeader",
2964
+ "ecommerceFieldLabel": "Yvqh9W_ecommerceFieldLabel",
2965
+ "historyNew": "Yvqh9W_historyNew",
2966
+ "ecommerceGroupGrid": "Yvqh9W_ecommerceGroupGrid",
2967
+ "modelMenuList": "Yvqh9W_modelMenuList",
2968
+ "ecommercePlanList": "Yvqh9W_ecommercePlanList",
2969
+ "generateButton": "Yvqh9W_generateButton",
2970
+ "canvasStateHint": "Yvqh9W_canvasStateHint",
2971
+ "lightboxTool": "Yvqh9W_lightboxTool",
2972
+ "ecommerceFooterBody": "Yvqh9W_ecommerceFooterBody",
2973
+ "footer": "Yvqh9W_footer",
2974
+ "conversationToast": "Yvqh9W_conversationToast",
2975
+ "galleryViewToggle": "Yvqh9W_galleryViewToggle",
2976
+ "compareToggle": "Yvqh9W_compareToggle",
2152
2977
  "paramHint": "Yvqh9W_paramHint",
2153
- "configResizer": "Yvqh9W_configResizer",
2154
- "paramGroup": "Yvqh9W_paramGroup",
2155
- "modelLabel": "Yvqh9W_modelLabel",
2156
- "canvasEmptyIcon": "Yvqh9W_canvasEmptyIcon",
2157
- "lightboxZoomLevel": "Yvqh9W_lightboxZoomLevel",
2158
- "comparisonFullscreenGrid": "Yvqh9W_comparisonFullscreenGrid",
2159
- "gallerySelectionBar": "Yvqh9W_gallerySelectionBar",
2160
- "canvas": "Yvqh9W_canvas",
2161
- "galleryMasonry": "Yvqh9W_galleryMasonry",
2162
- "taskRow": "Yvqh9W_taskRow",
2163
- "image": "Yvqh9W_image",
2164
- "uploadBox": "Yvqh9W_uploadBox",
2165
- "panelHeader": "Yvqh9W_panelHeader",
2166
- "panelTitle": "Yvqh9W_panelTitle",
2978
+ "galleryTagFilterList": "Yvqh9W_galleryTagFilterList",
2979
+ "lightboxFigure": "Yvqh9W_lightboxFigure",
2980
+ "galleryCardFooter": "Yvqh9W_galleryCardFooter",
2167
2981
  "historyAction": "Yvqh9W_historyAction",
2168
- "zoomHint": "Yvqh9W_zoomHint",
2169
- "galleryFilters": "Yvqh9W_galleryFilters",
2982
+ "galleryAvatar": "Yvqh9W_galleryAvatar",
2983
+ "galleryTags": "Yvqh9W_galleryTags",
2984
+ "sessionTabs": "Yvqh9W_sessionTabs",
2985
+ "lightboxClose": "Yvqh9W_lightboxClose",
2986
+ "ecommerceResultsEmpty": "Yvqh9W_ecommerceResultsEmpty",
2987
+ "ecommerceTaskCard": "Yvqh9W_ecommerceTaskCard",
2988
+ "topNav": "Yvqh9W_topNav",
2989
+ "optionRow": "Yvqh9W_optionRow",
2990
+ "updateActions": "Yvqh9W_updateActions",
2991
+ "studio": "Yvqh9W_studio",
2992
+ "updateRelease": "Yvqh9W_updateRelease",
2170
2993
  "generation": "Yvqh9W_generation",
2171
- "panel": "Yvqh9W_panel",
2172
- "historyTitle": "Yvqh9W_historyTitle",
2173
- "historyHeaderActions": "Yvqh9W_historyHeaderActions",
2174
- "ecommerceResultsActions": "Yvqh9W_ecommerceResultsActions",
2175
- "modelSelect": "Yvqh9W_modelSelect"
2994
+ "canvasStateTitle": "Yvqh9W_canvasStateTitle",
2995
+ "taskTrayToggle": "Yvqh9W_taskTrayToggle",
2996
+ "ecommerceTaskState": "Yvqh9W_ecommerceTaskState",
2997
+ "ecommerceUploadHero": "Yvqh9W_ecommerceUploadHero",
2998
+ "compareControl": "Yvqh9W_compareControl",
2999
+ "galleryCardInfo": "Yvqh9W_galleryCardInfo",
3000
+ "taskTrayCount": "Yvqh9W_taskTrayCount",
3001
+ "taskPrompt": "Yvqh9W_taskPrompt",
3002
+ "prompt": "Yvqh9W_prompt",
3003
+ "conversationAdd": "Yvqh9W_conversationAdd",
3004
+ "spinner": "Yvqh9W_spinner",
3005
+ "hiddenFile": "Yvqh9W_hiddenFile",
3006
+ "ecommerceSection": "Yvqh9W_ecommerceSection",
3007
+ "taskStatus": "Yvqh9W_taskStatus",
3008
+ "lightboxEdit": "Yvqh9W_lightboxEdit",
3009
+ "canvasHistoryTag": "Yvqh9W_canvasHistoryTag",
3010
+ "ecommerceAssets": "Yvqh9W_ecommerceAssets",
3011
+ "historyItem": "Yvqh9W_historyItem",
3012
+ "entry": "Yvqh9W_entry",
3013
+ "galleryToolbar": "Yvqh9W_galleryToolbar",
3014
+ "canvasError": "Yvqh9W_canvasError",
3015
+ "dshImageGenToastIn": "Yvqh9W_dshImageGenToastIn"
2176
3016
  };
2177
3017
  //#endregion
2178
3018
  //#region src/client/ImageGenPanel.tsx
@@ -2606,6 +3446,7 @@ window.__ModuleLoader__.load({
2606
3446
  function ImageGenPanel(props) {
2607
3447
  const { api, scope, sessions, conversation } = props;
2608
3448
  const config = useConfig(scope);
3449
+ useImageGenLanguageTick();
2609
3450
  const enabled = config?.enabled ?? true;
2610
3451
  const modelOptions = imageModelOptions(config);
2611
3452
  const imageModels = (config?.channels ?? []).length > 0 ? modelOptions.models : normalizeImageModels(config?.imageModels);
@@ -5312,47 +6153,12 @@ window.__ModuleLoader__.load({
5312
6153
  role: "alert",
5313
6154
  children: tt("canvas.error", { error })
5314
6155
  }) : null,
5315
- !generating && !error && images.length === 0 && workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5316
- className: panel_module_css_default.canvasState,
5317
- children: [
5318
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5319
- className: panel_module_css_default.canvasEmptyIcon,
5320
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
5321
- viewBox: "0 0 24 24",
5322
- width: "34",
5323
- height: "34",
5324
- fill: "none",
5325
- stroke: "currentColor",
5326
- strokeWidth: "1.2",
5327
- strokeLinecap: "round",
5328
- strokeLinejoin: "round",
5329
- "aria-hidden": "true",
5330
- children: [
5331
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
5332
- x: "3",
5333
- y: "3",
5334
- width: "18",
5335
- height: "18",
5336
- rx: "3"
5337
- }),
5338
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
5339
- cx: "8.5",
5340
- cy: "8.5",
5341
- r: "1.5"
5342
- }),
5343
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M21 15l-5-5L5 21" })
5344
- ]
5345
- })
5346
- }),
5347
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5348
- className: panel_module_css_default.canvasStateTitle,
5349
- children: tt("canvas.emptyTitle")
5350
- }),
5351
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5352
- className: panel_module_css_default.canvasStateHint,
5353
- children: tt("canvas.emptyHint")
5354
- })
5355
- ]
6156
+ !generating && !error && images.length === 0 && workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InspirationGallery, {
6157
+ api,
6158
+ onUse: (text) => {
6159
+ setPrompt(text);
6160
+ setError(null);
6161
+ }
5356
6162
  }) : null,
5357
6163
  !generating && images.length > 0 && workspace !== "ecommerce" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5358
6164
  className: panel_module_css_default.canvasBody,
@@ -6653,97 +7459,97 @@ window.__ModuleLoader__.load({
6653
7459
  document.head.appendChild(tag);
6654
7460
  }
6655
7461
  var settings_card_module_css_default = {
7462
+ "channelAction": "i1cc5G_channelAction",
7463
+ "channelHost": "i1cc5G_channelHost",
7464
+ "presetMeta": "i1cc5G_presetMeta",
7465
+ "disclosure": "i1cc5G_disclosure",
7466
+ "modelFetchRow": "i1cc5G_modelFetchRow",
7467
+ "sectionHint": "i1cc5G_sectionHint",
7468
+ "footer": "i1cc5G_footer",
7469
+ "presetRow": "i1cc5G_presetRow",
7470
+ "editorSectionHeader": "i1cc5G_editorSectionHeader",
7471
+ "modelArrow": "i1cc5G_modelArrow",
7472
+ "inlineDisclosure": "i1cc5G_inlineDisclosure",
7473
+ "presetInlineHeader": "i1cc5G_presetInlineHeader",
7474
+ "reset": "i1cc5G_reset",
7475
+ "channelDanger": "i1cc5G_channelDanger",
7476
+ "modelSection": "i1cc5G_modelSection",
7477
+ "channelMeta": "i1cc5G_channelMeta",
7478
+ "editorBackdrop": "i1cc5G_editorBackdrop",
7479
+ "modelRowBadges": "i1cc5G_modelRowBadges",
7480
+ "modelRow": "i1cc5G_modelRow",
7481
+ "chevron": "i1cc5G_chevron",
7482
+ "body": "i1cc5G_body",
7483
+ "versionRow": "i1cc5G_versionRow",
7484
+ "sectionTitle": "i1cc5G_sectionTitle",
7485
+ "inputInvalid": "i1cc5G_inputInvalid",
6656
7486
  "textareaInvalid": "i1cc5G_textareaInvalid",
6657
- "readOnly": "i1cc5G_readOnly",
6658
- "badge": "i1cc5G_badge",
7487
+ "failed": "i1cc5G_failed",
7488
+ "modelChip": "i1cc5G_modelChip",
7489
+ "select": "i1cc5G_select",
6659
7490
  "channelName": "i1cc5G_channelName",
6660
7491
  "channelAdd": "i1cc5G_channelAdd",
6661
- "save": "i1cc5G_save",
6662
- "channelBadge": "i1cc5G_channelBadge",
6663
- "detectOk": "i1cc5G_detectOk",
6664
- "modelRowBadges": "i1cc5G_modelRowBadges",
6665
- "modelBadge": "i1cc5G_modelBadge",
6666
- "label": "i1cc5G_label",
6667
- "inputInvalid": "i1cc5G_inputInvalid",
6668
- "channelDotWarn": "i1cc5G_channelDotWarn",
7492
+ "spacer": "i1cc5G_spacer",
7493
+ "card": "i1cc5G_card",
7494
+ "channelEmpty": "i1cc5G_channelEmpty",
7495
+ "modelRows": "i1cc5G_modelRows",
7496
+ "presetList": "i1cc5G_presetList",
7497
+ "editorField": "i1cc5G_editorField",
6669
7498
  "presetName": "i1cc5G_presetName",
6670
- "presetHint": "i1cc5G_presetHint",
6671
- "modelRow": "i1cc5G_modelRow",
6672
- "select": "i1cc5G_select",
6673
- "input": "i1cc5G_input",
6674
7499
  "badges": "i1cc5G_badges",
6675
- "channelList": "i1cc5G_channelList",
7500
+ "notExposed": "i1cc5G_notExposed",
7501
+ "sectionHeader": "i1cc5G_sectionHeader",
7502
+ "modelChoices": "i1cc5G_modelChoices",
7503
+ "modelCandidateLabel": "i1cc5G_modelCandidateLabel",
7504
+ "discard": "i1cc5G_discard",
7505
+ "textarea": "i1cc5G_textarea",
7506
+ "versionLabel": "i1cc5G_versionLabel",
7507
+ "header": "i1cc5G_header",
7508
+ "editorFooter": "i1cc5G_editorFooter",
7509
+ "deleteConfirmText": "i1cc5G_deleteConfirmText",
7510
+ "label": "i1cc5G_label",
7511
+ "input": "i1cc5G_input",
7512
+ "modelSummary": "i1cc5G_modelSummary",
7513
+ "channelAddRow": "i1cc5G_channelAddRow",
7514
+ "modelFetch": "i1cc5G_modelFetch",
6676
7515
  "editorTools": "i1cc5G_editorTools",
6677
- "channelMeta": "i1cc5G_channelMeta",
6678
7516
  "modelRowRemove": "i1cc5G_modelRowRemove",
6679
- "channelHost": "i1cc5G_channelHost",
6680
- "chevronOpen": "i1cc5G_chevronOpen",
6681
- "sectionTitle": "i1cc5G_sectionTitle",
6682
- "sectionHeader": "i1cc5G_sectionHeader",
6683
- "editorField": "i1cc5G_editorField",
6684
- "editorSectionHeader": "i1cc5G_editorSectionHeader",
7517
+ "head": "i1cc5G_head",
7518
+ "hint": "i1cc5G_hint",
7519
+ "detectOk": "i1cc5G_detectOk",
6685
7520
  "editorHeader": "i1cc5G_editorHeader",
7521
+ "chevronOpen": "i1cc5G_chevronOpen",
7522
+ "channelSection": "i1cc5G_channelSection",
7523
+ "invalid": "i1cc5G_invalid",
7524
+ "channelBadge": "i1cc5G_channelBadge",
6686
7525
  "editorClose": "i1cc5G_editorClose",
6687
- "modelFetchRow": "i1cc5G_modelFetchRow",
6688
- "failed": "i1cc5G_failed",
6689
- "editorDivider": "i1cc5G_editorDivider",
6690
- "discard": "i1cc5G_discard",
6691
- "name": "i1cc5G_name",
6692
- "channelAddRow": "i1cc5G_channelAddRow",
6693
- "editorBackdrop": "i1cc5G_editorBackdrop",
6694
- "channelDotReady": "i1cc5G_channelDotReady",
6695
7526
  "headText": "i1cc5G_headText",
6696
- "versionValue": "i1cc5G_versionValue",
7527
+ "channelList": "i1cc5G_channelList",
7528
+ "pending": "i1cc5G_pending",
7529
+ "channelDotWarn": "i1cc5G_channelDotWarn",
7530
+ "manualModelRow": "i1cc5G_manualModelRow",
7531
+ "presetHint": "i1cc5G_presetHint",
7532
+ "channelMain": "i1cc5G_channelMain",
6697
7533
  "editorPanel": "i1cc5G_editorPanel",
6698
- "presetList": "i1cc5G_presetList",
6699
- "modelCandidate": "i1cc5G_modelCandidate",
6700
- "optionalContent": "i1cc5G_optionalContent",
6701
- "modelRows": "i1cc5G_modelRows",
6702
- "modelRowInputs": "i1cc5G_modelRowInputs",
6703
7534
  "addModel": "i1cc5G_addModel",
6704
- "channelSection": "i1cc5G_channelSection",
6705
- "presetRow": "i1cc5G_presetRow",
6706
- "modelCandidateLabel": "i1cc5G_modelCandidateLabel",
6707
- "channelDanger": "i1cc5G_channelDanger",
6708
- "presetMeta": "i1cc5G_presetMeta",
7535
+ "channelControls": "i1cc5G_channelControls",
7536
+ "versionValue": "i1cc5G_versionValue",
7537
+ "badge": "i1cc5G_badge",
7538
+ "channelDotReady": "i1cc5G_channelDotReady",
7539
+ "editorDivider": "i1cc5G_editorDivider",
7540
+ "modelBadge": "i1cc5G_modelBadge",
7541
+ "save": "i1cc5G_save",
7542
+ "description": "i1cc5G_description",
6709
7543
  "field": "i1cc5G_field",
6710
- "hint": "i1cc5G_hint",
6711
- "versionLabel": "i1cc5G_versionLabel",
6712
- "body": "i1cc5G_body",
6713
- "manualModelRow": "i1cc5G_manualModelRow",
6714
- "pending": "i1cc5G_pending",
6715
- "notExposed": "i1cc5G_notExposed",
6716
- "modelCandidateList": "i1cc5G_modelCandidateList",
6717
- "modelArrow": "i1cc5G_modelArrow",
6718
- "chevron": "i1cc5G_chevron",
6719
- "modelChoices": "i1cc5G_modelChoices",
6720
- "head": "i1cc5G_head",
6721
- "inlineDisclosure": "i1cc5G_inlineDisclosure",
6722
- "disclosure": "i1cc5G_disclosure",
7544
+ "name": "i1cc5G_name",
6723
7545
  "channelRow": "i1cc5G_channelRow",
6724
- "card": "i1cc5G_card",
6725
- "description": "i1cc5G_description",
6726
- "modelFetch": "i1cc5G_modelFetch",
6727
- "channelMain": "i1cc5G_channelMain",
6728
- "header": "i1cc5G_header",
6729
- "modelSummary": "i1cc5G_modelSummary",
6730
- "versionRow": "i1cc5G_versionRow",
6731
- "channelAction": "i1cc5G_channelAction",
7546
+ "optionalContent": "i1cc5G_optionalContent",
6732
7547
  "sectionDivider": "i1cc5G_sectionDivider",
6733
- "channelControls": "i1cc5G_channelControls",
6734
- "presetInlineHeader": "i1cc5G_presetInlineHeader",
7548
+ "modelRowInputs": "i1cc5G_modelRowInputs",
6735
7549
  "presetInline": "i1cc5G_presetInline",
6736
- "deleteConfirmText": "i1cc5G_deleteConfirmText",
6737
- "textarea": "i1cc5G_textarea",
6738
- "modelChip": "i1cc5G_modelChip",
6739
- "spacer": "i1cc5G_spacer",
6740
- "editorFooter": "i1cc5G_editorFooter",
6741
- "reset": "i1cc5G_reset",
6742
- "channelEmpty": "i1cc5G_channelEmpty",
6743
- "invalid": "i1cc5G_invalid",
6744
- "modelSection": "i1cc5G_modelSection",
6745
- "footer": "i1cc5G_footer",
6746
- "sectionHint": "i1cc5G_sectionHint"
7550
+ "modelCandidateList": "i1cc5G_modelCandidateList",
7551
+ "readOnly": "i1cc5G_readOnly",
7552
+ "modelCandidate": "i1cc5G_modelCandidate"
6747
7553
  };
6748
7554
  //#endregion
6749
7555
  //#region src/client/SettingsCard.tsx
@@ -6814,7 +7620,8 @@ window.__ModuleLoader__.load({
6814
7620
  * @returns the card, or nothing while the namespace is still loading.
6815
7621
  */
6816
7622
  function ImageGenSettingsCard(props) {
6817
- const { t } = props;
7623
+ const t = tt;
7624
+ useImageGenLanguageTick();
6818
7625
  const state = props.useImageGenSettingsCard((snapshot) => snapshot);
6819
7626
  const [open, setOpen] = (0, react.useState)(false);
6820
7627
  const [promptModels, setPromptModels] = (0, react.useState)([]);
@@ -7316,10 +8123,10 @@ window.__ModuleLoader__.load({
7316
8123
  ...fieldProps,
7317
8124
  ...state.allowAgentImageGeneration,
7318
8125
  onEdit: (text) => {
7319
- props.edit("allowAgentImageGeneration", text);
8126
+ props.edit("enabled", text);
7320
8127
  },
7321
8128
  onReset: () => {
7322
- props.resetField("allowAgentImageGeneration");
8129
+ props.resetField("enabled");
7323
8130
  }
7324
8131
  })
7325
8132
  ]
@@ -8006,16 +8813,16 @@ window.__ModuleLoader__.load({
8006
8813
  document.head.appendChild(tag);
8007
8814
  }
8008
8815
  var image_toolview_module_css_default = {
8009
- "imageLink": "_3eGrYG_imageLink",
8010
- "error": "_3eGrYG_error",
8011
8816
  "root": "_3eGrYG_root",
8012
- "loading": "_3eGrYG_loading",
8817
+ "status": "_3eGrYG_status",
8818
+ "error": "_3eGrYG_error",
8013
8819
  "images": "_3eGrYG_images",
8014
- "header": "_3eGrYG_header",
8820
+ "imageLink": "_3eGrYG_imageLink",
8821
+ "message": "_3eGrYG_message",
8015
8822
  "image": "_3eGrYG_image",
8016
- "status": "_3eGrYG_status",
8823
+ "header": "_3eGrYG_header",
8017
8824
  "icon": "_3eGrYG_icon",
8018
- "message": "_3eGrYG_message"
8825
+ "loading": "_3eGrYG_loading"
8019
8826
  };
8020
8827
  //#endregion
8021
8828
  //#region src/client/image-toolview.tsx
@@ -8206,6 +9013,34 @@ window.__ModuleLoader__.load({
8206
9013
  zh,
8207
9014
  en
8208
9015
  }), "dsh-imagegen: dictionaries");
9016
+ ctx.effect(() => {
9017
+ try {
9018
+ return ctx.locale.register(NS, "ru", ru);
9019
+ } catch (error) {
9020
+ console.warn("[dsh-imagegen] ru dictionary not registered:", error);
9021
+ return () => {};
9022
+ }
9023
+ }, "dsh-imagegen: ru dictionary");
9024
+ ctx.effect(() => {
9025
+ try {
9026
+ if (ctx.locale.getLocale().locales.some((locale) => locale.id === "ru")) return () => {};
9027
+ return ctx.locale.addLanguage({
9028
+ id: "ru",
9029
+ label: "Русский",
9030
+ fallback: "en"
9031
+ });
9032
+ } catch (error) {
9033
+ console.warn("[dsh-imagegen] ru language not added to the catalog:", error);
9034
+ return () => {};
9035
+ }
9036
+ }, "dsh-imagegen: ru language pack");
9037
+ ctx.effect(() => {
9038
+ const applyLocale = () => {
9039
+ applyHostLocale(ctx.locale.getLocale().active);
9040
+ };
9041
+ applyLocale();
9042
+ return ctx.locale.subscribe(applyLocale);
9043
+ }, "dsh-imagegen: follow host locale");
8209
9044
  registerImageToolviews(ctx);
8210
9045
  const scope = bindImageGenScope(ctx.get("connection")?.isLoopback === true ? (input, init) => fetch(input, init) : () => {
8211
9046
  throw new Error("settings bridge is loopback-only");
@@ -8239,6 +9074,28 @@ window.__ModuleLoader__.load({
8239
9074
  sessions,
8240
9075
  conversation
8241
9076
  }));
9077
+ disposers.push(ctx.locale.subscribe(() => {
9078
+ const root = document.querySelector("[data-dsh-imagegen-sidebar-root]");
9079
+ if (root === null) return;
9080
+ const labels = [[
9081
+ "new-session",
9082
+ tt("entry.newSession"),
9083
+ tt("entry.newSessionTooltip")
9084
+ ], [
9085
+ "image",
9086
+ tt("entry.image"),
9087
+ tt("entry.tooltip")
9088
+ ]];
9089
+ for (const [tab, label, tooltip] of labels) {
9090
+ const button = root.querySelector(`[data-dsh-imagegen-tab="${tab}"]`);
9091
+ if (button === null) continue;
9092
+ button.setAttribute("aria-label", label);
9093
+ button.setAttribute("title", tooltip);
9094
+ const labelSpan = button.querySelector("span:nth-child(2)");
9095
+ if (labelSpan !== null) labelSpan.textContent = label;
9096
+ }
9097
+ root.querySelector("[role=\"tablist\"][data-dsh-imagegen-session-tabs]")?.setAttribute("aria-label", tt("entry.tooltip"));
9098
+ }));
8242
9099
  } catch (error) {
8243
9100
  console.warn("[dsh-imagegen] mount failed:", error);
8244
9101
  }