@goodandready/dsh-russian-lang 0.3.8 → 0.3.9

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
@@ -5,7 +5,7 @@
5
5
  <h3>Полная русская локализация, умная типографика и исправление раскладки клавиатуры для DeepSeek Harness</h3>
6
6
 
7
7
  <p align="center">
8
- <a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.8-6366f1.svg?style=for-the-badge&labelColor=1e1b4b" alt="npm version"></a>
8
+ <a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.9-6366f1.svg?style=for-the-badge&labelColor=1e1b4b" alt="npm version"></a>
9
9
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-10b981.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
10
10
  <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
11
11
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
@@ -192,3 +192,22 @@ russian-lang:
192
192
  ## 📄 Лицензия
193
193
 
194
194
  MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
195
+
196
+
197
+ ## Движок перевода реплик
198
+
199
+ Плагин позволяет переводить ответы модели на русский язык. Для этого используется один из двух движков — по выбору пользователя в настройках. **По умолчанию оба движка выключены** (`translateEngine: off`).
200
+
201
+ ### Google Translate (неофициальный)
202
+
203
+ Текст ответов модели отправляется на `translate.googleapis.com/translate_a/single?client=gtx` — это **неофициальный** публичный эндпоинт Google без SLA и без гарантий доступности. В ответах модели могут содержаться фрагменты кода, пути, содержимое файлов — **всё это уходит третьей стороне**. При включении в карточке настроек отображается жёлтый бейдж.
204
+
205
+ ### LibreTranslate (локальный)
206
+
207
+ Плагин запускает Docker-контейнер `libretranslate/libretranslate:v1.5.3` на `127.0.0.1:5000`. Перевод выполняется полностью локально — текст **не покидает машину**. Для работы необходим установленный Docker.
208
+
209
+ | Движок | Куда уходит текст | Приватность | Интернет |
210
+ |---|---|---|---|
211
+ | `off` (по умолчанию) | никуда | ✅ полная | не нужен |
212
+ | `google` | Google (googleapis.com) | ⚠️ текст отправляется третьей стороне | нужен |
213
+ | `libretranslate` | localhost:5000 (Docker) | ✅ локально | нужен только для загрузки образа |
package/lib/client.js CHANGED
@@ -257,10 +257,14 @@ const formatInputLive = (text) => {if (!text || typeof text !== 'string') return
257
257
  const parts = text.split(/(```[\s\S]*?```|`[^`\n]*`)/g)
258
258
  for (let i = 0; i < parts.length; i += 2) {let s = parts[i]
259
259
  if (!s) continue
260
+ const lq = [], cf = []
261
+ s = s.replace(/"([^"\n]*[a-zA-Z][^"\n]*)"/g, (m) => `\x01L${lq.push(m) - 1}\x01`)
262
+ s = s.replace(/(^|\s)(--[a-zA-Z0-9_\u0400-\u04FF-]+)/g, (m, p1, p2) => `${p1}\x01C${cf.push(p2) - 1}\x01`)
263
+ s = s.replace(/(^|\s)(--)(?=\s|$)/g, (m, p1, p2) => /[a-zA-Z]/.test(text) ? `${p1}\x01C${cf.push(p2) - 1}\x01` : m)
260
264
  s = s.replace(/--/g, '—')
261
- s = s.replace(/(^|[\s([{-])"/g, '$1«')
262
- s = s.replace(/"/g, '»')
265
+ s = s.replace(/(^|[\s([{-])"/g, '$1«').replace(/"/g, '»')
263
266
  s = s.replace(SHORT_WORDS_RE, '$1$2\u00A0')
267
+ s = s.replace(/\x01C(\d+)\x01/g, (_, j) => cf[+j]).replace(/\x01L(\d+)\x01/g, (_, j) => lq[+j])
264
268
  parts[i] = s
265
269
  }
266
270
  return parts.join('')
@@ -433,9 +437,9 @@ if (res.matched) {results.push({ item: cmd, ...res })
433
437
  }
434
438
  return results.sort((a, b) => b.score - a.score).map((r) => r.item)
435
439
  }
436
- const RU = {"common":{"back":"Назад","brand.localBuild":"Локальная сборка DSH","cancel":"Отмена","close":"Закрыть","codeBlock.title":"Блок кода","codeBlock.unwrap":"Не переносить строки","codeBlock.wrap":"Переносить строки","collapse":"Свернуть","copied":"Скопировано","copy":"Копировать","copy.compactJson":"Копировать компактный JSON","copy.failed":"Не удалось скопировать","copy.json":"Копировать JSON","copy.optionsHint":"{action}; правый клик для вариантов копирования","copy.path":"Копировать путь к свойству","copy.prettyJson":"Копировать форматированный JSON","copy.value":"Копировать значение","delete":"Удалить","edit":"Изменить","expand":"Развернуть","json.collapseNode":"Свернуть узел JSON","json.expandNode":"Развернуть узел JSON","json.label":"JSON","load.failed":"Не удалось загрузить","loading":"Загрузка…","markdown.footnotes":"Сноски","markdown.truncatedCharacters":"… обрезано на {total} символах","more":"Ещё","next":"Дальше","none":"Нет","number.million":"{value}M","number.thousand":"{value}K","ok":"ОК","previous":"Назад","retry":"Повторить","save":"Сохранить","search":"Поиск","skip":"Пропустить","submit":"Отправить","submitting":"Отправка…","truncated":"Обрезано","unknown":"Неизвестно"},"dsh-cron":{"sidebar.label":"Задачи по расписанию"},"dsh-usage-stats":{"footer.todayLabel":"Сегодня"},"pluginMarket":{"trigger":"Магазин плагинов"}}
440
+ const RU = {"common":{"back":"Назад","brand.localBuild":"Локальная сборка DSH","cancel":"Отмена","close":"Закрыть","codeBlock.title":"Блок кода","codeBlock.unwrap":"Не переносить строки","codeBlock.wrap":"Переносить строки","collapse":"Свернуть","copied":"Скопировано","copy":"Копировать","copy.compactJson":"Копировать компактный JSON","copy.failed":"Не удалось скопировать","copy.json":"Копировать JSON","copy.optionsHint":"{action}; правый клик для вариантов копирования","copy.path":"Копировать путь к свойству","copy.prettyJson":"Копировать форматированный JSON","copy.value":"Копировать значение","delete":"Удалить","edit":"Изменить","expand":"Развернуть","json.label":"JSON","load.failed":"Не удалось загрузить","loading":"Загрузка…","markdown.footnotes":"Сноски","markdown.truncatedCharacters":"… обрезано на {total} символах","more":"Ещё","next":"Дальше","none":"Нет","number.million":"{value}M","number.thousand":"{value}K","ok":"ОК","previous":"Назад","retry":"Повторить","save":"Сохранить","search":"Поиск","skip":"Пропустить","submit":"Отправить","submitting":"Отправка…","truncated":"Обрезано","unknown":"Неизвестно"},"dsh-cron":{"sidebar.label":"Задачи по расписанию"},"dsh-usage-stats":{"footer.todayLabel":"Сегодня"},"pluginMarket":{"trigger":"Магазин плагинов"}}
437
441
  const ZH_RU = {}
438
- RU['russian-lang'] = {"cardTitle":"Русская локализация","cardSub":"Язык интерфейса, типографика, раскладка","badgeRu":"🟢 RU активен","badgeEn":"⚪ EN активен","badgeCoverage":"🟢 100% (7,902 ключа)","badgeSmartUx":"⚡ Smart UX активен","secLanguage":"🌐 Язык интерфейса","secLanguageDesc":"Нативное переключение языка интерфейса DSH на русский без перезагрузки страницы.","enabled":"Русский язык включён","enabledDesc":"Переключает язык интерфейса DeepSeek Harness на русский.","quickSwitchNote":"Быстрый переключатель RU ⇄ EN доступен в шапке сессии рядом с кнопками диалога.","secTypography":"✍️ Умная типографика и ввод (Smart UX)","secTypographyDesc":"Автоматическое улучшение текстов по нормам русской типографики во время диалога и набора.","typography":"Типографика вывода","typographyDesc":"Исправляет типографику ответов модели: кавычки-«ёлочки», тире («—») вместо дефисов, неразрывные пробелы после предлогов.","yo":"Буква «ё»","yoDesc":"Восстанавливать «ё» в частых словах (ещё, чёрный, идёт и др.), написанных через «е». Неоднозначные слова (все/всё) не трогаются.","liveInput":"Живая типографика инпута","liveInputDesc":"Автоматически заменять \"\" на «» и -- на — прямо во время набора промпта (код в бэктиках игнорируется).","slashAliases":"Русские алиасы команд","slashAliasesDesc":"Поддержка русских команд: /цель -> /goal, /сжать -> /compact, /план -> /plan, /справка -> /help, /память -> /memory.","altLHintText":"Мгновенная конвертация раскладки текущего поля (ghbdtn ⇄ привет, /vjltkm ⇄ /model). В углу поля ввода также отображается метка раскладки.","secAgentPrompt":"🤖 Системный промпт агента","secAgentPromptDesc":"Официальная секция расширения DSH systemPrompt для ведения диалога на русском языке.","agentPrompt":"Русский промпт агента","agentPromptDesc":"Добавляет в системный промпт инструкцию отвечать по-русски в выбранном стиле.","agentPromptPreset":"Стиль ответов агента","presetExpert":"Технический эксперт (строгая терминология, чистый код)","presetWriter":"Технический писатель (Markdown, таблицы, ГОСТ)","presetConcise":"Лаконичный режим (кратко, без лишней воды)","presetReviewer":"Код-ревьюер (поиск багов, безопасность, аудит)","presetArchitect":"Системный архитектор (контракты, масштабируемость)","presetTutor":"Наставник (понятные объяснения, пошаговый разбор)","secUpdater":"🔄 Обновление языкового пакета","secUpdaterDesc":"Проверка наличия новых релизов в реестре npm и обновление в один клик.","updaterCurrent":"Текущая версия: v{version}","updaterLatest":"Доступна новая версия: v{version}","updaterUpToDate":"Установлена актуальная версия","updaterChecking":"Проверка…","updaterCheckBtn":"Проверить обновления","updaterBtn":"Обновить до v{version} в 1 клик","updaterUpdating":"Установка обновления…","updaterSuccess":"✅ Плагин успешно обновлён! Перезапустите DSH для применения.","updaterFailed":"❌ Не удалось проверить/обновить плагин. Проверьте сеть или логи сервера.","badgeUpdateAvailable":"Доступно обновление","badgeUpToDate":"Актуальная версия","secTranslator":"🌐 Перевод сообщений ассистента","secTranslatorDesc":"Настройка перевода ответов модели в чате на русский язык (локально в Docker или онлайн).","translateEngine":"Движок перевода","engineOff":"Выключен (по умолчанию, без сетевых вызовов)","engineLocal":"Локальный LibreTranslate (приватно, ~600 МБ RAM)","engineGoogle":"Google Translate (онлайн)","googleWarn":"⚠️ Внимание: при онлайн-переводе текст сообщений ассистента передаётся на публичные серверы Google для перевода на лету. Не используйте для конфиденциальных данных.","localStatusRunning":"🟢 Контейнер LibreTranslate активен","localStatusStopped":"⚪ Контейнер LibreTranslate не запущен","localStartBtn":"Запустить LibreTranslate в Docker (~600 МБ RAM)","localStarting":"Запуск контейнера…","localInfo":"LibreTranslate запускается изолированно в Docker на сервере, потребляет ~500–700 МБ RAM и переводит на 100% локально.","secOverrides":"✍️ Пользовательские переопределения (Custom Overrides)","secOverridesDesc":"Возможность заменить любую формулировку интерфейса DSH на собственную. Переопределения сохраняются в настройках и имеют наивысший приоритет.","overrideKeyPlaceholder":"Ключ (например, common.back или settings.title)","overrideValuePlaceholder":"Ваш русский перевод","overrideAddBtn":"Добавить","overrideEmpty":"Переопределений пока нет.","overrideDelete":"Удалить","overrideTip":"💡 Совет: зажмите Alt и кликните на любой переведённый элемент интерфейса, чтобы открыть всплывающий инспектор перевода и сразу изменить формулировку.","secSupport":"📊 Покрытие экосистемы и поддержка","secSupportDesc":"Словари синхронизированы с DSH v0.1.6-alpha.1. 100.0% UI-покрытие ядра и всех установленных плагинов (7,902 ключа) без черновых машинных переводов.","statNamespaces":"Пространств имён","statCoreKeys":"Ключей ядра","statPluginKeys":"Ключей плагинов","overridesCount":"Своих переопределений","statusLoading":"Настройки загружаются…","statusUnavailable":"Настройки недоступны на этом хосте","translateTurn":"Перевести на русский","reportIssue":"Сообщить о неточности перевода","exportMdHint":"Экспорт диалога в Markdown доступен по кнопке [ 📥 MD ] в шапке сессии.","translateTurnHint":"Перевод ответов ассистента на русский доступен по кнопке [ RU ↗ ] на блоках сообщений."}
442
+ RU['russian-lang'] = {"cardTitle":"Русская локализация","cardSub":"Язык интерфейса, типографика, раскладка","badgeRu":"🟢 RU активен","badgeEn":"⚪ EN активен","badgeCoverage":"🟢 100% (7,902 ключа)","badgeSmartUx":"⚡ Smart UX активен","secLanguage":"🌐 Язык интерфейса","secLanguageDesc":"Нативное переключение языка интерфейса DSH на русский без перезагрузки страницы.","enabled":"Русский язык включён","enabledDesc":"Переключает язык интерфейса DeepSeek Harness на русский.","quickSwitchNote":"Быстрый переключатель RU ⇄ EN доступен в шапке сессии рядом с кнопками диалога.","secTypography":"✍️ Умная типографика и ввод (Smart UX)","secTypographyDesc":"Автоматическое улучшение текстов по нормам русской типографики во время диалога и набора.","typography":"Типографика вывода","typographyDesc":"Исправляет типографику ответов модели: кавычки-«ёлочки», тире («—») вместо дефисов, неразрывные пробелы после предлогов.","yo":"Буква «ё»","yoDesc":"Восстанавливать «ё» в частых словах (ещё, чёрный, идёт и др.), написанных через «е». Неоднозначные слова (все/всё) не трогаются.","liveInput":"Живая типографика инпута","liveInputDesc":"Форматировать текст сообщения перед отправкой: кавычки «», тире — и неразрывные пробелы. Внимание: изменяет отправляемый агенту текст (команды и флаги с латиницей защищены).","slashAliases":"Русские алиасы команд","slashAliasesDesc":"Поддержка русских команд: /цель -> /goal, /сжать -> /compact, /план -> /plan, /справка -> /help, /память -> /memory.","altLHintText":"Мгновенная конвертация раскладки текущего поля (ghbdtn ⇄ привет, /vjltkm ⇄ /model). В углу поля ввода также отображается метка раскладки.","secAgentPrompt":"🤖 Системный промпт агента","secAgentPromptDesc":"Официальная секция расширения DSH systemPrompt для ведения диалога на русском языке.","agentPrompt":"Русский промпт агента","agentPromptDesc":"Добавляет в системный промпт инструкцию отвечать по-русски в выбранном стиле.","agentPromptPreset":"Стиль ответов агента","presetExpert":"Технический эксперт (строгая терминология, чистый код)","presetWriter":"Технический писатель (Markdown, таблицы, ГОСТ)","presetConcise":"Лаконичный режим (кратко, без лишней воды)","presetReviewer":"Код-ревьюер (поиск багов, безопасность, аудит)","presetArchitect":"Системный архитектор (контракты, масштабируемость)","presetTutor":"Наставник (понятные объяснения, пошаговый разбор)","secUpdater":"🔄 Обновление языкового пакета","secUpdaterDesc":"Проверка наличия новых релизов в реестре npm и обновление в один клик.","updaterCurrent":"Текущая версия: v{version}","updaterLatest":"Доступна новая версия: v{version}","updaterUpToDate":"Установлена актуальная версия","updaterChecking":"Проверка…","updaterCheckBtn":"Проверить обновления","updaterBtn":"Обновить до v{version} в 1 клик","updaterUpdating":"Установка обновления…","updaterSuccess":"✅ Плагин успешно обновлён! Перезапустите DSH для применения.","updaterFailed":"❌ Не удалось проверить/обновить плагин. Проверьте сеть или логи сервера.","badgeUpdateAvailable":"Доступно обновление","badgeUpToDate":"Актуальная версия","secTranslator":"🌐 Перевод сообщений ассистента","secTranslatorDesc":"Настройка перевода ответов модели в чате на русский язык (локально в Docker или онлайн).","translateEngine":"Движок перевода","engineOff":"Выключен (по умолчанию, без сетевых вызовов)","engineLocal":"Локальный LibreTranslate (приватно, ~600 МБ RAM)","engineGoogle":"Google Translate (онлайн)","googleWarn":"⚠️ Внимание: при онлайн-переводе текст сообщений ассистента передаётся на публичные серверы Google для перевода на лету. Не используйте для конфиденциальных данных.","localStatusRunning":"🟢 Контейнер LibreTranslate активен","localStatusStopped":"⚪ Контейнер LibreTranslate не запущен","localStartBtn":"Запустить LibreTranslate в Docker (~600 МБ RAM)","localStarting":"Запуск контейнера…","localInfo":"LibreTranslate запускается изолированно в Docker на сервере, потребляет ~500–700 МБ RAM и переводит на 100% локально.","secOverrides":"✍️ Пользовательские переопределения (Custom Overrides)","secOverridesDesc":"Возможность заменить любую формулировку интерфейса DSH на собственную. Переопределения сохраняются в настройках и имеют наивысший приоритет.","overrideKeyPlaceholder":"Ключ (например, common.back или settings.title)","overrideValuePlaceholder":"Ваш русский перевод","overrideAddBtn":"Добавить","overrideEmpty":"Переопределений пока нет.","overrideDelete":"Удалить","overrideTip":"💡 Совет: зажмите Alt и кликните на любой переведённый элемент интерфейса, чтобы открыть всплывающий инспектор перевода и сразу изменить формулировку.","secSupport":"📊 Покрытие экосистемы и поддержка","secSupportDesc":"Словари синхронизированы с DSH v0.1.6-alpha.1. 100.0% UI-покрытие ядра и всех установленных плагинов (7,902 ключа) без черновых машинных переводов.","statNamespaces":"Пространств имён","statCoreKeys":"Ключей ядра","statPluginKeys":"Ключей плагинов","overridesCount":"Своих переопределений","statusLoading":"Настройки загружаются…","statusUnavailable":"Настройки недоступны на этом хосте","translateTurn":"Перевести на русский","reportIssue":"Сообщить о неточности перевода","exportMdHint":"Экспорт диалога в Markdown доступен по кнопке [ 📥 MD ] в шапке сессии.","translateTurnHint":"Перевод ответов ассистента на русский доступен по кнопке [ RU ↗ ] на блоках сообщений."}
439
443
  const SETTINGS_NS_NAME = 'russian-lang'
440
444
  const ROW_CONFIG_KEY = '@goodandready/dsh-russian-lang#russian-lang'
441
445
  function apply(ctx) {const runtime = ctx.locale
@@ -1133,14 +1137,14 @@ try {setComposerText(el, expanded, expanded.length, expanded.length)
1133
1137
  }
1134
1138
  }
1135
1139
  if (ev.key === 'Enter' && !ev.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {const snapVal = scope ? (scope.getSnapshot().value || {}) : {}
1136
- const typoLive = snapVal.typography ? snapVal.typography.liveInput !== false : true
1140
+ const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
1137
1141
  if (typoLive && typeof formatInputLive === 'function' && (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT')) {const formatted = formatInputLive(value)
1138
1142
  if (formatted && formatted !== value) {setNativeInputValue(el, formatted, formatted.length, formatted.length)
1139
1143
  }
1140
1144
  }
1141
1145
  }
1142
1146
  try {const snapVal = scope ? (scope.getSnapshot().value || {}) : {}
1143
- const typoLive = snapVal.typography ? snapVal.typography.liveInput !== false : true
1147
+ const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
1144
1148
  if (typoLive && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {const isTextarea = el.tagName === 'TEXTAREA' || el.tagName === 'INPUT'
1145
1149
  if (isTextarea) {const caretPos = el.selectionStart || 0
1146
1150
  if (!isCaretInCode(el, value, caretPos)) {const textBefore = caretPos >= 0 ? value.slice(0, caretPos) : value
@@ -1266,7 +1270,7 @@ setTimeout(() => { copyKeyBtn.textContent = '📋' }, 1500)
1266
1270
  }
1267
1271
  const copyReportBtn = modal.querySelector('.rl-copy-report-btn')
1268
1272
  if (copyReportBtn) {copyReportBtn.onclick = () => {const snippet = typeof generateBugReportSnippet === 'function'
1269
- ? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.8' })
1273
+ ? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.9' })
1270
1274
  : ('### Репорт: ' + (k || rawText) + '\n- Исходный: ' + (origText || rawText) + '\n- Перевод: ' + textarea.value.trim())
1271
1275
  try { navigator.clipboard.writeText(snippet) } catch (err) { void err; }
1272
1276
  copyReportBtn.textContent = '✓ Скопировано'
@@ -1482,7 +1486,7 @@ setTimeout(() => setDone(false), 2000)
1482
1486
  return React.createElement('button', {type: 'button',className: 'rl-lang-chip rl-export-md-btn',title: 'Экспорт диалога в Markdown (.md)',onClick: onExport,}, React.createElement('span', { className: 'rl-lang-text' }, done ? '✓ MD' : '📥 MD'))
1483
1487
  }
1484
1488
  async function translateTurnContent(text) {if (!text || typeof text !== 'string') return { error: 'Текст для перевода пуст.' }
1485
- try {const res = await fetch('/api/dsh-russian-lang/translate', {headers: { 'content-type': 'application/json', 'x-dsh-translator': '1' },method: 'POST',headers: { 'content-type': 'application/json' },body: JSON.stringify({ text })
1489
+ try {const res = await fetch('/api/dsh-russian-lang/translate', {method: 'POST',headers: { 'content-type': 'application/json', 'x-dsh-translator': '1' },body: JSON.stringify({ text })
1486
1490
  })
1487
1491
  const data = await res.json().catch(() => ({}))
1488
1492
  if (!res.ok) {return { error: data.error || ('Ошибка перевода (HTTP ' + res.status + ')') }
@@ -1583,7 +1587,7 @@ const [ruActive, setRuActive] = React.useState(
1583
1587
  () => { try { return runtime.getLocale().active === 'ru' } catch (e) { return false } })
1584
1588
  const [typo, setTypoState] = React.useState(() =>
1585
1589
  (snap.value && snap.value.typography) || {})
1586
- const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.8',latestVersion: undefined,updateAvailable: false
1590
+ const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.9',latestVersion: undefined,updateAvailable: false
1587
1591
  })
1588
1592
  const [upLoading, setUpLoading] = React.useState(false)
1589
1593
  const [upMsg, setUpMsg] = React.useState(null)
@@ -1703,7 +1707,7 @@ return React.createElement('div', { className: page ? 'rl-page-seat' : 'rl-card'
1703
1707
  ),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(typography.yo === true && ruActive,(ev) => setTypo({ yo: ev.target.checked }), !ruActive || disabled),t('yo')
1704
1708
  )
1705
1709
  ),React.createElement('div', { className: 'rl-item-desc' }, t('yoDesc'))
1706
- ),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(value.slashAliases !== false && ruActive,(ev) => {try { scope.set('slashAliases', ev.target.checked) } catch (err) { void e; }
1710
+ ),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(value.slashAliases !== false && ruActive,(ev) => {try { scope.set('slashAliases', ev.target.checked) } catch (err) { void err; }
1707
1711
  }, !ruActive || disabled),t('slashAliases')
1708
1712
  )
1709
1713
  ),React.createElement('div', { className: 'rl-item-desc' }, t('slashAliasesDesc'))
@@ -1729,7 +1733,7 @@ scope.set('agentPromptPreset', val)
1729
1733
  ) : null
1730
1734
  ) : null
1731
1735
  ),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secUpdater')),React.createElement('span', { className: 'rl-badge ' + (upStatus.updateAvailable ? 'rl-badge-warn' : 'rl-badge-ok') },upStatus.updateAvailable ? t('badgeUpdateAvailable') : t('badgeUpToDate'))
1732
- ),React.createElement('div', { className: 'rl-section-desc' }, t('secUpdaterDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },React.createElement('div', null,React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },t('updaterCurrent').replace('{version}', upStatus.currentVersion || '0.3.8')),upStatus.updateAvailable
1736
+ ),React.createElement('div', { className: 'rl-section-desc' }, t('secUpdaterDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },React.createElement('div', null,React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },t('updaterCurrent').replace('{version}', upStatus.currentVersion || '0.3.9')),upStatus.updateAvailable
1733
1737
  ? React.createElement('div', { style: { color: 'var(--dsw-alias-state-warning-primary)', marginTop: '2px', fontWeight: 500 } },t('updaterLatest').replace('{version}', upStatus.latestVersion || ''))
1734
1738
  : React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', marginTop: '2px' } },t('updaterUpToDate'))
1735
1739
  ),upStatus.updateAvailable
@@ -1764,7 +1768,7 @@ if (val === 'local') fetchTransStatus()
1764
1768
  const v = newVal.trim()
1765
1769
  if (!k || !v) return
1766
1770
  const next = Object.assign({}, overrides, { [k]: v })
1767
- try { scope.set('overrides', updated) } catch (_e) { void _e }
1771
+ try { scope.set('overrides', next) } catch (_e) { void _e }
1768
1772
  setNewKey('')
1769
1773
  setNewVal('')
1770
1774
  },}, t('overrideAddBtn'))
@@ -1774,7 +1778,7 @@ setNewVal('')
1774
1778
  React.createElement('div', {key: k,style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '6px 10px', background: 'var(--dsw-alias-bg-layer-3)', borderRadius: '6px', border: '1px solid var(--dsw-alias-border-l2)', fontSize: '12px' },},React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', overflow: 'hidden' } },React.createElement('code', { style: { color: 'var(--dsw-alias-state-brand-primary)', fontWeight: 600 } }, k),React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)' } }, '→'),React.createElement('span', { style: { color: 'var(--dsw-alias-label-primary)' } }, overrides[k])
1775
1779
  ),React.createElement('button', {type: 'button',className: 'rl-btn',style: { padding: '2px 8px', height: '24px', fontSize: '11px', color: 'var(--dsw-alias-state-error-primary)' },onClick: () => {const next = Object.assign({}, overrides)
1776
1780
  delete next[k]
1777
- try { scope.set('overrides', updated) } catch (_e) { void _e }
1781
+ try { scope.set('overrides', next) } catch (_e) { void _e }
1778
1782
  },}, t('overrideDelete'))
1779
1783
  )
1780
1784
  )
@@ -1799,7 +1803,7 @@ scope.set('overrides', merged)
1799
1803
  ),React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '1 225'),React.createElement('div', { className: 'rl-stat-label' }, t('statCoreKeys'))
1800
1804
  ),React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '5 924'),React.createElement('div', { className: 'rl-stat-label' }, t('statPluginKeys'))
1801
1805
  )
1802
- ),React.createElement('div', { className: 'rl-actions-row' },React.createElement('a', {href: makeIssueUrl({}, '0.3.8'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
1806
+ ),React.createElement('div', { className: 'rl-actions-row' },React.createElement('a', {href: makeIssueUrl({}, '0.3.9'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
1803
1807
  }, '💬 ' + t('reportIssue')),React.createElement('div', { className: 'rl-hint-text', style: { flex: 1 } },t('exportMdHint')
1804
1808
  )
1805
1809
  )
@@ -1333,7 +1333,7 @@ window.__ModuleLoader__.load({
1333
1333
  // #363: Живая типографика formatInputLive перед отправкой по Enter
1334
1334
  if (ev.key === 'Enter' && !ev.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {
1335
1335
  const snapVal = scope ? (scope.getSnapshot().value || {}) : {}
1336
- const typoLive = snapVal.typography ? snapVal.typography.liveInput !== false : true
1336
+ const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
1337
1337
  if (typoLive && typeof formatInputLive === 'function' && (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT')) {
1338
1338
  const formatted = formatInputLive(value)
1339
1339
  if (formatted && formatted !== value) {
@@ -1345,7 +1345,7 @@ window.__ModuleLoader__.load({
1345
1345
  // #198 / #202: Защита ввода в Lexical composer и нативных полях
1346
1346
  try {
1347
1347
  const snapVal = scope ? (scope.getSnapshot().value || {}) : {}
1348
- const typoLive = snapVal.typography ? snapVal.typography.liveInput !== false : true
1348
+ const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
1349
1349
  if (typoLive && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {
1350
1350
  const isTextarea = el.tagName === 'TEXTAREA' || el.tagName === 'INPUT'
1351
1351
 
@@ -1884,9 +1884,8 @@ window.__ModuleLoader__.load({
1884
1884
  if (!text || typeof text !== 'string') return { error: 'Текст для перевода пуст.' }
1885
1885
  try {
1886
1886
  const res = await fetch('/api/dsh-russian-lang/translate', {
1887
- headers: { 'content-type': 'application/json', 'x-dsh-translator': '1' },
1888
1887
  method: 'POST',
1889
- headers: { 'content-type': 'application/json' },
1888
+ headers: { 'content-type': 'application/json', 'x-dsh-translator': '1' },
1890
1889
  body: JSON.stringify({ text })
1891
1890
  })
1892
1891
  const data = await res.json().catch(() => ({}))
@@ -2293,7 +2292,7 @@ window.__ModuleLoader__.load({
2293
2292
  React.createElement('label', { className: 'rl-item-label' },
2294
2293
  checkbox(value.slashAliases !== false && ruActive,
2295
2294
  (ev) => {
2296
- try { scope.set('slashAliases', ev.target.checked) } catch (err) { /* bestEffort */ void e; }
2295
+ try { scope.set('slashAliases', ev.target.checked) } catch (err) { /* bestEffort */ void err; }
2297
2296
  }, !ruActive || disabled),
2298
2297
  t('slashAliases')
2299
2298
  )
@@ -2498,7 +2497,7 @@ window.__ModuleLoader__.load({
2498
2497
  const v = newVal.trim()
2499
2498
  if (!k || !v) return
2500
2499
  const next = Object.assign({}, overrides, { [k]: v })
2501
- try { scope.set('overrides', updated) } catch (_e) { void _e }
2500
+ try { scope.set('overrides', next) } catch (_e) { void _e }
2502
2501
  setNewKey('')
2503
2502
  setNewVal('')
2504
2503
  },
@@ -2524,7 +2523,7 @@ window.__ModuleLoader__.load({
2524
2523
  onClick: () => {
2525
2524
  const next = Object.assign({}, overrides)
2526
2525
  delete next[k]
2527
- try { scope.set('overrides', updated) } catch (_e) { void _e }
2526
+ try { scope.set('overrides', next) } catch (_e) { void _e }
2528
2527
  },
2529
2528
  }, t('overrideDelete'))
2530
2529
  )