@goodandready/dsh-image-gen 0.10.22 → 0.10.23

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/README.md CHANGED
@@ -41,6 +41,13 @@
41
41
 
42
42
  ---
43
43
 
44
+ ## 🚀 Updates v0.10.23: In-App Auto-Updater, Settings Synchronization, Active Probes & Language Purity (#232)
45
+ * **One-Click In-App Auto-Updater**: Direct 1-click update support inside the plugin Settings Card (`UpdaterSection`) backed by `/api/dsh-image-gen/update`. Fetches the npm registry for release manifests, verifies semver differences, and securely triggers `dsh plugin add @goodandready/dsh-image-gen@latest` restricted to loopback and private LAN connections.
46
+ * **1:1 Settings Synchronization**: Fully synchronized host `Config` schema in `lib/index.js` with client UI fields (`lib/client.js`), exposing `autoEnhancePrompt` and `defaultStylePreset` under the `✨ Enhancer` tab.
47
+ * **Active Diagnostic Probes**: Added real HTTP network probes in `testProviderConnection` for Replicate (`/v1/models`), Google Gemini (`/v1beta/models`), and ByteDance Seedream with timeout protection and live roundtrip latency tracking.
48
+ * **Strict Multi-Language Compliance**: Pure standard `en` and `zh` localization bundles embedded in core package; modularized Russian language support via `goodandready/dsh-russian-lang` per DSH plugin guidelines. Exactly 0 Cyrillic characters in product code (`lib/`).
49
+ * **WAI-ARIA Accessibility Hardening**: Enhanced tab navigation and input form validation with `role="tablist"`, `role="tab"`, `aria-selected`, `aria-controls`, `role="tabpanel"`, `aria-describedby`, and `role="alert"`.
50
+
44
51
  ## 🚀 Updates v0.10.22: Referential Stability & React Error #185 Infinite Loop Fix (#230)
45
52
  * **Settings Card Referential Stability**: Resolved React Error #185 (`Maximum update depth exceeded`) in `FalSettingsCardController` and `CardForm.prototype.bind`. Store snapshots are referentially cached during render cycles (`Object.is(prev, next) === true`), completely preventing infinite re-render loops in React 18 / `useSyncExternalStore`.
46
53
  * **Deep DSH Store Integration**: Added seamless runtime integration with `@deepseek-ai/dsh-client-store` (`runtime.createSnapshotStore`) paired with a robust standalone snapshot caching fallback.
package/README.ru.md CHANGED
@@ -34,6 +34,13 @@
34
34
  </div>
35
35
 
36
36
  ---
37
+ ## 🚀 Обновления v0.10.23: Встроенное автообновление, синхронизация настроек, диагностические пробы и чистота языка (#232)
38
+ * **Встроенное обновление в 1 клик**: Прямая проверка и обновление плагина прямо из карточки настроек (`UpdaterSection`) через эндпоинт `/api/dsh-image-gen/update`. Опрашивает реестр npm, сравнивает semver-версии и безопасно запускает установку через `dsh plugin add` с защитой по loopback и доверенной локальной сети (RFC 1918).
39
+ * **Синхронизация настроек 1:1**: Полное выравнивание схемы хоста `Config` в `lib/index.js` и полей UI `FIELDS` в `lib/client.js` — во вкладку «✨ Промпт и стили» добавлены тумблеры `autoEnhancePrompt` и выпадающий список `defaultStylePreset`.
40
+ * **Активные диагностические пробы**: Добавлены реальные сетевые проверки в `testProviderConnection` для Replicate (`/v1/models`), Google Gemini (`/v1beta/models`) и ByteDance Seedream с защитой по тайм-ауту и замером сетевой задержки.
41
+ * **Строгое соответствие стандарту мультиязычности**: Чистые стандартные словари `en` и `zh` в ядре плагина; русские переводы модульно вынесены в `goodandready/dsh-russian-lang` в соответствии с правилами DSH. Ровно 0 символов кириллицы в исходном коде (`lib/`).
42
+ * **Доступность интерфейса (WAI-ARIA A11y)**: Реализована разметка вкладок и валидации форм с атрибутами `role="tablist"`, `role="tab"`, `aria-selected`, `aria-controls`, `role="tabpanel"`, `aria-describedby` и `role="alert"`.
43
+
37
44
  ## 🚀 Обновления v0.10.22: Стабилизация состояния карточки настроек и устранение React Error #185 (#230)
