@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 +20 -1
- package/lib/client.js +18 -14
- package/lib/client.template.js +6 -7
- package/lib/locales/core.json +1 -1
- package/lib/pure.js +6 -11
- package/lib/translator.js +5 -7
- package/package.json +1 -1
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
|
+
<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.
|
|
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":"
|
|
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
|
|
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
|
|
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.
|
|
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' },
|
|
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.
|
|
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
|
|
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.
|
|
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',
|
|
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',
|
|
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.
|
|
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
|
)
|
package/lib/client.template.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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',
|
|
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',
|
|
2526
|
+
try { scope.set('overrides', next) } catch (_e) { void _e }
|
|
2528
2527
|
},
|
|
2529
2528
|
}, t('overrideDelete'))
|
|
2530
2529
|
)
|