@goodandready/dsh-russian-lang 0.3.25 → 0.3.27
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 -11
- package/bin/dsh-i18n.mjs +20 -10
- package/glossary.json +206 -0
- package/lib/client.js +176 -112
- package/lib/locales/plugins/15-commandcode.json +1 -1
- package/lib/locales/plugins/17-dsh-context.json +1 -1
- package/lib/locales/plugins/26-config-manager.json +1 -1
- package/lib/locales/plugins/34-key-rotation.json +1 -1
- package/lib/translator.js +4 -1
- package/package.json +4 -2
- package/lib/client.template.js +0 -2800
package/lib/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({id: '@goodandready/dsh-russian-lang',factory: (require) => {var module = { exports: {} }
|
|
2
2
|
var React = null
|
|
3
3
|
try { React = require('react') } catch (e) { void e; }
|
|
4
|
+
const h = (...args) => (React ? React.createElement(...args) : null)
|
|
4
5
|
let isInspectorActive = false
|
|
5
6
|
let hoverBoxEl = null
|
|
6
7
|
let pillEl = null
|
|
@@ -400,6 +401,9 @@ const SETTINGS_NS_NAME = 'russian-lang'
|
|
|
400
401
|
const FORM_NS = 'dsh-russian-lang'
|
|
401
402
|
const LEGACY_NS = 'russian-lang'
|
|
402
403
|
const ROW_CONFIG_KEY = '@goodandready/dsh-russian-lang#' + FORM_NS
|
|
404
|
+
const resolveTypography = (val) => {const t = (val && val.typography) || {}
|
|
405
|
+
return {enabled: t.enabled === true,liveInput: t.liveInput === true,yo: t.yo === true
|
|
406
|
+
}}
|
|
403
407
|
function apply(ctx) {const runtime = ctx.locale
|
|
404
408
|
const configForms = ctx && ctx.configForms
|
|
405
409
|
const resolveScope = (c) => {try {if (c && c.configForms) return c.configForms.get(FORM_NS)
|
|
@@ -432,7 +436,8 @@ try {const cachedCore = loadLocalDict('core')
|
|
|
432
436
|
if (cachedCore && cachedCore.data && cachedCore.data.core) {registerDictMap(cachedCore.data.core)
|
|
433
437
|
if (cachedCore.data.zhRu && typeof ZH_RU === 'object') {Object.assign(ZH_RU, cachedCore.data.zhRu)
|
|
434
438
|
}}} catch (_e) { void _e }
|
|
435
|
-
const fetchCachedResource = async (url, cacheKey, onData) => {
|
|
439
|
+
const fetchCachedResource = async (url, cacheKey, onData) => {if (disposed) return
|
|
440
|
+
let etag = null
|
|
436
441
|
let delivered = false
|
|
437
442
|
if (typeof caches !== 'undefined') {try {const cache = await caches.open(CACHE_NAME)
|
|
438
443
|
const matched = await cache.match(url)
|
|
@@ -446,7 +451,10 @@ if (local && local.data) {etag = local.etag
|
|
|
446
451
|
delivered = true
|
|
447
452
|
onData(local.data)
|
|
448
453
|
}}
|
|
449
|
-
if (typeof fetch !== 'function')
|
|
454
|
+
if (typeof fetch !== 'function') {if (!delivered) throw new Error('Fetch API is not available')
|
|
455
|
+
return
|
|
456
|
+
}
|
|
457
|
+
let networkError = null
|
|
450
458
|
try {const headers = { 'Accept': 'application/json' }
|
|
451
459
|
if (etag) headers['If-None-Match'] = etag
|
|
452
460
|
const res = await fetch(url, { headers })
|
|
@@ -459,29 +467,44 @@ if (typeof caches !== 'undefined') {try {const cache = await caches.open(CACHE_N
|
|
|
459
467
|
await cache.put(url, cloned)
|
|
460
468
|
} catch (_e) { void _e }}
|
|
461
469
|
if (cacheKey) saveLocalDict(cacheKey, newEtag, data)
|
|
462
|
-
}}
|
|
470
|
+
}} else {networkError = new Error(`HTTP ${res.status}: ${res.statusText}`)
|
|
471
|
+
}} catch (err) {networkError = err
|
|
472
|
+
}
|
|
473
|
+
if (!delivered && networkError) {throw networkError
|
|
474
|
+
}}
|
|
475
|
+
let disposed = false
|
|
476
|
+
let corePreloadTimer = null
|
|
463
477
|
const loadedPluginNames = new Set()
|
|
464
|
-
const
|
|
478
|
+
const inFlightPluginNames = new Set()
|
|
479
|
+
const queuedPluginNames = new Set()
|
|
465
480
|
let pluginBatchTimer = null
|
|
466
|
-
const loadPluginDictionaries = (names) => {if (typeof fetch !== 'function') return
|
|
481
|
+
const loadPluginDictionaries = (names) => {if (disposed || typeof fetch !== 'function') return
|
|
482
|
+
const isSelective = Array.isArray(names) && names.length > 0
|
|
467
483
|
const toLoad = []
|
|
468
|
-
for (const raw of names) {const n = String(raw).trim()
|
|
469
|
-
if (!n || loadedPluginNames.has(n) ||
|
|
484
|
+
if (isSelective) {for (const raw of names) {const n = String(raw).trim()
|
|
485
|
+
if (!n || loadedPluginNames.has(n) || inFlightPluginNames.has(n)) continue
|
|
470
486
|
toLoad.push(n)
|
|
471
|
-
|
|
487
|
+
inFlightPluginNames.add(n)
|
|
488
|
+
}
|
|
489
|
+
if (toLoad.length === 0) return
|
|
472
490
|
}
|
|
473
491
|
const qs = toLoad.length > 0 ? ('?names=' + encodeURIComponent(toLoad.join(','))) : ''
|
|
474
492
|
const url = '/api/dsh-russian-lang/dict/plugins' + qs
|
|
475
|
-
const cacheKey = toLoad.length > 0 ? ('plugins_' + toLoad.sort().join('_')) : 'plugins_all'
|
|
493
|
+
const cacheKey = toLoad.length > 0 ? ('plugins_' + toLoad.slice().sort().join('_')) : 'plugins_all'
|
|
476
494
|
fetchCachedResource(url, cacheKey, (data) => {if (data && data.plugins) {registerDictMap(data.plugins)
|
|
477
|
-
for (const n of toLoad) loadedPluginNames.add(n)
|
|
478
|
-
}
|
|
495
|
+
if (toLoad.length > 0) {for (const n of toLoad) loadedPluginNames.add(n)
|
|
496
|
+
} else if (data.plugins) {for (const n of Object.keys(data.plugins)) loadedPluginNames.add(n)
|
|
497
|
+
}}}).catch(() => {}).finally(() => {for (const n of toLoad) inFlightPluginNames.delete(n)
|
|
479
498
|
})
|
|
480
499
|
}
|
|
481
|
-
const schedulePluginLoad = (name) => {if (!name
|
|
482
|
-
|
|
500
|
+
const schedulePluginLoad = (name) => {if (!name) return
|
|
501
|
+
const n = String(name).trim()
|
|
502
|
+
if (!n || loadedPluginNames.has(n) || inFlightPluginNames.has(n) || queuedPluginNames.has(n)) return
|
|
503
|
+
queuedPluginNames.add(n)
|
|
483
504
|
if (pluginBatchTimer) clearTimeout(pluginBatchTimer)
|
|
484
|
-
pluginBatchTimer = setTimeout(() => {
|
|
505
|
+
pluginBatchTimer = setTimeout(() => {pluginBatchTimer = null
|
|
506
|
+
const batch = Array.from(queuedPluginNames)
|
|
507
|
+
queuedPluginNames.clear()
|
|
485
508
|
if (batch.length > 0) loadPluginDictionaries(batch)
|
|
486
509
|
}, 50)
|
|
487
510
|
}
|
|
@@ -491,15 +514,18 @@ if (data.zhRu && typeof ZH_RU === 'object') {Object.assign(ZH_RU, data.zhRu)
|
|
|
491
514
|
if (typeof updateZhRu === 'function') updateZhRu(data.zhRu)
|
|
492
515
|
}
|
|
493
516
|
if (typeof syncZhDom === 'function') syncZhDom()
|
|
494
|
-
setTimeout(() => { loadPluginDictionaries([]) }, 100)
|
|
495
|
-
}).catch(() => {if (typeof fetch === 'function') {fetch('/api/dsh-russian-lang/dict/all', { headers: { 'Accept': 'application/json' } }).then((res) => res.ok
|
|
517
|
+
corePreloadTimer = setTimeout(() => { if (!disposed) loadPluginDictionaries([]) }, 100)
|
|
518
|
+
}).catch((coreErr) => {if (typeof fetch === 'function') {fetch('/api/dsh-russian-lang/dict/all', { headers: { 'Accept': 'application/json' } }).then((res) => {if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
|
519
|
+
return res.json()
|
|
520
|
+
}).then((data) => {if (!data) return
|
|
496
521
|
const allDicts = Object.assign({}, data.core || {}, data.plugins || {}, data)
|
|
497
522
|
if (data.zhRu && typeof ZH_RU === 'object') {Object.assign(ZH_RU, data.zhRu)
|
|
498
523
|
if (typeof updateZhRu === 'function') updateZhRu(data.zhRu)
|
|
499
524
|
}
|
|
500
525
|
registerDictMap(allDicts)
|
|
501
526
|
if (typeof syncZhDom === 'function') syncZhDom()
|
|
502
|
-
}).catch(() => {
|
|
527
|
+
}).catch((allErr) => {if (typeof console !== 'undefined' && console.warn) {console.warn('[dsh-russian-lang] Не удалось загрузить словари: core (' + (coreErr?.message || coreErr) + ') и fallback /dict/all (' + (allErr?.message || allErr) + ')')
|
|
528
|
+
}})
|
|
503
529
|
}})
|
|
504
530
|
const getOverrides = () => {try {const value = scope.getSnapshot().value
|
|
505
531
|
return value && value.overrides ? value.overrides : {}} catch (err) { return {} }}
|
|
@@ -513,7 +539,8 @@ const translationRegistry = new Map()
|
|
|
513
539
|
const lookup = (ns, key) => (runtime.lookup.length >= 3 ? runtime.lookup(ns, key, lookupChain()) : runtime.lookup(ns, key))
|
|
514
540
|
const syncLang = () => {try {if (typeof document !== 'undefined' && document.documentElement && runtime.getLocale().active === 'ru') document.documentElement.lang = 'ru-RU'
|
|
515
541
|
} catch (err) { void err; }}
|
|
516
|
-
const RUNTIME_PROP_NAMES = ['plural','pluralForm','formatDate','formatCompactNumber','formatTokens','formatNumber','formatRelativeTime','formatCurrency','inflect','getPluginLocalizationStatus','stemRussian','fuzzyMatchRu','humanizeError'
|
|
542
|
+
const RUNTIME_PROP_NAMES = ['plural', 'pluralForm', 'formatDate', 'formatCompactNumber', 'formatTokens','formatNumber', 'formatRelativeTime', 'formatCurrency', 'inflect','getPluginLocalizationStatus', 'stemRussian', 'fuzzyMatchRu', 'humanizeError','findTranslationKey', 'openInspector', 'translationRegistry','bilingualCommandMatch', 'filterCommands', 'formatErrorToast'
|
|
543
|
+
]
|
|
517
544
|
ctx.effect(() => {const origTranslate = runtime.translate
|
|
518
545
|
const origProps = {}
|
|
519
546
|
for (const p of RUNTIME_PROP_NAMES) origProps[p] = runtime[p]
|
|
@@ -522,12 +549,24 @@ runtime.pluralForm = pluralForm
|
|
|
522
549
|
runtime.formatDate = formatDate
|
|
523
550
|
runtime.formatCompactNumber = formatCompactNumber
|
|
524
551
|
runtime.formatTokens = formatTokens
|
|
525
|
-
|
|
552
|
+
runtime.humanizeError = humanizeError
|
|
553
|
+
Object.assign(runtime, {formatNumber, formatRelativeTime, formatCurrency, inflect,getPluginLocalizationStatus, stemRussian, fuzzyMatchRu
|
|
554
|
+
})
|
|
555
|
+
runtime.findTranslationKey = (text) => (typeof findTranslationKey === 'function' ? findTranslationKey(text, RU, getOverrides(), ZH_RU) : null)
|
|
556
|
+
runtime.openInspector = (...args) => (typeof openInspectorModal === 'function' ? openInspectorModal(...args) : null)
|
|
557
|
+
runtime.translationRegistry = translationRegistry
|
|
558
|
+
runtime.bilingualCommandMatch = (query, cmd, opts) => (typeof bilingualCommandMatch === 'function' ? bilingualCommandMatch(query, cmd, opts) : { matched: false, score: 0 })
|
|
559
|
+
runtime.filterCommands = (query, cmds, opts) => (typeof filterCommands === 'function' ? filterCommands(query, cmds, opts) : cmds)
|
|
560
|
+
runtime.formatErrorToast = (err) => (typeof formatErrorToast === 'function' ? formatErrorToast(err) : null)
|
|
526
561
|
const boundOrig = typeof origTranslate === 'function' ? origTranslate.bind(runtime) : (() => '')
|
|
527
562
|
runtime.translate = function (ns, key, params) {if (ns && !RU[ns] && !loadedPluginNames.has(ns)) schedulePluginLoad(ns)
|
|
528
|
-
const
|
|
529
|
-
if (
|
|
530
|
-
|
|
563
|
+
const activeLang = runtime.getLocale ? (runtime.getLocale().active || 'ru') : 'ru'
|
|
564
|
+
if (activeLang === 'ru') {const ov = getOverrides()
|
|
565
|
+
const fullKey = ns ? ns + '.' + key : key
|
|
566
|
+
const val = ov[fullKey] !== undefined ? ov[fullKey] : ov[key]
|
|
567
|
+
if (val !== undefined) return params ? fill(val, params) : val
|
|
568
|
+
}
|
|
569
|
+
if (activeLang === 'ru' && params) {const n = params.n ?? params.count
|
|
531
570
|
if (typeof n === 'number') {const form = pluralForm(n)
|
|
532
571
|
const m = /^(.*)[.](one|other)$/.exec(key)
|
|
533
572
|
if (m) {if (form === 'few' || form === 'many') {const tmpl = lookup(ns, m[1] + '.' + form) ?? lookup('common', m[1] + '.' + form)
|
|
@@ -543,7 +582,11 @@ let removeLang = null
|
|
|
543
582
|
if (!runtime.getLocale().locales.some((l) => l.id === 'ru') && typeof runtime.addLanguage === 'function') {removeLang = runtime.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })
|
|
544
583
|
syncLang()
|
|
545
584
|
}
|
|
546
|
-
return () => {
|
|
585
|
+
return () => {disposed = true
|
|
586
|
+
if (corePreloadTimer) { clearTimeout(corePreloadTimer); corePreloadTimer = null }
|
|
587
|
+
if (pluginBatchTimer) { clearTimeout(pluginBatchTimer); pluginBatchTimer = null }
|
|
588
|
+
if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }
|
|
589
|
+
runtime.translate = origTranslate
|
|
547
590
|
for (const p of RUNTIME_PROP_NAMES) {if (origProps[p] !== undefined) runtime[p] = origProps[p]
|
|
548
591
|
else delete runtime[p]
|
|
549
592
|
}
|
|
@@ -663,19 +706,33 @@ return null
|
|
|
663
706
|
const DOM_EN_ATTRS = {'Streaming preview': 'Предпросмотр стриминга','Visualization streaming preview': 'Предпросмотр визуализации','Security Auditor Shield': 'Защитный щит аудитора','Shield: Safe': 'Щит: Безопасно','Shield: Alert': 'Щит: Тревога','e.g. Implement report export and cover with unit tests': 'Например: Реализовать экспорт отчёта и покрыть юнит-тестами','e.g. Implement report export and cover with unit tests...': 'Например: Реализовать экспорт отчёта и покрыть юнит-тестами…','e.g. Implement user profile settings card with theme tokens': 'Например: Реализовать карточку настроек профиля с токенами темы','Detailed functional specs, acceptance criteria, constraints...': 'Детальная функциональная спецификация, критерии приёмки, ограничения…','Specific instructions, questions, or requirements for this specialist...': 'Конкретные инструкции, вопросы или требования для данного специалиста…',}
|
|
664
707
|
const DOM_EN_TEXT = {'Scheduled tasks': 'Задачи по расписанию','Files': 'Файлы','Back': 'Назад','Hosts': 'Хосты','Containers': 'Контейнеры','Tunnels': 'Туннели','Cluster': 'Кластер','Remote Workspace': 'Удалённое рабочее пространство','Native SSH connections to remote development environments with terminal, sftp, and tunneling.': 'SSH-соединения с удалёнными средами разработки: терминал, SFTP и туннели.','Import ~/.ssh/config': 'Импорт ~/.ssh/config','Terminal font': 'Шрифт терминала','Save Profile': 'Сохранить профиль','Edit Host Profile': 'Редактирование профиля хоста','Add New Host Profile': 'Добавление профиля хоста','Profile Name': 'Название профиля','Host / IP Address': 'Хост / IP-адрес','Port': 'Порт','Username': 'Имя пользователя','Jump hosts': 'Jump-хосты','Proxy command': 'Команда прокси','Environment': 'Окружение','Location': 'Расположение','Tags': 'Теги','Description': 'Описание','Authentication Method': 'Метод аутентификации','SSH Private Key': 'Приватный SSH-ключ','SSH Agent': 'SSH-агент','Password': 'Пароль','Private Key Path': 'Путь к приватному ключу','Key Passphrase (optional)': 'Парольная фраза ключа (опционально)','Remote Workspace Directory': 'Каталог удалённой рабочей области','Browse Remote...': 'Обзор удалённых…','Local Mirror Directory': 'Каталог локального зеркала','Search names, fields or keywords': 'Поиск по имени, сфере или ключевым словам','Search names, f[专家] or keywords': 'Поиск по имени, сфере или ключевым словам','No enabled experts yet. Type a keyword to find one.': 'Нет активных экспертов. Введите ключевое слово для поиска.','Search teams or goals': 'Поиск команды или цели работы','Team service unavailable. Reload the plugin.': 'Сервис команд экспертов недоступен, перезагрузите плагин.','Image Studio & Vault': 'Студия изображений и хранилище','Agent Browser': 'Браузер агента','Request approval': 'Запрашивать подтверждение','Today': 'Сегодня','Plugin Market': 'Магазин плагинов','GitHub ops': 'Операции с GitHub','Auto mode': 'Автоматический режим','Full access': 'Полный доступ','Read only': 'Только чтение','Side card': 'Боковая панель','panelName': 'Палитра команд','Hooks': 'Хуки','Manage what the side card shows and how it behaves': 'Настройка содержимого и поведения боковой панели','Inject the sidebar-open tool for the model': 'Предоставить модели инструмент sidebar-open','Position compatibility mode': 'Режим совместимости расположения','Auto-detect': 'Автоопределение','Sidebar content': 'Содержимое боковой панели','Changes': 'Изменения','Tasks': 'Задачи','Time Machine': 'Машина времени','Live Canvas': 'Живой холст','Side Chat (beta)': 'Боковой чат (бета)','Terminal': 'Терминал','Feature settings': 'Настройки функции','Low': 'Низкий','Medium': 'Средний','High': 'Высокий','Effort': 'Рассуждения','Search engine (ModSearch)': 'Поисковая система (ModSearch)','Search engine provider configuration.': 'Настройка провайдера поисковой системы.','X search only': 'Только поиск в X','Security Auditor Shield': 'Защитный щит аудитора','Shield: Safe': 'Щит: Безопасно','Shield: Alert': 'Щит: Тревога','Lens': 'Линза','Gallery': 'Галерея','SSH: Local': 'SSH: Локально','SSH: Remote': 'SSH: Удалённо','7/7 rot': '7/7 рот.','Smoke chat': 'Тестовый чат','Quick Launch Goal': 'Быстрый запуск цели','Define the objective for the agent in autonomous mode:': 'Сформулируйте задачу для автономной работы агента:','Fix Bug': 'Исправление бага','Refactor (YAGNI)': 'Рефакторинг (YAGNI)','Tests & Coverage': 'Тесты и покрытие','Code Review': 'Ревью кода','New Feature': 'Новая функция','Security Audit': 'Аудит безопасности','Docs & Contract': 'Документация и контракт','Upgrade Deps': 'Обновление зависимостей','Dead Code': 'Мёртвый код','Performance': 'Производительность','Start Goal': 'Запустить цель','Launch Multi-Agent Orchestrator': 'Запуск мультиагентного оркестратора','Full DAG Pipeline': 'Полный DAG-пайплайн','Direct Specialist Subagent': 'Прямой субагент-специалист','Objective Title': 'Название задачи','Scope & Requirements': 'Объём и требования','Topology Scenario': 'Топологический сценарий','Auto (Infer based on prompt complexity)': 'Авто (определить по сложности задачи)','Hotfix (1 Stage: Triage & Minimal Fix)': 'Хотфикс (1 этап: анализ и точечный фикс)','Simple (2 Stages: Spec + Exec)': 'Простой (2 этапа: ТЗ + реализация)','Medium (4 Stages: Spec -> Design -> Code -> QA)': 'Средний (4 этапа: ТЗ -> Дизайн -> Код -> Тестирование)','Complex (6 Stages: Full Engineering Lifecycle)': 'Сложный (6 этапов: полный инженерный цикл)','Enterprise (7 Stages: R&D Spike -> Fullstack -> Gate)': 'Enterprise (7 этапов: R&D исследование -> Фулстек -> Гейт приёмки)','Target Specialist Role': 'Роль целевого специалиста','UI/UX Interface Designer': 'UI/UX дизайнер интерфейсов','System Architect (DESIGN.md / ADR)': 'Системный архитектор (DESIGN.md / ADR)','Technical Spec Analyst': 'Аналитик технических спецификаций','Senior Frontend Developer': 'Ведущий frontend-разработчик','Senior Backend Developer': 'Ведущий backend-разработчик','QA Automation Engineer': 'Инженер автоматизации тестирования','Refactoring & Complexity Specialist': 'Специалист по рефакторингу и сложности','Hotfix & Diagnostic Engineer': 'Инженер хотфиксов и диагностики','Documentation Specialist': 'Технический писатель / Документация','Spike & R&D Researcher': 'Исследователь R&D и прототипирования','DevOps & Tooling Specialist': 'DevOps и инфраструктурный специалист','Instructions for Subagent': 'Инструкции для субагента','Start Pipeline': 'Запустить пайплайн','Delegate Subagent': 'Делегировать субагенту','Dispatching...': 'Отправка…','Discounted rate active': 'Действует сниженный тариф','off-peak': 'непиковый','peak': 'пиковый','Context cache saved:': 'Сэкономлено на кэше:','1M tokens, $': '1 млн токенов, $','Input (cache hit)': 'Ввод (попадание в кэш)','Input (cache miss)': 'Ввод (промах кэша)','Output': 'Вывод','SESSION TOKENS': 'ТОКЕНЫ СЕССИИ','SPEND BY MODEL': 'РАСХОД ПО МОДЕЛЯМ','Session total:': 'Всего за сессию:','Copy Summary': 'Скопировать сводку','Context Lens': 'Линза контекста','Saved tokens': 'Сэкономлено токенов','% Saved': '% экономии','ops': 'операций','Budget': 'Бюджет','Active focus': 'Активный фокус','No focus paths set': 'Пути фокусировки не заданы','Status': 'Статус','Graph & CI': 'Граф и CI','Events & PRs': 'События и PR','Branch': 'Ветка','Sync': 'Синхронизация','Up to date with remote (@{upstream})': 'Синхронизировано с удалённым репозиторием (@{upstream})','Up to date with remote': 'Синхронизировано с удалённым репозиторием','Clean': 'Чисто','No modified or untracked files in the working directory.': 'В рабочем каталоге нет изменённых или неотслеживаемых файлов.','RECENT COMMITS': 'ПОСЛЕДНИЕ КОММИТЫ','Active jobs': 'Активные задачи','No active jobs': 'Нет активных задач','Register in Models': 'Зарегистрировать в моделях','Base URL': 'Базовый URL','API key env / credential name': 'Имя переменной окружения / ключа API','Default model': 'Модель по умолчанию','Enabled': 'Включено','Save': 'Сохранить','Saved': 'Сохранено','Refresh': 'Обновить','Loading settings…': 'Загрузка настроек…','Preferred engine': 'Предпочитаемый движок','API key': 'API-ключ','stored, leave empty to keep it': 'сохранён, оставьте пустым для сохранения','Built-in official endpoint, leave blank to use it': 'Встроенная официальная конечная точка, оставьте пустым','Discard': 'Сбросить','Model synchronization': 'Синхронизация моделей','All API-key providers': 'Все провайдеры API-ключей','Preview only (dry-run)': 'Только предпросмотр (dry-run)','Confirm stale removal': 'Подтверждать удаление устаревших','Refresh status': 'Обновить статус','Refresh all': 'Обновить всё','Discover': 'Обнаружить','Check availability': 'Проверить доступность','Check credentials': 'Проверить учётные данные','Choose models': 'Выбрать модели','Manual model selection': 'Выбор моделей вручную','Mode': 'Режим','Hybrid (auto-rewrite + tools)': 'Гибридный (авто-переписывание + инструменты)','Describe strategy': 'Стратегия описания','Auto (use vision LLM)': 'Авто (использовать Vision LLM)','Escalation': 'Эскалация','Simple only (one pass)': 'Только простая (один проход)','Routing': 'Маршрутизация','Channel order': 'Порядок каналов','Issue Reporter': 'Репортёр проблем','GitHub sign-in is not configured for this installation.': 'Вход через GitHub не настроен для этой установки.','0 plugins': '0 плагинов','Catalog': 'Каталог','Report Editor': 'Редактор отчёта','My Reports': 'Мои отчёты','Authorization': 'Авторизация','Search installed plugins…': 'Поиск установленных плагинов…','Search installed plugins...': 'Поиск установленных плагинов...','Refresh inventory': 'Обновить список','Loading inventory & status…': 'Загрузка списка и статуса…','Loading inventory & status...': 'Загрузка списка и статуса...'
|
|
665
708
|
}
|
|
666
|
-
const
|
|
709
|
+
const isProtectedTextNode = (node) => {const p = node.parentElement
|
|
710
|
+
if (!p) return true
|
|
711
|
+
if (p.closest('code, pre, script, style, textarea, input, select, kbd, samp, [contenteditable="true"], [data-composer-input], [role="textbox"]')) return true
|
|
712
|
+
if (p.closest('.katex, [data-latex], math')) return true
|
|
713
|
+
if (p.closest('[data-chat-flow-status="running"], [data-turn-running], [data-turn-tail], [data-streaming="true"], .dsw-turn-running, [class*="streaming"], [class*="Streaming"]')) return true
|
|
714
|
+
const sel = typeof window !== 'undefined' && window.getSelection && window.getSelection()
|
|
715
|
+
if (sel && !sel.isCollapsed && sel.rangeCount > 0) {try {const range = sel.getRangeAt(0)
|
|
716
|
+
if (range.intersectsNode ? range.intersectsNode(node) : (sel.containsNode && sel.containsNode(node, true))) {return true
|
|
717
|
+
}} catch (e) { void e; }}
|
|
718
|
+
return false
|
|
719
|
+
}
|
|
720
|
+
const ZH_WALKER = (root) => {try {if (runtime.getLocale && runtime.getLocale().active !== 'ru') return
|
|
721
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT)
|
|
667
722
|
const hits = []
|
|
668
723
|
const curOverrides = getOverrides()
|
|
669
724
|
for (let node = walker.nextNode(); node; node = walker.nextNode()) {const val = node.nodeValue
|
|
670
725
|
if (!val) continue
|
|
671
726
|
const trimmed = val.trim()
|
|
727
|
+
if (!trimmed) continue
|
|
728
|
+
if (isProtectedTextNode(node)) continue
|
|
672
729
|
if (curOverrides[trimmed] !== undefined) {node.nodeValue = val.replace(trimmed, curOverrides[trimmed])
|
|
673
730
|
continue
|
|
674
731
|
}
|
|
675
732
|
const p = node.parentElement
|
|
676
|
-
const
|
|
677
|
-
if (trimmed.length >= 1 && ZH_CJK.test(val)) {if (!
|
|
678
|
-
} else if (trimmed.length >= 1 && DOM_EN_TEXT[trimmed]) {if (!
|
|
733
|
+
const isProtectedContent = p && p.closest('.chat-message, .markdown-body')
|
|
734
|
+
if (trimmed.length >= 1 && ZH_CJK.test(val)) {if (!isProtectedContent) hits.push(node)
|
|
735
|
+
} else if (trimmed.length >= 1 && DOM_EN_TEXT[trimmed]) {if (!isProtectedContent) {node.nodeValue = val.replace(trimmed, DOM_EN_TEXT[trimmed])
|
|
679
736
|
}}}
|
|
680
737
|
for (const node of hits) {const next = zhTranslateText(node.nodeValue)
|
|
681
738
|
if (next && next !== node.nodeValue) node.nodeValue = next
|
|
@@ -732,20 +789,14 @@ document.removeEventListener('click', onInteraction)
|
|
|
732
789
|
syncZhDom()
|
|
733
790
|
const TYPO_YO_PAIRS = [["еще","ещё"],["ее","её"],["черный","чёрный"],["черная","чёрная"],["черные","чёрные"],["зеленый","зелёный"],["желтый","жёлтый"],["легкий","лёгкий"],["тяжелый","тяжёлый"],["надежный","надёжный"],["дешевый","дешёвый"],["идет","идёт"],["дает","даёт"],["ведет","ведёт"],["несет","несёт"],["живет","живёт"],["привел","привёл"],["шел","шёл"],["нее","неё"],["мое","моё"],["серьезно","серьёзно"],["придется","придётся"],["насчет","насчёт"],["твое","твоё"],["свое","своё"],["пойдем","пойдём"],["пришел","пришёл"],["нашел","нашёл"],["идем","идём"],["вперед","вперёд"],["пошел","пошёл"],["ребенка","ребёнка"],["ребенок","ребёнок"],["счет","счёт"],["своем","своём"],["придет","придёт"],["ушел","ушёл"],["ждет","ждёт"],["мертв","мёртв"],["твоем","твоём"],["вернется","вернётся"],["днем","днём"],["найдем","найдём"],["пойдет","пойдёт"],["начнем","начнём"],["вернемся","вернёмся"],["остается","остаётся"],["принес","принёс"],["идешь","идёшь"],["трех","трёх"],["ребенком","ребёнком"],["самолет","самолёт"],["определенно","определённо"],["пойдешь","пойдёшь"],["умрет","умрёт"],["прием","приём"],["прошел","прошёл"],["убьет","убьёт"],["тетя","тётя"],["провел","провёл"],["произойдет","произойдёт"],["семьей","семьёй"],["пойдемте","пойдёмте"],["пройдет","пройдёт"],["возьмем","возьмём"],["вернешься","вернёшься"],["найдешь","найдёшь"],["живешь","живёшь"],["отчет","отчёт"],["займет","займёт"],["ждем","ждём"],["сошел","сошёл"],["вдвоем","вдвоём"],["найдет","найдёт"],["вел","вёл"],["живем","живём"],["зашел","зашёл"],["начнется","начнётся"],["путем","путём"],["подойдет","подойдёт"],["режиссер","режиссёр"],["уйдет","уйдёт"],["придешь","придёшь"],["идемте","идёмте"],["ведешь","ведёшь"],["пес","пёс"],["умрешь","умрёшь"],["ждешь","ждёшь"],["найдете","найдёте"],["найдется","найдётся"],["клево","клёво"],["поймешь","поймёшь"],["разберемся","разберёмся"],["времен","времён"],["введен","введён"],["включен","включён"],["включенных","включённых"],["возьмется","возьмётся"],["завершен","завершён"],["завершенного","завершённого"],["задает","задаёт"],["задается","задаётся"],["заменен","заменён"],["звезд","звёзд"],["звезды","звёзды"],["изменен","изменён"],["истек","истёк"],["незавершенная","незавершённая"],["неподтвержденные","неподтверждённые"],["несохраненные","несохранённые"],["обновлен","обновлён"],["обновленные","обновлённые"],["определен","определён"],["отклонен","отклонён"],["отменен","отменён"],["очередность","очерёдность"],["поврежден","повреждён"],["повторен","повторён"],["подключен","подключён"],["подтвержденные","подтверждённые"],["приемки","приёмки"],["раздает","раздаёт"],["разрешенная","разрешённая"],["разрешенные","разрешённые"],["создаем","создаём"],["сойдется","сойдётся"],["сохранен","сохранён"],["сохраненный","сохранённый"],["счету","счёту"],["тяжелые","тяжёлые"],["темная","тёмная"],["уберет","уберёт"],["удален","удалён"],["учетные","учётные"],["учетных","учётных"],["емкости","ёмкости"],["емкость","ёмкость"]]
|
|
734
791
|
const typoYo = makeTypoYo(TYPO_YO_PAIRS)
|
|
735
|
-
const getTypoConf = () => {try {const t = scope.getSnapshot().value
|
|
736
|
-
if (!t
|
|
737
|
-
return { yo: t.yo
|
|
792
|
+
const getTypoConf = () => {try {const t = resolveTypography(scope.getSnapshot().value)
|
|
793
|
+
if (!t.enabled) return null
|
|
794
|
+
return { yo: t.yo }} catch (err) { return null }}
|
|
738
795
|
const typoNode = (node, conf) => {const before = node.nodeValue
|
|
739
796
|
if (!before || !before.match || (before.match(/[\u0400-\u04FF]/g) || []).length < 3) return
|
|
740
|
-
if (node
|
|
741
|
-
if (node.parentElement && node.parentElement.closest('
|
|
797
|
+
if (isProtectedTextNode(node)) return
|
|
798
|
+
if (node.parentElement && node.parentElement.closest('a, button')) return
|
|
742
799
|
if (/\$[^$\n]+\$/.test(before)) return
|
|
743
|
-
if (node.parentElement && node.parentElement.closest('[data-chat-flow-status="running"], [data-turn-running], [data-turn-tail], [data-streaming="true"], .dsw-turn-running, [class*="streaming"], [class*="Streaming"]')) {return
|
|
744
|
-
}
|
|
745
|
-
const sel = typeof window !== 'undefined' && window.getSelection && window.getSelection()
|
|
746
|
-
if (sel && !sel.isCollapsed && sel.rangeCount > 0) {try {const range = sel.getRangeAt(0)
|
|
747
|
-
if (range.intersectsNode ? range.intersectsNode(node) : (sel.containsNode && sel.containsNode(node, true))) {return
|
|
748
|
-
}} catch (e) { void e; }}
|
|
749
800
|
let after = typoQuotes(before)
|
|
750
801
|
after = typoDash(after)
|
|
751
802
|
after = typoPunct(after)
|
|
@@ -771,11 +822,17 @@ const queueTypo = (roots) => {for (const r of roots) typoPending.add(r)
|
|
|
771
822
|
if (!typoQueued) typoQueued = requestAnimationFrame(flushTypo)
|
|
772
823
|
}
|
|
773
824
|
const syncTypo = () => {try {if (typeof document === 'undefined') return
|
|
774
|
-
|
|
825
|
+
const conf = getTypoConf()
|
|
826
|
+
const isRu = runtime.getLocale ? runtime.getLocale().active === 'ru' : true
|
|
827
|
+
if (!isRu || !conf) {if (typoObserver) { typoObserver.disconnect(); typoObserver = null }
|
|
828
|
+
if (typoQueued) { cancelAnimationFrame(typoQueued); typoQueued = null }
|
|
829
|
+
typoPending.clear()
|
|
830
|
+
return
|
|
831
|
+
}
|
|
832
|
+
if (typoObserver) {typoWalk(document.body, conf)
|
|
775
833
|
return
|
|
776
834
|
}
|
|
777
|
-
|
|
778
|
-
typoWalk(document.body, getTypoConf())
|
|
835
|
+
typoWalk(document.body, conf)
|
|
779
836
|
typoObserver = new MutationObserver((records) => {const roots = []
|
|
780
837
|
for (const record of records) {if (record.type === 'childList') {for (const added of record.addedNodes) {if (added.nodeType === 3 || added.nodeType === 1) roots.push(added)
|
|
781
838
|
}}}
|
|
@@ -783,15 +840,23 @@ if (roots.length > 0) queueTypo(roots)
|
|
|
783
840
|
})
|
|
784
841
|
typoObserver.observe(document.body, { childList: true, subtree: true })
|
|
785
842
|
} catch (err) { void err; }}
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
if (typoObserver) typoObserver.disconnect()
|
|
789
|
-
}}, 'dsh-russian-lang: typography')
|
|
843
|
+
ctx.effect(() => {const unsubscribeLocale = runtime.subscribe(syncTypo)
|
|
844
|
+
const unsubscribeScope = typeof scope?.subscribe === 'function' ? scope.subscribe(syncTypo) : (() => {})
|
|
790
845
|
syncTypo()
|
|
846
|
+
return () => {unsubscribeLocale()
|
|
847
|
+
unsubscribeScope()
|
|
848
|
+
if (typoObserver) { typoObserver.disconnect(); typoObserver = null }
|
|
849
|
+
if (typoQueued) { cancelAnimationFrame(typoQueued); typoQueued = null }
|
|
850
|
+
typoPending.clear()
|
|
851
|
+
}}, 'dsh-russian-lang: typography')
|
|
791
852
|
const layout = makeLayout(new Set(["не","что","ты","это","на","он","мы","как","вы","да","мне","нет","меня","так","но","его","все","она","тебя","если","за","бы","тебе","они","чтобы","же","есть","просто","из","для"]), new Set())
|
|
792
853
|
const layoutFixCandidate = layout.candidate
|
|
793
854
|
const learnWords = layout.learnWords
|
|
794
855
|
const layoutEnabled = () => scope.getSnapshot().value?.layoutConversion !== false
|
|
856
|
+
const slashAliasesEnabled = () => {const val = scope.getSnapshot().value
|
|
857
|
+
const ruActive = runtime.getLocale ? (runtime.getLocale().active === 'ru') : true
|
|
858
|
+
return ruActive && val?.slashAliases !== false
|
|
859
|
+
}
|
|
795
860
|
function setNativeInputValue(el, value, cursorStart, cursorEnd) {if (!el) return
|
|
796
861
|
const oldVal = el.value || ''
|
|
797
862
|
if (oldVal === value) return
|
|
@@ -998,7 +1063,7 @@ learnWords(c.converted)
|
|
|
998
1063
|
}}
|
|
999
1064
|
return
|
|
1000
1065
|
}
|
|
1001
|
-
if (ev.key === ' ' || ev.key === 'Enter') {if (value && value.startsWith('/') && typeof expandSlashAlias === 'function') {const expanded = expandSlashAlias(value)
|
|
1066
|
+
if ((ev.key === ' ' || ev.key === 'Enter') && slashAliasesEnabled()) {if (value && value.startsWith('/') && typeof expandSlashAlias === 'function') {const expanded = expandSlashAlias(value)
|
|
1002
1067
|
if (expanded !== value) {if (ev.key === ' ') {ev.preventDefault()
|
|
1003
1068
|
isFormatting = true
|
|
1004
1069
|
try {const nextVal = expanded + ' '
|
|
@@ -1010,13 +1075,11 @@ return
|
|
|
1010
1075
|
try {setComposerText(el, expanded, expanded.length, expanded.length)
|
|
1011
1076
|
} finally {isFormatting = false
|
|
1012
1077
|
}}}}}
|
|
1013
|
-
if (ev.key === 'Enter' && !ev.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {const
|
|
1014
|
-
const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
|
|
1078
|
+
if (ev.key === 'Enter' && !ev.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {const typoLive = resolveTypography(scope ? scope.getSnapshot().value : null).liveInput
|
|
1015
1079
|
if (typoLive && typeof formatInputLive === 'function' && (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT')) {const formatted = formatInputLive(value)
|
|
1016
1080
|
if (formatted && formatted !== value) {setNativeInputValue(el, formatted, formatted.length, formatted.length)
|
|
1017
1081
|
}}}
|
|
1018
|
-
try {const
|
|
1019
|
-
const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
|
|
1082
|
+
try {const typoLive = resolveTypography(scope ? scope.getSnapshot().value : null).liveInput
|
|
1020
1083
|
if (typoLive && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {const isTextarea = el.tagName === 'TEXTAREA' || el.tagName === 'INPUT'
|
|
1021
1084
|
if (isTextarea) {const caretPos = el.selectionStart || 0
|
|
1022
1085
|
if (!isCaretInCode(el, value, caretPos)) {const textBefore = caretPos >= 0 ? value.slice(0, caretPos) : value
|
|
@@ -1070,7 +1133,7 @@ if (old) old.remove()
|
|
|
1070
1133
|
const modal = document.createElement('div')
|
|
1071
1134
|
modal.id = 'dsh-ru-inspector-modal'
|
|
1072
1135
|
modal.className = 'rl-modal-mask'
|
|
1073
|
-
const k = meta ? (meta.
|
|
1136
|
+
const k = meta ? (meta.ns && meta.key ? meta.ns + '.' + meta.key : (meta.key || '')) : ''
|
|
1074
1137
|
const ns = meta && meta.ns ? meta.ns : ''
|
|
1075
1138
|
const origText = meta ? (meta.zh || meta.en || '') : ''
|
|
1076
1139
|
let srcLabel = 'Текст'
|
|
@@ -1136,7 +1199,7 @@ setTimeout(() => { copyKeyBtn.textContent = '📋' }, 1500)
|
|
|
1136
1199
|
}}
|
|
1137
1200
|
const copyReportBtn = modal.querySelector('.rl-copy-report-btn')
|
|
1138
1201
|
if (copyReportBtn) {copyReportBtn.onclick = () => {const snippet = typeof generateBugReportSnippet === 'function'
|
|
1139
|
-
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.
|
|
1202
|
+
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.27' })
|
|
1140
1203
|
: ('### Репорт: ' + (k || rawText) + '\n- Исходный: ' + (origText || rawText) + '\n- Перевод: ' + textarea.value.trim())
|
|
1141
1204
|
try { navigator.clipboard.writeText(snippet) } catch (err) { void err; }
|
|
1142
1205
|
copyReportBtn.textContent = '✓ Скопировано'
|
|
@@ -1151,21 +1214,30 @@ try { scope.set('overrides', updated) } catch (err) { void err; }
|
|
|
1151
1214
|
modal.remove()
|
|
1152
1215
|
if (typeof queueZhWalk === 'function') queueZhWalk()
|
|
1153
1216
|
}}
|
|
1154
|
-
const
|
|
1155
|
-
|
|
1217
|
+
const inspSnap = typeof scope?.getSnapshot === 'function' ? scope.getSnapshot() : { status: 'ready', writable: true }
|
|
1218
|
+
if (inspSnap.status !== 'ready' || inspSnap.writable === false) {saveBtn.disabled = true
|
|
1219
|
+
saveBtn.title = 'Настройки доступны только для чтения'
|
|
1220
|
+
if (resetBtn) resetBtn.disabled = true
|
|
1221
|
+
}
|
|
1222
|
+
saveBtn.onclick = async () => {const val = textarea.value.trim()
|
|
1156
1223
|
const targetKey = k || rawText
|
|
1157
|
-
if (!targetKey || !val) return
|
|
1224
|
+
if (!targetKey || !val || saveBtn.disabled) return
|
|
1158
1225
|
const cur = getOverrides()
|
|
1159
1226
|
const updated = Object.assign({}, cur, { [targetKey]: val })
|
|
1160
|
-
|
|
1227
|
+
saveBtn.disabled = true
|
|
1228
|
+
saveBtn.textContent = 'Сохранение…'
|
|
1229
|
+
try {const res = await Promise.resolve(scope.set('overrides', updated))
|
|
1230
|
+
if (res === false) throw new Error('Save rejected')
|
|
1161
1231
|
if (targetElement) {try {if (targetElement.childNodes && targetElement.childNodes.length === 1 && targetElement.childNodes[0].nodeType === 3) {targetElement.childNodes[0].nodeValue = val
|
|
1162
1232
|
} else {targetElement.textContent = val
|
|
1163
1233
|
}} catch (err) { void err; }}
|
|
1164
1234
|
saveBtn.textContent = '✓ Применено'
|
|
1165
|
-
saveBtn.disabled = true
|
|
1166
1235
|
if (typeof queueZhWalk === 'function') queueZhWalk()
|
|
1167
1236
|
setTimeout(() => modal.remove(), 500)
|
|
1168
|
-
}
|
|
1237
|
+
} catch (err) {saveBtn.disabled = false
|
|
1238
|
+
saveBtn.textContent = 'Ошибка сохранения'
|
|
1239
|
+
console.warn('dsh-russian-lang: save override failed', err && err.message || err)
|
|
1240
|
+
}}
|
|
1169
1241
|
document.body.appendChild(modal)
|
|
1170
1242
|
setTimeout(() => textarea.focus(), 50)
|
|
1171
1243
|
}
|
|
@@ -1225,13 +1297,6 @@ const m = document.getElementById('dsh-ru-inspector-modal')
|
|
|
1225
1297
|
if (m) m.remove()
|
|
1226
1298
|
if (hoverBoxEl) { hoverBoxEl.remove(); hoverBoxEl = null }
|
|
1227
1299
|
if (pillEl) { pillEl.remove(); pillEl = null }}}, 'dsh-russian-lang: inspector')
|
|
1228
|
-
runtime.findTranslationKey = (text) => (typeof findTranslationKey === 'function' ? findTranslationKey(text, RU, getOverrides(), ZH_RU) : null)
|
|
1229
|
-
runtime.openInspector = openInspectorModal
|
|
1230
|
-
runtime.translationRegistry = translationRegistry
|
|
1231
|
-
runtime.bilingualCommandMatch = (query, cmd, opts) => (typeof bilingualCommandMatch === 'function' ? bilingualCommandMatch(query, cmd, opts) : { matched: false, score: 0 })
|
|
1232
|
-
runtime.filterCommands = (query, cmds, opts) => (typeof filterCommands === 'function' ? filterCommands(query, cmds, opts) : cmds)
|
|
1233
|
-
runtime.humanizeError = (err) => (typeof humanizeError === 'function' ? humanizeError(err) : null)
|
|
1234
|
-
runtime.formatErrorToast = (err) => (typeof formatErrorToast === 'function' ? formatErrorToast(err) : null)
|
|
1235
1300
|
const toastObserver = typeof MutationObserver !== 'undefined' ? new MutationObserver((mutations) => {for (const m of mutations) {for (const node of m.addedNodes) {if (!node || node.nodeType !== 1) continue
|
|
1236
1301
|
if (node.matches && (node.matches('.dsw-toast, [role="alert"], .toast, .ant-message-notice'))) {const text = (node.innerText || node.textContent || '').trim()
|
|
1237
1302
|
if (/failed to fetch|network error|econnrefused|etimedout/i.test(text)) {const h = typeof humanizeError === 'function' ? humanizeError(text) : null
|
|
@@ -1288,7 +1353,7 @@ return runtime.subscribe(() => {try { setLocaleState(runtime.getLocale().active
|
|
|
1288
1353
|
}, [runtime])
|
|
1289
1354
|
if (snap.status !== 'ready' || snap.value?.quickSwitch === false) return null
|
|
1290
1355
|
const isRu = locale === 'ru'
|
|
1291
|
-
return
|
|
1356
|
+
return h('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) },}, h('span', { className: 'rl-lang-text' }, isRu ? 'RU' : 'EN'))
|
|
1292
1357
|
}
|
|
1293
1358
|
function ExportMarkdownButton(props) {const [done, setDone] = React.useState(false)
|
|
1294
1359
|
const onExport = () => {try {const titleEl = document.querySelector('.dsw-session-title, [data-session-title], header h1, header h2, [class*="title"]')
|
|
@@ -1338,7 +1403,7 @@ setTimeout(() => {try { document.body.removeChild(a); URL.revokeObjectURL(url) }
|
|
|
1338
1403
|
setTimeout(() => setDone(false), 2000)
|
|
1339
1404
|
} catch (err) {console.warn('dsh-russian-lang: export md failed', err)
|
|
1340
1405
|
}}
|
|
1341
|
-
return
|
|
1406
|
+
return h('button', {type: 'button',className: 'rl-lang-chip rl-export-md-btn',title: 'Экспорт диалога в Markdown (.md)',onClick: onExport,}, h('span', { className: 'rl-lang-text' }, done ? '✓ MD' : '📥 MD'))
|
|
1342
1407
|
}
|
|
1343
1408
|
async function translateTurnContent(text) {if (!text || typeof text !== 'string') return { error: 'Текст для перевода пуст.' }
|
|
1344
1409
|
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 })
|
|
@@ -1349,7 +1414,7 @@ return { translatedText: data.translatedText || text }} catch (err) {return { er
|
|
|
1349
1414
|
function TranslateTurnAction(props) {const t = typeof props.t === 'function' ? props.t : ((k) => k)
|
|
1350
1415
|
const [loading, setLoading] = React.useState(false)
|
|
1351
1416
|
const [open, setOpen] = React.useState(false)
|
|
1352
|
-
return
|
|
1417
|
+
return h('button', {type: 'button',className: 'rl-action-btn' + (open ? ' rl-action-btn-active' : ''),title: t('translateTurn'),disabled: loading,onClick: async (ev) => {ev.stopPropagation()
|
|
1353
1418
|
const btn = ev.currentTarget
|
|
1354
1419
|
const tailFlowItem = btn.closest('[data-chat-flow-kind="turn-tail"]') || btn.closest('[data-chat-flow-kind]') || btn.closest('[data-turn-tail]')?.closest('[data-chat-flow-kind]') || btn.closest('[data-turn-tail]')
|
|
1355
1420
|
if (!tailFlowItem) return
|
|
@@ -1420,7 +1485,7 @@ if (parentContainer) {parentContainer.insertBefore(box, tailFlowItem)
|
|
|
1420
1485
|
setOpen(true)
|
|
1421
1486
|
} catch (err) {console.warn('dsh-russian-lang: translate turn failed', err)
|
|
1422
1487
|
} finally {setLoading(false)
|
|
1423
|
-
}},},
|
|
1488
|
+
}},}, h('span', null, loading ? '...' : (open ? 'RU ✓' : 'RU ↗')))
|
|
1424
1489
|
}
|
|
1425
1490
|
function SettingsCard(props) {const inj = typeof props.inject === 'function'
|
|
1426
1491
|
? (props.inject() || {})
|
|
@@ -1434,7 +1499,7 @@ const [snap, setSnap] = React.useState(() => (scope && scope.getSnapshot ? scope
|
|
|
1434
1499
|
const [ruActive, setRuActive] = React.useState(() => { try { return runtime.getLocale().active === 'ru' } catch (e) { return false } })
|
|
1435
1500
|
const [typo, setTypoState] = React.useState(() =>
|
|
1436
1501
|
(snap.value && snap.value.typography) || {})
|
|
1437
|
-
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.
|
|
1502
|
+
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.27',latestVersion: undefined,updateAvailable: false
|
|
1438
1503
|
})
|
|
1439
1504
|
const [upLoading, setUpLoading] = React.useState(false)
|
|
1440
1505
|
const [upMsg, setUpMsg] = React.useState(null)
|
|
@@ -1487,24 +1552,24 @@ return un
|
|
|
1487
1552
|
} catch (e) { return undefined }}, [runtime])
|
|
1488
1553
|
const status = snap.status || 'loading'
|
|
1489
1554
|
const value = snap.value || {}
|
|
1490
|
-
const
|
|
1491
|
-
const smartUxActive =
|
|
1492
|
-
|
|
1555
|
+
const effectiveTypo = resolveTypography(Object.assign({}, value, { typography: typo }))
|
|
1556
|
+
const smartUxActive = effectiveTypo.enabled || effectiveTypo.liveInput ||
|
|
1557
|
+
effectiveTypo.yo || value.slashAliases !== false || value.layoutConversion !== false
|
|
1493
1558
|
const overrides = value.overrides || {}
|
|
1494
1559
|
const overridesCount = Object.keys(overrides).length
|
|
1495
1560
|
const [newKey, setNewKey] = React.useState('')
|
|
1496
1561
|
const [newVal, setNewVal] = React.useState('')
|
|
1497
|
-
const setTypo = (patch) => {
|
|
1498
|
-
|
|
1499
|
-
try {const
|
|
1500
|
-
if (
|
|
1501
|
-
})
|
|
1502
|
-
} catch (err) {console.warn('dsh-russian-lang: scope.set typography
|
|
1562
|
+
const setTypo = async (patch) => {if (disabled) return
|
|
1563
|
+
const next = Object.assign({}, typo, patch)
|
|
1564
|
+
try {const res = await Promise.resolve(scope.set('typography', next))
|
|
1565
|
+
if (res !== false) {setTypoState(next)
|
|
1566
|
+
} else {console.warn('dsh-russian-lang: scope.set typography returned false')
|
|
1567
|
+
}} catch (err) {console.warn('dsh-russian-lang: scope.set typography failed', err && err.message || err)
|
|
1503
1568
|
}}
|
|
1504
1569
|
const onEnabled = (ev) => { if (toggleRu) toggleRu(ev.target.checked) }
|
|
1505
1570
|
const checkbox = (checked, onChange, disabled) =>
|
|
1506
|
-
|
|
1507
|
-
const disabled = status !== 'ready'
|
|
1571
|
+
h('input', {type: 'checkbox', checked: !!checked, disabled: !!disabled,className: 'rl-check', onChange: (ev) => onChange(ev),})
|
|
1572
|
+
const disabled = status !== 'ready' || snap.writable === false
|
|
1508
1573
|
const statusLine = status === 'ready'
|
|
1509
1574
|
? ''
|
|
1510
1575
|
: (status === 'unavailable' ? t('statusUnavailable') : t('statusLoading'))
|
|
@@ -1512,42 +1577,41 @@ let ChevronIcon = null
|
|
|
1512
1577
|
try {const primitives = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
1513
1578
|
ChevronIcon = primitives && primitives.IconChevronDownOutline14
|
|
1514
1579
|
} catch (_) { ChevronIcon = null }
|
|
1515
|
-
const FallbackChevron = () =>
|
|
1580
|
+
const FallbackChevron = () => h('svg', {width: 14, height: 14, viewBox: '0 0 14 14', fill: 'none','aria-hidden': 'true',}, h('path', {d: 'M3.5 5.25 7 8.75l3.5-3.5', stroke: 'currentColor',strokeWidth: 1.5, strokeLinecap: 'round', strokeLinejoin: 'round',}))
|
|
1516
1581
|
const Chevron = ChevronIcon || FallbackChevron
|
|
1517
1582
|
const presetKey = value.agentPromptPreset || 'technical_expert'
|
|
1518
1583
|
const presetInfo = typeof SYSTEM_PROMPT_PRESETS !== 'undefined' ? SYSTEM_PROMPT_PRESETS[presetKey] : null
|
|
1519
|
-
if (props && props.view === 'summary') {return
|
|
1584
|
+
if (props && props.view === 'summary') {return h('div', { className: 'rl-sub' }, statusLine || t('cardSub'))
|
|
1520
1585
|
}
|
|
1521
1586
|
const page = !!(props && props.view === 'page')
|
|
1522
|
-
return
|
|
1523
|
-
),
|
|
1524
|
-
}, t(smartUxActive ? 'badgeSmartUx' : 'badgeSmartUxOff'))),
|
|
1525
|
-
|
|
1526
|
-
})
|
|
1527
|
-
}
|
|
1528
|
-
}}, false),t('agentPrompt'))),React.createElement('div', { className: 'rl-item-desc' }, t('agentPromptDesc'))),value.agentPrompt ? React.createElement(React.Fragment, null,React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },React.createElement('label', { className: 'rl-item-label', style: { fontWeight: 500 } }, t('agentPromptPreset')),React.createElement('select', {className: 'rl-select',value: value.agentPromptPreset || 'technical_expert',onChange: (ev) => {try {const val = ev.target.value
|
|
1587
|
+
return h('div', { className: page ? 'rl-page-seat' : 'rl-card' },h('button', {type: 'button',className: 'rl-head',style: page ? { display: 'none' } : undefined,'aria-expanded': page ? 'true' : String(open),onClick: () => setOpen(!open),},h('span', { className: 'rl-head-main' },h('div', { className: 'rl-title' },'🇷🇺 ' + t('cardTitle'),h('span', { className: 'rl-badge ' + (ruActive ? 'rl-badge-ok' : 'rl-badge-warn') },ruActive ? t('badgeRu') : t('badgeEn')),h('span', { className: 'rl-badge rl-badge-ok' }, t('badgeCoverage'))),h('div', { className: 'rl-sub' },statusLine || t('cardSub'))),h('span', {className: 'rl-chev' + (open ? ' rl-chev-open' : ''),}, h(Chevron, null))),(page || open) && h('div', { className: 'rl-body' },h('div', { className: 'rl-page' },h('div', { className: 'rl-section-card' },h('div', { className: 'rl-section-title' },h('span', null, t('secLanguage')),h('span', { className: 'rl-badge ' + (ruActive ? 'rl-badge-ok' : 'rl-badge-dim') },ruActive ? t('badgeRu') : t('badgeEn'))),h('div', { className: 'rl-section-desc' }, t('secLanguageDesc')),h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(ruActive, onEnabled, disabled),t('enabled'))),h('div', { className: 'rl-item-desc' }, t('enabledDesc'))),h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(value.quickSwitch !== false,(ev) => { try { scope.set('quickSwitch', ev.target.checked) } catch (err) { console.warn('dsh-russian-lang: set quickSwitch failed', err) } }, disabled),t('quickSwitch'))),h('div', { className: 'rl-item-desc' }, t('quickSwitchDesc'))),overridesCount > 0 ? h('div', { className: 'rl-badge rl-badge-dim', style: { alignSelf: 'flex-start' } },t('overridesCount') + ': ' + overridesCount) : null
|
|
1588
|
+
),h('div', { className: 'rl-section-card' },h('div', { className: 'rl-section-title' },h('span', null, t('secTypography')),status === 'ready' && h('span', {className: 'rl-badge ' + (smartUxActive ? 'rl-badge-ok' : 'rl-badge-dim')
|
|
1589
|
+
}, t(smartUxActive ? 'badgeSmartUx' : 'badgeSmartUxOff'))),h('div', { className: 'rl-section-desc' }, t('secTypographyDesc')),h('div', { className: 'rl-grid-2', style: { gridAutoRows: '1fr' } },h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(effectiveTypo.enabled && ruActive,(ev) => setTypo({ enabled: ev.target.checked }), !ruActive || disabled),t('typography'))),h('div', { className: 'rl-item-desc' }, t('typographyDesc'))),h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(effectiveTypo.liveInput && ruActive,(ev) => setTypo({ liveInput: ev.target.checked }), !ruActive || disabled),t('liveInput'))),h('div', { className: 'rl-item-desc' }, t('liveInputDesc'))),h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(effectiveTypo.yo && ruActive,(ev) => setTypo({ yo: ev.target.checked }), !ruActive || disabled),t('yo'))),h('div', { className: 'rl-item-desc' }, t('yoDesc'))),h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(value.slashAliases !== false && ruActive,(ev) => {try { scope.set('slashAliases', ev.target.checked) } catch (err) { void err; }}, !ruActive || disabled),t('slashAliases'))),h('div', { className: 'rl-item-desc' }, t('slashAliasesDesc'))),h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(value.layoutConversion !== false,(ev) => { try { scope.set('layoutConversion', ev.target.checked) } catch (err) { console.warn('dsh-russian-lang: set layoutConversion failed', err) } }, disabled),t('altLHintText'))),h('div', { className: 'rl-item-desc' }, t('layoutConversionDesc'))))),h('div', { className: 'rl-section-card' },h('div', { className: 'rl-section-title' },h('span', null, t('secAgentPrompt')),h('span', { className: 'rl-badge ' + (value.agentPrompt ? 'rl-badge-ok' : 'rl-badge-dim') },value.agentPrompt ? 'Активен' : 'Выключен')),h('div', { className: 'rl-section-desc' }, t('secAgentPromptDesc')),h('div', { className: 'rl-item-card' },h('div', { className: 'rl-item-head' },h('label', { className: 'rl-item-label' },checkbox(value.agentPrompt === true,async (ev) => {if (disabled) return
|
|
1590
|
+
try {await Promise.resolve(scope.set('agentPrompt', ev.target.checked))
|
|
1591
|
+
} catch (err) {console.warn('dsh-russian-lang: scope.set agentPrompt failed', err && err.message || err)
|
|
1592
|
+
}}, disabled),t('agentPrompt'))),h('div', { className: 'rl-item-desc' }, t('agentPromptDesc'))),value.agentPrompt ? h(React.Fragment, null,h('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },h('label', { className: 'rl-item-label', style: { fontWeight: 500 } }, t('agentPromptPreset')),h('select', {className: 'rl-select',disabled: !!disabled,value: value.agentPromptPreset || 'technical_expert',onChange: (ev) => {try {const val = ev.target.value
|
|
1529
1593
|
scope.set('agentPromptPreset', val)
|
|
1530
|
-
} catch (err) { console.warn('dsh-russian-lang: set agentPromptPreset failed', err) }}},
|
|
1594
|
+
} catch (err) { console.warn('dsh-russian-lang: set agentPromptPreset failed', err) }}},h('option', { value: 'technical_expert' }, t('presetExpert')),h('option', { value: 'tech_writer' }, t('presetWriter')),h('option', { value: 'concise' }, t('presetConcise')),h('option', { value: 'code_reviewer' }, t('presetReviewer')),h('option', { value: 'architect' }, t('presetArchitect')),h('option', { value: 'tutor' }, t('presetTutor')))),presetInfo ? h('div', { className: 'rl-preview-box' },h('div', { style: { fontWeight: 600, marginBottom: '4px', color: 'var(--dsw-alias-label-primary)' } }, '💬 ' + presetInfo.label + ':'),presetInfo.text
|
|
1531
1595
|
) : null
|
|
1532
1596
|
) : null
|
|
1533
|
-
),
|
|
1534
|
-
?
|
|
1535
|
-
:
|
|
1536
|
-
?
|
|
1537
|
-
:
|
|
1597
|
+
),h('div', { className: 'rl-section-card' },h('div', { className: 'rl-section-title' },h('span', null, t('secUpdater')),h('span', { className: 'rl-badge ' + (upStatus.updateAvailable ? 'rl-badge-warn' : 'rl-badge-ok') },upStatus.updateAvailable ? t('badgeUpdateAvailable') : t('badgeUpToDate'))),h('div', { className: 'rl-section-desc' }, t('secUpdaterDesc')),h('div', { className: 'rl-item-card' },h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },h('div', null,h('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },t('updaterCurrent').replace('{version}', upStatus.currentVersion || '0.3.27')),upStatus.updateAvailable
|
|
1598
|
+
? h('div', { style: { color: 'var(--dsw-alias-state-warning-primary)', marginTop: '2px', fontWeight: 500 } },t('updaterLatest').replace('{version}', upStatus.latestVersion || ''))
|
|
1599
|
+
: h('div', { style: { color: 'var(--dsw-alias-label-secondary)', marginTop: '2px' } },t('updaterUpToDate'))),upStatus.updateAvailable
|
|
1600
|
+
? h('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: upLoading,onClick: triggerUpdate,}, upLoading ? t('updaterUpdating') : t('updaterBtn').replace('{version}', upStatus.latestVersion || ''))
|
|
1601
|
+
: h('button', {type: 'button',className: 'rl-btn',disabled: upLoading,onClick: checkUpdate,}, upLoading ? t('updaterChecking') : t('updaterCheckBtn'))),upMsg ? h('div', {style: {marginTop: '10px', padding: '8px 12px', borderRadius: '8px',background: upMsg.type === 'ok' ? 'color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)' : 'color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)',color: upMsg.type === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontSize: '12px', fontWeight: 500
|
|
1538
1602
|
}}, upMsg.text) : null
|
|
1539
|
-
)),
|
|
1540
|
-
}, value.translateEngine === 'local' ? 'Локально' : (value.translateEngine === 'google' ? 'Google' : 'Выключен'))),
|
|
1603
|
+
)),h('div', { className: 'rl-section-card' },h('div', { className: 'rl-section-title' },h('span', null, t('secTranslator')),h('span', {className: 'rl-badge ' + (value.translateEngine === 'local' ? 'rl-badge-ok' : (value.translateEngine === 'google' ? 'rl-badge-warn' : 'rl-badge-dim'))
|
|
1604
|
+
}, value.translateEngine === 'local' ? 'Локально' : (value.translateEngine === 'google' ? 'Google' : 'Выключен'))),h('div', { className: 'rl-section-desc' }, t('secTranslatorDesc')),h('div', { className: 'rl-item-card' },h('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },h('label', { className: 'rl-item-label', style: { fontWeight: 500 } }, t('translateEngine')),h('select', {className: 'rl-select',value: value.translateEngine || 'off',onChange: (ev) => {try {const val = ev.target.value
|
|
1541
1605
|
scope.set('translateEngine', val)
|
|
1542
1606
|
if (val === 'local') fetchTransStatus()
|
|
1543
|
-
} catch (err) { console.warn('dsh-russian-lang: set translateEngine failed', err) }}},
|
|
1544
|
-
}}, t('googleWarn')) : null,value.translateEngine === 'local' ?
|
|
1545
|
-
}, transStatus && transStatus.running ? t('localStatusRunning') : t('localStatusStopped')),(!transStatus || !transStatus.running) ?
|
|
1607
|
+
} catch (err) { console.warn('dsh-russian-lang: set translateEngine failed', err) }}},h('option', { value: 'off' }, t('engineOff')),h('option', { value: 'local' }, t('engineLocal')),h('option', { value: 'google' }, t('engineGoogle')))),value.translateEngine === 'google' ? h('div', {style: {marginTop: '10px', padding: '8px 12px', borderRadius: '8px',background: 'color-mix(in srgb, var(--dsw-alias-state-warning-primary) 12%, transparent)', border: '1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary) 30%, transparent)',color: 'var(--dsw-alias-state-warning-primary)', fontSize: '12px', fontWeight: 500, lineHeight: 1.4
|
|
1608
|
+
}}, t('googleWarn')) : null,value.translateEngine === 'local' ? h('div', {style: { marginTop: '10px', display: 'flex', flexDirection: 'column', gap: '8px' }},h('div', { style: { fontSize: '12px', color: 'var(--dsw-alias-label-secondary)' } }, t('localInfo')),h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },h('span', {className: 'rl-badge ' + (transStatus && transStatus.running ? 'rl-badge-ok' : 'rl-badge-dim')
|
|
1609
|
+
}, transStatus && transStatus.running ? t('localStatusRunning') : t('localStatusStopped')),(!transStatus || !transStatus.running) ? h('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: transStarting,onClick: startLibreTranslate
|
|
1546
1610
|
}, transStarting ? t('localStarting') : t('localStartBtn')) : null
|
|
1547
|
-
),transMsg ?
|
|
1611
|
+
),transMsg ? h('div', {style: {marginTop: '6px', padding: '8px 12px', borderRadius: '8px',background: transMsg.type === 'ok' ? 'color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)' : 'color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)',color: transMsg.type === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontSize: '12px', fontWeight: 500
|
|
1548
1612
|
}}, transMsg.text) : null
|
|
1549
1613
|
) : null
|
|
1550
|
-
)),
|
|
1614
|
+
)),h('div', { className: 'rl-section-card' },h('div', { className: 'rl-section-title' },h('span', null, t('secOverrides')),h('span', { className: 'rl-badge rl-badge-dim' },String(overridesCount) + ' ' + (typeof plural === 'function' ? plural(overridesCount, ['запись', 'записи', 'записей']) : 'записей'))),h('div', { className: 'rl-section-desc' }, t('secOverridesDesc')),h('div', { style: { display: 'flex', gap: '8px', flexWrap: 'wrap' } },h('input', {className: 'rl-select',style: { flex: '1 1 180px', maxWidth: 'none' },placeholder: t('overrideKeyPlaceholder'),value: newKey,onChange: (e) => setNewKey(e.target.value),}),h('input', {className: 'rl-select',style: { flex: '2 1 240px', maxWidth: 'none' },placeholder: t('overrideValuePlaceholder'),value: newVal,onChange: (e) => setNewVal(e.target.value),}),h('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: !newKey.trim() || !newVal.trim() || !!disabled,onClick: () => {const k = newKey.trim()
|
|
1551
1615
|
const v = newVal.trim()
|
|
1552
1616
|
if (!k || !v) return
|
|
1553
1617
|
const next = Object.assign({}, overrides, { [k]: v })
|
|
@@ -1555,23 +1619,23 @@ try { scope.set('overrides', next) } catch (_e) { void _e }
|
|
|
1555
1619
|
setNewKey('')
|
|
1556
1620
|
setNewVal('')
|
|
1557
1621
|
},}, t('overrideAddBtn'))),overridesCount === 0
|
|
1558
|
-
?
|
|
1559
|
-
:
|
|
1560
|
-
|
|
1622
|
+
? h('div', { className: 'rl-hint-text', style: { fontStyle: 'italic', padding: '4px 0' } }, t('overrideEmpty'))
|
|
1623
|
+
: h('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px', marginTop: '6px' } },Object.keys(overrides).map((k) =>
|
|
1624
|
+
h('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' },},h('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', overflow: 'hidden' } },h('code', { style: { color: 'var(--dsw-alias-state-brand-primary)', fontWeight: 600 } }, k),h('span', { style: { color: 'var(--dsw-alias-label-secondary)' } }, '→'),h('span', { style: { color: 'var(--dsw-alias-label-primary)' } }, overrides[k])),h('button', {type: 'button',className: 'rl-btn',disabled: !!disabled,style: { padding: '2px 8px', height: '24px', fontSize: '11px', color: 'var(--dsw-alias-state-error-primary)' },onClick: () => {const next = Object.assign({}, overrides)
|
|
1561
1625
|
delete next[k]
|
|
1562
|
-
try { scope.set('overrides', next) } catch (_e) { void _e }},}, t('overrideDelete'))))),
|
|
1626
|
+
try { scope.set('overrides', next) } catch (_e) { void _e }},}, t('overrideDelete'))))),h('div', { className: 'rl-hint-text', style: { marginTop: '8px' } }, t('overrideTip')),h('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', flexWrap: 'wrap', marginTop: '10px' } },h('button', {type: 'button',className: 'rl-btn' + (isInspectorActive ? ' rl-btn-primary' : ''),onClick: () => {isInspectorActive = !isInspectorActive
|
|
1563
1627
|
updateInspectorPill()
|
|
1564
|
-
},}, isInspectorActive ? t('inspectorToggleOn') : t('inspectorToggleOff')),
|
|
1628
|
+
},}, isInspectorActive ? t('inspectorToggleOn') : t('inspectorToggleOff')),h('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {try {navigator.clipboard.writeText(JSON.stringify(overrides, null, 2))
|
|
1565
1629
|
alert(t('overridesCopied'))
|
|
1566
|
-
} catch (err) { void err; }}}, t('exportJsonBtn')),
|
|
1630
|
+
} catch (err) { void err; }}}, t('exportJsonBtn')),h('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {const input = typeof window !== 'undefined' && typeof window.prompt === 'function' ? window.prompt(t('overridesImportPrompt')) : null
|
|
1567
1631
|
if (!input) return
|
|
1568
1632
|
try {const parsed = JSON.parse(input)
|
|
1569
1633
|
if (parsed && typeof parsed === 'object') {const merged = Object.assign({}, overrides, parsed)
|
|
1570
1634
|
scope.set('overrides', merged)
|
|
1571
|
-
}} catch (err) { alert(t('overridesImportError') + (err && err.message || err)) }}}, t('importJsonBtn')))),
|
|
1572
|
-
}, '💬 ' + t('reportIssue')),
|
|
1635
|
+
}} catch (err) { alert(t('overridesImportError') + (err && err.message || err)) }}}, t('importJsonBtn')))),h('div', { className: 'rl-section-card' },h('div', { className: 'rl-section-title' },h('span', null, t('secSupport')),h('span', { className: 'rl-badge rl-badge-ok' }, '🟢 100.0%')),h('div', { className: 'rl-section-desc' }, t('secSupportDesc')),h('div', { className: 'rl-grid-3' },h('div', { className: 'rl-stat-box' },h('div', { className: 'rl-stat-val' }, '101'),h('div', { className: 'rl-stat-label' }, t('statNamespaces'))),h('div', { className: 'rl-stat-box' },h('div', { className: 'rl-stat-val' }, '2 442'),h('div', { className: 'rl-stat-label' }, t('statCoreKeys'))),h('div', { className: 'rl-stat-box' },h('div', { className: 'rl-stat-val' }, '6 142'),h('div', { className: 'rl-stat-label' }, t('statPluginKeys')))),h('div', { className: 'rl-actions-row' },h('a', {href: makeIssueUrl({}, '0.3.27'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
|
|
1636
|
+
}, '💬 ' + t('reportIssue')),h('div', { className: 'rl-hint-text', style: { flex: 1 } },t('exportMdHint')))))))
|
|
1573
1637
|
}
|
|
1574
|
-
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(--
|
|
1638
|
+
const RL_CSS = [':root{--rl-sb:var(--dsw-alias-state-brand-primary);--rl-ls:var(--dsw-alias-label-secondary);--rl-lp:var(--dsw-alias-label-primary);--rl-b:var(--dsw-alias-border-l2);--rl-l3:var(--dsw-alias-bg-layer-3);--rl-l2:var(--dsw-alias-bg-layer-2);--rl-l1:var(--dsw-alias-bg-layer-1)}.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(--rl-l2);color:var(--rl-lp);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(--rl-b);background:var(--rl-l3);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(--rl-l2)}.rl-head-main{flex:1;display:flex;flex-direction:column;gap:4px}.rl-title{color:var(--rl-lp);font-size:16px;font-weight:600;line-height:1.4;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.rl-sub{color:var(--rl-ls);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(--rl-b);padding:18px}.rl-page{display:flex;flex-direction:column;gap:16px;max-width:960px}.rl-section-card{border:1px solid var(--rl-b);background:var(--rl-l2);border-radius:10px;padding:16px 18px;display:flex;flex-direction:column;gap:12px}.rl-section-title{font-size:14px;font-weight:600;color:var(--rl-lp);display:flex;align-items:center;justify-content:space-between;gap:8px}.rl-section-desc{font-size:12px;color:var(--rl-ls);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(--rl-b);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(--rl-b);color:var(--rl-ls);background:var(--rl-l3)}.rl-item-card{border:1px solid var(--rl-b);background:var(--rl-l3);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(--rl-lp);display:flex;align-items:center;gap:8px;cursor:pointer}.rl-item-desc{font-size:11px;color:var(--rl-ls);line-height:1.4}.rl-stat-box{border:1px solid var(--rl-b);background:var(--rl-l3);border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:2px}.rl-stat-val{font-size:16px;font-weight:700;color:var(--rl-lp)}.rl-stat-label{font-size:11px;color:var(--rl-ls);letter-spacing:0.3px}.rl-preview-box{border:1px solid var(--rl-b);background:var(--rl-l3);border-radius:8px;padding:10px 12px;font-size:12px;color:var(--rl-ls);line-height:1.4}.rl-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--rl-b);border-radius:8px;padding:6px 12px;font-size:12px;background:var(--rl-l3);color:var(--rl-lp);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(--rl-l1);border-color:var(--rl-ls)}.rl-btn-primary{background:var(--rl-lp);color:var(--rl-l3);border-color:transparent}.rl-btn-primary:hover:not(:disabled){opacity:0.9}.rl-select{height:32px;border:1px solid var(--rl-b);background:var(--rl-l3);color:var(--rl-lp);border-radius:6px;padding:0 8px;font-size:12px;outline:none;width:100%;max-width:380px}.rl-select:focus{border-color:var(--rl-sb)}.rl-check{width:16px;height:16px;accent-color:var(--rl-lp);cursor:pointer;flex-shrink:0}.rl-hint-text{font-size:11px;color:var(--rl-ls);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(--rl-b);background:var(--rl-l3);color:var(--rl-ls);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(--rl-lp);color:var(--rl-lp)}.rl-lang-chip-active{color:var(--rl-lp);border-color:var(--rl-lp);background:var(--rl-l2)}.rl-action-btn{appearance:none;background:0 0;border:1px solid transparent;border-radius:4px;color:var(--rl-ls);cursor:pointer;font-size:11px;padding:2px 5px;display:inline-flex;align-items:center;transition:all .15s}.rl-action-btn:hover{color:var(--rl-lp);border-color:var(--rl-b);background:var(--rl-l3)}.rl-action-btn-active{color:var(--rl-lp);background:var(--rl-l2);border-color:var(--rl-b)}.rl-turn-translation{margin:8px 0;padding:10px 12px;border:1px solid var(--rl-b);background:var(--rl-l2);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(--rl-b);padding-bottom:4px}.rl-trans-title{font-size:11px;font-weight:600;color:var(--rl-ls);text-transform:uppercase;letter-spacing:0.5px}.rl-trans-tools{display:flex;gap:4px}.rl-trans-btn{appearance:none;background:var(--rl-l3);border:1px solid var(--rl-b);color:var(--rl-ls);cursor:pointer;padding:2px 7px;border-radius:4px;font-size:11px}.rl-trans-btn:hover{background:var(--rl-l1);color:var(--rl-lp)}.rl-trans-body{color:var(--rl-lp);white-space:pre-wrap;word-break:break-word;user-select:text}.rl-export-md-btn{appearance:none;border:1px solid var(--rl-b);height:32px;color:var(--rl-lp);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(--rl-sb);border-radius:4px;background:color-mix(in srgb,var(--rl-sb) 12%,transparent);box-shadow:0 0 10px color-mix(in srgb,var(--rl-sb) 30%,transparent);z-index:99998;transition:all .06s ease;display:none}.rl-inspector-badge{position:absolute;bottom:calc(100% + 4px);left:0;background:var(--rl-sb);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(--rl-l2);color:var(--rl-lp);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(--rl-sb);box-shadow:0 0 12px color-mix(in srgb,var(--rl-sb) 35%,transparent)}'
|
|
1575
1639
|
].join('')
|
|
1576
1640
|
const STYLE_ID = 'dsh-russian-lang-styles'
|
|
1577
1641
|
if (typeof document !== 'undefined' && (document.getElementById && !document.getElementById(STYLE_ID))) {const tag = document.createElement('style')
|