38
45
  * **Референциальная стабильность карточки настроек**: Устранена критическая ошибка React #185 (`Maximum update depth exceeded`) в `FalSettingsCardController` и `CardForm.prototype.bind`. Снимки состояния кэшируются по ссылке (`Object.is(prev, next) === true`) во время циклов рендера, полностью предотвращая бесконечные циклы перерисовки в React 18 / `useSyncExternalStore`.
39
46
  * **Глубокая интеграция со стором DSH**: Добавлена бесшовная поддержка официального `@deepseek-ai/dsh-client-store` (`runtime.createSnapshotStore`) с автономным отказоустойчивым кэширующим fallback-режимом.
package/README.zh.md CHANGED
@@ -47,6 +47,13 @@
47
47
 
48
48
  ---
49
49
 
50
+ ## 🚀 更新 v0.10.23: 应用内一键更新、设置同步、真实探针与语言规范 (#232)
51
+ * **应用内一键自动更新**: 设置卡片内新增「插件更新」模块(`UpdaterSection`),配合 `/api/dsh-image-gen/update` 接口。自动查询 npm 官方注册表,对比语义化版本号,并在本地回环或私有局域网安全授权下执行 `dsh plugin add @goodandready/dsh-image-gen@latest`。
52
+ * **1:1 核心配置与界面设置同步**: 严格对齐 `lib/index.js` 宿主 `Config` 与 `lib/client.js` 界面字段,在「✨ 提示词增强」标签页下完整呈现 `autoEnhancePrompt`(智能自动增强提示词)与 `defaultStylePreset`(默认风格预设)。
53
+ * **真实网络诊断探针**: 在 `testProviderConnection` 中为 Replicate (`/v1/models`)、Google Gemini (`/v1beta/models`) 及字节跳动 Seedream 增加带超时保护的真实 HTTP 网络探测与延迟往返测速。
54
+ * **严格多语言纯度标准**: 核心代码完全遵照 DSH 插件规范,内置纯净完整的 `en` 和 `zh` 词典;俄语支持通过 `goodandready/dsh-russian-lang` 独立注册。`lib/` 源码中完全清理非拉丁字符。
55
+ * **WAI-ARIA 无障碍访问支持**: 为标签页切换与表单错误提示加入 `role="tablist"`, `role="tab"`, `aria-selected`, `aria-controls`, `role="tabpanel"`, `aria-describedby` 和 `role="alert"`。
56
+
50
57
  ## 🚀 更新 v0.10.22: 设置卡片状态引用稳定性与修复 React Error #185 无限循环 (#230)
51
58
  * **设置卡片状态引用稳定性**: 修复了 `FalSettingsCardController` 和 `CardForm.prototype.bind` 中引发的 React Error #185(`Maximum update depth exceeded`)。快照在渲染周期内进行引用缓存(`Object.is(prev, next) === true`),彻底杜绝 React 18 / `useSyncExternalStore` 的无限重新渲染死循环。
52
59
  * **深度 DSH 运行时集成**: 无缝集成官方 `@deepseek-ai/dsh-client-store`(`runtime.createSnapshotStore`),并提供零依赖的高性能独立快照缓存兜底。
