@goodandready/dsh-russian-lang 0.3.14 → 0.3.16
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 +45 -37
- package/lib/client.template.js +63 -47
- package/lib/index.js +137 -27
- package/lib/locales.js +1 -1
- package/lib/translator.js +27 -5
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({id: '@goodandready/dsh-russian-lang',factory: (require) => {var module = { exports: {} }
|
|
2
2
|
var React = null
|
|
3
|
-
try { React = require('react') } catch (e) { }
|
|
3
|
+
try { React = require('react') } catch (e) { void e; }
|
|
4
4
|
let isInspectorActive = false
|
|
5
5
|
let hoverBoxEl = null
|
|
6
6
|
let pillEl = null
|
|
7
|
+
let updateInspectorPill = () => {}
|
|
7
8
|
const numberFormat = new Intl.NumberFormat('ru-RU')
|
|
8
9
|
const compactNumberFormat = new Intl.NumberFormat('ru-RU', { notation: 'compact', compactDisplay: 'short' })
|
|
9
10
|
const relativeTimeFormat = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
|
|
@@ -394,16 +395,20 @@ const RU = {"common":{"back":"Назад","cancel":"Отмена","close":"За
|
|
|
394
395
|
const ZH_RU = {}
|
|
395
396
|
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":"Команды: /цель, /сжать, /план, /справка, /память.","altLHintText":"Конвертация раскладки текущего поля (ghbdtn ⇄ привет).","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":"Перевод ответов модели в чате на русский язык.","translateEngine":"Движок перевода","engineOff":"Выключен (по умолчанию, без сетевых вызовов)","engineLocal":"Локальный LibreTranslate (приватно, ~600 МБ RAM)","engineGoogle":"Google Translate (онлайн)","googleWarn":"⚠️ Текст сообщений передаётся на публичные серверы Google.","localStatusRunning":"🟢 Контейнер LibreTranslate активен","localStatusStopped":"⚪ Контейнер LibreTranslate не запущен","localStartBtn":"Запустить LibreTranslate в Docker (~600 МБ RAM)","localStarting":"Запуск контейнера…","localInfo":"LibreTranslate в Docker: изолированно, ~600 МБ RAM.","secOverrides":"✍️ Пользовательские переопределения (Custom Overrides)","secOverridesDesc":"Замена любой формулировки интерфейса DSH на собственную.","overrideKeyPlaceholder":"Ключ (например, common.back или settings.title)","overrideValuePlaceholder":"Ваш русский перевод","overrideAddBtn":"Добавить","overrideEmpty":"Переопределений пока нет.","overrideDelete":"Удалить","overrideTip":"💡 Зажмите Alt и кликните на элемент UI для быстрой правки.","secSupport":"📊 Покрытие экосистемы и поддержка","secSupportDesc":"100% UI-покрытие ядра и плагинов DSH v0.1.6-alpha.1.","statNamespaces":"Пространств имён","statCoreKeys":"Ключей ядра","statPluginKeys":"Ключей плагинов","overridesCount":"Своих переопределений","statusLoading":"Настройки загружаются…","statusUnavailable":"Настройки недоступны на этом хосте","translateTurn":"Перевести на русский","reportIssue":"Сообщить о неточности перевода","exportMdHint":"Экспорт диалога в Markdown доступен по кнопке [ 📥 MD ] в шапке сессии.","translateTurnHint":"Перевод ответов ассистента на русский доступен по кнопке [ RU ↗ ] на блоках сообщений."}
|
|
396
397
|
const SETTINGS_NS_NAME = 'russian-lang'
|
|
397
|
-
const
|
|
398
|
+
const FORM_NS = 'dsh-russian-lang'
|
|
399
|
+
const LEGACY_NS = 'russian-lang'
|
|
400
|
+
const ROW_CONFIG_KEY = '@goodandready/dsh-russian-lang#' + FORM_NS
|
|
398
401
|
function apply(ctx) {const runtime = ctx.locale
|
|
399
|
-
const
|
|
400
|
-
if (c && c.
|
|
402
|
+
const configForms = ctx && ctx.configForms
|
|
403
|
+
const resolveScope = (c) => {try {if (c && c.configForms) return c.configForms.get(FORM_NS)
|
|
404
|
+
if (c && c.settingsScope) return c.settingsScope.bind({ namespace: LEGACY_NS })
|
|
401
405
|
} catch (err) { void err }
|
|
402
406
|
return null
|
|
403
407
|
}
|
|
404
408
|
const rawScope = resolveScope(ctx)
|
|
405
409
|
const scope = (rawScope && typeof rawScope.getSnapshot === 'function') ? rawScope : {getSnapshot: () => ({ status: 'ready', value: {} }),subscribe: () => () => {},set: () => Promise.resolve()
|
|
406
410
|
}
|
|
411
|
+
const legacyFlagMode = !(ctx && ctx.configForms)
|
|
407
412
|
for (const ns of Object.keys(RU)) {ctx.effect(() => {try { return ctx.locale.register(ns, 'ru', RU[ns]) } catch (err) { return () => {} }}, 'dsh-russian-lang: ' + ns)
|
|
408
413
|
}
|
|
409
414
|
const registerDictMap = (dictMap) => {if (!dictMap || typeof dictMap !== 'object') return
|
|
@@ -413,7 +418,7 @@ if (typeof dict !== 'object' || !dict) continue
|
|
|
413
418
|
if (!RU[ns]) {RU[ns] = dict
|
|
414
419
|
ctx.effect(() => {try { return ctx.locale.register(ns, 'ru', dict) } catch (err) { return () => {} }}, 'dsh-russian-lang: ' + ns)
|
|
415
420
|
} else {Object.assign(RU[ns], dict)
|
|
416
|
-
try { ctx.locale.register(ns, 'ru', RU[ns]) } catch (_) {}}}}
|
|
421
|
+
try { ctx.locale.register(ns, 'ru', RU[ns]) } catch (_) { void _; }}}}
|
|
417
422
|
const CACHE_NAME = 'dsh-ru-cache-v1'
|
|
418
423
|
const loadLocalDict = (key) => {try {const raw = typeof localStorage !== 'undefined' ? localStorage.getItem('dsh_ru_' + key) : null
|
|
419
424
|
return raw ? JSON.parse(raw) : null
|
|
@@ -498,13 +503,13 @@ return value && value.overrides ? value.overrides : {}} catch (err) { return {}
|
|
|
498
503
|
const getPluginLocalizationStatus = makePluginLocalizationStatus(RU)
|
|
499
504
|
const lookupChain = () => {try {const chain = runtime.fallbackChain && runtime.fallbackChain(runtime.getLocale().active)
|
|
500
505
|
if (Array.isArray(chain) && chain.length) return chain
|
|
501
|
-
} catch (err) { }
|
|
506
|
+
} catch (err) { void err; }
|
|
502
507
|
return [runtime.getLocale().active]
|
|
503
508
|
}
|
|
504
509
|
const translationRegistry = new Map()
|
|
505
510
|
const lookup = (ns, key) => (runtime.lookup.length >= 3 ? runtime.lookup(ns, key, lookupChain()) : runtime.lookup(ns, key))
|
|
506
511
|
const syncLang = () => {try {if (typeof document !== 'undefined' && document.documentElement && runtime.getLocale().active === 'ru') document.documentElement.lang = 'ru-RU'
|
|
507
|
-
} catch (err) { }}
|
|
512
|
+
} catch (err) { void err; }}
|
|
508
513
|
const RUNTIME_PROP_NAMES = ['plural','pluralForm','formatDate','formatCompactNumber','formatTokens','formatNumber','formatRelativeTime','formatCurrency','inflect','getPluginLocalizationStatus','stemRussian','fuzzyMatchRu','humanizeError']
|
|
509
514
|
ctx.effect(() => {const origTranslate = runtime.translate
|
|
510
515
|
const origProps = {}
|
|
@@ -539,8 +544,9 @@ return () => {runtime.translate = origTranslate
|
|
|
539
544
|
for (const p of RUNTIME_PROP_NAMES) {if (origProps[p] !== undefined) runtime[p] = origProps[p]
|
|
540
545
|
else delete runtime[p]
|
|
541
546
|
}
|
|
542
|
-
if (typeof removeLang === 'function') { try { removeLang() } catch (_) { } }}}, 'dsh-russian-lang: runtime-lifecycle')
|
|
543
|
-
const syncFlag = () => {
|
|
547
|
+
if (typeof removeLang === 'function') { try { removeLang() } catch (_) { void _; } }}}, 'dsh-russian-lang: runtime-lifecycle')
|
|
548
|
+
const syncFlag = () => {if (!legacyFlagMode) return
|
|
549
|
+
try {const wantRu = runtime.getLocale().active === 'ru'
|
|
544
550
|
const value = scope.getSnapshot().value || {}
|
|
545
551
|
if (!!value.enabled !== wantRu) {const r = scope.set('enabled', wantRu)
|
|
546
552
|
if (r && typeof r.catch === 'function') {r.catch((err) => {console.warn('dsh-russian-lang: scope.set enabled failed', err && err.message || err)
|
|
@@ -554,7 +560,7 @@ runtime.setLocale('ru')
|
|
|
554
560
|
let booted = false
|
|
555
561
|
const tryBoot = () => {if (booted) return
|
|
556
562
|
try {const value = scope.getSnapshot().value
|
|
557
|
-
if (value && value.enabled === true) { booted = true; activate() }} catch (err) { }}
|
|
563
|
+
if (legacyFlagMode && value && value.enabled === true) { booted = true; activate() }} catch (err) { void err; }}
|
|
558
564
|
ctx.effect(() => scope.subscribe(tryBoot), 'dsh-russian-lang: boot')
|
|
559
565
|
tryBoot()
|
|
560
566
|
const unsubscribeLang = runtime.subscribe(syncLang)
|
|
@@ -598,11 +604,11 @@ if (node.matches(EDITABLE)) spellOn(node)
|
|
|
598
604
|
node.querySelectorAll ? node.querySelectorAll(EDITABLE).forEach(spellOn) : null
|
|
599
605
|
}}})
|
|
600
606
|
spellObserver.observe(document.body, { childList: true, subtree: true })
|
|
601
|
-
} catch (err) { }}
|
|
607
|
+
} catch (err) { void err; }}
|
|
602
608
|
const unsubscribeSpell = runtime.subscribe(syncSpell)
|
|
603
609
|
ctx.effect(() => {return () => {unsubscribeSpell()
|
|
604
610
|
if (spellObserver) spellObserver.disconnect()
|
|
605
|
-
try { document.querySelectorAll('[' + SPELL_ON + ']').forEach(spellOff) } catch (err) { }}}, 'dsh-russian-lang: spellcheck')
|
|
611
|
+
try { document.querySelectorAll('[' + SPELL_ON + ']').forEach(spellOff) } catch (err) { void err; }}}, 'dsh-russian-lang: spellcheck')
|
|
606
612
|
syncSpell()
|
|
607
613
|
const ZH_CJK = /[\u3400-\u9fff\uf900-\ufaff]/
|
|
608
614
|
const ZH_RE_ESC = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
@@ -663,9 +669,10 @@ const trimmed = val.trim()
|
|
|
663
669
|
if (curOverrides[trimmed] !== undefined) {node.nodeValue = val.replace(trimmed, curOverrides[trimmed])
|
|
664
670
|
continue
|
|
665
671
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
if (
|
|
672
|
+
const p = node.parentElement
|
|
673
|
+
const isProtected = p && p.closest('code, pre, script, style, textarea, input, select, kbd, samp, [contenteditable="true"], [data-composer-input], [role="textbox"], .chat-message, .markdown-body')
|
|
674
|
+
if (trimmed.length >= 1 && ZH_CJK.test(val)) {if (!isProtected) hits.push(node)
|
|
675
|
+
} else if (trimmed.length >= 1 && DOM_EN_TEXT[trimmed]) {if (!isProtected) {node.nodeValue = val.replace(trimmed, DOM_EN_TEXT[trimmed])
|
|
669
676
|
}}}
|
|
670
677
|
for (const node of hits) {const next = zhTranslateText(node.nodeValue)
|
|
671
678
|
if (next && next !== node.nodeValue) node.nodeValue = next
|
|
@@ -681,7 +688,7 @@ if (root.getAttribute) {for (const attr of ['title', 'placeholder', 'aria-label'
|
|
|
681
688
|
if (v) {const tr = v.trim()
|
|
682
689
|
if (DOM_EN_TEXT[tr]) root.setAttribute(attr, v.replace(tr, DOM_EN_TEXT[tr]))
|
|
683
690
|
else if (DOM_EN_ATTRS[tr]) root.setAttribute(attr, v.replace(tr, DOM_EN_ATTRS[tr]))
|
|
684
|
-
}}}} catch (err) { }}
|
|
691
|
+
}}}} catch (err) { void err; }}
|
|
685
692
|
let zhObserver = null
|
|
686
693
|
let zhDebounceTimer = null
|
|
687
694
|
let zhWalking = false
|
|
@@ -711,7 +718,7 @@ document.addEventListener('pointerup', onInteraction, { passive: true })
|
|
|
711
718
|
document.addEventListener('click', onInteraction, { passive: true })
|
|
712
719
|
}
|
|
713
720
|
queueZhWalk()
|
|
714
|
-
} catch (err) { }}
|
|
721
|
+
} catch (err) { void err; }}
|
|
715
722
|
const unsubscribeZh = runtime.subscribe(syncZhDom)
|
|
716
723
|
ctx.effect(() => {return () => {unsubscribeZh()
|
|
717
724
|
if (zhObserver) zhObserver.disconnect()
|
|
@@ -755,7 +762,7 @@ for (const root of typoPending) {if (root.nodeType === 3) typoNode(root, conf)
|
|
|
755
762
|
else typoWalk(root, conf)
|
|
756
763
|
}
|
|
757
764
|
typoPending.clear()
|
|
758
|
-
} catch (err) { }}
|
|
765
|
+
} catch (err) { void err; }}
|
|
759
766
|
const typoPending = new Set()
|
|
760
767
|
const queueTypo = (roots) => {for (const r of roots) typoPending.add(r)
|
|
761
768
|
if (!typoQueued) typoQueued = requestAnimationFrame(flushTypo)
|
|
@@ -772,7 +779,7 @@ for (const record of records) {if (record.type === 'childList') {for (const adde
|
|
|
772
779
|
if (roots.length > 0) queueTypo(roots)
|
|
773
780
|
})
|
|
774
781
|
typoObserver.observe(document.body, { childList: true, subtree: true })
|
|
775
|
-
} catch (err) { }}
|
|
782
|
+
} catch (err) { void err; }}
|
|
776
783
|
const unsubscribeTypo = runtime.subscribe(syncTypo)
|
|
777
784
|
ctx.effect(() => {return () => {unsubscribeTypo()
|
|
778
785
|
if (typoObserver) typoObserver.disconnect()
|
|
@@ -947,7 +954,7 @@ if (c) { layoutShowHint(el, c.converted, 'ru'); return }}
|
|
|
947
954
|
if (cyrCount > 0 && latCount === 0 && value.trim().startsWith('/')) {const c = layoutFixCandidate(value, 'cyr2lat')
|
|
948
955
|
if (c) { layoutShowHint(el, c.converted, 'cmd'); return }}
|
|
949
956
|
layoutDismiss()
|
|
950
|
-
} catch (err) { }}
|
|
957
|
+
} catch (err) { void err; }}
|
|
951
958
|
const unsubscribeLayout = runtime.subscribe(layoutOnInput)
|
|
952
959
|
const layoutOnKeydown = (ev) => {const el = layoutCurrentInput(ev)
|
|
953
960
|
if (!el) return
|
|
@@ -1034,7 +1041,7 @@ document.body.appendChild(hoverBoxEl)
|
|
|
1034
1041
|
}
|
|
1035
1042
|
return hoverBoxEl
|
|
1036
1043
|
}
|
|
1037
|
-
|
|
1044
|
+
updateInspectorPill = () => {if (typeof document === 'undefined') return
|
|
1038
1045
|
if (!pillEl) {pillEl = document.createElement('div')
|
|
1039
1046
|
pillEl.id = 'dsh-ru-inspector-pill'
|
|
1040
1047
|
pillEl.className = 'rl-inspector-pill'
|
|
@@ -1047,7 +1054,7 @@ if (hoverBoxEl) hoverBoxEl.style.display = 'none'
|
|
|
1047
1054
|
document.body.appendChild(pillEl)
|
|
1048
1055
|
}
|
|
1049
1056
|
pillEl.style.display = isInspectorActive ? 'flex' : 'none'
|
|
1050
|
-
if (isInspectorActive) pillEl.classList.add('rl-inspector-pill-active')
|
|
1057
|
+
if (isInspectorActive && pillEl.classList) pillEl.classList.add('rl-inspector-pill-active')
|
|
1051
1058
|
}
|
|
1052
1059
|
const openInspectorModal = (meta, rawText, targetElement) => {if (typeof document === 'undefined') return
|
|
1053
1060
|
if (hoverBoxEl) hoverBoxEl.style.display = 'none'
|
|
@@ -1122,7 +1129,7 @@ setTimeout(() => { copyKeyBtn.textContent = '📋' }, 1500)
|
|
|
1122
1129
|
}}
|
|
1123
1130
|
const copyReportBtn = modal.querySelector('.rl-copy-report-btn')
|
|
1124
1131
|
if (copyReportBtn) {copyReportBtn.onclick = () => {const snippet = typeof generateBugReportSnippet === 'function'
|
|
1125
|
-
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.
|
|
1132
|
+
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.16' })
|
|
1126
1133
|
: ('### Репорт: ' + (k || rawText) + '\n- Исходный: ' + (origText || rawText) + '\n- Перевод: ' + textarea.value.trim())
|
|
1127
1134
|
try { navigator.clipboard.writeText(snippet) } catch (err) { void err; }
|
|
1128
1135
|
copyReportBtn.textContent = '✓ Скопировано'
|
|
@@ -1239,28 +1246,29 @@ if (!ctx.slots || !React) return
|
|
|
1239
1246
|
const toggleRu = (wantRu) => {try {if (runtime.getLocale().active === wantRu) return
|
|
1240
1247
|
runtime.setLocale(wantRu ? 'ru' : 'en')
|
|
1241
1248
|
} catch (err) { console.warn('dsh-russian-lang: toggle failed', err) }}
|
|
1242
|
-
|
|
1243
|
-
ctx.slots.register({name: 'plugins.item',id:
|
|
1249
|
+
const registerPluginsItem = () => {ctx.slots.inject('plugins.item', () =>
|
|
1250
|
+
ctx.slots.register({name: 'plugins.item',id: FORM_NS,order: 60,label: () => 'Russian language',locale: SETTINGS_NS_NAME,inject: () => ({ scope, runtime, toggleRu }),}, SettingsCard),)
|
|
1251
|
+
}
|
|
1252
|
+
if (typeof configForms?.whileServed === 'function') {ctx.effect(() => configForms.whileServed([FORM_NS], registerPluginsItem), 'dsh-russian-lang: plugins.item')
|
|
1253
|
+
} else {registerPluginsItem()
|
|
1254
|
+
}
|
|
1244
1255
|
ctx.slots.inject('plugins.row.config', () =>
|
|
1245
1256
|
ctx.slots.register({name: 'plugins.row.config',key: ROW_CONFIG_KEY,locale: SETTINGS_NS_NAME,inject: () => ({ scope, runtime, toggleRu }),}, SettingsCard),)
|
|
1246
|
-
ctx.slots.inject('settings.plugin.item', () =>
|
|
1247
|
-
ctx.slots.register({name: 'settings.plugin.item',key: SETTINGS_NS_NAME,locale: SETTINGS_NS_NAME,inject: () => ({ scope, runtime, toggleRu }),}, SettingsCard),)
|
|
1248
|
-
} catch (err) { console.warn('dsh-russian-lang: settings slot unavailable', err) }
|
|
1249
1257
|
try {ctx.slots.inject('conversation.session.header.utilities', () =>
|
|
1250
1258
|
ctx.slots.register({name: 'conversation.session.header.utilities',id: 'dsh-russian-lang-quick-switch',order: 100,locale: SETTINGS_NS_NAME,inject: () => ({ runtime, toggleRu }),}, QuickLangSwitch),)
|
|
1251
|
-
} catch (err) { }
|
|
1259
|
+
} catch (err) { void err; }
|
|
1252
1260
|
try {ctx.slots.inject('conversation.chat.assistant-actions', () =>
|
|
1253
1261
|
ctx.slots.register({name: 'conversation.chat.assistant-actions',id: 'dsh-russian-lang-translate-action',order: 50,locale: SETTINGS_NS_NAME,inject: () => ({ runtime }),}, TranslateTurnAction),)
|
|
1254
|
-
} catch (err) { }
|
|
1262
|
+
} catch (err) { void err; }
|
|
1255
1263
|
try {ctx.slots.inject('conversation.session.header.utilities', () =>
|
|
1256
1264
|
ctx.slots.register({name: 'conversation.session.header.utilities',id: 'dsh-russian-lang-md-export',order: 101,locale: SETTINGS_NS_NAME,inject: () => ({ runtime }),}, ExportMarkdownButton),)
|
|
1257
|
-
} catch (err) { }}
|
|
1265
|
+
} catch (err) { void err; }}
|
|
1258
1266
|
function QuickLangSwitch(props) {const inj = typeof props.inject === 'function' ? (props.inject() || {}) : (props.inject || {})
|
|
1259
1267
|
const runtime = props.runtime || inj.runtime
|
|
1260
1268
|
const toggleRu = props.toggleRu || inj.toggleRu
|
|
1261
1269
|
const [locale, setLocaleState] = React.useState(runtime ? (runtime.getLocale().active || 'en') : 'ru')
|
|
1262
1270
|
React.useEffect(() => {if (!runtime) return
|
|
1263
|
-
return runtime.subscribe(() => {try { setLocaleState(runtime.getLocale().active || 'en') } catch (e) { }})
|
|
1271
|
+
return runtime.subscribe(() => {try { setLocaleState(runtime.getLocale().active || 'en') } catch (e) { void e; }})
|
|
1264
1272
|
}, [runtime])
|
|
1265
1273
|
const isRu = locale === 'ru'
|
|
1266
1274
|
return React.createElement('button', {type: 'button',className: 'rl-lang-chip' + (isRu ? ' rl-lang-chip-active' : ''),title: isRu ? 'Интерфейс: Русский (нажмите для переключения на EN)' : 'Interface: English (click for RU)',onClick: () => { if (toggleRu) toggleRu(!isRu) },}, React.createElement('span', { className: 'rl-lang-text' }, isRu ? 'RU' : 'EN'))
|
|
@@ -1409,7 +1417,7 @@ const [snap, setSnap] = React.useState(() => (scope && scope.getSnapshot ? scope
|
|
|
1409
1417
|
const [ruActive, setRuActive] = React.useState(() => { try { return runtime.getLocale().active === 'ru' } catch (e) { return false } })
|
|
1410
1418
|
const [typo, setTypoState] = React.useState(() =>
|
|
1411
1419
|
(snap.value && snap.value.typography) || {})
|
|
1412
|
-
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.
|
|
1420
|
+
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.16',latestVersion: undefined,updateAvailable: false
|
|
1413
1421
|
})
|
|
1414
1422
|
const [upLoading, setUpLoading] = React.useState(false)
|
|
1415
1423
|
const [upMsg, setUpMsg] = React.useState(null)
|
|
@@ -1457,7 +1465,7 @@ if (s.value && s.value.typography) setTypoState(s.value.typography)
|
|
|
1457
1465
|
setSnap(scope.getSnapshot())
|
|
1458
1466
|
return un
|
|
1459
1467
|
}, [scope])
|
|
1460
|
-
React.useEffect(() => {try {const un = runtime.subscribe(() => {try { setRuActive(runtime.getLocale().active === 'ru') } catch (e) { }})
|
|
1468
|
+
React.useEffect(() => {try {const un = runtime.subscribe(() => {try { setRuActive(runtime.getLocale().active === 'ru') } catch (e) { void e; }})
|
|
1461
1469
|
return un
|
|
1462
1470
|
} catch (e) { return undefined }}, [runtime])
|
|
1463
1471
|
const status = snap.status || 'loading'
|
|
@@ -1502,7 +1510,7 @@ scope.set('agentPromptPreset', val)
|
|
|
1502
1510
|
} catch (err) { console.warn('dsh-russian-lang: set agentPromptPreset failed', err) }}},React.createElement('option', { value: 'technical_expert' }, t('presetExpert')),React.createElement('option', { value: 'tech_writer' }, t('presetWriter')),React.createElement('option', { value: 'concise' }, t('presetConcise')),React.createElement('option', { value: 'code_reviewer' }, t('presetReviewer')),React.createElement('option', { value: 'architect' }, t('presetArchitect')),React.createElement('option', { value: 'tutor' }, t('presetTutor')))),presetInfo ? React.createElement('div', { className: 'rl-preview-box' },React.createElement('div', { style: { fontWeight: 600, marginBottom: '4px', color: 'var(--dsw-alias-label-primary)' } }, '💬 ' + presetInfo.label + ':'),presetInfo.text
|
|
1503
1511
|
) : null
|
|
1504
1512
|
) : null
|
|
1505
|
-
),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'))),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.
|
|
1513
|
+
),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'))),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.16')),upStatus.updateAvailable
|
|
1506
1514
|
? React.createElement('div', { style: { color: 'var(--dsw-alias-state-warning-primary)', marginTop: '2px', fontWeight: 500 } },t('updaterLatest').replace('{version}', upStatus.latestVersion || ''))
|
|
1507
1515
|
: React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', marginTop: '2px' } },t('updaterUpToDate'))),upStatus.updateAvailable
|
|
1508
1516
|
? React.createElement('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: upLoading,onClick: triggerUpdate,}, upLoading ? t('updaterUpdating') : t('updaterBtn').replace('{version}', upStatus.latestVersion || ''))
|
|
@@ -1535,12 +1543,12 @@ try { scope.set('overrides', next) } catch (_e) { void _e }},}, t('overrideDelet
|
|
|
1535
1543
|
updateInspectorPill()
|
|
1536
1544
|
},}, isInspectorActive ? '🔍 Инспектор перевода (включён)' : '🔍 Включить инспектор перевода (Alt+I)'),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {try {navigator.clipboard.writeText(JSON.stringify(overrides, null, 2))
|
|
1537
1545
|
alert('Оверрайды скопированы в буфер обмена как JSON!')
|
|
1538
|
-
} catch (err) { void err; }}}, '📤 Экспорт JSON'),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {const input = prompt('Вставьте JSON с оверрайдами:')
|
|
1546
|
+
} catch (err) { void err; }}}, '📤 Экспорт JSON'),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {const input = typeof window !== 'undefined' && typeof window.prompt === 'function' ? window.prompt('Вставьте JSON с оверрайдами:') : null
|
|
1539
1547
|
if (!input) return
|
|
1540
1548
|
try {const parsed = JSON.parse(input)
|
|
1541
1549
|
if (parsed && typeof parsed === 'object') {const merged = Object.assign({}, overrides, parsed)
|
|
1542
1550
|
scope.set('overrides', merged)
|
|
1543
|
-
}} catch (err) { alert('Ошибка разбора JSON: ' + (err && err.message || err)) }}}, '📥 Импорт JSON'))),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secSupport')),React.createElement('span', { className: 'rl-badge rl-badge-ok' }, '🟢 100.0%')),React.createElement('div', { className: 'rl-section-desc' }, t('secSupportDesc')),React.createElement('div', { className: 'rl-grid-3' },React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '113'),React.createElement('div', { className: 'rl-stat-label' }, t('statNamespaces'))),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'))),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')))),React.createElement('div', { className: 'rl-actions-row' },React.createElement('a', {href: makeIssueUrl({}, '0.3.
|
|
1551
|
+
}} catch (err) { alert('Ошибка разбора JSON: ' + (err && err.message || err)) }}}, '📥 Импорт JSON'))),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secSupport')),React.createElement('span', { className: 'rl-badge rl-badge-ok' }, '🟢 100.0%')),React.createElement('div', { className: 'rl-section-desc' }, t('secSupportDesc')),React.createElement('div', { className: 'rl-grid-3' },React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '113'),React.createElement('div', { className: 'rl-stat-label' }, t('statNamespaces'))),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'))),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')))),React.createElement('div', { className: 'rl-actions-row' },React.createElement('a', {href: makeIssueUrl({}, '0.3.16'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
|
|
1544
1552
|
}, '💬 ' + t('reportIssue')),React.createElement('div', { className: 'rl-hint-text', style: { flex: 1 } },t('exportMdHint')))))))
|
|
1545
1553
|
}
|
|
1546
1554
|
const RL_CSS = ['.rl-modal-mask{position:fixed;top:0;left:0;width:100vw;height:100vh;background:var(--dsw-alias-bg-mask);display:flex;align-items:center;justify-content:center;z-index:99999;backdrop-filter:blur(2px)}.rl-modal-box{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;width:calc(100vw - 40px);max-width:500px;box-shadow:var(--dsw-alias-shadow-l3);display:flex;flex-direction:column;gap:12px;padding:18px}.rl-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;overflow:hidden;transition:border-color .15s ease}.rl-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;display:flex;align-items:center;gap:12px;padding:14px 18px}.rl-head:hover{background:var(--dsw-alias-bg-layer-2)}.rl-head-main{flex:1;display:flex;flex-direction:column;gap:4px}.rl-title{color:var(--dsw-alias-label-primary);font-size:16px;font-weight:600;line-height:1.4;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.rl-sub{color:var(--dsw-alias-label-secondary);font-size:13px}.rl-chev{margin-left:auto;flex:none;color:var(--dsw-alias-label-tertiary);display:inline-flex;transition:transform .16s}.rl-chev-open{transform:rotate(180deg)}.rl-body{border-top:1px solid var(--dsw-alias-border-l2);padding:18px}.rl-page{display:flex;flex-direction:column;gap:16px;max-width:960px}.rl-section-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:16px 18px;display:flex;flex-direction:column;gap:12px}.rl-section-title{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between;gap:8px}.rl-section-desc{font-size:12px;color:var(--dsw-alias-label-secondary);margin-top:-4px;line-height:1.4}.rl-grid-2{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:10px}.rl-grid-3{display:grid;grid-template-columns:repeat(auto-fit, minmax(140px, 1fr));gap:10px}.rl-badge{font-size:11px;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);display:inline-flex;align-items:center;gap:4px;font-weight:500}.rl-badge-ok{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 8%, transparent)}.rl-badge-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:color-mix(in srgb, var(--dsw-alias-state-warning-primary) 8%, transparent)}.rl-badge-dim{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-3)}.rl-item-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:4px}.rl-item-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.rl-item-label{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary);display:flex;align-items:center;gap:8px;cursor:pointer}.rl-item-desc{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.4}.rl-stat-box{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:2px}.rl-stat-val{font-size:16px;font-weight:700;color:var(--dsw-alias-label-primary)}.rl-stat-label{font-size:11px;color:var(--dsw-alias-label-secondary);letter-spacing:0.3px}.rl-hotkey-box{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.4;display:flex;align-items:flex-start;gap:10px}.rl-hotkey-tag{font-size:11px;font-weight:700;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);white-space:nowrap}.rl-preview-box{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.4}.rl-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:6px 12px;font-size:12px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font-weight:500;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .15s ease;text-decoration:none}.rl-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-1);border-color:var(--dsw-alias-label-secondary)}.rl-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:transparent}.rl-btn-primary:hover:not(:disabled){opacity:0.9}.rl-select{height:32px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:6px;padding:0 8px;font-size:12px;outline:none;width:100%;max-width:380px}.rl-select:focus{border-color:var(--dsw-alias-state-brand-primary)}.rl-check{width:16px;height:16px;accent-color:var(--dsw-alias-label-primary);cursor:pointer;flex-shrink:0}.rl-hint-text{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.4}.rl-actions-row{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding-top:4px}.rl-lang-chip{appearance:none;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:700;padding:2px 7px;border-radius:6px;display:inline-flex;align-items:center;transition:all .15s;margin:0 4px}.rl-lang-chip:hover{border-color:var(--dsw-alias-label-primary);color:var(--dsw-alias-label-primary)}.rl-lang-chip-active{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.rl-action-btn{appearance:none;background:0 0;border:1px solid transparent;border-radius:4px;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:11px;padding:2px 5px;display:inline-flex;align-items:center;transition:all .15s}.rl-action-btn:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3)}.rl-action-btn-active{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l2)}.rl-turn-translation{margin:8px 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;font-size:13px;line-height:1.5}.rl-trans-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;border-bottom:1px solid var(--dsw-alias-border-l2);padding-bottom:4px}.rl-trans-title{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary);text-transform:uppercase;letter-spacing:0.5px}.rl-trans-tools{display:flex;gap:4px}.rl-trans-btn{appearance:none;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;padding:2px 7px;border-radius:4px;font-size:11px}.rl-trans-btn:hover{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.rl-trans-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;user-select:text}.rl-export-md-btn{appearance:none;border:1px solid var(--dsw-alias-border-l2);height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;background:transparent;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:6px 12px;font-size:13px;font-weight:500;display:inline-flex;white-space:nowrap;margin-left:6px;transition:all .15s ease}.rl-export-md-btn:hover{background:var(--dsw-alias-interactive-bg-hover)}.rl-inspector-hover{position:absolute;pointer-events:none;border:2px solid var(--dsw-alias-state-brand-primary);border-radius:4px;background:color-mix(in srgb,var(--dsw-alias-state-brand-primary) 12%,transparent);box-shadow:0 0 10px color-mix(in srgb,var(--dsw-alias-state-brand-primary) 30%,transparent);z-index:99998;transition:all .06s ease;display:none}.rl-inspector-badge{position:absolute;bottom:calc(100% + 4px);left:0;background:var(--dsw-alias-state-brand-primary);color:var(--dsw-alias-label-inverse);font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;white-space:nowrap;box-shadow:var(--dsw-alias-shadow-l2);pointer-events:none}.rl-inspector-pill{position:fixed;bottom:18px;right:18px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:20px;padding:6px 12px;box-shadow:var(--dsw-alias-shadow-l3);display:none;align-items:center;gap:8px;font-size:12px;z-index:99990;user-select:none}.rl-inspector-pill-active{border-color:var(--dsw-alias-state-brand-primary);box-shadow:0 0 12px color-mix(in srgb,var(--dsw-alias-state-brand-primary) 35%,transparent)}'
|
package/lib/client.template.js
CHANGED
|
@@ -15,11 +15,12 @@ window.__ModuleLoader__.load({
|
|
|
15
15
|
factory: (require) => {
|
|
16
16
|
var module = { exports: {} }
|
|
17
17
|
var React = null
|
|
18
|
-
try { React = require('react') } catch (e) { /* карточка настроек необязательна */ }
|
|
18
|
+
try { React = require('react') } catch (e) { /* карточка настроек необязательна */ void e; }
|
|
19
19
|
|
|
20
20
|
let isInspectorActive = false
|
|
21
21
|
let hoverBoxEl = null
|
|
22
22
|
let pillEl = null
|
|
23
|
+
let updateInspectorPill = () => {}
|
|
23
24
|
|
|
24
25
|
//__PURE_JS__
|
|
25
26
|
|
|
@@ -33,15 +34,21 @@ window.__ModuleLoader__.load({
|
|
|
33
34
|
RU['russian-lang'] = /*__CARD_RU__*/{}
|
|
34
35
|
|
|
35
36
|
const SETTINGS_NS_NAME = 'russian-lang'
|
|
37
|
+
// Namespace формы настроек: на 0.1.7+ это id записи загрузчика
|
|
38
|
+
// ('dsh-russian-lang' из cordis.patch.yml), а не имя, выбранное плагином
|
|
39
|
+
// ('russian-lang'), как было на 0.1.6 и ниже. Это разные вещи.
|
|
40
|
+
const FORM_NS = 'dsh-russian-lang'
|
|
41
|
+
const LEGACY_NS = 'russian-lang'
|
|
36
42
|
// Plugins page row seat (DSH 0.1.6-alpha.2): key = '<package name>#<row id>'.
|
|
37
|
-
const ROW_CONFIG_KEY = '@goodandready/dsh-russian-lang#
|
|
43
|
+
const ROW_CONFIG_KEY = '@goodandready/dsh-russian-lang#' + FORM_NS
|
|
38
44
|
|
|
39
45
|
function apply(ctx) {
|
|
40
46
|
const runtime = ctx.locale
|
|
47
|
+
const configForms = ctx && ctx.configForms
|
|
41
48
|
const resolveScope = (c) => {
|
|
42
49
|
try {
|
|
43
|
-
if (c && c.configForms) return c.configForms.get(
|
|
44
|
-
if (c && c.settingsScope) return c.settingsScope.bind({ namespace:
|
|
50
|
+
if (c && c.configForms) return c.configForms.get(FORM_NS)
|
|
51
|
+
if (c && c.settingsScope) return c.settingsScope.bind({ namespace: LEGACY_NS })
|
|
45
52
|
} catch (err) { void err }
|
|
46
53
|
return null
|
|
47
54
|
}
|
|
@@ -51,6 +58,10 @@ window.__ModuleLoader__.load({
|
|
|
51
58
|
subscribe: () => () => {},
|
|
52
59
|
set: () => Promise.resolve()
|
|
53
60
|
}
|
|
61
|
+
// На 0.1.7+ выбор языка хранит сам DSH (locale.preference): собственный
|
|
62
|
+
// флаг там не читаем и не пишем, виджет работает просто с локалью.
|
|
63
|
+
// На 0.1.6 и ниже поведение прежнее — флаг живёт в настройках плагина.
|
|
64
|
+
const legacyFlagMode = !(ctx && ctx.configForms)
|
|
54
65
|
|
|
55
66
|
// 1. Словари ядра DSH: каждый namespace — свой эффект, снимается вместе с плагином
|
|
56
67
|
for (const ns of Object.keys(RU)) {
|
|
@@ -75,7 +86,7 @@ window.__ModuleLoader__.load({
|
|
|
75
86
|
}, 'dsh-russian-lang: ' + ns)
|
|
76
87
|
} else {
|
|
77
88
|
Object.assign(RU[ns], dict)
|
|
78
|
-
try { ctx.locale.register(ns, 'ru', RU[ns]) } catch (_) {}
|
|
89
|
+
try { ctx.locale.register(ns, 'ru', RU[ns]) } catch (_) { /* bestEffort */ void _; }
|
|
79
90
|
}
|
|
80
91
|
}
|
|
81
92
|
}
|
|
@@ -239,7 +250,7 @@ window.__ModuleLoader__.load({
|
|
|
239
250
|
try {
|
|
240
251
|
const chain = runtime.fallbackChain && runtime.fallbackChain(runtime.getLocale().active)
|
|
241
252
|
if (Array.isArray(chain) && chain.length) return chain
|
|
242
|
-
} catch (err) { /* ignore */ }
|
|
253
|
+
} catch (err) { /* ignore */ void err; }
|
|
243
254
|
return [runtime.getLocale().active]
|
|
244
255
|
}
|
|
245
256
|
const translationRegistry = new Map()
|
|
@@ -247,7 +258,7 @@ window.__ModuleLoader__.load({
|
|
|
247
258
|
const syncLang = () => {
|
|
248
259
|
try {
|
|
249
260
|
if (typeof document !== 'undefined' && document.documentElement && runtime.getLocale().active === 'ru') document.documentElement.lang = 'ru-RU'
|
|
250
|
-
} catch (err) { /* ignore */ }
|
|
261
|
+
} catch (err) { /* ignore */ void err; }
|
|
251
262
|
}
|
|
252
263
|
const RUNTIME_PROP_NAMES = ['plural','pluralForm','formatDate','formatCompactNumber','formatTokens','formatNumber','formatRelativeTime','formatCurrency','inflect','getPluginLocalizationStatus','stemRussian','fuzzyMatchRu','humanizeError']
|
|
253
264
|
ctx.effect(() => {
|
|
@@ -296,7 +307,7 @@ window.__ModuleLoader__.load({
|
|
|
296
307
|
if (origProps[p] !== undefined) runtime[p] = origProps[p]
|
|
297
308
|
else delete runtime[p]
|
|
298
309
|
}
|
|
299
|
-
if (typeof removeLang === 'function') { try { removeLang() } catch (_) { /* bestEffort */ } }
|
|
310
|
+
if (typeof removeLang === 'function') { try { removeLang() } catch (_) { /* bestEffort */ void _; } }
|
|
300
311
|
}
|
|
301
312
|
}, 'dsh-russian-lang: runtime-lifecycle')
|
|
302
313
|
|
|
@@ -305,6 +316,7 @@ window.__ModuleLoader__.load({
|
|
|
305
316
|
// scope.set возвращает Promise<void> в v0.1.2-alpha.2, ошибка приходит
|
|
306
317
|
// через rejection и try/catch её не ловит — обрабатываем оба канала.
|
|
307
318
|
const syncFlag = () => {
|
|
319
|
+
if (!legacyFlagMode) return
|
|
308
320
|
try {
|
|
309
321
|
const wantRu = runtime.getLocale().active === 'ru'
|
|
310
322
|
const value = scope.getSnapshot().value || {}
|
|
@@ -338,8 +350,8 @@ window.__ModuleLoader__.load({
|
|
|
338
350
|
if (booted) return
|
|
339
351
|
try {
|
|
340
352
|
const value = scope.getSnapshot().value
|
|
341
|
-
if (value && value.enabled === true) { booted = true; activate() }
|
|
342
|
-
} catch (err) { /* ignore */ }
|
|
353
|
+
if (legacyFlagMode && value && value.enabled === true) { booted = true; activate() }
|
|
354
|
+
} catch (err) { /* ignore */ void err; }
|
|
343
355
|
}
|
|
344
356
|
ctx.effect(() => scope.subscribe(tryBoot), 'dsh-russian-lang: boot')
|
|
345
357
|
tryBoot()
|
|
@@ -408,14 +420,14 @@ window.__ModuleLoader__.load({
|
|
|
408
420
|
}
|
|
409
421
|
})
|
|
410
422
|
spellObserver.observe(document.body, { childList: true, subtree: true })
|
|
411
|
-
} catch (err) { /* ignore */ }
|
|
423
|
+
} catch (err) { /* ignore */ void err; }
|
|
412
424
|
}
|
|
413
425
|
const unsubscribeSpell = runtime.subscribe(syncSpell)
|
|
414
426
|
ctx.effect(() => {
|
|
415
427
|
return () => {
|
|
416
428
|
unsubscribeSpell()
|
|
417
429
|
if (spellObserver) spellObserver.disconnect()
|
|
418
|
-
try { document.querySelectorAll('[' + SPELL_ON + ']').forEach(spellOff) } catch (err) { /* ignore */ }
|
|
430
|
+
try { document.querySelectorAll('[' + SPELL_ON + ']').forEach(spellOff) } catch (err) { /* ignore */ void err; }
|
|
419
431
|
}
|
|
420
432
|
}, 'dsh-russian-lang: spellcheck')
|
|
421
433
|
syncSpell()
|
|
@@ -785,11 +797,12 @@ window.__ModuleLoader__.load({
|
|
|
785
797
|
node.nodeValue = val.replace(trimmed, curOverrides[trimmed])
|
|
786
798
|
continue
|
|
787
799
|
}
|
|
800
|
+
const p = node.parentElement
|
|
801
|
+
const isProtected = p && p.closest('code, pre, script, style, textarea, input, select, kbd, samp, [contenteditable="true"], [data-composer-input], [role="textbox"], .chat-message, .markdown-body')
|
|
788
802
|
if (trimmed.length >= 1 && ZH_CJK.test(val)) {
|
|
789
|
-
hits.push(node)
|
|
803
|
+
if (!isProtected) hits.push(node)
|
|
790
804
|
} else if (trimmed.length >= 1 && DOM_EN_TEXT[trimmed]) {
|
|
791
|
-
|
|
792
|
-
if (p && !p.closest('code, pre, script, style, textarea, input, select, kbd, samp, [contenteditable="true"], [data-composer-input], [role="textbox"]')) {
|
|
805
|
+
if (!isProtected) {
|
|
793
806
|
node.nodeValue = val.replace(trimmed, DOM_EN_TEXT[trimmed])
|
|
794
807
|
}
|
|
795
808
|
}
|
|
@@ -824,7 +837,7 @@ window.__ModuleLoader__.load({
|
|
|
824
837
|
}
|
|
825
838
|
}
|
|
826
839
|
}
|
|
827
|
-
} catch (err) { /* ignore */ }
|
|
840
|
+
} catch (err) { /* ignore */ void err; }
|
|
828
841
|
}
|
|
829
842
|
|
|
830
843
|
let zhObserver = null
|
|
@@ -876,7 +889,7 @@ window.__ModuleLoader__.load({
|
|
|
876
889
|
document.addEventListener('click', onInteraction, { passive: true })
|
|
877
890
|
}
|
|
878
891
|
queueZhWalk()
|
|
879
|
-
} catch (err) { /* ignore */ }
|
|
892
|
+
} catch (err) { /* ignore */ void err; }
|
|
880
893
|
}
|
|
881
894
|
const unsubscribeZh = runtime.subscribe(syncZhDom)
|
|
882
895
|
ctx.effect(() => {
|
|
@@ -955,7 +968,7 @@ window.__ModuleLoader__.load({
|
|
|
955
968
|
else typoWalk(root, conf)
|
|
956
969
|
}
|
|
957
970
|
typoPending.clear()
|
|
958
|
-
} catch (err) { /* ignore */ }
|
|
971
|
+
} catch (err) { /* ignore */ void err; }
|
|
959
972
|
}
|
|
960
973
|
const typoPending = new Set()
|
|
961
974
|
const queueTypo = (roots) => {
|
|
@@ -983,7 +996,7 @@ window.__ModuleLoader__.load({
|
|
|
983
996
|
if (roots.length > 0) queueTypo(roots)
|
|
984
997
|
})
|
|
985
998
|
typoObserver.observe(document.body, { childList: true, subtree: true })
|
|
986
|
-
} catch (err) { /* ignore */ }
|
|
999
|
+
} catch (err) { /* ignore */ void err; }
|
|
987
1000
|
}
|
|
988
1001
|
const unsubscribeTypo = runtime.subscribe(syncTypo)
|
|
989
1002
|
ctx.effect(() => {
|
|
@@ -1264,7 +1277,7 @@ window.__ModuleLoader__.load({
|
|
|
1264
1277
|
if (c) { layoutShowHint(el, c.converted, 'cmd'); return }
|
|
1265
1278
|
}
|
|
1266
1279
|
layoutDismiss()
|
|
1267
|
-
} catch (err) { /* ignore */ }
|
|
1280
|
+
} catch (err) { /* ignore */ void err; }
|
|
1268
1281
|
}
|
|
1269
1282
|
const unsubscribeLayout = runtime.subscribe(layoutOnInput)
|
|
1270
1283
|
|
|
@@ -1428,7 +1441,7 @@ window.__ModuleLoader__.load({
|
|
|
1428
1441
|
return hoverBoxEl
|
|
1429
1442
|
}
|
|
1430
1443
|
|
|
1431
|
-
|
|
1444
|
+
updateInspectorPill = () => {
|
|
1432
1445
|
if (typeof document === 'undefined') return
|
|
1433
1446
|
if (!pillEl) {
|
|
1434
1447
|
pillEl = document.createElement('div')
|
|
@@ -1444,7 +1457,7 @@ window.__ModuleLoader__.load({
|
|
|
1444
1457
|
document.body.appendChild(pillEl)
|
|
1445
1458
|
}
|
|
1446
1459
|
pillEl.style.display = isInspectorActive ? 'flex' : 'none'
|
|
1447
|
-
if (isInspectorActive) pillEl.classList.add('rl-inspector-pill-active')
|
|
1460
|
+
if (isInspectorActive && pillEl.classList) pillEl.classList.add('rl-inspector-pill-active')
|
|
1448
1461
|
}
|
|
1449
1462
|
|
|
1450
1463
|
const openInspectorModal = (meta, rawText, targetElement) => {
|
|
@@ -1724,34 +1737,37 @@ window.__ModuleLoader__.load({
|
|
|
1724
1737
|
// ядро (0.1.6-alpha.2) рендерит как страницу плагина с настройками. label —
|
|
1725
1738
|
// статичная строка: он резолвится во время рендера страницы, и обращение к
|
|
1726
1739
|
// локали там роняет весь клиентский батч. Прежние посадки сохранены фолбэками.
|
|
1727
|
-
|
|
1740
|
+
// Посадочные места карточки настроек плагина.
|
|
1741
|
+
// DSH 0.2.0-rc.1:
|
|
1742
|
+
// - plugins.item (#388): id = FORM_NS ('dsh-russian-lang'), монтируется через whileServed([FORM_NS])
|
|
1743
|
+
// - plugins.row.config (#387): key = '@goodandready/dsh-russian-lang#' + FORM_NS
|
|
1744
|
+
// - settings-plugin-item (#389): устаревший слот упразднен в DSH 0.2.0-rc.1, удален
|
|
1745
|
+
const registerPluginsItem = () => {
|
|
1728
1746
|
ctx.slots.inject('plugins.item', () =>
|
|
1729
1747
|
ctx.slots.register({
|
|
1730
1748
|
name: 'plugins.item',
|
|
1731
|
-
id:
|
|
1749
|
+
id: FORM_NS,
|
|
1732
1750
|
order: 60,
|
|
1733
1751
|
label: () => 'Russian language',
|
|
1734
1752
|
locale: SETTINGS_NS_NAME,
|
|
1735
1753
|
inject: () => ({ scope, runtime, toggleRu }),
|
|
1736
1754
|
}, SettingsCard),
|
|
1737
1755
|
)
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
ctx.slots.
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
)
|
|
1754
|
-
} catch (err) { console.warn('dsh-russian-lang: settings slot unavailable', err) }
|
|
1756
|
+
}
|
|
1757
|
+
if (typeof configForms?.whileServed === 'function') {
|
|
1758
|
+
ctx.effect(() => configForms.whileServed([FORM_NS], registerPluginsItem), 'dsh-russian-lang: plugins.item')
|
|
1759
|
+
} else {
|
|
1760
|
+
registerPluginsItem()
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
ctx.slots.inject('plugins.row.config', () =>
|
|
1764
|
+
ctx.slots.register({
|
|
1765
|
+
name: 'plugins.row.config',
|
|
1766
|
+
key: ROW_CONFIG_KEY,
|
|
1767
|
+
locale: SETTINGS_NS_NAME,
|
|
1768
|
+
inject: () => ({ scope, runtime, toggleRu }),
|
|
1769
|
+
}, SettingsCard),
|
|
1770
|
+
)
|
|
1755
1771
|
|
|
1756
1772
|
// #158: Быстрый переключатель RU ⇄ EN в шапке сессии
|
|
1757
1773
|
try {
|
|
@@ -1764,7 +1780,7 @@ window.__ModuleLoader__.load({
|
|
|
1764
1780
|
inject: () => ({ runtime, toggleRu }),
|
|
1765
1781
|
}, QuickLangSwitch),
|
|
1766
1782
|
)
|
|
1767
|
-
} catch (err) { /* ignore if slot not declared */ }
|
|
1783
|
+
} catch (err) { /* ignore if slot not declared */ void err; }
|
|
1768
1784
|
|
|
1769
1785
|
// #158: Кнопка перевода реплики на русский в действиях ассистента
|
|
1770
1786
|
try {
|
|
@@ -1777,7 +1793,7 @@ window.__ModuleLoader__.load({
|
|
|
1777
1793
|
inject: () => ({ runtime }),
|
|
1778
1794
|
}, TranslateTurnAction),
|
|
1779
1795
|
)
|
|
1780
|
-
} catch (err) { /* ignore if slot not declared */ }
|
|
1796
|
+
} catch (err) { /* ignore if slot not declared */ void err; }
|
|
1781
1797
|
|
|
1782
1798
|
// #158: Экспорт сессии в Markdown
|
|
1783
1799
|
try {
|
|
@@ -1790,7 +1806,7 @@ window.__ModuleLoader__.load({
|
|
|
1790
1806
|
inject: () => ({ runtime }),
|
|
1791
1807
|
}, ExportMarkdownButton),
|
|
1792
1808
|
)
|
|
1793
|
-
} catch (err) { /* ignore if slot not declared */ }
|
|
1809
|
+
} catch (err) { /* ignore if slot not declared */ void err; }
|
|
1794
1810
|
}
|
|
1795
1811
|
|
|
1796
1812
|
// Карточка настроек: React-компонент вне apply.
|
|
@@ -1810,7 +1826,7 @@ window.__ModuleLoader__.load({
|
|
|
1810
1826
|
React.useEffect(() => {
|
|
1811
1827
|
if (!runtime) return
|
|
1812
1828
|
return runtime.subscribe(() => {
|
|
1813
|
-
try { setLocaleState(runtime.getLocale().active || 'en') } catch (e) { /* ignore */ }
|
|
1829
|
+
try { setLocaleState(runtime.getLocale().active || 'en') } catch (e) { /* ignore */ void e; }
|
|
1814
1830
|
})
|
|
1815
1831
|
}, [runtime])
|
|
1816
1832
|
const isRu = locale === 'ru'
|
|
@@ -2156,7 +2172,7 @@ window.__ModuleLoader__.load({
|
|
|
2156
2172
|
React.useEffect(() => {
|
|
2157
2173
|
try {
|
|
2158
2174
|
const un = runtime.subscribe(() => {
|
|
2159
|
-
try { setRuActive(runtime.getLocale().active === 'ru') } catch (e) { /* ignore */ }
|
|
2175
|
+
try { setRuActive(runtime.getLocale().active === 'ru') } catch (e) { /* ignore */ void e; }
|
|
2160
2176
|
})
|
|
2161
2177
|
return un
|
|
2162
2178
|
} catch (e) { return undefined }
|
|
@@ -2576,7 +2592,7 @@ window.__ModuleLoader__.load({
|
|
|
2576
2592
|
className: 'rl-btn',
|
|
2577
2593
|
style: { fontSize: '11px' },
|
|
2578
2594
|
onClick: () => {
|
|
2579
|
-
const input = prompt('Вставьте JSON с оверрайдами:')
|
|
2595
|
+
const input = typeof window !== 'undefined' && typeof window.prompt === 'function' ? window.prompt('Вставьте JSON с оверрайдами:') : null
|
|
2580
2596
|
if (!input) return
|
|
2581
2597
|
try {
|
|
2582
2598
|
const parsed = JSON.parse(input)
|
package/lib/index.js
CHANGED
|
@@ -39,48 +39,158 @@ const sendJsonWithEtag = (request, response, obj, maxAge = 300) => {
|
|
|
39
39
|
|
|
40
40
|
export const name = '@goodandready/dsh-russian-lang'
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
// Легаси-namespace настроек (DSH <= 0.1.6). Namespace форм на 0.1.7+ —
|
|
43
|
+
// id записи загрузчика ('dsh-russian-lang' из cordis.patch.yml), схема
|
|
44
|
+
// попадает туда через экспортированный Config ниже.
|
|
45
|
+
const NS = 'russian-lang'
|
|
46
|
+
|
|
47
|
+
// `.volatile()` есть только в новом schemastery; на старом его вызов
|
|
48
|
+
// бросает TypeError на уровне модуля — поэтому только feature-detect.
|
|
49
|
+
const vol = (s) => (typeof s?.volatile === 'function' ? s.volatile() : s)
|
|
50
|
+
|
|
51
|
+
export const Config = z.object({
|
|
52
|
+
enabled: vol(z.boolean().required(false)),
|
|
53
|
+
overrides: vol(z.dict(z.string()).required(false)),
|
|
54
|
+
typography: vol(z.object({
|
|
46
55
|
enabled: z.boolean().required(false),
|
|
47
56
|
yo: z.boolean().required(false),
|
|
48
57
|
liveInput: z.boolean().required(false)
|
|
49
|
-
}).required(false),
|
|
50
|
-
agentPrompt: z.boolean().required(false),
|
|
51
|
-
agentPromptPreset: z.string().required(false),
|
|
52
|
-
slashAliases: z.boolean().required(false),
|
|
53
|
-
quickSwitch: z.boolean().required(false),
|
|
54
|
-
translateEngine: z.string().required(false),
|
|
55
|
-
localApiUrl: z.string().required(false)
|
|
58
|
+
}).required(false)),
|
|
59
|
+
agentPrompt: vol(z.boolean().required(false)),
|
|
60
|
+
agentPromptPreset: vol(z.string().required(false)),
|
|
61
|
+
slashAliases: vol(z.boolean().required(false)),
|
|
62
|
+
quickSwitch: vol(z.boolean().required(false)),
|
|
63
|
+
translateEngine: vol(z.string().required(false)),
|
|
64
|
+
localApiUrl: vol(z.string().required(false))
|
|
56
65
|
})
|
|
57
66
|
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
// Историческое имя схемы; сама схема — Config выше.
|
|
68
|
+
const RussianLangSettingsSchema = Config
|
|
69
|
+
|
|
70
|
+
// Plain-схема для легаси-ветки register(): старый провайдер ждёт schemastery-схему,
|
|
71
|
+
// поэтому сериализуем, рекурсивно снимаем meta.volatile и рехидрируем обратно
|
|
72
|
+
// в схему — иначе volatile-поля остались бы рефами и сломали бы старое хранилище.
|
|
73
|
+
// Любая ошибка — возвращаем исходную схему, наружу не бросаем.
|
|
74
|
+
function plainSchema(schema) {
|
|
75
|
+
try {
|
|
76
|
+
const json = (schema && typeof schema.toJSON === 'function') ? schema.toJSON() : schema
|
|
77
|
+
const clone = JSON.parse(JSON.stringify(json))
|
|
78
|
+
const seen = new Set()
|
|
79
|
+
const strip = (node) => {
|
|
80
|
+
if (!node || typeof node !== 'object' || seen.has(node)) return
|
|
81
|
+
seen.add(node)
|
|
82
|
+
if (node.meta && typeof node.meta === 'object') {
|
|
83
|
+
try { delete node.meta.volatile } catch (_) { /* bestEffort */ void _ }
|
|
84
|
+
}
|
|
85
|
+
for (const key of Object.keys(node)) strip(node[key])
|
|
86
|
+
}
|
|
87
|
+
strip(clone)
|
|
88
|
+
return new z(clone)
|
|
89
|
+
} catch (_) {
|
|
90
|
+
return schema
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function apply(ctx, config) {
|
|
95
|
+
// Живой провайдер настроек; на 0.1.7+ у него есть configure(),
|
|
96
|
+
// на старых — register()/get(). Пусто до первого inject.
|
|
97
|
+
let settingsProvider = null
|
|
98
|
+
|
|
99
|
+
// Единый снимок значений: на 0.1.7+ разворачиваем volatile-рефы второго
|
|
100
|
+
// аргумента (ссылки стабильны — загрузчик обновляет их на месте),
|
|
101
|
+
// на старой ветке читаем документ провайдера. Никогда не бросает.
|
|
102
|
+
const readSettings = () => {
|
|
103
|
+
try {
|
|
104
|
+
const unwrap = (src) => {
|
|
105
|
+
if (!src || typeof src !== 'object') return {}
|
|
106
|
+
const out = {}
|
|
107
|
+
for (const key of Object.keys(src)) {
|
|
108
|
+
const v = src[key]
|
|
109
|
+
out[key] = (v && typeof v.get === 'function') ? v.get() : v
|
|
110
|
+
}
|
|
111
|
+
return out
|
|
112
|
+
}
|
|
113
|
+
if (settingsProvider && typeof settingsProvider.configure === 'function') {
|
|
114
|
+
return unwrap(config)
|
|
115
|
+
}
|
|
116
|
+
if (settingsProvider && typeof settingsProvider.get === 'function') {
|
|
117
|
+
return settingsProvider.get(NS) || {}
|
|
118
|
+
}
|
|
119
|
+
return unwrap(config)
|
|
120
|
+
} catch (_) {
|
|
121
|
+
return {}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
60
125
|
ctx.inject(['settings'], (settingsCtx) => {
|
|
61
|
-
|
|
62
|
-
|
|
126
|
+
let provider = null
|
|
127
|
+
try {
|
|
128
|
+
provider = settingsCtx?.settings
|
|
129
|
+
?? (typeof settingsCtx?.get === 'function' ? settingsCtx.get('settings') : null)
|
|
130
|
+
?? null
|
|
131
|
+
} catch (_) { provider = null }
|
|
132
|
+
settingsProvider = provider
|
|
133
|
+
if (!provider) return
|
|
134
|
+
try {
|
|
135
|
+
if (typeof provider.configure === 'function') {
|
|
136
|
+
// 0.1.7+: схема уже у загрузчика через экспортированный Config;
|
|
137
|
+
// отключаем автогенерацию страницы (карточка у нас кастомная).
|
|
138
|
+
if (typeof settingsCtx?.effect === 'function') {
|
|
139
|
+
settingsCtx.effect(() => provider.configure({ auto: false }, ctx.fiber))
|
|
140
|
+
} else {
|
|
141
|
+
provider.configure({ auto: false }, ctx.fiber)
|
|
142
|
+
}
|
|
143
|
+
} else if (typeof provider.register === 'function') {
|
|
144
|
+
// Легаси: plain-схема без volatile-мета.
|
|
145
|
+
try {
|
|
146
|
+
provider.register(NS, plainSchema(RussianLangSettingsSchema))
|
|
147
|
+
} catch (_) {
|
|
148
|
+
provider.register(NS, RussianLangSettingsSchema)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Иначе тихая деградация: readSettings вернёт {} / развернёт рефы.
|
|
152
|
+
} catch (_) { /* never throw outwards */ void _ }
|
|
63
153
|
})
|
|
64
154
|
|
|
65
155
|
// #68: русский системный промпт агента (опционально). Регистрируем секцию
|
|
66
156
|
// промпта, которая задёт стиль ответов по-русски, когда включён флаг
|
|
67
157
|
// russian-lang.agentPrompt. Секция — официальная точка расширения ядра.
|
|
68
|
-
|
|
69
|
-
|
|
158
|
+
// Значения — через readSettings() (volatile-рефы на 0.1.7+, документ
|
|
159
|
+
// провайдера на легаси). Пересинхронизация: loader/volatile-update
|
|
160
|
+
// (свой ctx, 0.1.7) и settings/updated (старые ядра).
|
|
161
|
+
ctx.inject(['systemPrompt'], (promptCtx) => {
|
|
162
|
+
let prompt = promptCtx?.systemPrompt
|
|
163
|
+
try {
|
|
164
|
+
if (!prompt && typeof promptCtx?.get === 'function') prompt = promptCtx.get('systemPrompt')
|
|
165
|
+
} catch (_) { /* bestEffort */ void _ }
|
|
166
|
+
if (!prompt || typeof prompt.section !== 'function') return
|
|
70
167
|
const RU_SECTION = 'dsh-russian-lang-agent-prompt'
|
|
71
168
|
const DEFAULT_RU_TEXT = 'Отвечай пользователю на русском языке. Если пользователь пишет на другом языке, отвечай на его языке.'
|
|
169
|
+
let unregisterSection = null
|
|
72
170
|
let currentText = ''
|
|
73
171
|
const sync = () => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
172
|
+
try {
|
|
173
|
+
const value = readSettings()
|
|
174
|
+
const want = !!(value && value.agentPrompt)
|
|
175
|
+
const presetKey = value && value.agentPromptPreset
|
|
176
|
+
const preset = SYSTEM_PROMPT_PRESETS[presetKey]
|
|
177
|
+
const text = want ? (preset ? preset.text : DEFAULT_RU_TEXT) : ''
|
|
178
|
+
if (text === currentText) return
|
|
179
|
+
// section() не идемпотентен: повторная регистрация того же имени
|
|
180
|
+
// бросает, поэтому сначала снимаем прежнюю секцию через disposer.
|
|
181
|
+
if (typeof unregisterSection === 'function') {
|
|
182
|
+
try { unregisterSection() } catch (_) { /* bestEffort */ void _ }
|
|
183
|
+
}
|
|
184
|
+
unregisterSection = null
|
|
185
|
+
currentText = text
|
|
186
|
+
// Выключение флага снимает секцию, а не пушит пустой текст.
|
|
187
|
+
if (!text) return
|
|
188
|
+
const dispose = prompt.section({ name: RU_SECTION, order: 1000, text })
|
|
189
|
+
unregisterSection = typeof dispose === 'function' ? dispose : null
|
|
190
|
+
} catch (_) { /* never throw outwards */ void _ }
|
|
82
191
|
}
|
|
83
|
-
ctx.on('
|
|
192
|
+
try { ctx.on('loader/volatile-update', sync) } catch (_) { /* bestEffort */ void _ }
|
|
193
|
+
try { ctx.on('settings/updated', (ns) => { if (ns === NS) sync() }) } catch (_) { /* bestEffort */ void _ }
|
|
84
194
|
sync()
|
|
85
195
|
})
|
|
86
196
|
|
|
@@ -95,7 +205,7 @@ export function apply(ctx) {
|
|
|
95
205
|
manifestUrl: new URL('../package.json', import.meta.url),
|
|
96
206
|
})
|
|
97
207
|
|
|
98
|
-
const unregTranslator = registerTranslator(wctx,
|
|
208
|
+
const unregTranslator = registerTranslator(wctx, readSettings)
|
|
99
209
|
|
|
100
210
|
let ws = null
|
|
101
211
|
try { ws = wctx?.webServer } catch (_) { ws = null }
|
package/lib/locales.js
CHANGED
|
@@ -79,7 +79,7 @@ export function getAllDictionaries() {
|
|
|
79
79
|
export function getZhRuMap() {
|
|
80
80
|
if (existsSync(ZH_RU_FILE)) {
|
|
81
81
|
try {
|
|
82
|
-
return JSON.parse(readFileSync(
|
|
82
|
+
return JSON.parse(readFileSync(ZH_RU_FILE, "utf8"))
|
|
83
83
|
} catch (_) { /* bestEffort */ void _; }
|
|
84
84
|
}
|
|
85
85
|
return {}
|
package/lib/translator.js
CHANGED
|
@@ -101,8 +101,30 @@ export function isTrustedTranslatorRequest(request) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Нормализует источник настроек к plain-объекту: принимает либо готовый
|
|
106
|
+
* объект, либо функцию-ридер (как readSettings из lib/index.js), либо —
|
|
107
|
+
* для обратной совместимости — легаси settings-контекст с settings.get().
|
|
108
|
+
*/
|
|
109
|
+
function resolveTranslatorSettings(source) {
|
|
110
|
+
try {
|
|
111
|
+
if (typeof source === 'function') return source() || {}
|
|
112
|
+
if (source && typeof source === 'object') {
|
|
113
|
+
if (source.settings && typeof source.settings.get === 'function') {
|
|
114
|
+
return source.settings.get('russian-lang') || {}
|
|
115
|
+
}
|
|
116
|
+
if (typeof source.translateEngine === 'string' || typeof source.localApiUrl === 'string'
|
|
117
|
+
|| typeof source.agentPrompt !== 'undefined' || typeof source.enabled !== 'undefined') {
|
|
118
|
+
return source
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (_) { /* bestEffort */ void _ }
|
|
122
|
+
return {}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
export async function getTranslatorStatus(settingsSource) {
|
|
127
|
+
const currentSettings = resolveTranslatorSettings(settingsSource)
|
|
106
128
|
const engine = currentSettings.translateEngine || 'off'
|
|
107
129
|
const localUrl = (currentSettings.localApiUrl || 'http://127.0.0.1:5000').replace(/\/+$/, '')
|
|
108
130
|
|
|
@@ -236,7 +258,7 @@ async function translateLibre(text, targetLang = 'ru', localUrl = 'http://127.0.
|
|
|
236
258
|
return data.translatedText || text
|
|
237
259
|
}
|
|
238
260
|
|
|
239
|
-
export function registerTranslator(wctx,
|
|
261
|
+
export function registerTranslator(wctx, settingsSource) {
|
|
240
262
|
let ws = null
|
|
241
263
|
try { ws = wctx?.webServer } catch (_) { ws = null }
|
|
242
264
|
if (!ws || typeof ws.register !== 'function') return () => {}
|
|
@@ -255,7 +277,7 @@ export function registerTranslator(wctx, settingsCtx) {
|
|
|
255
277
|
response.end(JSON.stringify({ error: 'Method not allowed' }))
|
|
256
278
|
return
|
|
257
279
|
}
|
|
258
|
-
const status = await getTranslatorStatus(
|
|
280
|
+
const status = await getTranslatorStatus(settingsSource)
|
|
259
281
|
response.writeHead(200, {
|
|
260
282
|
'content-type': 'application/json; charset=utf-8',
|
|
261
283
|
'cache-control': 'no-store'
|
|
@@ -336,7 +358,7 @@ export function registerTranslator(wctx, settingsCtx) {
|
|
|
336
358
|
return
|
|
337
359
|
}
|
|
338
360
|
|
|
339
|
-
const settings =
|
|
361
|
+
const settings = resolveTranslatorSettings(settingsSource)
|
|
340
362
|
const engine = settings.translateEngine || 'off'
|
|
341
363
|
const localUrl = settings.localApiUrl || 'http://127.0.0.1:5000'
|
|
342
364
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-russian-lang",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.16",
|
|
4
4
|
"description": "Russian localization for DeepSeek Harness (DSH v0.1.7-rc.1+) web UI: adds Русский as native language option. 100.0% coverage across 11,295 keys (127 namespaces). Features full DSH v0.1.7-rc.1 core support (terminal, archive, command palette, Auto review, trajectory, PTC/Creator presets) and complete GoodAndReady plugin catalog (subscriptions, tts, image-gen, voice, key-limits, notify, commandcode, plugin-console).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|