@goodandready/dsh-russian-lang 0.3.13 → 0.3.15
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 +30 -18
- package/lib/client.template.js +76 -9
- package/lib/index.js +137 -27
- package/lib/locales/plugins/22-remote-workspace.json +1 -1
- package/lib/locales/plugins/53-remote-workspace.json +1 -1
- package/lib/locales/plugins/85-agency-agents.json +1 -0
- package/lib/translator.js +27 -5
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -392,18 +392,21 @@ return results.sort((a, b) => b.score - a.score).map((r) => r.item)
|
|
|
392
392
|
}
|
|
393
393
|
const RU = {"common":{"back":"Назад","cancel":"Отмена","close":"Закрыть","collapse":"Свернуть","copied":"Скопировано","copy":"Копировать","delete":"Удалить","edit":"Изменить","expand":"Развернуть","loading":"Загрузка…","more":"Ещё","next":"Дальше","ok":"ОК","retry":"Повторить","save":"Сохранить","search":"Поиск","submit":"Отправить"},"dsh-cron":{"sidebar.label":"Задачи по расписанию"},"dsh-usage-stats":{"footer.todayLabel":"Сегодня"},"pluginMarket":{"trigger":"Магазин плагинов"}}
|
|
394
394
|
const ZH_RU = {}
|
|
395
|
-
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":"
|
|
395
|
+
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
396
|
const SETTINGS_NS_NAME = 'russian-lang'
|
|
397
|
+
const FORM_NS = 'dsh-russian-lang'
|
|
398
|
+
const LEGACY_NS = 'russian-lang'
|
|
397
399
|
const ROW_CONFIG_KEY = '@goodandready/dsh-russian-lang#russian-lang'
|
|
398
400
|
function apply(ctx) {const runtime = ctx.locale
|
|
399
|
-
const resolveScope = (c) => {try {if (c && c.configForms) return c.configForms.get(
|
|
400
|
-
if (c && c.settingsScope) return c.settingsScope.bind({ namespace:
|
|
401
|
+
const resolveScope = (c) => {try {if (c && c.configForms) return c.configForms.get(FORM_NS)
|
|
402
|
+
if (c && c.settingsScope) return c.settingsScope.bind({ namespace: LEGACY_NS })
|
|
401
403
|
} catch (err) { void err }
|
|
402
404
|
return null
|
|
403
405
|
}
|
|
404
406
|
const rawScope = resolveScope(ctx)
|
|
405
407
|
const scope = (rawScope && typeof rawScope.getSnapshot === 'function') ? rawScope : {getSnapshot: () => ({ status: 'ready', value: {} }),subscribe: () => () => {},set: () => Promise.resolve()
|
|
406
408
|
}
|
|
409
|
+
const legacyFlagMode = !(ctx && ctx.configForms)
|
|
407
410
|
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
411
|
}
|
|
409
412
|
const registerDictMap = (dictMap) => {if (!dictMap || typeof dictMap !== 'object') return
|
|
@@ -413,7 +416,7 @@ if (typeof dict !== 'object' || !dict) continue
|
|
|
413
416
|
if (!RU[ns]) {RU[ns] = dict
|
|
414
417
|
ctx.effect(() => {try { return ctx.locale.register(ns, 'ru', dict) } catch (err) { return () => {} }}, 'dsh-russian-lang: ' + ns)
|
|
415
418
|
} else {Object.assign(RU[ns], dict)
|
|
416
|
-
}}}
|
|
419
|
+
try { ctx.locale.register(ns, 'ru', RU[ns]) } catch (_) {}}}}
|
|
417
420
|
const CACHE_NAME = 'dsh-ru-cache-v1'
|
|
418
421
|
const loadLocalDict = (key) => {try {const raw = typeof localStorage !== 'undefined' ? localStorage.getItem('dsh_ru_' + key) : null
|
|
419
422
|
return raw ? JSON.parse(raw) : null
|
|
@@ -540,7 +543,8 @@ for (const p of RUNTIME_PROP_NAMES) {if (origProps[p] !== undefined) runtime[p]
|
|
|
540
543
|
else delete runtime[p]
|
|
541
544
|
}
|
|
542
545
|
if (typeof removeLang === 'function') { try { removeLang() } catch (_) { } }}}, 'dsh-russian-lang: runtime-lifecycle')
|
|
543
|
-
const syncFlag = () => {
|
|
546
|
+
const syncFlag = () => {if (!legacyFlagMode) return
|
|
547
|
+
try {const wantRu = runtime.getLocale().active === 'ru'
|
|
544
548
|
const value = scope.getSnapshot().value || {}
|
|
545
549
|
if (!!value.enabled !== wantRu) {const r = scope.set('enabled', wantRu)
|
|
546
550
|
if (r && typeof r.catch === 'function') {r.catch((err) => {console.warn('dsh-russian-lang: scope.set enabled failed', err && err.message || err)
|
|
@@ -554,7 +558,7 @@ runtime.setLocale('ru')
|
|
|
554
558
|
let booted = false
|
|
555
559
|
const tryBoot = () => {if (booted) return
|
|
556
560
|
try {const value = scope.getSnapshot().value
|
|
557
|
-
if (value && value.enabled === true) { booted = true; activate() }} catch (err) { }}
|
|
561
|
+
if (legacyFlagMode && value && value.enabled === true) { booted = true; activate() }} catch (err) { }}
|
|
558
562
|
ctx.effect(() => scope.subscribe(tryBoot), 'dsh-russian-lang: boot')
|
|
559
563
|
tryBoot()
|
|
560
564
|
const unsubscribeLang = runtime.subscribe(syncLang)
|
|
@@ -608,7 +612,7 @@ const ZH_CJK = /[\u3400-\u9fff\uf900-\ufaff]/
|
|
|
608
612
|
const ZH_RE_ESC = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
609
613
|
const ZH_EXACT = new Map()
|
|
610
614
|
const ZH_PATTERNS = []
|
|
611
|
-
const CORE_ZH_PRESETS = {'请求批准': 'Запрашивать подтверждение','完全放开': 'Полный доступ','只读': 'Только чтение','只能读,任何写入都需要审批。': 'Только чтение, любая запись требует подтверждения.','工作区内可写;工作区外的操作请求人工审批。': 'Запись в рабочей области разрешена; операции вне рабочей области требуют подтверждения.','全放行,不弹审批。': 'Полный доступ, запросы на подтверждение не выводятся.','确定性规则 + 两阶段分类器自动决定;危险或故障时 fail-closed。': 'Детерминированные правила + двухэтапный классификатор; при рисках — безопасная блокировка.','手动测试': 'Ручное тестирование','事件': 'Событие','模拟 (看匹配)': 'Симуляция (проверка)','执行 (真实触发)': 'Выполнение (реальный триггер)','通知渠道测试': 'Тест каналов уведомлений','渠道': 'Канал','Slack 风格单行摘要': 'Сводка в стиле Slack','发送测试通知': 'Отправить тестовое уведомление','飞书通知': 'Уведомления Feishu','扫码连接飞书': 'Подключить Feishu по QR-коду','将创建名为 [DSH 通知机器人] 的飞书应用': 'Будет создано приложение Feishu [DSH 通知机器人]','卡片截断长度': 'Длина обрезки карточки','复制 YAML': 'Копировать YAML','去抖': 'Дебаунс','已断开': 'Отключено','已连接': 'Подключено','已保存': 'Сохранено','保存失败': 'Не удалось сохранить','编辑': 'Редактировать','取消编辑': 'Отмена','启用': 'Включить','停用': 'Отключить','重试': 'Повторить','刷新': 'Обновить','网络请求失败': 'Сетевой запрос не удался','如未立即生效请重启': 'Если изменения не применились, перезапустите DSH','通知机器人': 'Бот уведомлений','飞书扫码授权二维码': 'QR-код авторизации Feishu','在浏览器中打开飞书授权链接': 'Открыть ссылку авторизации Feishu в браузере','重新扫码会覆盖现有应用凭据与本': 'Повторное сканирование перезапишет учётные данные','扫码者本人接收通知卡片': 'Получатель карточки уведомлений — авторизованный пользователь','tool (可选)': 'Инструмент (опц.)','runningSubagents (可选)': 'Подагенты (опц.)','durationMs (可选)': 'Длительность мс (опц.)','usage 输入 (可选)': 'Входные токены (опц.)','usage 输出 (可选)': 'Выходные токены (опц.)','profile (写入哪个 profile 的 cordis.patch.yml)': 'Профиль (куда записать cordis.patch.yml)','URL (留空用 DSH_HOOKS_WEBHOOK_URL)': 'URL (по умолчанию DSH_HOOKS_WEBHOOK_URL)','当前已配置的 Hook 规则列表': 'Список текущих настроенных правил хуков','测试通道': 'Тест канала'
|
|
615
|
+
const CORE_ZH_PRESETS = {'请求批准': 'Запрашивать подтверждение','完全放开': 'Полный доступ','只读': 'Только чтение','只能读,任何写入都需要审批。': 'Только чтение, любая запись требует подтверждения.','工作区内可写;工作区外的操作请求人工审批。': 'Запись в рабочей области разрешена; операции вне рабочей области требуют подтверждения.','全放行,不弹审批。': 'Полный доступ, запросы на подтверждение не выводятся.','确定性规则 + 两阶段分类器自动决定;危险或故障时 fail-closed。': 'Детерминированные правила + двухэтапный классификатор; при рисках — безопасная блокировка.','手动测试': 'Ручное тестирование','事件': 'Событие','模拟 (看匹配)': 'Симуляция (проверка)','执行 (真实触发)': 'Выполнение (реальный триггер)','通知渠道测试': 'Тест каналов уведомлений','渠道': 'Канал','Slack 风格单行摘要': 'Сводка в стиле Slack','发送测试通知': 'Отправить тестовое уведомление','飞书通知': 'Уведомления Feishu','扫码连接飞书': 'Подключить Feishu по QR-коду','将创建名为 [DSH 通知机器人] 的飞书应用': 'Будет создано приложение Feishu [DSH 通知机器人]','卡片截断长度': 'Длина обрезки карточки','复制 YAML': 'Копировать YAML','去抖': 'Дебаунс','已断开': 'Отключено','已连接': 'Подключено','已保存': 'Сохранено','保存失败': 'Не удалось сохранить','编辑': 'Редактировать','取消编辑': 'Отмена','启用': 'Включить','停用': 'Отключить','重试': 'Повторить','刷新': 'Обновить','网络请求失败': 'Сетевой запрос не удался','如未立即生效请重启': 'Если изменения не применились, перезапустите DSH','通知机器人': 'Бот уведомлений','飞书扫码授权二维码': 'QR-код авторизации Feishu','在浏览器中打开飞书授权链接': 'Открыть ссылку авторизации Feishu в браузере','重新扫码会覆盖现有应用凭据与本': 'Повторное сканирование перезапишет учётные данные','扫码者本人接收通知卡片': 'Получатель карточки уведомлений — авторизованный пользователь','tool (可选)': 'Инструмент (опц.)','runningSubagents (可选)': 'Подагенты (опц.)','durationMs (可选)': 'Длительность мс (опц.)','usage 输入 (可选)': 'Входные токены (опц.)','usage 输出 (可选)': 'Выходные токены (опц.)','profile (写入哪个 profile 的 cordis.patch.yml)': 'Профиль (куда записать cordis.patch.yml)','URL (留空用 DSH_HOOKS_WEBHOOK_URL)': 'URL (по умолчанию DSH_HOOKS_WEBHOOK_URL)','当前已配置的 Hook 规则列表': 'Список текущих настроенных правил хуков','测试通道': 'Тест канала','专家提示词服务尚未就绪,请稍后重试。': 'Сервис промптов экспертов ещё не готов, повторите попытку позже.','专家提示词服务尚未就绪,请稍后': 'Сервис промптов экспертов ещё не готов, пожалуйста, ','专家团服务不可用,请重新加载插件。': 'Сервис команд экспертов недоступен, перезагрузите плагин.','搜索团队或工作目标': 'Поиск команды или цели работы','专家': 'Эксперт','专家团': 'Команда экспертов'
|
|
612
616
|
}
|
|
613
617
|
for (const [k, v] of Object.entries(CORE_ZH_PRESETS)) ZH_EXACT.set(k, v)
|
|
614
618
|
const zhSortedExact = []
|
|
@@ -652,7 +656,7 @@ if (changed) return replaced
|
|
|
652
656
|
return null
|
|
653
657
|
}
|
|
654
658
|
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...': 'Конкретные инструкции, вопросы или требования для данного специалиста…',}
|
|
655
|
-
const DOM_EN_TEXT = {'Scheduled tasks': 'Задачи по расписанию','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...': 'Загрузка списка и статуса...'
|
|
659
|
+
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...': 'Загрузка списка и статуса...'
|
|
656
660
|
}
|
|
657
661
|
const ZH_WALKER = (root) => {try {const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT)
|
|
658
662
|
const hits = []
|
|
@@ -685,32 +689,40 @@ else if (DOM_EN_ATTRS[tr]) root.setAttribute(attr, v.replace(tr, DOM_EN_ATTRS[tr
|
|
|
685
689
|
let zhObserver = null
|
|
686
690
|
let zhDebounceTimer = null
|
|
687
691
|
let zhWalking = false
|
|
688
|
-
const queueZhWalk = (
|
|
692
|
+
const queueZhWalk = () => {if (zhDebounceTimer) return
|
|
689
693
|
zhDebounceTimer = setTimeout(() => {zhDebounceTimer = null
|
|
690
694
|
if (zhWalking) return
|
|
691
695
|
zhWalking = true
|
|
692
|
-
try {ZH_WALKER(
|
|
696
|
+
try {ZH_WALKER(document.body)
|
|
693
697
|
} finally {zhWalking = false
|
|
694
|
-
}},
|
|
698
|
+
}}, 80)
|
|
695
699
|
}
|
|
700
|
+
const onInteraction = () => queueZhWalk()
|
|
696
701
|
const syncZhDom = () => {try {if (typeof document === 'undefined') return
|
|
697
702
|
const ru = runtime.getLocale().active === 'ru'
|
|
698
703
|
if (!ru) {if (zhObserver) { zhObserver.disconnect(); zhObserver = null }
|
|
699
704
|
if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }
|
|
705
|
+
document.removeEventListener('pointerup', onInteraction)
|
|
706
|
+
document.removeEventListener('click', onInteraction)
|
|
700
707
|
return
|
|
701
708
|
}
|
|
702
709
|
if (!zhObserver) {zhObserver = new MutationObserver((records) => {for (const record of records) {if (record.type === 'childList') {for (const node of record.addedNodes) {if (node.nodeType === 1) {if (node.closest && node.closest('.chat-message, .markdown-body, pre, code, [data-stream]')) continue
|
|
703
|
-
queueZhWalk(
|
|
710
|
+
queueZhWalk()
|
|
704
711
|
return
|
|
705
712
|
}}}}})
|
|
706
713
|
zhObserver.observe(document.body, { childList: true, subtree: true })
|
|
714
|
+
document.addEventListener('pointerup', onInteraction, { passive: true })
|
|
715
|
+
document.addEventListener('click', onInteraction, { passive: true })
|
|
707
716
|
}
|
|
708
|
-
queueZhWalk(
|
|
717
|
+
queueZhWalk()
|
|
709
718
|
} catch (err) { }}
|
|
710
719
|
const unsubscribeZh = runtime.subscribe(syncZhDom)
|
|
711
720
|
ctx.effect(() => {return () => {unsubscribeZh()
|
|
712
721
|
if (zhObserver) zhObserver.disconnect()
|
|
713
|
-
if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }
|
|
722
|
+
if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }
|
|
723
|
+
if (typeof document !== 'undefined') {document.removeEventListener('pointerup', onInteraction)
|
|
724
|
+
document.removeEventListener('click', onInteraction)
|
|
725
|
+
}}}, 'dsh-russian-lang: zh-dom')
|
|
714
726
|
syncZhDom()
|
|
715
727
|
const TYPO_YO_PAIRS = [["еще","ещё"],["ее","её"],["черный","чёрный"],["черная","чёрная"],["черные","чёрные"],["зеленый","зелёный"],["желтый","жёлтый"],["легкий","лёгкий"],["тяжелый","тяжёлый"],["надежный","надёжный"],["дешевый","дешёвый"],["идет","идёт"],["дает","даёт"],["ведет","ведёт"],["несет","несёт"],["живет","живёт"],["привел","привёл"],["шел","шёл"],["нее","неё"],["мое","моё"],["серьезно","серьёзно"],["придется","придётся"],["насчет","насчёт"],["твое","твоё"],["свое","своё"],["пойдем","пойдём"],["пришел","пришёл"],["нашел","нашёл"],["идем","идём"],["вперед","вперёд"],["пошел","пошёл"],["ребенка","ребёнка"],["ребенок","ребёнок"],["счет","счёт"],["своем","своём"],["придет","придёт"],["ушел","ушёл"],["ждет","ждёт"],["мертв","мёртв"],["твоем","твоём"],["вернется","вернётся"],["днем","днём"],["найдем","найдём"],["пойдет","пойдёт"],["начнем","начнём"],["вернемся","вернёмся"],["остается","остаётся"],["принес","принёс"],["идешь","идёшь"],["трех","трёх"],["ребенком","ребёнком"],["самолет","самолёт"],["определенно","определённо"],["пойдешь","пойдёшь"],["умрет","умрёт"],["прием","приём"],["прошел","прошёл"],["убьет","убьёт"],["тетя","тётя"],["провел","провёл"],["произойдет","произойдёт"],["семьей","семьёй"],["пойдемте","пойдёмте"],["пройдет","пройдёт"],["возьмем","возьмём"],["вернешься","вернёшься"],["найдешь","найдёшь"],["живешь","живёшь"],["отчет","отчёт"],["займет","займёт"],["ждем","ждём"],["сошел","сошёл"],["вдвоем","вдвоём"],["найдет","найдёт"],["вел","вёл"],["живем","живём"],["зашел","зашёл"],["начнется","начнётся"],["путем","путём"],["подойдет","подойдёт"],["режиссер","режиссёр"],["уйдет","уйдёт"],["придешь","придёшь"],["идемте","идёмте"],["ведешь","ведёшь"],["пес","пёс"],["умрешь","умрёшь"],["ждешь","ждёшь"],["найдете","найдёте"],["найдется","найдётся"],["клево","клёво"],["поймешь","поймёшь"],["разберемся","разберёмся"],["времен","времён"],["введен","введён"],["включен","включён"],["включенных","включённых"],["возьмется","возьмётся"],["завершен","завершён"],["завершенного","завершённого"],["задает","задаёт"],["задается","задаётся"],["заменен","заменён"],["звезд","звёзд"],["звезды","звёзды"],["изменен","изменён"],["истек","истёк"],["незавершенная","незавершённая"],["неподтвержденные","неподтверждённые"],["несохраненные","несохранённые"],["обновлен","обновлён"],["обновленные","обновлённые"],["определен","определён"],["отклонен","отклонён"],["отменен","отменён"],["очередность","очерёдность"],["поврежден","повреждён"],["повторен","повторён"],["подключен","подключён"],["подтвержденные","подтверждённые"],["приемки","приёмки"],["раздает","раздаёт"],["разрешенная","разрешённая"],["разрешенные","разрешённые"],["создаем","создаём"],["сойдется","сойдётся"],["сохранен","сохранён"],["сохраненный","сохранённый"],["счету","счёту"],["тяжелые","тяжёлые"],["темная","тёмная"],["уберет","уберёт"],["удален","удалён"],["учетные","учётные"],["учетных","учётных"],["емкости","ёмкости"],["емкость","ёмкость"]]
|
|
716
728
|
const typoYo = makeTypoYo(TYPO_YO_PAIRS)
|
|
@@ -1114,7 +1126,7 @@ setTimeout(() => { copyKeyBtn.textContent = '📋' }, 1500)
|
|
|
1114
1126
|
}}
|
|
1115
1127
|
const copyReportBtn = modal.querySelector('.rl-copy-report-btn')
|
|
1116
1128
|
if (copyReportBtn) {copyReportBtn.onclick = () => {const snippet = typeof generateBugReportSnippet === 'function'
|
|
1117
|
-
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.
|
|
1129
|
+
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.15' })
|
|
1118
1130
|
: ('### Репорт: ' + (k || rawText) + '\n- Исходный: ' + (origText || rawText) + '\n- Перевод: ' + textarea.value.trim())
|
|
1119
1131
|
try { navigator.clipboard.writeText(snippet) } catch (err) { void err; }
|
|
1120
1132
|
copyReportBtn.textContent = '✓ Скопировано'
|
|
@@ -1401,7 +1413,7 @@ const [snap, setSnap] = React.useState(() => (scope && scope.getSnapshot ? scope
|
|
|
1401
1413
|
const [ruActive, setRuActive] = React.useState(() => { try { return runtime.getLocale().active === 'ru' } catch (e) { return false } })
|
|
1402
1414
|
const [typo, setTypoState] = React.useState(() =>
|
|
1403
1415
|
(snap.value && snap.value.typography) || {})
|
|
1404
|
-
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.
|
|
1416
|
+
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.15',latestVersion: undefined,updateAvailable: false
|
|
1405
1417
|
})
|
|
1406
1418
|
const [upLoading, setUpLoading] = React.useState(false)
|
|
1407
1419
|
const [upMsg, setUpMsg] = React.useState(null)
|
|
@@ -1494,7 +1506,7 @@ scope.set('agentPromptPreset', val)
|
|
|
1494
1506
|
} 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
|
|
1495
1507
|
) : null
|
|
1496
1508
|
) : null
|
|
1497
|
-
),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.
|
|
1509
|
+
),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.15')),upStatus.updateAvailable
|
|
1498
1510
|
? React.createElement('div', { style: { color: 'var(--dsw-alias-state-warning-primary)', marginTop: '2px', fontWeight: 500 } },t('updaterLatest').replace('{version}', upStatus.latestVersion || ''))
|
|
1499
1511
|
: React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', marginTop: '2px' } },t('updaterUpToDate'))),upStatus.updateAvailable
|
|
1500
1512
|
? React.createElement('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: upLoading,onClick: triggerUpdate,}, upLoading ? t('updaterUpdating') : t('updaterBtn').replace('{version}', upStatus.latestVersion || ''))
|
|
@@ -1532,7 +1544,7 @@ if (!input) return
|
|
|
1532
1544
|
try {const parsed = JSON.parse(input)
|
|
1533
1545
|
if (parsed && typeof parsed === 'object') {const merged = Object.assign({}, overrides, parsed)
|
|
1534
1546
|
scope.set('overrides', merged)
|
|
1535
|
-
}} 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.
|
|
1547
|
+
}} 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.15'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
|
|
1536
1548
|
}, '💬 ' + t('reportIssue')),React.createElement('div', { className: 'rl-hint-text', style: { flex: 1 } },t('exportMdHint')))))))
|
|
1537
1549
|
}
|
|
1538
1550
|
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
|
@@ -33,6 +33,11 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
RU['russian-lang'] = /*__CARD_RU__*/{}
|
|
34
34
|
|
|
35
35
|
const SETTINGS_NS_NAME = 'russian-lang'
|
|
36
|
+
// Namespace формы настроек: на 0.1.7+ это id записи загрузчика
|
|
37
|
+
// ('dsh-russian-lang' из cordis.patch.yml), а не имя, выбранное плагином
|
|
38
|
+
// ('russian-lang'), как было на 0.1.6 и ниже. Это разные вещи.
|
|
39
|
+
const FORM_NS = 'dsh-russian-lang'
|
|
40
|
+
const LEGACY_NS = 'russian-lang'
|
|
36
41
|
// Plugins page row seat (DSH 0.1.6-alpha.2): key = '<package name>#<row id>'.
|
|
37
42
|
const ROW_CONFIG_KEY = '@goodandready/dsh-russian-lang#russian-lang'
|
|
38
43
|
|
|
@@ -40,8 +45,8 @@ window.__ModuleLoader__.load({
|
|
|
40
45
|
const runtime = ctx.locale
|
|
41
46
|
const resolveScope = (c) => {
|
|
42
47
|
try {
|
|
43
|
-
if (c && c.configForms) return c.configForms.get(
|
|
44
|
-
if (c && c.settingsScope) return c.settingsScope.bind({ namespace:
|
|
48
|
+
if (c && c.configForms) return c.configForms.get(FORM_NS)
|
|
49
|
+
if (c && c.settingsScope) return c.settingsScope.bind({ namespace: LEGACY_NS })
|
|
45
50
|
} catch (err) { void err }
|
|
46
51
|
return null
|
|
47
52
|
}
|
|
@@ -51,6 +56,10 @@ window.__ModuleLoader__.load({
|
|
|
51
56
|
subscribe: () => () => {},
|
|
52
57
|
set: () => Promise.resolve()
|
|
53
58
|
}
|
|
59
|
+
// На 0.1.7+ выбор языка хранит сам DSH (locale.preference): собственный
|
|
60
|
+
// флаг там не читаем и не пишем, виджет работает просто с локалью.
|
|
61
|
+
// На 0.1.6 и ниже поведение прежнее — флаг живёт в настройках плагина.
|
|
62
|
+
const legacyFlagMode = !(ctx && ctx.configForms)
|
|
54
63
|
|
|
55
64
|
// 1. Словари ядра DSH: каждый namespace — свой эффект, снимается вместе с плагином
|
|
56
65
|
for (const ns of Object.keys(RU)) {
|
|
@@ -75,6 +84,7 @@ window.__ModuleLoader__.load({
|
|
|
75
84
|
}, 'dsh-russian-lang: ' + ns)
|
|
76
85
|
} else {
|
|
77
86
|
Object.assign(RU[ns], dict)
|
|
87
|
+
try { ctx.locale.register(ns, 'ru', RU[ns]) } catch (_) {}
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
90
|
}
|
|
@@ -304,6 +314,7 @@ window.__ModuleLoader__.load({
|
|
|
304
314
|
// scope.set возвращает Promise<void> в v0.1.2-alpha.2, ошибка приходит
|
|
305
315
|
// через rejection и try/catch её не ловит — обрабатываем оба канала.
|
|
306
316
|
const syncFlag = () => {
|
|
317
|
+
if (!legacyFlagMode) return
|
|
307
318
|
try {
|
|
308
319
|
const wantRu = runtime.getLocale().active === 'ru'
|
|
309
320
|
const value = scope.getSnapshot().value || {}
|
|
@@ -337,7 +348,7 @@ window.__ModuleLoader__.load({
|
|
|
337
348
|
if (booted) return
|
|
338
349
|
try {
|
|
339
350
|
const value = scope.getSnapshot().value
|
|
340
|
-
if (value && value.enabled === true) { booted = true; activate() }
|
|
351
|
+
if (legacyFlagMode && value && value.enabled === true) { booted = true; activate() }
|
|
341
352
|
} catch (err) { /* ignore */ }
|
|
342
353
|
}
|
|
343
354
|
ctx.effect(() => scope.subscribe(tryBoot), 'dsh-russian-lang: boot')
|
|
@@ -482,7 +493,13 @@ window.__ModuleLoader__.load({
|
|
|
482
493
|
'profile (写入哪个 profile 的 cordis.patch.yml)': 'Профиль (куда записать cordis.patch.yml)',
|
|
483
494
|
'URL (留空用 DSH_HOOKS_WEBHOOK_URL)': 'URL (по умолчанию DSH_HOOKS_WEBHOOK_URL)',
|
|
484
495
|
'当前已配置的 Hook 规则列表': 'Список текущих настроенных правил хуков',
|
|
485
|
-
'测试通道': 'Тест канала'
|
|
496
|
+
'测试通道': 'Тест канала',
|
|
497
|
+
'专家提示词服务尚未就绪,请稍后重试。': 'Сервис промптов экспертов ещё не готов, повторите попытку позже.',
|
|
498
|
+
'专家提示词服务尚未就绪,请稍后': 'Сервис промптов экспертов ещё не готов, пожалуйста, ',
|
|
499
|
+
'专家团服务不可用,请重新加载插件。': 'Сервис команд экспертов недоступен, перезагрузите плагин.',
|
|
500
|
+
'搜索团队或工作目标': 'Поиск команды или цели работы',
|
|
501
|
+
'专家': 'Эксперт',
|
|
502
|
+
'专家团': 'Команда экспертов'
|
|
486
503
|
}
|
|
487
504
|
for (const [k, v] of Object.entries(CORE_ZH_PRESETS)) ZH_EXACT.set(k, v)
|
|
488
505
|
|
|
@@ -558,6 +575,46 @@ window.__ModuleLoader__.load({
|
|
|
558
575
|
}
|
|
559
576
|
const DOM_EN_TEXT = {
|
|
560
577
|
'Scheduled tasks': 'Задачи по расписанию',
|
|
578
|
+
'Files': 'Файлы',
|
|
579
|
+
'Back': 'Назад',
|
|
580
|
+
'Hosts': 'Хосты',
|
|
581
|
+
'Containers': 'Контейнеры',
|
|
582
|
+
'Tunnels': 'Туннели',
|
|
583
|
+
'Cluster': 'Кластер',
|
|
584
|
+
'Remote Workspace': 'Удалённое рабочее пространство',
|
|
585
|
+
'Native SSH connections to remote development environments with terminal, sftp, and tunneling.': 'SSH-соединения с удалёнными средами разработки: терминал, SFTP и туннели.',
|
|
586
|
+
'Import ~/.ssh/config': 'Импорт ~/.ssh/config',
|
|
587
|
+
'Terminal font': 'Шрифт терминала',
|
|
588
|
+
'Save Profile': 'Сохранить профиль',
|
|
589
|
+
'Edit Host Profile': 'Редактирование профиля хоста',
|
|
590
|
+
'Add New Host Profile': 'Добавление профиля хоста',
|
|
591
|
+
'Profile Name': 'Название профиля',
|
|
592
|
+
'Host / IP Address': 'Хост / IP-адрес',
|
|
593
|
+
'Port': 'Порт',
|
|
594
|
+
'Username': 'Имя пользователя',
|
|
595
|
+
'Jump hosts': 'Jump-хосты',
|
|
596
|
+
'Proxy command': 'Команда прокси',
|
|
597
|
+
'Environment': 'Окружение',
|
|
598
|
+
'Location': 'Расположение',
|
|
599
|
+
'Tags': 'Теги',
|
|
600
|
+
'Description': 'Описание',
|
|
601
|
+
'Authentication Method': 'Метод аутентификации',
|
|
602
|
+
'SSH Private Key': 'Приватный SSH-ключ',
|
|
603
|
+
'SSH Agent': 'SSH-агент',
|
|
604
|
+
'Password': 'Пароль',
|
|
605
|
+
'Private Key Path': 'Путь к приватному ключу',
|
|
606
|
+
'Key Passphrase (optional)': 'Парольная фраза ключа (опционально)',
|
|
607
|
+
'Remote Workspace Directory': 'Каталог удалённой рабочей области',
|
|
608
|
+
'Browse Remote...': 'Обзор удалённых…',
|
|
609
|
+
'Local Mirror Directory': 'Каталог локального зеркала',
|
|
610
|
+
'Search names, fields or keywords': 'Поиск по имени, сфере или ключевым словам',
|
|
611
|
+
'Search names, f[专家] or keywords': 'Поиск по имени, сфере или ключевым словам',
|
|
612
|
+
'No enabled experts yet. Type a keyword to find one.': 'Нет активных экспертов. Введите ключевое слово для поиска.',
|
|
613
|
+
'Search teams or goals': 'Поиск команды или цели работы',
|
|
614
|
+
'Team service unavailable. Reload the plugin.': 'Сервис команд экспертов недоступен, перезагрузите плагин.',
|
|
615
|
+
'Image Studio & Vault': 'Студия изображений и хранилище',
|
|
616
|
+
'Agent Browser': 'Браузер агента',
|
|
617
|
+
'Request approval': 'Запрашивать подтверждение',
|
|
561
618
|
'Today': 'Сегодня',
|
|
562
619
|
'Plugin Market': 'Магазин плагинов',
|
|
563
620
|
'GitHub ops': 'Операции с GitHub',
|
|
@@ -783,20 +840,22 @@ window.__ModuleLoader__.load({
|
|
|
783
840
|
let zhObserver = null
|
|
784
841
|
let zhDebounceTimer = null
|
|
785
842
|
let zhWalking = false
|
|
786
|
-
const queueZhWalk = (
|
|
843
|
+
const queueZhWalk = () => {
|
|
787
844
|
if (zhDebounceTimer) return
|
|
788
845
|
zhDebounceTimer = setTimeout(() => {
|
|
789
846
|
zhDebounceTimer = null
|
|
790
847
|
if (zhWalking) return
|
|
791
848
|
zhWalking = true
|
|
792
849
|
try {
|
|
793
|
-
ZH_WALKER(
|
|
850
|
+
ZH_WALKER(document.body)
|
|
794
851
|
} finally {
|
|
795
852
|
zhWalking = false
|
|
796
853
|
}
|
|
797
|
-
},
|
|
854
|
+
}, 80)
|
|
798
855
|
}
|
|
799
856
|
|
|
857
|
+
const onInteraction = () => queueZhWalk()
|
|
858
|
+
|
|
800
859
|
const syncZhDom = () => {
|
|
801
860
|
try {
|
|
802
861
|
if (typeof document === 'undefined') return
|
|
@@ -804,6 +863,8 @@ window.__ModuleLoader__.load({
|
|
|
804
863
|
if (!ru) {
|
|
805
864
|
if (zhObserver) { zhObserver.disconnect(); zhObserver = null }
|
|
806
865
|
if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }
|
|
866
|
+
document.removeEventListener('pointerup', onInteraction)
|
|
867
|
+
document.removeEventListener('click', onInteraction)
|
|
807
868
|
return
|
|
808
869
|
}
|
|
809
870
|
if (!zhObserver) {
|
|
@@ -813,7 +874,7 @@ window.__ModuleLoader__.load({
|
|
|
813
874
|
for (const node of record.addedNodes) {
|
|
814
875
|
if (node.nodeType === 1) {
|
|
815
876
|
if (node.closest && node.closest('.chat-message, .markdown-body, pre, code, [data-stream]')) continue
|
|
816
|
-
queueZhWalk(
|
|
877
|
+
queueZhWalk()
|
|
817
878
|
return
|
|
818
879
|
}
|
|
819
880
|
}
|
|
@@ -821,8 +882,10 @@ window.__ModuleLoader__.load({
|
|
|
821
882
|
}
|
|
822
883
|
})
|
|
823
884
|
zhObserver.observe(document.body, { childList: true, subtree: true })
|
|
885
|
+
document.addEventListener('pointerup', onInteraction, { passive: true })
|
|
886
|
+
document.addEventListener('click', onInteraction, { passive: true })
|
|
824
887
|
}
|
|
825
|
-
queueZhWalk(
|
|
888
|
+
queueZhWalk()
|
|
826
889
|
} catch (err) { /* ignore */ }
|
|
827
890
|
}
|
|
828
891
|
const unsubscribeZh = runtime.subscribe(syncZhDom)
|
|
@@ -831,6 +894,10 @@ window.__ModuleLoader__.load({
|
|
|
831
894
|
unsubscribeZh()
|
|
832
895
|
if (zhObserver) zhObserver.disconnect()
|
|
833
896
|
if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }
|
|
897
|
+
if (typeof document !== 'undefined') {
|
|
898
|
+
document.removeEventListener('pointerup', onInteraction)
|
|
899
|
+
document.removeEventListener('click', onInteraction)
|
|
900
|
+
}
|
|
834
901
|
}
|
|
835
902
|
}, 'dsh-russian-lang: zh-dom')
|
|
836
903
|
syncZhDom()
|
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 }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"@goodandready/dsh-remote-workspace":{"checkUpdate":"Проверить обновления","checking":"Проверка...","currentVersion":"Текущая версия","latestVersion":"npm Последняя","restartRequired":"Обновление успешно установлено. Перезапустите DSH для применения.","upToDate":"Актуальная версия","updateAvailable":"Доступно обновление","updateFailed":"Сбой обновления","updateNow":"Обновить сейчас","updaterTitle":"Обновление плагина","updating":"Обновление..."},"dsh-remote-workspace":{"actionFailed":"Запрос не выполнен.","authPromptCode":"Одноразовый код","authPromptSubmit":"Продолжить","authPromptTitle":"Код авторизации","btnDownload":"Скачать","btnUpload":"Загрузить","checkUpdate":"Проверить обновления","checking":"Проверка…","clusterDeleteConfirm":"Удалить этот узел кластера?","clusterHealth":"Кластер: {status}","clusterNoNodes":"Узлы кластера не настроены.","clusterNodeAdd":"Добавить узел","clusterNodeName":"Имя узла","clusterNodeRole":"Роль","clusterTitle":"Узлы кластера","currentVersion":"Установленная версия","diagAuth":"Аутентификация","diagLatency":"Задержка","diagOs":"Операционная система","diagPorts":"Открытые порты","diagUptime":"Время работы","dockerCompose":"Стеки Compose","dockerComposeDown":"Остановить","dockerComposeRestart":"Перезапустить","dockerComposeUp":"Запустить","dockerNoStacks":"Стеков Compose не найдено.","emptyClusterNodes":"В этом кластере нет узлов.","emptyContainers":"Нет работающих контейнеров.","emptyFiles":"Каталог пуст.","emptyHosts":"Хосты не настроены.","emptyTunnels":"Нет активных туннелей.","fAuthCode":"Код подтверждения","fKey":"Закрытый ключ","fKeyUpload":"Загрузить файл ключа","fPortForward":"Проброшенные порты","fTunnelLocal":"Локальная привязка","fTunnelRemote":"Удалённый адрес","importEmpty":"В ~/.ssh/config не найдено хостов","importFound":"Найдено хостов в ~/.ssh/config: {count}","importSelected":"Импортировать выбранные","importSsh":"Импорт из ~/.ssh/config","importSuccess":"Импортировано хостов: {count}","latestVersion":"Последняя версия в npm","noFiles":"Файлов не найдено.","noHostsConfigured":"Удалённые хосты не настроены.","pinned":"Закреплён","restartRequired":"Обновление установлено. Перезапустите DSH для применения.","setActiveBtn":"Сделать текущим","skipDuplicate":"уже импортирован","skipMatch":"Блоки Match не импортируются","skipMissing":"включённый файл не найден","skipWildcard":"шаблон не является отдельным хостом","statusEmpty":"Нет активного хоста","statusError":"Статус недоступен","statusLoading":"Проверка соединения","statusReady":"Хост готов","tabCluster":"Кластер","tabFiles":"Файлы","testGroup":"Группа тестов","transferCancel":"Отмена","transferFailed":"Сбой передачи: {name}","transferProgress":"{name}: {percent}%","upToDate":"Уже последняя версия","updateAvailable":"Доступно обновление","updateFailed":"Не удалось обновить","updateNow":"Обновить","updaterTitle":"Обновление плагина","updating":"Обновление…","versionUnknown":"Версия неизвестна","wsBack":"Назад","wsCluster":"Кластер","wsDocker":"Контейнеры","wsFiles":"Файлы","wsHosts":"Хосты","wsTerminal":"Терминал","wsTunnels":"Туннели"}}
|
|
1
|
+
{"@goodandready/dsh-remote-workspace":{"actionFailed":"Запрос не выполнен.","actionsCol":"Действия","activeBadge":"Активен","addBtn":"+ Добавить хост","alertLabel":"Оповещения ({count})","authPromptCode":"Одноразовый код","authPromptSubmit":"Продолжить","authPromptTitle":"Код авторизации","autoSyncDesc":"Автоматически отправлять изменённые файлы в удалённое рабочее пространство при локальном сохранении.","autoSyncTitle":"Автосинхронизация при сохранении","badgeKey":"🔑 Вход по ключу","badgeOffline":"Хост не выбран","badgeOnline":"Активный хост: {name} ({latency} мс)","badgePassword":"🔒 Вход по паролю","badgeTunnels":"Туннелей: {count}","btnBrowse":"📁 Выбрать на сервере…","btnCancel":"Отмена","btnDelete":"Удалить","btnDownload":"Скачать","btnImportSsh":"Импорт из ~/.ssh/config","btnLogs":"Логи","btnRestart":"Перезапустить","btnSave":"Сохранить профиль","btnSaveFile":"Сохранить изменения","btnSelectThis":"Выбрать эту папку","btnStop":"Остановить","btnTest":"⚡ Проверить связь (Ping)","btnTransfer":"Передать на хост…","btnUpload":"Загрузить","checkUpdate":"Проверить обновления","checking":"Проверка…","clusterCommand":"Команда","clusterCount":"Настроено серверов: {count}","clusterFail":"ошибка","clusterOk":"успешно","clusterRun":"Выполнить","clusterRunning":"Выполняется...","clusterTitle":"Запуск на группе","containerImage":"Образ","containerName":"Контейнер","containerPorts":"Порты","containerStatus":"Статус","containersTitle":"Удалённый Docker / Podman Engine","currentVersion":"Установленная версия","delBtn":"Удалить","delConfirm":"Удалить профиль хоста «{name}»?","diagDesc":"Проверка доступности и измерение задержки активного сервера.","diagProbeBtn":"⚡ Быстрая диагностика","diagTitle":"📊 Диагностика активного подключения","diagnoseDisk":"Использование диска","diagnoseOom":"Проверка OOM Killer","diagnosePorts":"Открытые порты","diagnoseTitle":"Экспресс-диагностика","editBtn":"Изменить","envHideVal":"Скрыть","envKey":"Ключ","envSaveVar":"Сохранить","envShowVal":"Показать","envTitle":"Удалённые переменные окружения (.env)","envVal":"Значение","explorerTitle":"Удалённый файловый менеджер","fAgentPath":"Сокет агента","fAgentPathHint":"Оставьте пустым для использования SSH_AUTH_SOCK. В Windows используйте pageant.","fAuthAgent":"SSH Agent","fAuthAgentShort":"агент","fAuthKey":"SSH-ключ","fAuthKeyShort":"ключ","fAuthPass":"Пароль","fAuthPassShort":"пароль","fAuthType":"Способ авторизации","fDescription":"Описание","fEnvironment":"Окружение","fHost":"Хост / IP-адрес","fJumpHost":"Jump-хост (бастион)","fJumpHostNone":"Прямое подключение (без jump-хоста)","fJumpHosts":"Jump-хосты","fJumpHostsHint":"ID профилей по порядку через запятую. Первый хост — входной бастион.","fKeyPath":"Путь к ключу","fKeyPathHint":"Путь к ~/.ssh/id_rsa или /home/user/.ssh/id_ed25519","fLocalMirror":"Локальная папка зеркала","fLocalMirrorHint":"Локальная директория для зеркалирования файлов при быстрой трёхсторонней синхронизации","fLocation":"Локация","fName":"Название подключения","fPassphrase":"Кодовая фраза ключа (если есть)","fPassword":"Пароль учетной записи","fPort":"Порт","fProxyCommand":"Proxy-команда","fProxyCommandHint":"OpenSSH ProxyCommand. Заполнители: %h хост, %p порт, %r пользователь, %n имя профиля.","fRemotePath":"Удалённая папка проекта","fRemotePathHint":"(Не обязательно сейчас — можно выбрать позже при работе)","fTags":"Теги","fTerminalFont":"Шрифт терминала","fTerminalFontHint":"Семейство шрифтов для терминала. Оставьте пустым для моноширинного стека по умолчанию.","fUser":"Имя пользователя","filePreviewTitle":"Просмотр файла: {path}","fileSaved":"Файл успешно сохранён.","fileSize":"{size} КБ","formTitleEdit":"Редактирование профиля хоста","formTitleNew":"Добавление нового SSH хоста","groupEnvironment":"По окружению","groupFlat":"Сплошной список","groupOffline":"не в сети: {err}","groupOnline":"в сети, {latency} мс","groupTag":"По тегам","groupUngrouped":"Без группы","healthCpu":"ЦП: {val}%","healthDisk":"Диск: {val}%","healthMem":"ОЗУ: {val}% ({used}/{total} МБ)","healthUptime":"Аптайм: {val}","hostCol":"Хост / Пользователь","importAdded":"Импортировано серверов: {count}.","latestVersion":"Последняя на npm","nameCol":"Имя","navLabel":"Удаленные серверы","noContainers":"Активных контейнеров не найдено или демон Docker не запущен.","noHosts":"Удалённые хосты ещё не настроены. Нажмите «+ Добавить хост».","openLocalLink":"🌐 Открыть локальный порт","pathCol":"Удалённый путь","pinBtn":"Закрепить активный","pinned":"Активный хост","portCol":"Порт","profilesDesc":"Настройка удалённых хостов для выполнения команд, SFTP доступа и синхронизации.","profilesTitle":"🖥️ Профили SSH серверов","pullBtn":"⬇️ Скачать с сервера (Pull)","pulling":"Получение файлов…","pushBtn":"⬆️ Загрузить на сервер (Push)","pushing":"Отправка файлов…","restartRequired":"Обновление установлено. Перезапустите DSH для применения.","saving":"Сохранение…","setActiveBtn":"Сделать активным","skipDuplicate":"уже импортировано","skipMatch":"Блоки Match не импортируются","skipMissing":"включённый файл не найден","skipWildcard":"шаблон не является отдельным хостом","status":"Готово","statusEmpty":"Нет активного хоста","statusError":"Статус недоступен","statusLoading":"Проверка соединения","statusReady":"Хост готов","subtitle":"Управление подключениями по SSH, синхронизацией SFTP и туннелями","syncConflict":"Предупреждение о конфликте: изменено с обеих сторон файлов — {count}.","syncDesc":"Ручная синхронизация файлов с удалённым сервером и статус проброса портов.","syncErr":"Ошибка синхронизации: {err}","syncFailed":"Ошибка синхронизации: ","syncOk":"Синхронизация завершена! Обновлено файлов: {count}.","syncSuccess":"Синхронизация успешно выполнена","syncTitle":"🔄 Синхронизация зеркала и туннели","tLocalPort":"Локальный порт","tNoTunnels":"Нет активных туннелей проброса портов.","tOpenBtn":"+ Открыть туннель","tOpenErr":"Не удалось открыть туннель: {err}","tOpenOk":"Туннель открыт: 127.0.0.1:{localPort} -> Удалённый:{remotePort}","tOpening":"Открытие…","tRemotePort":"Удалённый порт","tStopBtn":"Остановить","tabCluster":"Кластер","tabContainers":"🐳 Контейнеры","tabEnv":"🗃️ Менеджер .env","tabExplorer":"📂 Проводник файлов","tabFiles":"Файлы","tabProfiles":"🖥️ Профили хостов","tabSync":"🔄 Станция синхронизации","tabTerminal":"⚡ Веб-терминал","tabTunnels":"🔗 Порт-туннели","tarSyncFast":"🚀 Быстрый поток архива (Tarball Stream)","termClear":"Очистить","termConnected":"Подключено к PTY-сессии {id} на {host}","termDisconnected":"Терминал отключён.","termPlaceholder":"Введите команду и нажмите Enter (напр. htop, ls, docker ps)…","termStart":"Подключить терминал","termStop":"Отключить","testFail":"❌ Ошибка подключения: {err}","testGroup":"Тестовая группа","testOk":"✅ Соединение установлено! Задержка: {latency} мс | ОС: {os}","testing":"Проверка соединения…","title":"Удалённое рабочее пространство","transferBtnExec":"Передать напрямую","transferCancel":"Отмена","transferDestHost":"Целевой сервер","transferDestPath":"Целевой путь к файлу","transferFailed":"Сбой передачи: {name}","transferProgress":"{name}: {percent}%","transferSuccess":"Успешно передано {bytes} байт на {host}:{path}","transferTitle":"Прямая передача сервер-сервер","tunnelTraffic":"Вход: {rx} | Выход: {tx}","tunnelUptime":"Работает: {sec} с","tunnelsColActions":"Действия","tunnelsColId":"ID туннеля","tunnelsColRoute":"Маршрут проброса","tunnelsDesc":"Проброс удалённых портов (базы данных, веб-серверы, API) на локальный компьютер через зашифрованные SSH-туннели.","tunnelsTitle":"🔀 Проброс портов SSH (туннели)","upToDate":"Актуальная версия","updateAvailable":"Доступно обновление","updateFailed":"Сбой обновления","updateNow":"Обновить сейчас","updaterTitle":"Обновление плагина","updating":"Обновление…","vaultProtectedBadge":"🛡️ Защищено .env Vault","versionUnknown":"Версия неизвестна","wsBack":"Назад","wsCluster":"Кластер","wsDocker":"Контейнеры","wsFiles":"Файлы","wsHosts":"Хосты","wsTerminal":"Терминал","wsTunnels":"Туннели"},"dsh-remote-workspace":{"actionFailed":"Запрос не выполнен.","actionsCol":"Действия","activeBadge":"Активен","addBtn":"+ Добавить хост","alertLabel":"Оповещения ({count})","authPromptCode":"Одноразовый код","authPromptSubmit":"Продолжить","authPromptTitle":"Код авторизации","autoSyncDesc":"Автоматически отправлять изменённые файлы в удалённое рабочее пространство при локальном сохранении.","autoSyncTitle":"Автосинхронизация при сохранении","badgeKey":"🔑 Вход по ключу","badgeOffline":"Хост не выбран","badgeOnline":"Активный хост: {name} ({latency} мс)","badgePassword":"🔒 Вход по паролю","badgeTunnels":"Туннелей: {count}","btnBrowse":"📁 Выбрать на сервере…","btnCancel":"Отмена","btnDelete":"Удалить","btnDownload":"Скачать","btnImportSsh":"Импорт из ~/.ssh/config","btnLogs":"Логи","btnRestart":"Перезапустить","btnSave":"Сохранить профиль","btnSaveFile":"Сохранить изменения","btnSelectThis":"Выбрать эту папку","btnStop":"Остановить","btnTest":"⚡ Проверить связь (Ping)","btnTransfer":"Передать на хост…","btnUpload":"Загрузить","checkUpdate":"Проверить обновления","checking":"Проверка…","clusterCommand":"Команда","clusterCount":"Настроено серверов: {count}","clusterDeleteConfirm":"Удалить этот узел кластера?","clusterFail":"ошибка","clusterHealth":"Кластер: {status}","clusterNoNodes":"Узлы кластера не настроены.","clusterNodeAdd":"Добавить узел","clusterNodeName":"Имя узла","clusterNodeRole":"Роль","clusterOk":"успешно","clusterRun":"Выполнить","clusterRunning":"Выполняется...","clusterTitle":"Запуск на группе","containerImage":"Образ","containerName":"Контейнер","containerPorts":"Порты","containerStatus":"Статус","containersTitle":"Удалённый Docker / Podman Engine","currentVersion":"Установленная версия","delBtn":"Удалить","delConfirm":"Удалить профиль хоста «{name}»?","diagAuth":"Аутентификация","diagDesc":"Проверка доступности и измерение задержки активного сервера.","diagLatency":"Задержка","diagOs":"Операционная система","diagPorts":"Открытые порты","diagProbeBtn":"⚡ Быстрая диагностика","diagTitle":"📊 Диагностика активного подключения","diagUptime":"Время работы","diagnoseDisk":"Использование диска","diagnoseOom":"Проверка OOM Killer","diagnosePorts":"Открытые порты","diagnoseTitle":"Экспресс-диагностика","dockerCompose":"Стеки Compose","dockerComposeDown":"Остановить","dockerComposeRestart":"Перезапустить","dockerComposeUp":"Запустить","dockerNoStacks":"Стеков Compose не найдено.","editBtn":"Изменить","emptyClusterNodes":"В этом кластере нет узлов.","emptyContainers":"Нет работающих контейнеров.","emptyFiles":"Каталог пуст.","emptyHosts":"Хосты не настроены.","emptyTunnels":"Нет активных туннелей.","envHideVal":"Скрыть","envKey":"Ключ","envSaveVar":"Сохранить","envShowVal":"Показать","envTitle":"Удалённые переменные окружения (.env)","envVal":"Значение","explorerTitle":"Удалённый файловый менеджер","fAgentPath":"Сокет агента","fAgentPathHint":"Оставьте пустым для использования SSH_AUTH_SOCK. В Windows используйте pageant.","fAuthAgent":"SSH Agent","fAuthAgentShort":"агент","fAuthCode":"Код подтверждения","fAuthKey":"SSH-ключ","fAuthKeyShort":"ключ","fAuthPass":"Пароль","fAuthPassShort":"пароль","fAuthType":"Способ авторизации","fDescription":"Описание","fEnvironment":"Окружение","fHost":"Хост / IP-адрес","fJumpHost":"Jump-хост (бастион)","fJumpHostNone":"Прямое подключение (без jump-хоста)","fJumpHosts":"Jump-хосты","fJumpHostsHint":"ID профилей по порядку через запятую. Первый хост — входной бастион.","fKey":"Закрытый ключ","fKeyPath":"Путь к ключу","fKeyPathHint":"Путь к ~/.ssh/id_rsa или /home/user/.ssh/id_ed25519","fKeyUpload":"Загрузить файл ключа","fLocalMirror":"Локальная папка зеркала","fLocalMirrorHint":"Локальная директория для зеркалирования файлов при быстрой трёхсторонней синхронизации","fLocation":"Локация","fName":"Название подключения","fPassphrase":"Кодовая фраза ключа (если есть)","fPassword":"Пароль учетной записи","fPort":"Порт","fPortForward":"Проброшенные порты","fProxyCommand":"Proxy-команда","fProxyCommandHint":"OpenSSH ProxyCommand. Заполнители: %h хост, %p порт, %r пользователь, %n имя профиля.","fRemotePath":"Удалённая папка проекта","fRemotePathHint":"(Не обязательно сейчас — можно выбрать позже при работе)","fTags":"Теги","fTerminalFont":"Шрифт терминала","fTerminalFontHint":"Семейство шрифтов для терминала. Оставьте пустым для моноширинного стека по умолчанию.","fTunnelLocal":"Локальная привязка","fTunnelRemote":"Удалённый адрес","fUser":"Имя пользователя","filePreviewTitle":"Просмотр файла: {path}","fileSaved":"Файл успешно сохранён.","fileSize":"{size} КБ","formTitleEdit":"Редактирование профиля хоста","formTitleNew":"Добавление нового SSH хоста","groupEnvironment":"По окружению","groupFlat":"Сплошной список","groupOffline":"не в сети: {err}","groupOnline":"в сети, {latency} мс","groupTag":"По тегам","groupUngrouped":"Без группы","healthCpu":"ЦП: {val}%","healthDisk":"Диск: {val}%","healthMem":"ОЗУ: {val}% ({used}/{total} МБ)","healthUptime":"Аптайм: {val}","hostCol":"Хост / Пользователь","importAdded":"Импортировано серверов: {count}.","importEmpty":"В ~/.ssh/config не найдено хостов","importFound":"Найдено хостов в ~/.ssh/config: {count}","importSelected":"Импортировать выбранные","importSsh":"Импорт из ~/.ssh/config","importSuccess":"Импортировано хостов: {count}","latestVersion":"Последняя на npm","nameCol":"Имя","navLabel":"Удаленные серверы","noContainers":"Активных контейнеров не найдено или демон Docker не запущен.","noFiles":"Файлов не найдено.","noHosts":"Удалённые хосты ещё не настроены. Нажмите «+ Добавить хост».","noHostsConfigured":"Удалённые хосты не настроены.","openLocalLink":"🌐 Открыть локальный порт","pathCol":"Удалённый путь","pinBtn":"Закрепить активный","pinned":"Активный хост","portCol":"Порт","profilesDesc":"Настройка удалённых хостов для выполнения команд, SFTP доступа и синхронизации.","profilesTitle":"🖥️ Профили SSH серверов","pullBtn":"⬇️ Скачать с сервера (Pull)","pulling":"Получение файлов…","pushBtn":"⬆️ Загрузить на сервер (Push)","pushing":"Отправка файлов…","restartRequired":"Обновление установлено. Перезапустите DSH для применения.","saving":"Сохранение…","setActiveBtn":"Сделать активным","skipDuplicate":"уже импортировано","skipMatch":"Блоки Match не импортируются","skipMissing":"включённый файл не найден","skipWildcard":"шаблон не является отдельным хостом","status":"Готово","statusEmpty":"Нет активного хоста","statusError":"Статус недоступен","statusLoading":"Проверка соединения","statusReady":"Хост готов","subtitle":"Управление подключениями по SSH, синхронизацией SFTP и туннелями","syncConflict":"Предупреждение о конфликте: изменено с обеих сторон файлов — {count}.","syncDesc":"Ручная синхронизация файлов с удалённым сервером и статус проброса портов.","syncErr":"Ошибка синхронизации: {err}","syncFailed":"Ошибка синхронизации: ","syncOk":"Синхронизация завершена! Обновлено файлов: {count}.","syncSuccess":"Синхронизация успешно выполнена","syncTitle":"🔄 Синхронизация зеркала и туннели","tLocalPort":"Локальный порт","tNoTunnels":"Нет активных туннелей проброса портов.","tOpenBtn":"+ Открыть туннель","tOpenErr":"Не удалось открыть туннель: {err}","tOpenOk":"Туннель открыт: 127.0.0.1:{localPort} -> Удалённый:{remotePort}","tOpening":"Открытие…","tRemotePort":"Удалённый порт","tStopBtn":"Остановить","tabCluster":"Кластер","tabContainers":"🐳 Контейнеры","tabEnv":"🗃️ Менеджер .env","tabExplorer":"📂 Проводник файлов","tabFiles":"Файлы","tabProfiles":"🖥️ Профили хостов","tabSync":"🔄 Станция синхронизации","tabTerminal":"⚡ Веб-терминал","tabTunnels":"🔗 Порт-туннели","tarSyncFast":"🚀 Быстрый поток архива (Tarball Stream)","termClear":"Очистить","termConnected":"Подключено к PTY-сессии {id} на {host}","termDisconnected":"Терминал отключён.","termPlaceholder":"Введите команду и нажмите Enter (напр. htop, ls, docker ps)…","termStart":"Подключить терминал","termStop":"Отключить","testFail":"❌ Ошибка подключения: {err}","testGroup":"Тестовая группа","testOk":"✅ Соединение установлено! Задержка: {latency} мс | ОС: {os}","testing":"Проверка соединения…","title":"Удалённое рабочее пространство","transferBtnExec":"Передать напрямую","transferCancel":"Отмена","transferDestHost":"Целевой сервер","transferDestPath":"Целевой путь к файлу","transferFailed":"Сбой передачи: {name}","transferProgress":"{name}: {percent}%","transferSuccess":"Успешно передано {bytes} байт на {host}:{path}","transferTitle":"Прямая передача сервер-сервер","tunnelTraffic":"Вход: {rx} | Выход: {tx}","tunnelUptime":"Работает: {sec} с","tunnelsColActions":"Действия","tunnelsColId":"ID туннеля","tunnelsColRoute":"Маршрут проброса","tunnelsDesc":"Проброс удалённых портов (базы данных, веб-серверы, API) на локальный компьютер через зашифрованные SSH-туннели.","tunnelsTitle":"🔀 Проброс портов SSH (туннели)","upToDate":"Актуальная версия","updateAvailable":"Доступно обновление","updateFailed":"Сбой обновления","updateNow":"Обновить сейчас","updaterTitle":"Обновление плагина","updating":"Обновление…","vaultProtectedBadge":"🛡️ Защищено .env Vault","versionUnknown":"Версия неизвестна","wsBack":"Назад","wsCluster":"Кластер","wsDocker":"Контейнеры","wsFiles":"Файлы","wsHosts":"Хосты","wsTerminal":"Терминал","wsTunnels":"Туннели"}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"dsh-remote-workspace":{"actionsCol":"Действия","activeBadge":"Активен","addBtn":"+ Добавить хост","alertLabel":"Оповещения ({count})","autoSyncDesc":"Автоматически отправлять изменённые файлы в удалённое рабочее пространство при локальном сохранении.","autoSyncTitle":"Автосинхронизация при сохранении","badgeKey":"🔑 Вход по ключу","badgeOffline":"Хост не выбран","badgeOnline":"Активный хост: {name} ({latency} мс)","badgePassword":"🔒 Вход по паролю","badgeTunnels":"Туннелей: {count}","btnBrowse":"📁 Выбрать на сервере…","btnCancel":"Отмена","btnDelete":"Удалить","btnImportSsh":"Импорт из ~/.ssh/config","btnLogs":"Логи","btnRestart":"Перезапустить","btnSave":"Сохранить профиль","btnSaveFile":"Сохранить изменения","btnSelectThis":"Выбрать эту папку","btnStop":"Остановить","btnTest":"⚡ Проверить связь (Ping)","btnTransfer":"Передать на хост…","clusterCommand":"Команда","clusterCount":"Настроено серверов: {count}","clusterFail":"ошибка","clusterOk":"успешно","clusterRun":"Выполнить","clusterRunning":"Выполняется...","containerImage":"Образ","containerName":"Контейнер","containerPorts":"Порты","containerStatus":"Статус","containersTitle":"Удалённый Docker / Podman Engine","delBtn":"Удалить","delConfirm":"Удалить профиль хоста «{name}»?","diagDesc":"Проверка доступности и измерение задержки активного сервера.","diagProbeBtn":"⚡ Быстрая диагностика","diagTitle":"📊 Диагностика активного подключения","diagnoseDisk":"Использование диска","diagnoseOom":"Проверка OOM Killer","diagnosePorts":"Открытые порты","diagnoseTitle":"Экспресс-диагностика","editBtn":"Изменить","envHideVal":"Скрыть","envKey":"Ключ","envSaveVar":"Сохранить","envShowVal":"Показать","envTitle":"Удалённые переменные окружения (.env)","envVal":"Значение","explorerTitle":"Удалённый файловый менеджер","fAgentPath":"Сокет агента","fAgentPathHint":"Оставьте пустым для использования SSH_AUTH_SOCK. В Windows используйте pageant.","fAuthAgent":"SSH Agent","fAuthAgentShort":"агент","fAuthKey":"SSH-ключ","fAuthKeyShort":"ключ","fAuthPass":"Пароль","fAuthPassShort":"пароль","fAuthType":"Способ авторизации","fDescription":"Описание","fEnvironment":"Окружение","fHost":"Хост / IP-адрес","fJumpHost":"Jump-хост (бастион)","fJumpHostNone":"Прямое подключение (без jump-хоста)","fJumpHosts":"Jump-хосты","fJumpHostsHint":"ID профилей по порядку через запятую. Первый хост — входной бастион.","fKeyPath":"Путь к ключу","fKeyPathHint":"Путь к ~/.ssh/id_rsa или /home/user/.ssh/id_ed25519","fLocalMirror":"Локальная папка зеркала","fLocalMirrorHint":"Локальная директория для зеркалирования файлов при быстрой трёхсторонней синхронизации","fLocation":"Локация","fName":"Название подключения","fPassphrase":"Кодовая фраза ключа (если есть)","fPassword":"Пароль учетной записи","fPort":"Порт","fProxyCommand":"Proxy-команда","fProxyCommandHint":"OpenSSH ProxyCommand. Заполнители: %h хост, %p порт, %r пользователь, %n имя профиля.","fRemotePath":"Удалённая папка проекта","fRemotePathHint":"(Не обязательно сейчас — можно выбрать позже при работе)","fTags":"Теги","fTerminalFont":"Шрифт терминала","fTerminalFontHint":"Семейство шрифтов для терминала. Оставьте пустым для моноширинного стека по умолчанию.","fUser":"Имя пользователя","filePreviewTitle":"Просмотр файла: {path}","fileSaved":"Файл успешно сохранён.","fileSize":"{size} КБ","formTitleEdit":"Редактирование профиля хоста","formTitleNew":"Добавление нового SSH хоста","groupEnvironment":"По окружению","groupFlat":"Сплошной список","groupOffline":"не в сети: {err}","groupOnline":"в сети, {latency} мс","groupTag":"По тегам","groupUngrouped":"Без группы","healthCpu":"ЦП: {val}%","healthDisk":"Диск: {val}%","healthMem":"ОЗУ: {val}% ({used}/{total} МБ)","healthUptime":"Аптайм: {val}","hostCol":"Хост / Пользователь","importAdded":"Импортировано серверов: {count}.","nameCol":"Имя","navLabel":"Удаленные серверы","noContainers":"Активных контейнеров не найдено или демон Docker не запущен.","noHosts":"Удалённые хосты ещё не настроены. Нажмите «+ Добавить хост».","openLocalLink":"🌐 Открыть локальный порт","pathCol":"Удалённый путь","pinBtn":"Закрепить активный","pinned":"Активный хост","portCol":"Порт","profilesDesc":"Настройка удалённых хостов для выполнения команд, SFTP доступа и синхронизации.","profilesTitle":"🖥️ Профили SSH серверов","pullBtn":"⬇️ Скачать с сервера (Pull)","pulling":"Получение файлов…","pushBtn":"⬆️ Загрузить на сервер (Push)","pushing":"Отправка файлов…","saving":"Сохранение…","setActiveBtn":"Сделать активным","status":"Готово","subtitle":"Управление подключениями по SSH, синхронизацией SFTP и туннелями","syncConflict":"Предупреждение о конфликте: изменено с обеих сторон файлов — {count}.","syncDesc":"Ручная синхронизация файлов с удалённым сервером и статус проброса портов.","syncErr":"Ошибка синхронизации: {err}","syncFailed":"Ошибка синхронизации: ","syncOk":"Синхронизация завершена! Обновлено файлов: {count}.","syncSuccess":"Синхронизация успешно выполнена","syncTitle":"🔄 Синхронизация зеркала и туннели","tLocalPort":"Локальный порт","tNoTunnels":"Нет активных туннелей проброса портов.","tOpenBtn":"+ Открыть туннель","tOpenErr":"Не удалось открыть туннель: {err}","tOpenOk":"Туннель открыт: 127.0.0.1:{localPort} -> Удалённый:{remotePort}","tOpening":"Открытие…","tRemotePort":"Удалённый порт","tStopBtn":"Остановить","tabContainers":"🐳 Контейнеры","tabEnv":"🗃️ Менеджер .env","tabExplorer":"📂 Проводник файлов","tabProfiles":"🖥️ Профили хостов","tabSync":"🔄 Станция синхронизации","tabTerminal":"⚡ Веб-терминал","tabTunnels":"🔗 Порт-туннели","tarSyncFast":"🚀 Быстрый поток архива (Tarball Stream)","termClear":"Очистить","termConnected":"Подключено к PTY-сессии {id} на {host}","termDisconnected":"Терминал отключён.","termPlaceholder":"Введите команду и нажмите Enter (напр. htop, ls, docker ps)…","termStart":"Подключить терминал","termStop":"Отключить","testFail":"❌ Ошибка подключения: {err}","testOk":"✅ Соединение установлено! Задержка: {latency} мс | ОС: {os}","testing":"Проверка соединения…","title":"Удалённое рабочее пространство","transferBtnExec":"Передать напрямую","transferDestHost":"Целевой сервер","transferDestPath":"Целевой путь к файлу","transferSuccess":"Успешно передано {bytes} байт на {host}:{path}","transferTitle":"Прямая передача сервер-сервер","tunnelTraffic":"Вход: {rx} | Выход: {tx}","tunnelUptime":"Работает: {sec} с","tunnelsColActions":"Действия","tunnelsColId":"ID туннеля","tunnelsColRoute":"Маршрут проброса","tunnelsDesc":"Проброс удалённых портов (базы данных, веб-серверы, API) на локальный компьютер через зашифрованные SSH-туннели.","tunnelsTitle":"🔀 Проброс портов SSH (туннели)","vaultProtectedBadge":"🛡️ Защищено .env Vault"}}
|
|
1
|
+
{"@goodandready/dsh-remote-workspace":{"actionFailed":"Запрос не выполнен.","actionsCol":"Действия","activeBadge":"Активен","addBtn":"+ Добавить хост","alertLabel":"Оповещения ({count})","authPromptCode":"Одноразовый код","authPromptSubmit":"Продолжить","authPromptTitle":"Код авторизации","autoSyncDesc":"Автоматически отправлять изменённые файлы в удалённое рабочее пространство при локальном сохранении.","autoSyncTitle":"Автосинхронизация при сохранении","badgeKey":"🔑 Вход по ключу","badgeOffline":"Хост не выбран","badgeOnline":"Активный хост: {name} ({latency} мс)","badgePassword":"🔒 Вход по паролю","badgeTunnels":"Туннелей: {count}","btnBrowse":"📁 Выбрать на сервере…","btnCancel":"Отмена","btnDelete":"Удалить","btnDownload":"Скачать","btnImportSsh":"Импорт из ~/.ssh/config","btnLogs":"Логи","btnRestart":"Перезапустить","btnSave":"Сохранить профиль","btnSaveFile":"Сохранить изменения","btnSelectThis":"Выбрать эту папку","btnStop":"Остановить","btnTest":"⚡ Проверить связь (Ping)","btnTransfer":"Передать на хост…","btnUpload":"Загрузить","checkUpdate":"Проверить обновления","checking":"Проверка…","clusterCommand":"Команда","clusterCount":"Настроено серверов: {count}","clusterFail":"ошибка","clusterOk":"успешно","clusterRun":"Выполнить","clusterRunning":"Выполняется...","clusterTitle":"Запуск на группе","containerImage":"Образ","containerName":"Контейнер","containerPorts":"Порты","containerStatus":"Статус","containersTitle":"Удалённый Docker / Podman Engine","currentVersion":"Установленная версия","delBtn":"Удалить","delConfirm":"Удалить профиль хоста «{name}»?","diagDesc":"Проверка доступности и измерение задержки активного сервера.","diagProbeBtn":"⚡ Быстрая диагностика","diagTitle":"📊 Диагностика активного подключения","diagnoseDisk":"Использование диска","diagnoseOom":"Проверка OOM Killer","diagnosePorts":"Открытые порты","diagnoseTitle":"Экспресс-диагностика","editBtn":"Изменить","envHideVal":"Скрыть","envKey":"Ключ","envSaveVar":"Сохранить","envShowVal":"Показать","envTitle":"Удалённые переменные окружения (.env)","envVal":"Значение","explorerTitle":"Удалённый файловый менеджер","fAgentPath":"Сокет агента","fAgentPathHint":"Оставьте пустым для использования SSH_AUTH_SOCK. В Windows используйте pageant.","fAuthAgent":"SSH Agent","fAuthAgentShort":"агент","fAuthKey":"SSH-ключ","fAuthKeyShort":"ключ","fAuthPass":"Пароль","fAuthPassShort":"пароль","fAuthType":"Способ авторизации","fDescription":"Описание","fEnvironment":"Окружение","fHost":"Хост / IP-адрес","fJumpHost":"Jump-хост (бастион)","fJumpHostNone":"Прямое подключение (без jump-хоста)","fJumpHosts":"Jump-хосты","fJumpHostsHint":"ID профилей по порядку через запятую. Первый хост — входной бастион.","fKeyPath":"Путь к ключу","fKeyPathHint":"Путь к ~/.ssh/id_rsa или /home/user/.ssh/id_ed25519","fLocalMirror":"Локальная папка зеркала","fLocalMirrorHint":"Локальная директория для зеркалирования файлов при быстрой трёхсторонней синхронизации","fLocation":"Локация","fName":"Название подключения","fPassphrase":"Кодовая фраза ключа (если есть)","fPassword":"Пароль учетной записи","fPort":"Порт","fProxyCommand":"Proxy-команда","fProxyCommandHint":"OpenSSH ProxyCommand. Заполнители: %h хост, %p порт, %r пользователь, %n имя профиля.","fRemotePath":"Удалённая папка проекта","fRemotePathHint":"(Не обязательно сейчас — можно выбрать позже при работе)","fTags":"Теги","fTerminalFont":"Шрифт терминала","fTerminalFontHint":"Семейство шрифтов для терминала. Оставьте пустым для моноширинного стека по умолчанию.","fUser":"Имя пользователя","filePreviewTitle":"Просмотр файла: {path}","fileSaved":"Файл успешно сохранён.","fileSize":"{size} КБ","formTitleEdit":"Редактирование профиля хоста","formTitleNew":"Добавление нового SSH хоста","groupEnvironment":"По окружению","groupFlat":"Сплошной список","groupOffline":"не в сети: {err}","groupOnline":"в сети, {latency} мс","groupTag":"По тегам","groupUngrouped":"Без группы","healthCpu":"ЦП: {val}%","healthDisk":"Диск: {val}%","healthMem":"ОЗУ: {val}% ({used}/{total} МБ)","healthUptime":"Аптайм: {val}","hostCol":"Хост / Пользователь","importAdded":"Импортировано серверов: {count}.","latestVersion":"Последняя на npm","nameCol":"Имя","navLabel":"Удаленные серверы","noContainers":"Активных контейнеров не найдено или демон Docker не запущен.","noHosts":"Удалённые хосты ещё не настроены. Нажмите «+ Добавить хост».","openLocalLink":"🌐 Открыть локальный порт","pathCol":"Удалённый путь","pinBtn":"Закрепить активный","pinned":"Активный хост","portCol":"Порт","profilesDesc":"Настройка удалённых хостов для выполнения команд, SFTP доступа и синхронизации.","profilesTitle":"🖥️ Профили SSH серверов","pullBtn":"⬇️ Скачать с сервера (Pull)","pulling":"Получение файлов…","pushBtn":"⬆️ Загрузить на сервер (Push)","pushing":"Отправка файлов…","restartRequired":"Обновление установлено. Перезапустите DSH для применения.","saving":"Сохранение…","setActiveBtn":"Сделать активным","skipDuplicate":"уже импортировано","skipMatch":"Блоки Match не импортируются","skipMissing":"включённый файл не найден","skipWildcard":"шаблон не является отдельным хостом","status":"Готово","statusEmpty":"Нет активного хоста","statusError":"Статус недоступен","statusLoading":"Проверка соединения","statusReady":"Хост готов","subtitle":"Управление подключениями по SSH, синхронизацией SFTP и туннелями","syncConflict":"Предупреждение о конфликте: изменено с обеих сторон файлов — {count}.","syncDesc":"Ручная синхронизация файлов с удалённым сервером и статус проброса портов.","syncErr":"Ошибка синхронизации: {err}","syncFailed":"Ошибка синхронизации: ","syncOk":"Синхронизация завершена! Обновлено файлов: {count}.","syncSuccess":"Синхронизация успешно выполнена","syncTitle":"🔄 Синхронизация зеркала и туннели","tLocalPort":"Локальный порт","tNoTunnels":"Нет активных туннелей проброса портов.","tOpenBtn":"+ Открыть туннель","tOpenErr":"Не удалось открыть туннель: {err}","tOpenOk":"Туннель открыт: 127.0.0.1:{localPort} -> Удалённый:{remotePort}","tOpening":"Открытие…","tRemotePort":"Удалённый порт","tStopBtn":"Остановить","tabCluster":"Кластер","tabContainers":"🐳 Контейнеры","tabEnv":"🗃️ Менеджер .env","tabExplorer":"📂 Проводник файлов","tabFiles":"Файлы","tabProfiles":"🖥️ Профили хостов","tabSync":"🔄 Станция синхронизации","tabTerminal":"⚡ Веб-терминал","tabTunnels":"🔗 Порт-туннели","tarSyncFast":"🚀 Быстрый поток архива (Tarball Stream)","termClear":"Очистить","termConnected":"Подключено к PTY-сессии {id} на {host}","termDisconnected":"Терминал отключён.","termPlaceholder":"Введите команду и нажмите Enter (напр. htop, ls, docker ps)…","termStart":"Подключить терминал","termStop":"Отключить","testFail":"❌ Ошибка подключения: {err}","testGroup":"Тестовая группа","testOk":"✅ Соединение установлено! Задержка: {latency} мс | ОС: {os}","testing":"Проверка соединения…","title":"Удалённое рабочее пространство","transferBtnExec":"Передать напрямую","transferCancel":"Отмена","transferDestHost":"Целевой сервер","transferDestPath":"Целевой путь к файлу","transferFailed":"Сбой передачи: {name}","transferProgress":"{name}: {percent}%","transferSuccess":"Успешно передано {bytes} байт на {host}:{path}","transferTitle":"Прямая передача сервер-сервер","tunnelTraffic":"Вход: {rx} | Выход: {tx}","tunnelUptime":"Работает: {sec} с","tunnelsColActions":"Действия","tunnelsColId":"ID туннеля","tunnelsColRoute":"Маршрут проброса","tunnelsDesc":"Проброс удалённых портов (базы данных, веб-серверы, API) на локальный компьютер через зашифрованные SSH-туннели.","tunnelsTitle":"🔀 Проброс портов SSH (туннели)","upToDate":"Актуальная версия","updateAvailable":"Доступно обновление","updateFailed":"Сбой обновления","updateNow":"Обновить сейчас","updaterTitle":"Обновление плагина","updating":"Обновление…","vaultProtectedBadge":"🛡️ Защищено .env Vault","versionUnknown":"Версия неизвестна","wsBack":"Назад","wsCluster":"Кластер","wsDocker":"Контейнеры","wsFiles":"Файлы","wsHosts":"Хосты","wsTerminal":"Терминал","wsTunnels":"Туннели"},"dsh-remote-workspace":{"actionFailed":"Запрос не выполнен.","actionsCol":"Действия","activeBadge":"Активен","addBtn":"+ Добавить хост","alertLabel":"Оповещения ({count})","authPromptCode":"Одноразовый код","authPromptSubmit":"Продолжить","authPromptTitle":"Код авторизации","autoSyncDesc":"Автоматически отправлять изменённые файлы в удалённое рабочее пространство при локальном сохранении.","autoSyncTitle":"Автосинхронизация при сохранении","badgeKey":"🔑 Вход по ключу","badgeOffline":"Хост не выбран","badgeOnline":"Активный хост: {name} ({latency} мс)","badgePassword":"🔒 Вход по паролю","badgeTunnels":"Туннелей: {count}","btnBrowse":"📁 Выбрать на сервере…","btnCancel":"Отмена","btnDelete":"Удалить","btnDownload":"Скачать","btnImportSsh":"Импорт из ~/.ssh/config","btnLogs":"Логи","btnRestart":"Перезапустить","btnSave":"Сохранить профиль","btnSaveFile":"Сохранить изменения","btnSelectThis":"Выбрать эту папку","btnStop":"Остановить","btnTest":"⚡ Проверить связь (Ping)","btnTransfer":"Передать на хост…","btnUpload":"Загрузить","checkUpdate":"Проверить обновления","checking":"Проверка…","clusterCommand":"Команда","clusterCount":"Настроено серверов: {count}","clusterFail":"ошибка","clusterOk":"успешно","clusterRun":"Выполнить","clusterRunning":"Выполняется...","clusterTitle":"Запуск на группе","containerImage":"Образ","containerName":"Контейнер","containerPorts":"Порты","containerStatus":"Статус","containersTitle":"Удалённый Docker / Podman Engine","currentVersion":"Установленная версия","delBtn":"Удалить","delConfirm":"Удалить профиль хоста «{name}»?","diagDesc":"Проверка доступности и измерение задержки активного сервера.","diagProbeBtn":"⚡ Быстрая диагностика","diagTitle":"📊 Диагностика активного подключения","diagnoseDisk":"Использование диска","diagnoseOom":"Проверка OOM Killer","diagnosePorts":"Открытые порты","diagnoseTitle":"Экспресс-диагностика","editBtn":"Изменить","envHideVal":"Скрыть","envKey":"Ключ","envSaveVar":"Сохранить","envShowVal":"Показать","envTitle":"Удалённые переменные окружения (.env)","envVal":"Значение","explorerTitle":"Удалённый файловый менеджер","fAgentPath":"Сокет агента","fAgentPathHint":"Оставьте пустым для использования SSH_AUTH_SOCK. В Windows используйте pageant.","fAuthAgent":"SSH Agent","fAuthAgentShort":"агент","fAuthKey":"SSH-ключ","fAuthKeyShort":"ключ","fAuthPass":"Пароль","fAuthPassShort":"пароль","fAuthType":"Способ авторизации","fDescription":"Описание","fEnvironment":"Окружение","fHost":"Хост / IP-адрес","fJumpHost":"Jump-хост (бастион)","fJumpHostNone":"Прямое подключение (без jump-хоста)","fJumpHosts":"Jump-хосты","fJumpHostsHint":"ID профилей по порядку через запятую. Первый хост — входной бастион.","fKeyPath":"Путь к ключу","fKeyPathHint":"Путь к ~/.ssh/id_rsa или /home/user/.ssh/id_ed25519","fLocalMirror":"Локальная папка зеркала","fLocalMirrorHint":"Локальная директория для зеркалирования файлов при быстрой трёхсторонней синхронизации","fLocation":"Локация","fName":"Название подключения","fPassphrase":"Кодовая фраза ключа (если есть)","fPassword":"Пароль учетной записи","fPort":"Порт","fProxyCommand":"Proxy-команда","fProxyCommandHint":"OpenSSH ProxyCommand. Заполнители: %h хост, %p порт, %r пользователь, %n имя профиля.","fRemotePath":"Удалённая папка проекта","fRemotePathHint":"(Не обязательно сейчас — можно выбрать позже при работе)","fTags":"Теги","fTerminalFont":"Шрифт терминала","fTerminalFontHint":"Семейство шрифтов для терминала. Оставьте пустым для моноширинного стека по умолчанию.","fUser":"Имя пользователя","filePreviewTitle":"Просмотр файла: {path}","fileSaved":"Файл успешно сохранён.","fileSize":"{size} КБ","formTitleEdit":"Редактирование профиля хоста","formTitleNew":"Добавление нового SSH хоста","groupEnvironment":"По окружению","groupFlat":"Сплошной список","groupOffline":"не в сети: {err}","groupOnline":"в сети, {latency} мс","groupTag":"По тегам","groupUngrouped":"Без группы","healthCpu":"ЦП: {val}%","healthDisk":"Диск: {val}%","healthMem":"ОЗУ: {val}% ({used}/{total} МБ)","healthUptime":"Аптайм: {val}","hostCol":"Хост / Пользователь","importAdded":"Импортировано серверов: {count}.","latestVersion":"Последняя на npm","nameCol":"Имя","navLabel":"Удаленные серверы","noContainers":"Активных контейнеров не найдено или демон Docker не запущен.","noHosts":"Удалённые хосты ещё не настроены. Нажмите «+ Добавить хост».","openLocalLink":"🌐 Открыть локальный порт","pathCol":"Удалённый путь","pinBtn":"Закрепить активный","pinned":"Активный хост","portCol":"Порт","profilesDesc":"Настройка удалённых хостов для выполнения команд, SFTP доступа и синхронизации.","profilesTitle":"🖥️ Профили SSH серверов","pullBtn":"⬇️ Скачать с сервера (Pull)","pulling":"Получение файлов…","pushBtn":"⬆️ Загрузить на сервер (Push)","pushing":"Отправка файлов…","restartRequired":"Обновление установлено. Перезапустите DSH для применения.","saving":"Сохранение…","setActiveBtn":"Сделать активным","skipDuplicate":"уже импортировано","skipMatch":"Блоки Match не импортируются","skipMissing":"включённый файл не найден","skipWildcard":"шаблон не является отдельным хостом","status":"Готово","statusEmpty":"Нет активного хоста","statusError":"Статус недоступен","statusLoading":"Проверка соединения","statusReady":"Хост готов","subtitle":"Управление подключениями по SSH, синхронизацией SFTP и туннелями","syncConflict":"Предупреждение о конфликте: изменено с обеих сторон файлов — {count}.","syncDesc":"Ручная синхронизация файлов с удалённым сервером и статус проброса портов.","syncErr":"Ошибка синхронизации: {err}","syncFailed":"Ошибка синхронизации: ","syncOk":"Синхронизация завершена! Обновлено файлов: {count}.","syncSuccess":"Синхронизация успешно выполнена","syncTitle":"🔄 Синхронизация зеркала и туннели","tLocalPort":"Локальный порт","tNoTunnels":"Нет активных туннелей проброса портов.","tOpenBtn":"+ Открыть туннель","tOpenErr":"Не удалось открыть туннель: {err}","tOpenOk":"Туннель открыт: 127.0.0.1:{localPort} -> Удалённый:{remotePort}","tOpening":"Открытие…","tRemotePort":"Удалённый порт","tStopBtn":"Остановить","tabCluster":"Кластер","tabContainers":"🐳 Контейнеры","tabEnv":"🗃️ Менеджер .env","tabExplorer":"📂 Проводник файлов","tabFiles":"Файлы","tabProfiles":"🖥️ Профили хостов","tabSync":"🔄 Станция синхронизации","tabTerminal":"⚡ Веб-терминал","tabTunnels":"🔗 Порт-туннели","tarSyncFast":"🚀 Быстрый поток архива (Tarball Stream)","termClear":"Очистить","termConnected":"Подключено к PTY-сессии {id} на {host}","termDisconnected":"Терминал отключён.","termPlaceholder":"Введите команду и нажмите Enter (напр. htop, ls, docker ps)…","termStart":"Подключить терминал","termStop":"Отключить","testFail":"❌ Ошибка подключения: {err}","testGroup":"Тестовая группа","testOk":"✅ Соединение установлено! Задержка: {latency} мс | ОС: {os}","testing":"Проверка соединения…","title":"Удалённое рабочее пространство","transferBtnExec":"Передать напрямую","transferCancel":"Отмена","transferDestHost":"Целевой сервер","transferDestPath":"Целевой путь к файлу","transferFailed":"Сбой передачи: {name}","transferProgress":"{name}: {percent}%","transferSuccess":"Успешно передано {bytes} байт на {host}:{path}","transferTitle":"Прямая передача сервер-сервер","tunnelTraffic":"Вход: {rx} | Выход: {tx}","tunnelUptime":"Работает: {sec} с","tunnelsColActions":"Действия","tunnelsColId":"ID туннеля","tunnelsColRoute":"Маршрут проброса","tunnelsDesc":"Проброс удалённых портов (базы данных, веб-серверы, API) на локальный компьютер через зашифрованные SSH-туннели.","tunnelsTitle":"🔀 Проброс портов SSH (туннели)","upToDate":"Актуальная версия","updateAvailable":"Доступно обновление","updateFailed":"Сбой обновления","updateNow":"Обновить сейчас","updaterTitle":"Обновление плагина","updating":"Обновление…","vaultProtectedBadge":"🛡️ Защищено .env Vault","versionUnknown":"Версия неизвестна","wsBack":"Назад","wsCluster":"Кластер","wsDocker":"Контейнеры","wsFiles":"Файлы","wsHosts":"Хосты","wsTerminal":"Терминал","wsTunnels":"Туннели"}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"@michengai/dsh-agency-agents":{"btn.refresh":"Обновить","button.title":"Вызов эксперта","custom.avatar":"Аватар","custom.base":"Встроенный","custom.cancel":"Отмена","custom.conflict":"Параметры были изменены в другом окне. Ваш черновик сохранён в этой форме. Загрузите актуальное содержимое и сравните перед сохранением.","custom.continueAsNew":"Продолжить как нового эксперта","custom.copy":"Создать пользовательскую копию","custom.delete":"Удалить эксперта","custom.deleteHint":"Данные эксперта будут безвозвратно удалены. История сообщений сохранится.","custom.deleteTitle":"Удалить эксперта","custom.deleted":"Эксперт безвозвратно удалён.","custom.description":"Краткое описание","custom.descriptionPlaceholder":"В чём специализируется этот эксперт?","custom.discard":"Отменить изменения","custom.discardTitle":"Сбросить несохранённые изменения?","custom.division":"Категория","custom.edit":"Редактировать эксперта","custom.emptyHint":"Превратите свой опыт и методики в эксперта, которого можно вызывать по имени.","custom.emptyTitle":"Создайте первого эксперта","custom.enableHint":"После включения эксперт доступен через @. Перезагрузка не требуется.","custom.intro":"Определите обязанности и подход к работе, чтобы эксперт действовал по вашим правилам.","custom.invalid":"Проверьте обязательные поля и их длину.","custom.keep":"Продолжить редактирование","custom.keepDraft":"Оставить мой черновик","custom.latestContent":"Текущее сохранённое содержимое","custom.name":"Имя эксперта","custom.nameConflict":"Конфликт имён","custom.nameConflictHint":"Эксперты с конфликтующими именами не могут быть вызваны. Переименуйте или удалите эксперта.","custom.namePlaceholder":"Например: Консультант по аренде недвижимости","custom.new":"Новый эксперт","custom.prompt":"Промпт эксперта","custom.promptPlaceholder":"Опишите обязанности, методы анализа и формат ответа…","custom.required":"Обязательно","custom.reviewDeleted":"Этот эксперт был удалён. Оставьте черновик и сохраните как нового эксперта.","custom.reviewHint":"Продолжите редактирование после выбора, затем нажмите Сохранить.","custom.reviewLatest":"Загрузить актуальные данные","custom.reviewNew":"Каталог обновлён. Черновик нового эксперта сохранён без изменений.","custom.save":"Сохранить","custom.saveChanges":"Сохранить изменения","custom.saveEnable":"Сохранить и включить","custom.saved":"Сохранено. Активные эксперты доступны через @ и кнопку экспертов.","custom.saving":"Сохранение…","custom.source":"Пользовательский","custom.template":"Использовать шаблон","custom.templateText":"Вы — профильный эксперт-консультант.\n\nОбязанности\nОпределите вашу область и решаемые задачи.\n\nПодход\nСначала вникните в контекст и цели. Задавайте вопросы при нехватке данных.\n\nРезультат\nПредоставляйте выводы, факты и конкретные шаги. Не выдумывайте факты.","custom.unavailable":"Эксперт в черновике отключён, удалён или конфликтует по имени.","custom.useLatest":"Использовать актуальное содержимое","discovery.conflict":"Конфликт имён","discovery.empty":"Нет активных экспертов. Введите ключевое слово для поиска.","discovery.enableSelect":"Включить и выбрать","discovery.enabledInsertFailed":"Эксперт включён, но не удалось вставить ссылку. Попробуйте выбрать снова.","discovery.exampleFailed":"Эксперт выбран, но пример задачи не вставился. Введите задачу вручную.","discovery.noResults":"Нет подходящих экспертов. Попробуйте другие ключевые слова.","discovery.refine":"Показано 8 наиболее подходящих экспертов. Уточните запрос ключевыми словами.","discovery.search":"Поиск по имени, сфере или ключевым словам","discovery.working":"Обработка…","division.academic":"Академические","division.company":"Руководство компанией","division.design":"Дизайн","division.engineering":"Инженерия и разработка","division.finance":"Финансы","division.game-development":"Разработка игр","division.gis":"Геоинформационные системы","division.healthcare":"Здравоохранение","division.hr":"Управление персоналом","division.legal":"Юриспруденция","division.marketing":"Маркетинг","division.paid-media":"Платный трафик и реклама","division.product":"Продуктовый менеджмент","division.project-management":"Управление проектами","division.research":"Исследования","division.sales":"Продажи","division.security":"Информационная безопасность","division.spatial-computing":"Пространственные вычисления","division.specialized":"Специализированные","division.supply-chain":"Цепочки поставок","division.support":"Клиентская поддержка","division.testing":"Тестирование","error.conflict":"Параметры были изменены в другом окне.","error.conflict.refreshFailed":"Параметры были изменены в другом окне, но обновление не удалось.","error.conflict.refreshed":"Параметры были обновлены из актуального состояния.","error.insertFailed":"Не удалось вставить ссылку на эксперта. Попробуйте снова.","error.personaSourceUnavailable":"Сервис подсказок экспертов ещё не готов, повторите попытку позже.","error.promptCopy":"Не удалось скопировать промпт. Проверьте права на буфер обмена.","settings.copyPrompt":"Копировать","settings.copyPromptTitle":"Скопировать промпт","settings.copySuccess":"Скопировано","settings.disabled":"Отключено","settings.empty":"Нет подходящих экспертов. Попробуйте другое слово или переключитесь на {all}.","settings.empty.reset":"Сбросить фильтры","settings.enabled":"Включено","settings.feedback":"Обратная связь","settings.filter.allCategories":"Все категории","settings.filter.allSources":"Все источники","settings.filter.allStatuses":"Все статусы","settings.filter.category":"Категория","settings.filter.source":"Источник","settings.filter.status":"Статус","settings.loading":"Загрузка экспертов…","settings.nav":"Эксперты","settings.promptClose":"Закрыть","settings.promptTitle":"Промпт для {name}","settings.search":"Поиск","settings.search.clear":"Очистить поиск","settings.search.placeholder":"Поиск экспертов, обязанностей или сфер","settings.title":"Эксперты","settings.viewProject":"GitHub","settings.viewPrompt":"Просмотр","settings.viewPromptTitle":"Просмотреть промпт","summary.enabledPrefix":"Активно","summary.total.one":"эксперт","summary.total.other":"экспертов"},"agency":{"btn.refresh":"Обновить","button.title":"Вызов эксперта","custom.avatar":"Аватар","custom.base":"Встроенный","custom.cancel":"Отмена","custom.conflict":"Параметры были изменены в другом окне. Ваш черновик сохранён в этой форме. Загрузите актуальное содержимое и сравните перед сохранением.","custom.continueAsNew":"Продолжить как нового эксперта","custom.copy":"Создать пользовательскую копию","custom.delete":"Удалить эксперта","custom.deleteHint":"Данные эксперта будут безвозвратно удалены. История сообщений сохранится.","custom.deleteTitle":"Удалить эксперта","custom.deleted":"Эксперт безвозвратно удалён.","custom.description":"Краткое описание","custom.descriptionPlaceholder":"В чём специализируется этот эксперт?","custom.discard":"Отменить изменения","custom.discardTitle":"Сбросить несохранённые изменения?","custom.division":"Категория","custom.edit":"Редактировать эксперта","custom.emptyHint":"Превратите свой опыт и методики в эксперта, которого можно вызывать по имени.","custom.emptyTitle":"Создайте первого эксперта","custom.enableHint":"После включения эксперт доступен через @. Перезагрузка не требуется.","custom.intro":"Определите обязанности и подход к работе, чтобы эксперт действовал по вашим правилам.","custom.invalid":"Проверьте обязательные поля и их длину.","custom.keep":"Продолжить редактирование","custom.keepDraft":"Оставить мой черновик","custom.latestContent":"Текущее сохранённое содержимое","custom.name":"Имя эксперта","custom.nameConflict":"Конфликт имён","custom.nameConflictHint":"Эксперты с конфликтующими именами не могут быть вызваны. Переименуйте или удалите эксперта.","custom.namePlaceholder":"Например: Консультант по аренде недвижимости","custom.new":"Новый эксперт","custom.prompt":"Промпт эксперта","custom.promptPlaceholder":"Опишите обязанности, методы анализа и формат ответа…","custom.required":"Обязательно","custom.reviewDeleted":"Этот эксперт был удалён. Оставьте черновик и сохраните как нового эксперта.","custom.reviewHint":"Продолжите редактирование после выбора, затем нажмите Сохранить.","custom.reviewLatest":"Загрузить актуальные данные","custom.reviewNew":"Каталог обновлён. Черновик нового эксперта сохранён без изменений.","custom.save":"Сохранить","custom.saveChanges":"Сохранить изменения","custom.saveEnable":"Сохранить и включить","custom.saved":"Сохранено. Активные эксперты доступны через @ и кнопку экспертов.","custom.saving":"Сохранение…","custom.source":"Пользовательский","custom.template":"Использовать шаблон","custom.templateText":"Вы — профильный эксперт-консультант.\n\nОбязанности\nОпределите вашу область и решаемые задачи.\n\nПодход\nСначала вникните в контекст и цели. Задавайте вопросы при нехватке данных.\n\nРезультат\nПредоставляйте выводы, факты и конкретные шаги. Не выдумывайте факты.","custom.unavailable":"Эксперт в черновике отключён, удалён или конфликтует по имени.","custom.useLatest":"Использовать актуальное содержимое","discovery.conflict":"Конфликт имён","discovery.empty":"Нет активных экспертов. Введите ключевое слово для поиска.","discovery.enableSelect":"Включить и выбрать","discovery.enabledInsertFailed":"Эксперт включён, но не удалось вставить ссылку. Попробуйте выбрать снова.","discovery.exampleFailed":"Эксперт выбран, но пример задачи не вставился. Введите задачу вручную.","discovery.noResults":"Нет подходящих экспертов. Попробуйте другие ключевые слова.","discovery.refine":"Показано 8 наиболее подходящих экспертов. Уточните запрос ключевыми словами.","discovery.search":"Поиск по имени, сфере или ключевым словам","discovery.working":"Обработка…","division.academic":"Академические","division.company":"Руководство компанией","division.design":"Дизайн","division.engineering":"Инженерия и разработка","division.finance":"Финансы","division.game-development":"Разработка игр","division.gis":"Геоинформационные системы","division.healthcare":"Здравоохранение","division.hr":"Управление персоналом","division.legal":"Юриспруденция","division.marketing":"Маркетинг","division.paid-media":"Платный трафик и реклама","division.product":"Продуктовый менеджмент","division.project-management":"Управление проектами","division.research":"Исследования","division.sales":"Продажи","division.security":"Информационная безопасность","division.spatial-computing":"Пространственные вычисления","division.specialized":"Специализированные","division.supply-chain":"Цепочки поставок","division.support":"Клиентская поддержка","division.testing":"Тестирование","error.conflict":"Параметры были изменены в другом окне.","error.conflict.refreshFailed":"Параметры были изменены в другом окне, но обновление не удалось.","error.conflict.refreshed":"Параметры были обновлены из актуального состояния.","error.insertFailed":"Не удалось вставить ссылку на эксперта. Попробуйте снова.","error.personaSourceUnavailable":"Сервис подсказок экспертов ещё не готов, повторите попытку позже.","error.promptCopy":"Не удалось скопировать промпт. Проверьте права на буфер обмена.","settings.copyPrompt":"Копировать","settings.copyPromptTitle":"Скопировать промпт","settings.copySuccess":"Скопировано","settings.disabled":"Отключено","settings.empty":"Нет подходящих экспертов. Попробуйте другое слово или переключитесь на {all}.","settings.empty.reset":"Сбросить фильтры","settings.enabled":"Включено","settings.feedback":"Обратная связь","settings.filter.allCategories":"Все категории","settings.filter.allSources":"Все источники","settings.filter.allStatuses":"Все статусы","settings.filter.category":"Категория","settings.filter.source":"Источник","settings.filter.status":"Статус","settings.loading":"Загрузка экспертов…","settings.nav":"Эксперты","settings.promptClose":"Закрыть","settings.promptTitle":"Промпт для {name}","settings.search":"Поиск","settings.search.clear":"Очистить поиск","settings.search.placeholder":"Поиск экспертов, обязанностей или сфер","settings.title":"Эксперты","settings.viewProject":"GitHub","settings.viewPrompt":"Просмотр","settings.viewPromptTitle":"Просмотреть промпт","summary.enabledPrefix":"Активно","summary.total.one":"эксперт","summary.total.other":"экспертов"}}
|
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.15",
|
|
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",
|