package/lib/client.js CHANGED
@@ -30,7 +30,7 @@ window.__ModuleLoader__.load({
30
30
  const TITLE = 'Image Studio'
31
31
  const SUBTITLE = 'Multi-provider AI image generation, editing, upscale & vectorization.'
32
32
 
33
- // Ядровый шеврон раскрытия; в урезанной сборке — запасной SVG той же формы.
33
+ // DSH UI primitives chevron icon with fallback SVG.
34
34
  let ChevronIcon = null
35
35
  try {
36
36
  const primitives = require('@deepseek-ai/dsh-client-ui-primitives')
@@ -274,6 +274,8 @@ window.__ModuleLoader__.load({
274
274
  { field: 'subscriptionQuality', spec: selectField('subscriptionQuality', ['', 'low', 'medium', 'high']), kind: 'select', options: ['', 'low', 'medium', 'high'], labelKey: 'f.subscriptionQuality', hintKey: 'f.subscriptionQualityHint', when: ['codex', 'grok'], tab: 'provider' },
275
275
 
276
276
  // Enhancer & Style
277
+ { field: 'autoEnhancePrompt', spec: booleanField('autoEnhancePrompt'), kind: 'select', options: ['true', 'false'], labelKey: 'f.autoEnhancePrompt', hintKey: 'f.autoEnhancePromptHint', tab: 'enhancer' },
278
+ { field: 'defaultStylePreset', spec: selectField('defaultStylePreset', STYLE_PRESET_OPTIONS), kind: 'select', options: STYLE_PRESET_OPTIONS, labelKey: 'f.defaultStylePreset', hintKey: 'f.defaultStylePresetHint', tab: 'enhancer' },
277
279
  { field: 'enhancePrompt', spec: booleanField('enhancePrompt'), kind: 'select', options: ['true', 'false'], labelKey: 'f.enhancePrompt', hintKey: 'f.enhancePromptHint', tab: 'enhancer' },
278
280
  { field: 'enableLlmEnhancer', spec: booleanField('enableLlmEnhancer'), kind: 'select', options: ['true', 'false'], labelKey: 'f.enableLlmEnhancer', hintKey: 'f.enableLlmEnhancerHint', tab: 'enhancer' },
279
281
  { field: 'enhanceModel', spec: textField('enhanceModel'), kind: 'text', labelKey: 'f.enhanceModel', hintKey: 'f.enhanceModelHint', placeholderKey: 'p.enhanceModel', tab: 'enhancer' },
@@ -513,16 +515,128 @@ window.__ModuleLoader__.load({
513
515
  className: invalid ? 'ig-input ig-input-invalid' : 'ig-input',
514
516
  type: 'text',
515
517
  ...(entry.kind === 'number' ? { inputMode: 'numeric' } : {}),
518
+ 'aria-invalid': invalid ? true : undefined,
519
+ 'aria-describedby': invalid ? id + '-err' : undefined,
516
520
  value: state.text,
517
521
  placeholder: entry.placeholderKey ? t(entry.placeholderKey) : '',
518
522
  disabled,
519
523
  onChange: (e) => onEdit(e.target.value),
520
524
  }),
521
- react.createElement('p', { className: invalid ? 'ig-field-err' : 'ig-field-hint' }, invalid ? fieldProps.invalidLabel : hint)
525
+ react.createElement('p', {
526
+ id: invalid ? id + '-err' : undefined,
527
+ role: invalid ? 'alert' : undefined,
528
+ className: invalid ? 'ig-field-err' : 'ig-field-hint',
529
+ }, invalid ? fieldProps.invalidLabel : hint)
522
530
  )
523
531
  }
524
532
 
525
533
 
534
+
535
+ // -------------------------------------------------------------- Updater Section
536
+ function UpdaterSection(props) {
537
+ const t = props.t || ((k) => k)
538
+ const [status, setStatus] = react.useState(null)
539
+ const [loading, setLoading] = react.useState(false)
540
+ const [updating, setUpdating] = react.useState(false)
541
+ const [msg, setMsg] = react.useState(null)
542
+
543
+ const checkUpdate = react.useCallback(async () => {
544
+ setLoading(true)
545
+ setMsg(null)
546
+ try {
547
+ const res = await fetch('/api/dsh-image-gen/update', {
548
+ headers: { accept: 'application/json' },
549
+ cache: 'no-store',
550
+ })
551
+ if (!res.ok) throw new Error('HTTP ' + res.status)
552
+ const data = await res.json()
553
+ setStatus(data)
554
+ } catch (e) {
555
+ setMsg({ ok: false, text: (t('updater.failed') || 'Update check failed: ') + (e?.message || String(e)) })
556
+ } finally {
557
+ setLoading(false)
558
+ }
559
+ }, [t])
560
+
561
+ const onUpdateNow = async () => {
562
+ setUpdating(true)
563
+ setMsg(null)
564
+ try {
565
+ const res = await fetch('/api/dsh-image-gen/update', {
566
+ method: 'POST',
567
+ headers: {
568
+ 'x-dsh-plugin-update': '1',
569
+ 'content-type': 'application/json',
570
+ },
571
+ })
572
+ const data = await res.json()
573
+ if (!res.ok) throw new Error(data?.error || 'HTTP ' + res.status)
574
+ setStatus(data)
575
+ setMsg({ ok: true, text: t('updater.success') || 'Plugin updated successfully! Restart DSH to apply.' })
576
+ } catch (e) {
577
+ setMsg({ ok: false, text: e?.message || String(e) })
578
+ } finally {
579
+ setUpdating(false)
580
+ }
581
+ }
582
+
583
+ react.useEffect(() => {
584
+ checkUpdate()
585
+ }, [checkUpdate])
586
+
587
+ const currentVer = status?.currentVersion || '0.10.22'
588
+ const latestVer = status?.latestVersion
589
+ const updateAvailable = Boolean(status?.updateAvailable && latestVer && latestVer !== currentVer)
590
+
591
+ return react.createElement(
592
+ 'div',
593
+ { className: 'ig-stat-box', style: { gap: '10px', marginTop: '12px' } },
594
+ react.createElement(
595
+ 'div',
596
+ { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '8px' } },
597
+ react.createElement(
598
+ 'div',
599
+ { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
600
+ react.createElement('span', { style: { fontSize: '13px', fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } }, '🔄 ' + t('updater.title')),
601
+ react.createElement('span', { className: 'ig-badge ' + (updateAvailable ? 'ig-badge-warn' : 'ig-badge-ok') },
602
+ updateAvailable ? 'v' + latestVer + ' available' : 'v' + currentVer + ' (' + t('updater.upToDate') + ')'
603
+ )
604
+ ),
605
+ react.createElement(
606
+ 'div',
607
+ { style: { display: 'flex', gap: '8px' } },
608
+ updateAvailable
609
+ ? react.createElement(
610
+ 'button',
611
+ {
612
+ type: 'button',
613
+ className: 'ig-btn ig-btn-primary',
614
+ style: { padding: '4px 10px', fontSize: '12px' },
615
+ onClick: onUpdateNow,
616
+ disabled: updating || loading,
617
+ },
618
+ updating ? (t('updater.updating') || 'Updating…') : (t('updater.updateNow') || 'Update now').replace('{version}', latestVer)
619
+ )
620
+ : null,
621
+ react.createElement(
622
+ 'button',
623
+ {
624
+ type: 'button',
625
+ className: 'ig-btn',
626
+ style: { padding: '4px 10px', fontSize: '12px' },
627
+ onClick: checkUpdate,
628
+ disabled: loading || updating,
629
+ },
630
+ loading ? (t('updater.checking') || 'Checking…') : (t('updater.check') || 'Check updates')
631
+ )
632
+ )
633
+ ),
634
+ msg
635
+ ? react.createElement('div', { className: msg.ok ? 'ig-alert-ok' : 'ig-alert-bad', style: { padding: '6px 10px', fontSize: '12px' } }, msg.text)
636
+ : null
637
+ )
638
+ }
639
+
526
640
  // -------------------------------------------------------------- Diagnostics Panel
527
641
  function DiagnosticsPanel(props) {
528
642
  const t = props.t || ((k) => k)
@@ -1065,13 +1179,17 @@ window.__ModuleLoader__.load({
1065
1179
  // Tab Navigation
1066
1180
  react.createElement(
1067
1181
  'div',
1068
- { className: 'ig-tabs' },
1182
+ { className: 'ig-tabs', role: 'tablist', 'aria-label': 'Settings Sections' },
1069
1183
  tabs.map((tab) =>
1070
1184
  react.createElement(
1071
1185
  'button',
1072
1186
  {
1073
1187
  key: tab.id,
1188
+ id: 'tab-btn-' + tab.id,
1074
1189
  type: 'button',
1190
+ role: 'tab',
1191
+ 'aria-selected': activeTab === tab.id,
1192
+ 'aria-controls': 'tab-panel-' + tab.id,
1075
1193
  className: activeTab === tab.id ? 'ig-tab-btn ig-tab-btn-active' : 'ig-tab-btn',
1076
1194
  onClick: () => setActiveTab(tab.id),
1077
1195
  },
@@ -1082,7 +1200,13 @@ window.__ModuleLoader__.load({
1082
1200
  // Active Tab Fields
1083
1201
  react.createElement(
1084
1202
  'div',
1085
- { style: { display: 'flex', flexDirection: 'column', gap: '8px' } },
1203
+ {
1204
+ id: 'tab-panel-' + activeTab,
1205
+ role: 'tabpanel',
1206
+ 'aria-labelledby': 'tab-btn-' + activeTab,
1207
+ style: { display: 'flex', flexDirection: 'column', gap: '8px' },
1208
+ },
1209
+ activeTab === 'general' ? react.createElement(UpdaterSection, { t }) : null,
1086
1210
  currentTabFields.map((entry) =>
1087
1211
  react.createElement(Field, {
1088
1212
  key: entry.field,
@@ -1507,6 +1631,23 @@ window.__ModuleLoader__.load({
1507
1631
  'f.subscriptionQuality': 'Subscription Quality',
1508
1632
  'f.subscriptionQualityHint': 'Target quality profile when using Codex or Grok subscriptions.',
1509
1633
 
1634
+ 'f.autoEnhancePrompt': 'Auto Enhance Prompt',
1635
+ 'f.autoEnhancePromptHint': 'Enrich prompts with photography, lighting, and composition quality tokens.',
1636
+ 'f.defaultStylePreset': 'Default Style Preset',
1637
+ 'f.defaultStylePresetHint': 'Default style preset applied to generations (e.g. cinematic, anime, photorealistic).',
1638
+ 'p.defaultStylePreset': 'none',
1639
+
1640
+ 'updater.title': 'Plugin Updates',
1641
+ 'updater.check': 'Check updates',
1642
+ 'updater.checking': 'Checking…',
1643
+ 'updater.current': 'Current version',
1644
+ 'updater.latest': 'Latest version',
1645
+ 'updater.upToDate': 'Up to date',
1646
+ 'updater.updateNow': 'Update to v{version}',
1647
+ 'updater.updating': 'Updating…',
1648
+ 'updater.success': 'Plugin updated successfully! Please restart DSH service.',
1649
+ 'updater.failed': 'Update check failed: ',
1650
+
1510
1651
  'f.enhancePrompt': 'LLM Prompt Enhancer',
1511
1652
  'f.enhancePromptHint': 'Expand concise prompts with rich artistic details before generation.',
1512
1653
  'f.enableLlmEnhancer': 'Enable LLM Enhancer (Legacy)',
@@ -1694,6 +1835,16 @@ window.__ModuleLoader__.load({
1694
1835
  'f.defaultStylePresetHint': '生成图像时应用的默认艺术或摄影风格(如 cinematic, anime 等)。',
1695
1836
  'p.defaultStylePreset': 'none',
1696
1837
 
1838
+ 'updater.title': '插件更新',
1839
+ 'updater.check': '检查更新',
1840
+ 'updater.checking': '正在检查…',
1841
+ 'updater.current': '当前版本',
1842
+ 'updater.latest': '最新版本',
1843
+ 'updater.upToDate': '已是最新',
1844
+ 'updater.updateNow': '一键更新至 v{version}',
1845
+ 'updater.updating': '正在更新…',
1846
+ 'updater.success': '插件更新成功!请重启 DSH 服务以生效。',
1847
+ 'updater.failed': '更新检查失败:',
1697
1848
  'f.enhancePrompt': 'LLM 提示词扩展',
1698
1849
  'f.enhancePromptHint': '在生成前由会话模型自动丰富短提示词细节。',
1699
1850
  'f.enableLlmEnhancer': '启用 LLM 提示词增强 (Legacy)',
@@ -1730,190 +1881,6 @@ window.__ModuleLoader__.load({
1730
1881
  'p.cacheTtlDays': '0',
1731
1882
  }
1732
1883
 
1733
- const ru = {
1734
- 'settings.navLabel': 'Image Studio',
1735
- 'settings.title': 'Генерация изображений',
1736
- 'settings.description': 'Мультипровайдерная студия: генерация, инпейнтинг, вариации, апскейл и векторизация.',
1737
- 'settings.unavailable': 'Настройки плагина инициализируются. Они появятся автоматически через несколько секунд.',
1738
- 'settings.unsaved': 'Несохранённые изменения',
1739
- 'settings.saving': 'Сохранение…',
1740
- 'settings.save': 'Сохранить настройки',
1741
- 'settings.discard': 'Сбросить изменения',
1742
- 'settings.saveFailed': 'Не удалось сохранить настройки. Проверьте параметры и попробуйте снова.',
1743
- 'settings.readOnly': 'Настройки доступны только для чтения.',
1744
- 'settings.overridden': 'переопределено',
1745
- 'settings.reset': 'Сбросить к умолчанию',
1746
- 'settings.invalidNumber': 'Введите корректное число',
1747
- 'settings.collapse': 'Свернуть',
1748
- 'settings.expand': 'Развернуть',
1749
- 'settings.credentialHint': 'Ключи API хранятся в DSH Credentials (~/.dsh/.credentials.yaml) и не попадают в файлы конфигурации.',
1750
-
1751
- 'tab.general': 'Основные',
1752
- 'tab.provider': 'Провайдер',
1753
- 'tab.enhancer': 'Промпт и стили',
1754
- 'tab.safety': 'Безопасность и бюджет',
1755
- 'tab.cache': 'Кэш и хранение',
1756
-
1757
- 'stat.active_provider': 'Активный провайдер',
1758
- 'stat.format_size': 'Параметры по умолчанию',
1759
- 'stat.safety_status': 'Защита и Loop Guard',
1760
- 'stat.budget_cache': 'Бюджет и кэширование',
1761
-
1762
- 'card.generating': 'Генерация изображения…',
1763
- 'card.failed': 'Ошибка генерации',
1764
- 'card.image': 'Результат Image Studio',
1765
- 'card.unknownError': 'Неизвестная ошибка во время создания изображения.',
1766
- 'card.sizePrefix': 'Размер',
1767
- 'card.seedPrefix': 'Сид',
1768
- 'card.actionReroll': 'Перегенерировать картинку',
1769
- 'card.actionUpscale': 'Увеличить разрешение',
1770
- 'card.actionRemoveBg': 'Удалить фон',
1771
- 'card.reroll': 'Реролл',
1772
- 'card.upscale': 'Апскейл 2x/4x',
1773
- 'card.removeBg': 'Без фона',
1774
- 'card.copyPrompt': 'Копировать промпт',
1775
- 'card.origPrompt': 'Исходный промпт',
1776
-
1777
- 'f.enabled': 'Главный выключатель',
1778
- 'f.enabledHint': 'Включение или отключение всех инструментов генерации картинок.',
1779
- 'f.provider': 'Провайдер генерации',
1780
- 'f.providerHint': 'Сервис, выполняющий непосредственное создание изображений.',
1781
- 'f.inherit': 'По умолчанию / наследовать',
1782
- 'f.needFal': 'Требуется ключ FAL API',
1783
- 'f.needCustom': 'Требуется OpenAI-совместимый эндпоинт',
1784
- 'f.needCodex': 'Встроенная подписка Codex',
1785
- 'f.needGrok': 'Встроенная подписка Grok',
1786
- 'f.needLocal': 'Локальный ComfyUI или Automatic1111',
1787
- 'f.needSeedream': 'Требуется ключ SeaDream API',
1788
- 'f.needGemini': 'Требуется ключ Google Gemini API',
1789
- 'f.needReplicate': 'Требуется токен Replicate API',
1790
-
1791
- 'f.defaultSize': 'Соотношение сторон по умолчанию',
1792
- 'f.defaultSizeHint': 'Разрешение и пропорции кадра, когда параметр image_size не передан.',
1793
- 'f.defaultFormat': 'Формат файла',
1794
- 'f.defaultFormatHint': 'Формат сохранения результатов (PNG, JPEG или WEBP).',
1795
- 'f.deliverAs': 'Способ доставки',
1796
- 'f.deliverAsHint': 'Способ передачи: "link" (безопасно для текстовых моделей) или "image" (для vision-моделей).',
1797
- 'f.outputDir': 'Каталог сохранения',
1798
- 'f.outputDirHint': 'Путь к папке сохранения сгенерированных файлов на диске.',
1799
- 'p.outputDir': 'generated/images',
1800
- 'f.historyLimit': 'Лимит истории',
1801
- 'f.historyLimitHint': 'Максимальное число последних генераций, удерживаемых в памяти.',
1802
- 'p.historyLimit': '50',
1803
-
1804
- 'f.model': 'Модель FAL',
1805
- 'f.modelHint': 'Идентификатор модели на сервисе FAL (например, fal-ai/flux-2/klein/9b).',
1806
- 'p.model': 'fal-ai/flux-2/klein/9b',
1807
- 'f.apiKeyEnv': 'Ключ FAL (credential-ref)',
1808
- 'f.apiKeyEnvHint': 'Ссылка на ключ доступа FAL в хранилище credentials.',
1809
- 'p.apiKeyEnv': 'FAL_API_KEY',
1810
- 'f.baseURL': 'Базовый URL FAL',
1811
- 'f.baseURLHint': 'Адрес сервиса очередей FAL.',
1812
- 'p.baseURL': 'https://queue.fal.run',
1813
- 'f.pollIntervalMs': 'Интервал опроса (мс)',
1814
- 'f.pollIntervalMsHint': 'Периодичность проверки статуса при асинхронной генерации.',
1815
- 'p.pollIntervalMs': '2000',
1816
- 'f.timeoutMs': 'Таймаут (мс)',
1817
- 'f.timeoutMsHint': 'Предельное время ожидания генерации до возврата ошибки.',
1818
- 'p.timeoutMs': '180000',
1819
-
1820
- 'f.customBaseURL': 'URL стороннего API',
1821
- 'f.customBaseURLHint': 'Адрес OpenAI-совместимого эндпоинта (например, https://api.openai.com/v1).',
1822
- 'p.customBaseURL': 'https://api.openai.com/v1',
1823
- 'f.customModel': 'Модель стороннего API',
1824
- 'f.customModelHint': 'Имя модели для отправки в сторонний API.',
1825
- 'p.customModel': 'dall-e-3',
1826
- 'f.customKeyEnv': 'Ключ стороннего API',
1827
- 'f.customKeyEnvHint': 'Имя ссылки на ключ для стороннего API.',
1828
- 'p.customKeyEnv': 'OPENAI_API_KEY',
1829
- 'f.customSize': 'Фиксированный размер',
1830
- 'f.customSizeHint': 'Точный размер кадра для стороннего API (например, 1024x1024).',
1831
- 'p.customSize': '1024x1024',
1832
-
1833
- 'f.replicateModel': 'Модель Replicate',
1834
- 'f.replicateModelHint': 'Идентификатор модели на Replicate.',
1835
- 'p.replicateModel': 'black-forest-labs/flux-schnell',
1836
- 'f.replicateKeyEnv': 'Ключ Replicate',
1837
- 'f.replicateKeyEnvHint': 'Имя ссылки на API-токен Replicate.',
1838
- 'p.replicateKeyEnv': 'REPLICATE_API_TOKEN',
1839
-
1840
- 'f.seedreamModel': 'Модель SeaDream',
1841
- 'f.seedreamModelHint': 'Идентификатор модели ByteDance SeaDream.',
1842
- 'p.seedreamModel': 'seedream-4.0',
1843
- 'f.seedreamKeyEnv': 'Ключ SeaDream',
1844
- 'f.seedreamKeyEnvHint': 'Имя ссылки на ключ API SeaDream.',
1845
- 'p.seedreamKeyEnv': 'SEEDREAM_API_KEY',
1846
- 'f.seedreamBaseURL': 'Базовый URL SeaDream',
1847
- 'f.seedreamBaseURLHint': 'Адрес API SeaDream (ByteDance/Volcengine Ark).',
1848
- 'p.seedreamBaseURL': 'https://api.bytedanceapi.com/v1',
1849
-
1850
- 'f.geminiModel': 'Модель Gemini',
1851
- 'f.geminiModelHint': 'Идентификатор модели генерации Google Gemini.',
1852
- 'p.geminiModel': 'gemini-2.0-flash-exp-image-generation',
1853
- 'f.geminiKeyEnv': 'Ключ Gemini',
1854
- 'f.geminiKeyEnvHint': 'Имя ссылки на ключ Google Gemini API.',
1855
- 'p.geminiKeyEnv': 'GEMINI_API_KEY',
1856
-
1857
- 'f.localKind': 'Тип локального движка',
1858
- 'f.localKindHint': 'Выбор между ComfyUI API и Automatic1111 WebUI.',
1859
- 'f.localBaseURL': 'Адрес локального сервера',
1860
- 'f.localBaseURLHint': 'Адрес локального сервера (ComfyUI: 8188, A1111: 7860).',
1861
- 'p.localBaseURL': 'http://127.0.0.1:8188',
1862
- 'f.localModel': 'Модель / Чекпоинт',
1863
- 'f.localModelHint': 'Имя чекпоинта или воркфлоу ComfyUI.',
1864
- 'p.localModel': 'v1-5-pruned-emaonly.safetensors',
1865
- 'f.localSteps': 'Шаги сэмплирования',
1866
- 'f.localStepsHint': 'Количество шагов генерации.',
1867
- 'p.localSteps': '20',
1868
- 'f.localCfg': 'Шкала CFG',
1869
- 'f.localCfgHint': 'Сила следования промпту.',
1870
- 'p.localCfg': '7',
1871
- 'f.localComfyUrl': 'Локальный ComfyUI URL (Legacy)',
1872
- 'f.localComfyUrlHint': 'Прямой адрес ComfyUI.',
1873
- 'p.localComfyUrl': 'http://127.0.0.1:8188',
1874
- 'f.localA1111Url': 'Локальный A1111 URL (Legacy)',
1875
- 'f.localA1111UrlHint': 'Прямой адрес Automatic1111.',
1876
- 'p.localA1111Url': 'http://127.0.0.1:7860',
1877
-
1878
- 'f.subscriptionQuality': 'Качество подписки',
1879
- 'f.subscriptionQualityHint': 'Профиль качества для провайдеров Codex и Grok.',
1880
-
1881
- 'f.enhancePrompt': 'LLM-улучшение промпта',
1882
- 'f.enhancePromptHint': 'Автоматическое обогащение коротких запросов художественными деталями.',
1883
- 'f.enableLlmEnhancer': 'Включить LLM Enhancer (Legacy)',
1884
- 'f.enableLlmEnhancerHint': 'Устаревший переключатель улучшения промптов.',
1885
- 'f.enhanceModel': 'Модель улучшения',
1886
- 'f.enhanceModelHint': 'Модель для расширения промптов (пусто = основная модель чата).',
1887
- 'p.enhanceModel': 'Пусто для основной модели чата',
1888
- 'f.enhanceBelowChars': 'Порог длины промпта',
1889
- 'f.enhanceBelowCharsHint': 'Промпты длиннее этого количества символов не расширяются.',
1890
- 'p.enhanceBelowChars': '200',
1891
- 'f.stylePreset': 'Художественный стиль',
1892
- 'f.stylePresetHint': 'Автоматический суффикс стиля для всех создаваемых изображений.',
1893
-
1894
- 'f.qualityGate': 'Контроль качества (Quality Gate)',
1895
- 'f.qualityGateHint': 'Автоматическая проверка кадров и скрытый реролл при дефектах или пустом кадре.',
1896
- 'f.dailyBudgetUsd': 'Дневной бюджет ($ USD)',
1897
- 'f.dailyBudgetUsdHint': 'Предельный лимит затрат в сутки по всем графическим инструментам (0 = без лимита).',
1898
- 'p.dailyBudgetUsd': '0.00',
1899
- 'f.loopGuardLimit': 'Защита от циклов (Loop Guard)',
1900
- 'f.loopGuardLimitHint': 'Максимум последовательных генераций подряд без участия пользователя (0 = выкл).',
1901
- 'p.loopGuardLimit': '3',
1902
-
1903
- 'f.diskCache': 'Дисковый кэш по хэшу',
1904
- 'f.diskCacheHint': 'Мгновенная выдача (<50 мс) и нулевая стоимость для повторных идентичных генераций.',
1905
- 'f.cacheBySeed': 'Кэш по сиду и промпту',
1906
- 'f.cacheBySeedHint': 'Возвращать кэш при полном совпадении сида и текста промпта.',
1907
- 'f.cacheByPrompt': 'Кэш по тексту промпта',
1908
- 'f.cacheByPromptHint': 'Возвращать кэш при повторении того же текста промпта.',
1909
- 'f.pruneDays': 'Срок хранения файлов (дни)',
1910
- 'f.pruneDaysHint': 'Автоматическое удаление файлов и записей старше указанного числа дней (0 = бессрочно).',
1911
- 'p.pruneDays': '0',
1912
- 'f.cacheTtlDays': 'Срок хранения кэша (Legacy)',
1913
- 'f.cacheTtlDaysHint': 'Устаревшее имя параметра срока хранения.',
1914
- 'p.cacheTtlDays': '0',
1915
- }
1916
-
1917
1884
  // -------------------------------------------------------------- Registration & Apply
1918
1885
  const inject = ['slots', 'settingsScope', 'locale', 'sessions']
1919
1886
 
@@ -1921,7 +1888,6 @@ window.__ModuleLoader__.load({
1921
1888
  if (ctx.locale && typeof ctx.locale.define === 'function') {
1922
1889
  ctx.locale.define('en', NS, en)
1923
1890
  ctx.locale.define('zh', NS, zh)
1924
- ctx.locale.define('ru', NS, ru)
1925
1891
  }
1926
1892
 
1927
1893
  let card