@goodandready/dsh-russian-lang 0.3.9 → 0.3.10

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 CHANGED
@@ -15,8 +15,7 @@ if (typeof style === 'string' && datePresetFormats[style]) {return datePresetFor
15
15
  }
16
16
  if (typeof style === 'object' && style !== null) {try {return new Intl.DateTimeFormat('ru-RU', style).format(d)
17
17
  } catch (_) {return datePresetFormats.short.format(d)
18
- }
19
- }
18
+ }}
20
19
  return datePresetFormats.short.format(d)
21
20
  }
22
21
  const formatCompactNumber = (val) => {if (val === null || val === undefined || val === '') return ''
@@ -35,8 +34,7 @@ const currencyFormats = new Map()
35
34
  const getCurrencyFormat = (cur) => {const c = (cur || 'RUB').toUpperCase()
36
35
  if (!currencyFormats.has(c)) {try {currencyFormats.set(c, new Intl.NumberFormat('ru-RU', { style: 'currency', currency: c }))
37
36
  } catch (e) {currencyFormats.set(c, numberFormat)
38
- }
39
- }
37
+ }}
40
38
  return currencyFormats.get(c)
41
39
  }
42
40
  const formatNumber = (val) => {if (val === null || val === undefined || val === '') return ''
@@ -62,8 +60,7 @@ if (absSec < 2592000) return relativeTimeFormat.format(Math.round(diffSec / 8640
62
60
  if (absSec < 31536000) return relativeTimeFormat.format(Math.round(diffSec / 2592000), 'month')
63
61
  return relativeTimeFormat.format(Math.round(diffSec / 31536000), 'year')
64
62
  }
65
- const INFLECT_CUSTOM = {'пользователь': { gen: 'пользователя', dat: 'пользователю', acc: 'пользователя', ins: 'пользователем', pre: 'пользователе' },'агент': { gen: 'агента', dat: 'агенту', acc: 'агента', ins: 'агентом', pre: 'агенте' },'субагент': { gen: 'субагента', dat: 'субагенту', acc: 'субагента', ins: 'субагентом', pre: 'субагенте' },'модель': { gen: 'модели', dat: 'модели', acc: 'модель', ins: 'моделью', pre: 'модели' },'промпт': { gen: 'промпта', dat: 'промпту', acc: 'промпт', ins: 'промптом', pre: 'промпте' },'инструмент': { gen: 'инструмента', dat: 'инструменту', acc: 'инструмент', ins: 'инструментом', pre: 'инструменте' },'сессия': { gen: 'сессии', dat: 'сессии', acc: 'сессию', ins: 'сессией', pre: 'сессии' },'ветка': { gen: 'ветки', dat: 'ветке', acc: 'ветку', ins: 'веткой', pre: 'ветке' },'файл': { gen: 'файла', dat: 'файлу', acc: 'файл', ins: 'файлом', pre: 'файле' },'папка': { gen: 'папки', dat: 'папке', acc: 'папку', ins: 'папкой', pre: 'папке' }
66
- }
63
+ const INFLECT_CUSTOM = {'пользователь': { gen: 'пользователя', dat: 'пользователю', acc: 'пользователя', ins: 'пользователем', pre: 'пользователе' },'агент': { gen: 'агента', dat: 'агенту', acc: 'агента', ins: 'агентом', pre: 'агенте' },'субагент': { gen: 'субагента', dat: 'субагенту', acc: 'субагента', ins: 'субагентом', pre: 'субагенте' },'модель': { gen: 'модели', dat: 'модели', acc: 'модель', ins: 'моделью', pre: 'модели' },'промпт': { gen: 'промпта', dat: 'промпту', acc: 'промпт', ins: 'промптом', pre: 'промпте' },'инструмент': { gen: 'инструмента', dat: 'инструменту', acc: 'инструмент', ins: 'инструментом', pre: 'инструменте' },'сессия': { gen: 'сессии', dat: 'сессии', acc: 'сессию', ins: 'сессией', pre: 'сессии' },'ветка': { gen: 'ветки', dat: 'ветке', acc: 'ветку', ins: 'веткой', pre: 'ветке' },'файл': { gen: 'файла', dat: 'файлу', acc: 'файл', ins: 'файлом', pre: 'файле' },'папка': { gen: 'папки', dat: 'папке', acc: 'папку', ins: 'папкой', pre: 'папке' }}
67
64
  const keepCase = (src, out) => (src[0] === src[0].toUpperCase() ? out[0].toUpperCase() + out.slice(1) : out)
68
65
  const inflectWord = (word, cName) => {if (!word || typeof word !== 'string') return word
69
66
  const lower = word.toLowerCase()
@@ -72,10 +69,8 @@ if (INFLECT_CUSTOM[lower] && INFLECT_CUSTOM[lower][cName]) {return keepCase(word
72
69
  if (/[a-zA-Z0-9_-]/.test(word) || /^[А-ЯЁ]{2,}$/.test(word)) return word
73
70
  if (/[оеиую]$/i.test(word) && !/(ко|ло|но|то|во|ро|до|по|со|мо|го)$/i.test(word)) return word
74
71
  const w = lower
75
- const endings = [
76
- ['ия', 2, (s) => ({ gen: s + 'ии', dat: s + 'ии', acc: s + 'ию', ins: s + 'ией', pre: s + 'ии' })],['а', 1, (s) => {const genEnd = /[гкхжшчщ]/.test(s.slice(-1)) ? 'и' : 'ы'
77
- return { gen: s + genEnd, dat: s + 'е', acc: s + 'у', ins: s + 'ой', pre: s + 'е' }
78
- }],['я', 1, (s) => ({ gen: s + 'и', dat: s + 'е', acc: s + 'ю', ins: s + 'ей', pre: s + 'е' })],['ь', 1, (s) => ({ gen: s + 'и', dat: s + 'и', acc: s + 'ь', ins: s + 'ью', pre: s + 'и' })],['й', 1, (s) => ({ gen: s + 'я', dat: s + 'ю', acc: s + 'я', ins: s + 'ем', pre: s + 'е' })]
72
+ const endings = [['ия', 2, (s) => ({ gen: s + 'ии', dat: s + 'ии', acc: s + 'ию', ins: s + 'ией', pre: s + 'ии' })],['а', 1, (s) => {const genEnd = /[гкхжшчщ]/.test(s.slice(-1)) ? 'и' : 'ы'
73
+ return { gen: s + genEnd, dat: s + 'е', acc: s + 'у', ins: s + 'ой', pre: s + 'е' }}],['я', 1, (s) => ({ gen: s + 'и', dat: s + 'е', acc: s + 'ю', ins: s + 'ей', pre: s + 'е' })],['ь', 1, (s) => ({ gen: s + 'и', dat: s + 'и', acc: s + 'ь', ins: s + 'ью', pre: s + 'и' })],['й', 1, (s) => ({ gen: s + 'я', dat: s + 'ю', acc: s + 'я', ins: s + 'ем', pre: s + 'е' })]
79
74
  ]
80
75
  for (const [suffix, cut, build] of endings) {if (w.endsWith(suffix)) return keepCase(word, build(w.slice(0, -cut))[cName] || w)
81
76
  }
@@ -156,8 +151,7 @@ if (matched === qStems.length && qStems.length > 0) return 80
156
151
  if (matched > 0) return 50
157
152
  return 0
158
153
  }
159
- const ERROR_MAP = {ENOENT: { title: 'Файл не найден', message: 'Указанный файл или директория не существуют', hint: 'Проверьте правильность указанного пути к файлу.' },EACCES: { title: 'Отказано в доступе', message: 'Недостаточно прав для чтения или записи', hint: 'Проверьте права доступа к файлу или директории (chmod/chown).' },EPERM: { title: 'Операция запрещена', message: 'Недостаточно системных привилегий', hint: 'Запустите процесс с соответствующими правами.' },ECONNREFUSED: { title: 'Соединение отклонено', message: 'Целевой сервер или сервис не отвечает', hint: 'Убедитесь, что локальный или удаленный сервис запущен и слушает порт.' },ECONNRESET: { title: 'Сброс соединения', message: 'Соединение было принудительно разорвано удаленной стороной', hint: 'Проверьте стабильность сети и работу целевого сервера.' },ETIMEDOUT: { title: 'Таймаут соединения', message: 'Превышено время ожидания ответа', hint: 'Проверьте стабильность сети или увеличьте лимит ожидания.' },ENOTFOUND: { title: 'Хост не найден', message: 'Не удалось разрешить сетевой адрес', hint: 'Проверьте правильность URL или настройки DNS.' },EADDRINUSE: { title: 'Порт уже занят', message: 'Сетевой порт используется другим процессом', hint: 'Остановите конфликтующий процесс или выберите другой порт.' },ENOSPC: { title: 'Недостаточно места на диске', message: 'На устройстве закончилось свободное пространство', hint: 'Освободите место на диске и повторите операцию.' },400: { title: 'Некорректный запрос (Bad Request)', message: 'Параметры запроса не соответствуют ожидаемому формату', hint: 'Проверьте синтаксис команды или переданные аргументы.' },401: { title: 'Требуется авторизация', message: 'API-ключ или токен отсутствуют или недействительны', hint: 'Проверьте настройки учетных данных и актуальность токена.' },403: { title: 'Доступ запрещен', message: 'Недостаточно прав для выполнения операции', hint: 'Проверьте область действия токена или права роли.' },404: { title: 'Ресурс не найден', message: 'Запрошенный адрес или объект не существует', hint: 'Проверьте правильность пути или идентификатора ресурса.' },429: { title: 'Превышен лимит запросов', message: 'Слишком много запросов (Rate Limit)', hint: 'Подождите несколько минут перед повторным запросом.' },500: { title: 'Внутренняя ошибка сервера', message: 'На стороне сервера произошел сбой', hint: 'Попробуйте повторить запрос позже или проверьте серверные логи.' },502: { title: 'Ошибочный шлюз (Bad Gateway)', message: 'Промежуточный прокси не получил корректный ответ', hint: 'Проверьте работу нижележащей службы или upstream-сервера.' },503: { title: 'Служба временно недоступна', message: 'Сервер перегружен или находится на обслуживании', hint: 'Попробуйте повторить операцию через некоторое время.' },504: { title: 'Шлюз не отвечает (Gateway Timeout)', message: 'Превышено время ожидания ответа от upstream-сервера', hint: 'Попробуйте повторить запрос позже.' },FETCH_FAILED: { title: 'Сетевой запрос не удался', message: 'Не удалось связаться с сервером (Failed to fetch)', hint: 'Проверьте сетевое подключение и доступность адреса.' },CORS_ERROR: { title: 'Ошибка CORS', message: 'Запрос заблокирован политикой безопасности браузера', hint: 'Проверьте заголовки разрешённых источников на сервере.' },WS_CLOSED: { title: 'Разрыв WebSocket', message: 'Связь с сервером по WebSocket прервана', hint: 'Проверьте работу сервера и сетевой статус.' },QUOTA_EXCEEDED: { title: 'Исчерпана квота API', message: 'Превышен лимит запросов или баланс провайдера', hint: 'Проверьте баланс API-ключа в кабинете провайдера.' },CONTEXT_OVERFLOW: { title: 'Превышен контекст', message: 'Объём диалога превышает размер контекстного окна', hint: 'Очистите или сожмите историю сообщений сессии.' },MODEL_NOT_FOUND: { title: 'Модель не найдена', message: 'Указанная модель отсутствует или недоступна', hint: 'Проверьте идентификатор модели в настройках.' }
160
- }
154
+ const ERROR_MAP = {ENOENT: { title: 'Файл не найден', message: 'Указанный файл или директория не существуют', hint: 'Проверьте правильность указанного пути к файлу.' },EACCES: { title: 'Отказано в доступе', message: 'Недостаточно прав для чтения или записи', hint: 'Проверьте права доступа к файлу или директории (chmod/chown).' },EPERM: { title: 'Операция запрещена', message: 'Недостаточно системных привилегий', hint: 'Запустите процесс с соответствующими правами.' },ECONNREFUSED: { title: 'Соединение отклонено', message: 'Целевой сервер или сервис не отвечает', hint: 'Убедитесь, что локальный или удаленный сервис запущен и слушает порт.' },ECONNRESET: { title: 'Сброс соединения', message: 'Соединение было принудительно разорвано удаленной стороной', hint: 'Проверьте стабильность сети и работу целевого сервера.' },ETIMEDOUT: { title: 'Таймаут соединения', message: 'Превышено время ожидания ответа', hint: 'Проверьте стабильность сети или увеличьте лимит ожидания.' },ENOTFOUND: { title: 'Хост не найден', message: 'Не удалось разрешить сетевой адрес', hint: 'Проверьте правильность URL или настройки DNS.' },EADDRINUSE: { title: 'Порт уже занят', message: 'Сетевой порт используется другим процессом', hint: 'Остановите конфликтующий процесс или выберите другой порт.' },ENOSPC: { title: 'Недостаточно места на диске', message: 'На устройстве закончилось свободное пространство', hint: 'Освободите место на диске и повторите операцию.' },400: { title: 'Некорректный запрос (Bad Request)', message: 'Параметры запроса не соответствуют ожидаемому формату', hint: 'Проверьте синтаксис команды или переданные аргументы.' },401: { title: 'Требуется авторизация', message: 'API-ключ или токен отсутствуют или недействительны', hint: 'Проверьте настройки учетных данных и актуальность токена.' },403: { title: 'Доступ запрещен', message: 'Недостаточно прав для выполнения операции', hint: 'Проверьте область действия токена или права роли.' },404: { title: 'Ресурс не найден', message: 'Запрошенный адрес или объект не существует', hint: 'Проверьте правильность пути или идентификатора ресурса.' },429: { title: 'Превышен лимит запросов', message: 'Слишком много запросов (Rate Limit)', hint: 'Подождите несколько минут перед повторным запросом.' },500: { title: 'Внутренняя ошибка сервера', message: 'На стороне сервера произошел сбой', hint: 'Попробуйте повторить запрос позже или проверьте серверные логи.' },502: { title: 'Ошибочный шлюз (Bad Gateway)', message: 'Промежуточный прокси не получил корректный ответ', hint: 'Проверьте работу нижележащей службы или upstream-сервера.' },503: { title: 'Служба временно недоступна', message: 'Сервер перегружен или находится на обслуживании', hint: 'Попробуйте повторить операцию через некоторое время.' },504: { title: 'Шлюз не отвечает (Gateway Timeout)', message: 'Превышено время ожидания ответа от upstream-сервера', hint: 'Попробуйте повторить запрос позже.' },FETCH_FAILED: { title: 'Сетевой запрос не удался', message: 'Не удалось связаться с сервером (Failed to fetch)', hint: 'Проверьте сетевое подключение и доступность адреса.' },CORS_ERROR: { title: 'Ошибка CORS', message: 'Запрос заблокирован политикой безопасности браузера', hint: 'Проверьте заголовки разрешённых источников на сервере.' },WS_CLOSED: { title: 'Разрыв WebSocket', message: 'Связь с сервером по WebSocket прервана', hint: 'Проверьте работу сервера и сетевой статус.' },QUOTA_EXCEEDED: { title: 'Исчерпана квота API', message: 'Превышен лимит запросов или баланс провайдера', hint: 'Проверьте баланс API-ключа в кабинете провайдера.' },CONTEXT_OVERFLOW: { title: 'Превышен контекст', message: 'Объём диалога превышает размер контекстного окна', hint: 'Очистите или сожмите историю сообщений сессии.' },MODEL_NOT_FOUND: { title: 'Модель не найдена', message: 'Указанная модель отсутствует или недоступна', hint: 'Проверьте идентификатор модели в настройках.' }}
161
155
  const fromMap = (key, rawMsg) => ({code: String(key), title: ERROR_MAP[key].title, message: ERROR_MAP[key].message, hint: ERROR_MAP[key].hint, raw: rawMsg
162
156
  })
163
157
  const humanizeError = (err) => {if (!err) return null
@@ -174,19 +168,16 @@ if (/websocket.*(?:closed|failed)/i.test(rawMsg)) return fromMap('WS_CLOSED', ra
174
168
  if (/insufficient_quota|quota\s*exceeded/i.test(rawMsg)) return fromMap('QUOTA_EXCEEDED', rawMsg)
175
169
  if (/context.*(?:exceeded|overflow|length)/i.test(rawMsg)) return fromMap('CONTEXT_OVERFLOW', rawMsg)
176
170
  if (/model.*not.*found|model.*does not exist/i.test(rawMsg)) return fromMap('MODEL_NOT_FOUND', rawMsg)
177
- return { code: 'UNKNOWN', title: 'Ошибка операции', message: rawMsg, hint: 'Проверьте параметры операции и логи.', raw: rawMsg }
178
- }
171
+ return { code: 'UNKNOWN', title: 'Ошибка операции', message: rawMsg, hint: 'Проверьте параметры операции и логи.', raw: rawMsg }}
179
172
  const formatErrorToast = (err) => {const h = humanizeError(err)
180
173
  if (!h) return null
181
174
  return {type: 'error',title: h.title,description: h.message + (h.hint ? ' ' + h.hint : '')
182
- }
183
- }
175
+ }}
184
176
  const makeIssueUrl = (opts = {}, version = '') => {const repo = 'GooDAnDReaDY/dsh-russian-lang'
185
177
  const title = opts.title || (opts.plugin
186
178
  ? `[Перевод] Запрос локализации для плагина ${opts.plugin}`
187
179
  : '[Ошибка перевода] Неточный перевод фразы')
188
- const bodyLines = [
189
- '### Описание проблемы',opts.description || (opts.plugin
180
+ const bodyLines = ['### Описание проблемы',opts.description || (opts.plugin
190
181
  ? `Просьба добавить русскую локализацию для плагина \`${opts.plugin}\`.`
191
182
  : 'Обнаружена неточность в переводе интерфейса.'),'','### Технический контекст',opts.ns ? `- **Namespace**: \`${opts.ns}\`` : null,opts.key ? `- **Ключ**: \`${opts.key}\`` : null,opts.en ? `- **Оригинал (EN)**: ${opts.en}` : null,opts.ru ? `- **Текущий перевод (RU)**: ${opts.ru}` : null,opts.plugin ? `- **Плагин**: \`${opts.plugin}\`` : null,version ? `- **Версия dsh-russian-lang**: \`${version}\`` : null,typeof navigator !== 'undefined' ? `- **User Agent**: \`${navigator.userAgent}\`` : null,'','### Предлагаемый вариант перевода',opts.proposal || '_Опишите ваш вариант перевода..._'
192
183
  ].filter(Boolean)
@@ -201,16 +192,12 @@ const count = Object.keys((dicts && dicts[ns]) || {}).length
201
192
  return count > 0 ? { status: 'full', count, label: `RU: ${count} строк` } : none
202
193
  }
203
194
  const typoQuotes = (text) => text
204
- .replace(/"([^"\n]{1,200})"/g, '«$1»')
205
- .replace(/[“„]([^“”\n]{1,200})[”"]/g, '«$1»')
206
- .replace(/[『「]([^』」\n]{1,200})[』」]/g, '«$1»')
195
+ .replace(/"([^"\n]{1,200})"/g, '«$1»').replace(/[“„]([^“”\n]{1,200})[”"]/g, '«$1»').replace(/[『「]([^』」\n]{1,200})[』」]/g, '«$1»')
207
196
  const typoDash = (text) => text
208
- .replace(/(^|[\s(\[«])--(?=\s|$)/g, '$1—')
209
- .replace(/(^|[\s(\[«])-(?=\s)/g, '$1—')
197
+ .replace(/(^|[\s(\[«])--(?=\s|$)/g, '$1—').replace(/(^|[\s(\[«])-(?=\s)/g, '$1—')
210
198
  const typoPunct = (text) => text.replace(/\s+([,.:;!?])(?=\s|$)/g, '$1')
211
199
  const TYPO_SHORT = new Set(['в', 'с', 'к', 'о', 'у', 'а', 'и', 'но', 'не', 'ни', 'на', 'по', 'до', 'из', 'за', 'от', 'об'])
212
- const typoNbsp = (text) => text.replace(/(^|[\s(\[«])([а-яё]{1,2})(\s+)/g, (match, lead, word) => (
213
- TYPO_SHORT.has(word) ? lead + word + ' ' : match
200
+ const typoNbsp = (text) => text.replace(/(^|[\s(\[«])([а-яё]{1,2})(\s+)/g, (match, lead, word) => (TYPO_SHORT.has(word) ? lead + word + ' ' : match
214
201
  ))
215
202
  const YO_EDGE_L = '(?<![а-яёА-ЯЁ])'
216
203
  const YO_EDGE_R = '(?![а-яёА-ЯЁ])'
@@ -224,8 +211,7 @@ const makeTypoYo = (pairs) => {const list = typeof pairs === 'string'
224
211
  const compiled = list.map((p) => [new RegExp(YO_EDGE_L + p[0] + YO_EDGE_R, 'gi'), p[1]])
225
212
  return (text) => {for (const [re, repl] of compiled) text = text.replace(re, (match) => yoCase(match, repl))
226
213
  return text
227
- }
228
- }
214
+ }}
229
215
  const LAYOUT_LAT_TO_CYR = { ...EN_RU_KEYS, '/': '.', '`': 'ё' }
230
216
  const LAYOUT_CYR_TO_LAT = (() => {const out = {}
231
217
  for (const k in LAYOUT_LAT_TO_CYR) out[LAYOUT_LAT_TO_CYR[k]] = k
@@ -242,16 +228,13 @@ return words.filter((w) => freq.has(w) || localDict.has(w)).length / words.lengt
242
228
  const candidate = (value, direction) => {if (direction === 'lat2cyr') {const converted = translit(value, LAYOUT_LAT_TO_CYR)
243
229
  if (!/[а-яё]{2}/.test(converted)) return null
244
230
  if (ruWordFraction(converted) < 0.7) return null
245
- return { converted }
246
- }
231
+ return { converted }}
247
232
  const converted = translit(value, LAYOUT_CYR_TO_LAT)
248
233
  return converted.startsWith('/') ? { converted } : null
249
234
  }
250
235
  const learnWords = (text) => {for (const w of text.toLowerCase().split(/[^а-яё]+/).filter(Boolean)) {if (w.length >= 3) localDict.add(w)
251
- }
252
- }
253
- return { ruWordFraction, candidate, learnWords, localDict }
254
- }
236
+ }}
237
+ return { ruWordFraction, candidate, learnWords, localDict }}
255
238
  const SHORT_WORDS_RE = /(^|[\s(«])([вВнНсСпПоОкКуУиИаА]|из|от|до|за|по|со|во|ко|об|на|под|над|при|про|без|для|не|ни|но)( )/g
256
239
  const formatInputLive = (text) => {if (!text || typeof text !== 'string') return text
257
240
  const parts = text.split(/(```[\s\S]*?```|`[^`\n]*`)/g)
@@ -280,11 +263,9 @@ if (RUSSIAN_SLASH_ALIASES[lowerCmd]) {return RUSSIAN_SLASH_ALIASES[lowerCmd] + r
280
263
  }
281
264
  return input
282
265
  }
283
- const PHONETIC_LAT_TO_CYR = [
284
- ['shch', 'щ'], ['yo', 'ё'], ['zh', 'ж'], ['ch', 'ч'], ['sh', 'ш'],['yu', 'ю'], ['ya', 'я'], ['ts', 'ц'],['a', 'а'], ['b', 'б'], ['v', 'в'], ['g', 'г'], ['d', 'д'], ['e', 'е'],['z', 'з'], ['i', 'и'], ['j', 'й'], ['k', 'к'], ['l', 'л'], ['m', 'м'],['n', 'н'], ['o', 'о'], ['p', 'п'], ['r', 'р'], ['s', 'с'], ['t', 'т'],['u', 'у'], ['f', 'ф'], ['h', 'х'], ['c', 'ц'], ['y', 'ы'], ['x', 'кс']
266
+ const PHONETIC_LAT_TO_CYR = [['shch', 'щ'], ['yo', 'ё'], ['zh', 'ж'], ['ch', 'ч'], ['sh', 'ш'],['yu', 'ю'], ['ya', 'я'], ['ts', 'ц'],['a', 'а'], ['b', 'б'], ['v', 'в'], ['g', 'г'], ['d', 'д'], ['e', 'е'],['z', 'з'], ['i', 'и'], ['j', 'й'], ['k', 'к'], ['l', 'л'], ['m', 'м'],['n', 'н'], ['o', 'о'], ['p', 'п'], ['r', 'р'], ['s', 'с'], ['t', 'т'],['u', 'у'], ['f', 'ф'], ['h', 'х'], ['c', 'ц'], ['y', 'ы'], ['x', 'кс']
285
267
  ]
286
- const PHONETIC_CYR_TO_LAT = [
287
- ['щ', 'shch'], ['ё', 'yo'], ['ж', 'zh'], ['ч', 'ch'], ['ш', 'sh'],['ю', 'yu'], ['я', 'ya'], ['ц', 'ts'],['а', 'a'], ['б', 'b'], ['в', 'v'], ['г', 'g'], ['д', 'd'], ['е', 'e'],['з', 'z'], ['и', 'i'], ['й', 'j'], ['к', 'k'], ['л', 'l'], ['м', 'm'],['н', 'n'], ['о', 'o'], ['п', 'p'], ['р', 'r'], ['с', 's'], ['т', 't'],['у', 'u'], ['ф', 'f'], ['х', 'h'], ['ы', 'y'], ['э', 'e'], ['ъ', ''], ['ь', '']
268
+ const PHONETIC_CYR_TO_LAT = [['щ', 'shch'], ['ё', 'yo'], ['ж', 'zh'], ['ч', 'ch'], ['ш', 'sh'],['ю', 'yu'], ['я', 'ya'], ['ц', 'ts'],['а', 'a'], ['б', 'b'], ['в', 'v'], ['г', 'g'], ['д', 'd'], ['е', 'e'],['з', 'z'], ['и', 'i'], ['й', 'j'], ['к', 'k'], ['л', 'l'], ['м', 'm'],['н', 'n'], ['о', 'o'], ['п', 'p'], ['р', 'r'], ['с', 's'], ['т', 't'],['у', 'u'], ['ф', 'f'], ['х', 'h'], ['ы', 'y'], ['э', 'e'], ['ъ', ''], ['ь', '']
288
269
  ]
289
270
  const phoneticTranslit = (text, direction = 'lat2cyr') => {if (!text || typeof text !== 'string') return text
290
271
  let res = text
@@ -298,8 +279,7 @@ res = res.replaceAll(fromTitle, toTitle)
298
279
  }
299
280
  res = res.replaceAll(from, to)
300
281
  if (from.length === 1) {res = res.replaceAll(fromUpper, toUpper)
301
- }
302
- }
282
+ }}
303
283
  return res
304
284
  }
305
285
  const detectInputLayout = (text) => {if (!text || typeof text !== 'string') return null
@@ -320,8 +300,7 @@ const SYSTEM_PROMPT_PRESETS = {technical_expert: {id: 'technical_expert',label:
320
300
  },code_reviewer: {id: 'code_reviewer',label: 'Код-ревьюер',desc: 'Поиск багов, безопасность и архитектурный аудит',text: 'Анализируй код и отвечай на русском языке. Ищи уязвимости, граничные случаи и нарушения контрактов, предлагая исправления.'
321
301
  },architect: {id: 'architect',label: 'Системный архитектор',desc: 'Контракты API/CLI, модульность и надёжность',text: 'Отвечай на русском языке как архитектор. Проектируй модульные системы, чёткие контракты API/CLI и надёжные решения без лишней сложности.'
322
302
  },tutor: {id: 'tutor',label: 'Наставник (ментор)',desc: 'Понятные объяснения сложных концепций и пошаговый разбор',text: 'Отвечай на русском языке как наставник. Объясняй сложные темы простыми словами, наглядными аналогиями и пошаговыми примерами.'
323
- }
324
- }
303
+ }}
325
304
  const exportSessionToMarkdown = (session, options = {}) => {if (!session) return ''
326
305
  const title = session.title || session.name || 'Диалог DSH'
327
306
  const date = session.createdAt ? new Date(session.createdAt) : new Date()
@@ -354,53 +333,31 @@ return md
354
333
  const findTranslationKey = (text, dicts = {}, overrides = {}, zhRu = {}, domEn = {}, options = {}) => {if (!text || typeof text !== 'string') return null
355
334
  const trimmed = text.trim()
356
335
  if (!trimmed) return null
357
- if (overrides && typeof overrides === 'object') {if (overrides[trimmed] !== undefined) {return { key: trimmed, value: overrides[trimmed], source: 'override' }
358
- }
359
- for (const [k, v] of Object.entries(overrides)) {if (v === text || (typeof v === 'string' && v.trim() === trimmed)) {return { key: k, value: v, source: 'override' }
360
- }
361
- }
362
- }
336
+ if (overrides && typeof overrides === 'object') {if (overrides[trimmed] !== undefined) {return { key: trimmed, value: overrides[trimmed], source: 'override' }}
337
+ for (const [k, v] of Object.entries(overrides)) {if (v === text || (typeof v === 'string' && v.trim() === trimmed)) {return { key: k, value: v, source: 'override' }}}}
363
338
  if (dicts && typeof dicts === 'object') {for (const [ns, map] of Object.entries(dicts)) {if (!map || typeof map !== 'object') continue
364
- if (map[trimmed] !== undefined) {return { ns, key: trimmed, value: map[trimmed], source: 'dictionary' }
365
- }
366
- for (const [k, v] of Object.entries(map)) {if (v === text || (typeof v === 'string' && v.trim() === trimmed)) {return { ns, key: k, value: v, source: 'dictionary' }
367
- }
368
- }
369
- }
370
- }
371
- if (zhRu && typeof zhRu === 'object') {for (const [zh, ru] of Object.entries(zhRu)) {if (ru === text || (typeof ru === 'string' && ru.trim() === trimmed)) {return { zh, key: zh, value: ru, source: 'dom_zh' }
372
- }
373
- }
374
- if (zhRu[trimmed] !== undefined) {return { zh: trimmed, key: trimmed, value: zhRu[trimmed], source: 'dom_zh_original' }
375
- }
376
- }
377
- if (domEn && typeof domEn === 'object') {for (const [en, ru] of Object.entries(domEn)) {if (ru === text || (typeof ru === 'string' && ru.trim() === trimmed)) {return { en, key: en, value: ru, source: 'dom_en' }
378
- }
379
- }
380
- if (domEn[trimmed] !== undefined) {return { en: trimmed, key: trimmed, value: domEn[trimmed], source: 'dom_en_original' }
381
- }
382
- }
339
+ if (map[trimmed] !== undefined) {return { ns, key: trimmed, value: map[trimmed], source: 'dictionary' }}
340
+ for (const [k, v] of Object.entries(map)) {if (v === text || (typeof v === 'string' && v.trim() === trimmed)) {return { ns, key: k, value: v, source: 'dictionary' }}}}}
341
+ if (zhRu && typeof zhRu === 'object') {for (const [zh, ru] of Object.entries(zhRu)) {if (ru === text || (typeof ru === 'string' && ru.trim() === trimmed)) {return { zh, key: zh, value: ru, source: 'dom_zh' }}}
342
+ if (zhRu[trimmed] !== undefined) {return { zh: trimmed, key: trimmed, value: zhRu[trimmed], source: 'dom_zh_original' }}}
343
+ if (domEn && typeof domEn === 'object') {for (const [en, ru] of Object.entries(domEn)) {if (ru === text || (typeof ru === 'string' && ru.trim() === trimmed)) {return { en, key: en, value: ru, source: 'dom_en' }}}
344
+ if (domEn[trimmed] !== undefined) {return { en: trimmed, key: trimmed, value: domEn[trimmed], source: 'dom_en_original' }}}
383
345
  if (options && options.detectUntranslated) {const hasRu = /[а-яё]/i.test(trimmed)
384
346
  const hasZh = /[㐀-鿿豈-﫿]/.test(trimmed)
385
347
  const hasEn = /[a-z]/i.test(trimmed)
386
- if (!hasRu && hasZh) {return { key: trimmed, value: trimmed, source: 'untranslated_zh' }
387
- }
388
- if (!hasRu && hasEn) {return { key: trimmed, value: trimmed, source: 'untranslated_en' }
389
- }
390
- }
348
+ if (!hasRu && hasZh) {return { key: trimmed, value: trimmed, source: 'untranslated_zh' }}
349
+ if (!hasRu && hasEn) {return { key: trimmed, value: trimmed, source: 'untranslated_en' }}}
391
350
  return null
392
351
  }
393
352
  const generateBugReportSnippet = (info = {}) => {const { key, ns, original, current, override, pkgVersion = '0.3.1' } = info
394
- const lines = [
395
- '### 🌐 Неточность перевода / Пользовательское предложение','- **Пакет:** `@goodandready/dsh-russian-lang@v' + pkgVersion + '`',ns ? '- **Пространство имён:** `' + ns + '`' : null,key ? '- **Ключ:** `' + key + '`' : null,original ? '- **Оригинальный текст:** `' + original + '`' : null,current ? '- **Текущий перевод:** `' + current + '`' : null,override ? '- **Предлагаемый перевод:** `' + override + '`' : null,].filter(Boolean)
353
+ const lines = ['### 🌐 Неточность перевода / Пользовательское предложение','- **Пакет:** `@goodandready/dsh-russian-lang@v' + pkgVersion + '`',ns ? '- **Пространство имён:** `' + ns + '`' : null,key ? '- **Ключ:** `' + key + '`' : null,original ? '- **Оригинальный текст:** `' + original + '`' : null,current ? '- **Текущий перевод:** `' + current + '`' : null,override ? '- **Предлагаемый перевод:** `' + override + '`' : null,].filter(Boolean)
396
354
  return lines.join('\n')
397
355
  }
398
356
  const bilingualCommandMatch = (query, command, options = {}) => {if (!query || !command) return { matched: false, score: 0 }
399
357
  const q = String(query).trim().toLowerCase()
400
358
  if (!q) return { matched: false, score: 0 }
401
359
  const cmdObj = typeof command === 'string' ? { title: command } : command
402
- const candidates = [
403
- cmdObj.title,cmdObj.label,cmdObj.name,cmdObj.originalTitle,cmdObj.enTitle,cmdObj.id,cmdObj.description,cmdObj.category,...(Array.isArray(cmdObj.keywords) ? cmdObj.keywords : [])
360
+ const candidates = [cmdObj.title,cmdObj.label,cmdObj.name,cmdObj.originalTitle,cmdObj.enTitle,cmdObj.id,cmdObj.description,cmdObj.category,...(Array.isArray(cmdObj.keywords) ? cmdObj.keywords : [])
404
361
  ].filter((c) => typeof c === 'string' && c.trim())
405
362
  if (!candidates.length) return { matched: false, score: 0 }
406
363
  const queryVariants = [{ text: q, kind: 'direct' }]
@@ -421,20 +378,16 @@ for (const variant of queryVariants) {for (const cand of candidates) {const scor
421
378
  if (score > bestScore) {bestScore = score
422
379
  bestVariant = variant
423
380
  bestCandidate = cand
424
- }
425
- }
426
- }
381
+ }}}
427
382
  const threshold = options.threshold ?? 50
428
383
  return {matched: bestScore >= threshold,score: bestScore,matchedText: bestCandidate,variant: bestVariant ? bestVariant.text : q,kind: bestVariant ? bestVariant.kind : 'none'
429
- }
430
- }
384
+ }}
431
385
  const filterCommands = (query, commands = [], options = {}) => {if (!Array.isArray(commands)) return []
432
386
  if (!query || !String(query).trim()) return commands
433
387
  const results = []
434
388
  for (const cmd of commands) {const res = bilingualCommandMatch(query, cmd, options)
435
389
  if (res.matched) {results.push({ item: cmd, ...res })
436
- }
437
- }
390
+ }}
438
391
  return results.sort((a, b) => b.score - a.score).map((r) => r.item)
439
392
  }
440
393
  const RU = {"common":{"back":"Назад","brand.localBuild":"Локальная сборка DSH","cancel":"Отмена","close":"Закрыть","codeBlock.title":"Блок кода","codeBlock.unwrap":"Не переносить строки","codeBlock.wrap":"Переносить строки","collapse":"Свернуть","copied":"Скопировано","copy":"Копировать","copy.compactJson":"Копировать компактный JSON","copy.failed":"Не удалось скопировать","copy.json":"Копировать JSON","copy.optionsHint":"{action}; правый клик для вариантов копирования","copy.path":"Копировать путь к свойству","copy.prettyJson":"Копировать форматированный JSON","copy.value":"Копировать значение","delete":"Удалить","edit":"Изменить","expand":"Развернуть","json.label":"JSON","load.failed":"Не удалось загрузить","loading":"Загрузка…","markdown.footnotes":"Сноски","markdown.truncatedCharacters":"… обрезано на {total} символах","more":"Ещё","next":"Дальше","none":"Нет","number.million":"{value}M","number.thousand":"{value}K","ok":"ОК","previous":"Назад","retry":"Повторить","save":"Сохранить","search":"Поиск","skip":"Пропустить","submit":"Отправить","submitting":"Отправка…","truncated":"Обрезано","unknown":"Неизвестно"},"dsh-cron":{"sidebar.label":"Задачи по расписанию"},"dsh-usage-stats":{"footer.todayLabel":"Сегодня"},"pluginMarket":{"trigger":"Магазин плагинов"}}
@@ -451,37 +404,26 @@ return null
451
404
  const rawScope = resolveScope(ctx)
452
405
  const scope = (rawScope && typeof rawScope.getSnapshot === 'function') ? rawScope : {getSnapshot: () => ({ status: 'ready', value: {} }),subscribe: () => () => {},set: () => Promise.resolve()
453
406
  }
454
- for (const ns of Object.keys(RU)) {ctx.effect(() => {try { return ctx.locale.register(ns, 'ru', RU[ns]) }
455
- catch (err) { return () => {} }
456
- }, 'dsh-russian-lang: ' + ns)
407
+ 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)
457
408
  }
458
409
  const registerDictMap = (dictMap) => {if (!dictMap || typeof dictMap !== 'object') return
459
410
  for (const ns of Object.keys(dictMap)) {if (ns === 'zhRu' || ns === 'plugins' || ns === 'core') continue
460
411
  const dict = dictMap[ns]
461
412
  if (typeof dict !== 'object' || !dict) continue
462
413
  if (!RU[ns]) {RU[ns] = dict
463
- ctx.effect(() => {try { return ctx.locale.register(ns, 'ru', dict) }
464
- catch (err) { return () => {} }
465
- }, 'dsh-russian-lang: ' + ns)
414
+ ctx.effect(() => {try { return ctx.locale.register(ns, 'ru', dict) } catch (err) { return () => {} }}, 'dsh-russian-lang: ' + ns)
466
415
  } else {Object.assign(RU[ns], dict)
467
- }
468
- }
469
- }
416
+ }}}
470
417
  const CACHE_NAME = 'dsh-ru-cache-v1'
471
418
  const loadLocalDict = (key) => {try {const raw = typeof localStorage !== 'undefined' ? localStorage.getItem('dsh_ru_' + key) : null
472
419
  return raw ? JSON.parse(raw) : null
473
- } catch (_) { return null }
474
- }
420
+ } catch (_) { return null }}
475
421
  const saveLocalDict = (key, etag, data) => {try {if (typeof localStorage !== 'undefined') {localStorage.setItem('dsh_ru_' + key, JSON.stringify({ etag, data }))
476
- }
477
- } catch (_e) { void _e }
478
- }
422
+ }} catch (_e) { void _e }}
479
423
  try {const cachedCore = loadLocalDict('core')
480
424
  if (cachedCore && cachedCore.data && cachedCore.data.core) {registerDictMap(cachedCore.data.core)
481
425
  if (cachedCore.data.zhRu && typeof ZH_RU === 'object') {Object.assign(ZH_RU, cachedCore.data.zhRu)
482
- }
483
- }
484
- } catch (_e) { void _e }
426
+ }}} catch (_e) { void _e }
485
427
  const fetchCachedResource = async (url, cacheKey, onData) => {let etag = null
486
428
  let delivered = false
487
429
  if (typeof caches !== 'undefined') {try {const cache = await caches.open(CACHE_NAME)
@@ -490,16 +432,12 @@ if (matched) {etag = matched.headers.get('etag')
490
432
  const parsed = await matched.json()
491
433
  if (parsed) {delivered = true
492
434
  onData(parsed)
493
- }
494
- }
495
- } catch (_e) { void _e }
496
- }
435
+ }}} catch (_e) { void _e }}
497
436
  if (!delivered && cacheKey) {const local = loadLocalDict(cacheKey)
498
437
  if (local && local.data) {etag = local.etag
499
438
  delivered = true
500
439
  onData(local.data)
501
- }
502
- }
440
+ }}
503
441
  if (typeof fetch !== 'function') return
504
442
  try {const headers = { 'Accept': 'application/json' }
505
443
  if (etag) headers['If-None-Match'] = etag
@@ -511,13 +449,9 @@ const data = await res.json()
511
449
  if (data) {onData(data)
512
450
  if (typeof caches !== 'undefined') {try {const cache = await caches.open(CACHE_NAME)
513
451
  await cache.put(url, cloned)
514
- } catch (_e) { void _e }
515
- }
452
+ } catch (_e) { void _e }}
516
453
  if (cacheKey) saveLocalDict(cacheKey, newEtag, data)
517
- }
518
- }
519
- } catch (_e) { void _e }
520
- }
454
+ }}} catch (_e) { void _e }}
521
455
  const loadedPluginNames = new Set()
522
456
  const pendingPluginLoads = new Set()
523
457
  let pluginBatchTimer = null
@@ -533,8 +467,7 @@ const url = '/api/dsh-russian-lang/dict/plugins' + qs
533
467
  const cacheKey = toLoad.length > 0 ? ('plugins_' + toLoad.sort().join('_')) : 'plugins_all'
534
468
  fetchCachedResource(url, cacheKey, (data) => {if (data && data.plugins) {registerDictMap(data.plugins)
535
469
  for (const n of toLoad) loadedPluginNames.add(n)
536
- }
537
- }).catch(() => {}).finally(() => {for (const n of toLoad) pendingPluginLoads.delete(n)
470
+ }}).catch(() => {}).finally(() => {for (const n of toLoad) pendingPluginLoads.delete(n)
538
471
  })
539
472
  }
540
473
  const schedulePluginLoad = (name) => {if (!name || loadedPluginNames.has(name) || pendingPluginLoads.has(name)) return
@@ -551,103 +484,76 @@ if (typeof updateZhRu === 'function') updateZhRu(data.zhRu)
551
484
  }
552
485
  if (typeof syncZhDom === 'function') syncZhDom()
553
486
  setTimeout(() => { loadPluginDictionaries([]) }, 100)
554
- }).catch(() => {if (typeof fetch === 'function') {fetch('/api/dsh-russian-lang/dict/all', { headers: { 'Accept': 'application/json' } })
555
- .then((res) => res.ok ? res.json() : null)
556
- .then((data) => {if (!data) return
487
+ }).catch(() => {if (typeof fetch === 'function') {fetch('/api/dsh-russian-lang/dict/all', { headers: { 'Accept': 'application/json' } }).then((res) => res.ok ? res.json() : null).then((data) => {if (!data) return
557
488
  const allDicts = Object.assign({}, data.core || {}, data.plugins || {}, data)
558
489
  if (data.zhRu && typeof ZH_RU === 'object') {Object.assign(ZH_RU, data.zhRu)
559
490
  if (typeof updateZhRu === 'function') updateZhRu(data.zhRu)
560
491
  }
561
492
  registerDictMap(allDicts)
562
493
  if (typeof syncZhDom === 'function') syncZhDom()
563
- })
564
- .catch(() => {})
565
- }
566
- })
567
- const origTranslate = runtime.translate.bind(runtime)
494
+ }).catch(() => {})
495
+ }})
568
496
  const getOverrides = () => {try {const value = scope.getSnapshot().value
569
- return value && value.overrides ? value.overrides : {}
570
- } catch (err) { return {} }
571
- }
497
+ return value && value.overrides ? value.overrides : {}} catch (err) { return {} }}
572
498
  const getPluginLocalizationStatus = makePluginLocalizationStatus(RU)
573
- try {runtime.formatNumber = formatNumber
574
- runtime.formatCompactNumber = formatCompactNumber
575
- runtime.formatDate = formatDate
576
- runtime.formatTokens = formatTokens
577
- runtime.formatRelativeTime = formatRelativeTime
578
- runtime.formatCurrency = formatCurrency
579
- runtime.inflect = inflect
580
- runtime.getPluginLocalizationStatus = getPluginLocalizationStatus
581
- runtime.stemRussian = stemRussian
582
- runtime.fuzzyMatchRu = fuzzyMatchRu
583
- runtime.humanizeError = humanizeError
584
- runtime.plural = plural
585
- runtime.pluralForm = pluralForm
586
- } catch (err) { }
587
499
  const lookupChain = () => {try {const chain = runtime.fallbackChain && runtime.fallbackChain(runtime.getLocale().active)
588
500
  if (Array.isArray(chain) && chain.length) return chain
589
501
  } catch (err) { }
590
502
  return [runtime.getLocale().active]
591
503
  }
592
504
  const translationRegistry = new Map()
593
- const lookup = (ns, key) => {const chain = lookupChain()
594
- return runtime.lookup.length >= 3 ? runtime.lookup(ns, key, chain) : runtime.lookup(ns, key)
595
- }
505
+ const lookup = (ns, key) => (runtime.lookup.length >= 3 ? runtime.lookup(ns, key, lookupChain()) : runtime.lookup(ns, key))
506
+ const syncLang = () => {try {if (typeof document !== 'undefined' && document.documentElement && runtime.getLocale().active === 'ru') document.documentElement.lang = 'ru-RU'
507
+ } catch (err) { }}
508
+ const RUNTIME_PROP_NAMES = ['plural','pluralForm','formatDate','formatCompactNumber','formatTokens','formatNumber','formatRelativeTime','formatCurrency','inflect','getPluginLocalizationStatus','stemRussian','fuzzyMatchRu','humanizeError']
509
+ ctx.effect(() => {const origTranslate = runtime.translate
510
+ const origProps = {}
511
+ for (const p of RUNTIME_PROP_NAMES) origProps[p] = runtime[p]
512
+ runtime.plural = plural
513
+ runtime.pluralForm = pluralForm
514
+ runtime.formatDate = formatDate
515
+ runtime.formatCompactNumber = formatCompactNumber
516
+ runtime.formatTokens = formatTokens
517
+ Object.assign(runtime, { formatNumber, formatRelativeTime, formatCurrency, inflect, getPluginLocalizationStatus, stemRussian, fuzzyMatchRu, humanizeError })
518
+ const boundOrig = typeof origTranslate === 'function' ? origTranslate.bind(runtime) : (() => '')
596
519
  runtime.translate = function (ns, key, params) {if (ns && !RU[ns] && !loadedPluginNames.has(ns)) schedulePluginLoad(ns)
597
- const overrides = getOverrides()
598
- if (overrides[key] !== undefined) {return params ? fill(overrides[key], params) : overrides[key]
599
- }
520
+ const ov = getOverrides()
521
+ if (ov[key] !== undefined) return params ? fill(ov[key], params) : ov[key]
600
522
  if (runtime.getLocale().active === 'ru' && params) {const n = params.n ?? params.count
601
523
  if (typeof n === 'number') {const form = pluralForm(n)
602
524
  const m = /^(.*)[.](one|other)$/.exec(key)
603
- if (m) {if (form === 'few' || form === 'many') {const pluralKey = m[1] + '.' + form
604
- const template = lookup(ns, pluralKey) ?? lookup('common', pluralKey)
605
- if (template !== undefined) {return fill(template, params)
606
- }
607
- }
608
- } else if (form !== 'other' && !/[.](one|other|few|many)$/.test(key)) {const pluralKey = key + '.' + form
609
- const template = lookup(ns, pluralKey) ?? lookup('common', pluralKey)
610
- if (template !== undefined) {return fill(template, params)
611
- }
612
- }
613
- }
614
- }
615
- const res = origTranslate(ns, key, params)
616
- if (typeof res === 'string' && res && res.length < 300) {translationRegistry.set(res.trim(), { ns, key, params, value: res })
617
- }
525
+ if (m) {if (form === 'few' || form === 'many') {const tmpl = lookup(ns, m[1] + '.' + form) ?? lookup('common', m[1] + '.' + form)
526
+ if (tmpl !== undefined) return fill(tmpl, params)
527
+ }} else if (form !== 'other' && !/[.](one|other|few|many)$/.test(key)) {const tmpl = lookup(ns, key + '.' + form) ?? lookup('common', key + '.' + form)
528
+ if (tmpl !== undefined) return fill(tmpl, params)
529
+ }}}
530
+ const res = boundOrig(ns, key, params)
531
+ if (typeof res === 'string' && res && res.length < 300) translationRegistry.set(res.trim(), { ns, key, params, value: res })
618
532
  return res
619
533
  }
620
- const syncLang = () => {try {if (typeof document !== 'undefined' && document.documentElement
621
- && runtime.getLocale().active === 'ru') {document.documentElement.lang = 'ru-RU'
622
- }
623
- } catch (err) { }
624
- }
625
- const native = runtime.getLocale().locales.some((l) => l.id === 'ru')
626
- if (!native) {runtime.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })
534
+ let removeLang = null
535
+ if (!runtime.getLocale().locales.some((l) => l.id === 'ru') && typeof runtime.addLanguage === 'function') {removeLang = runtime.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })
627
536
  syncLang()
628
537
  }
538
+ return () => {runtime.translate = origTranslate
539
+ for (const p of RUNTIME_PROP_NAMES) {if (origProps[p] !== undefined) runtime[p] = origProps[p]
540
+ else delete runtime[p]
541
+ }
542
+ if (typeof removeLang === 'function') { try { removeLang() } catch (_) { } }}}, 'dsh-russian-lang: runtime-lifecycle')
629
543
  const syncFlag = () => {try {const wantRu = runtime.getLocale().active === 'ru'
630
544
  const value = scope.getSnapshot().value || {}
631
545
  if (!!value.enabled !== wantRu) {const r = scope.set('enabled', wantRu)
632
546
  if (r && typeof r.catch === 'function') {r.catch((err) => {console.warn('dsh-russian-lang: scope.set enabled failed', err && err.message || err)
633
547
  })
634
- }
635
- }
636
- } catch (err) {}
637
- }
638
- ctx.effect(() => {try { return runtime.subscribe(syncFlag) }
639
- catch (err) { return undefined }
640
- }, 'dsh-russian-lang: sync-flag')
548
+ }}} catch (err) {}}
549
+ ctx.effect(() => {try { return runtime.subscribe(syncFlag) } catch (err) { return undefined }}, 'dsh-russian-lang: sync-flag')
641
550
  const activate = () => {try {if (runtime.getLocale().active === 'ru') return
642
551
  runtime.setLocale('ru')
643
- } catch (err) { console.warn('dsh-russian-lang: activate failed', err) }
644
- }
552
+ } catch (err) { console.warn('dsh-russian-lang: activate failed', err) }}
645
553
  let booted = false
646
554
  const tryBoot = () => {if (booted) return
647
555
  try {const value = scope.getSnapshot().value
648
- if (value && value.enabled === true) { booted = true; activate() }
649
- } catch (err) { }
650
- }
556
+ if (value && value.enabled === true) { booted = true; activate() }} catch (err) { }}
651
557
  ctx.effect(() => scope.subscribe(tryBoot), 'dsh-russian-lang: boot')
652
558
  tryBoot()
653
559
  const unsubscribeLang = runtime.subscribe(syncLang)
@@ -658,9 +564,7 @@ const SPELL_WAS = 'data-russian-lang-spell-was'
658
564
  const LANG_WAS = 'data-russian-lang-lang-was'
659
565
  const EDITABLE = 'textarea, input[type=text], input[type=search], [contenteditable=""], [contenteditable="true"]'
660
566
  const MONO_RE = /mono|consol|courier/i
661
- const isMonoField = (el) => {try { return MONO_RE.test(getComputedStyle(el).fontFamily || '') }
662
- catch (err) { return false }
663
- }
567
+ const isMonoField = (el) => {try { return MONO_RE.test(getComputedStyle(el).fontFamily || '') } catch (err) { return false }}
664
568
  const spellOn = (el) => {if (el.hasAttribute(SPELL_ON) || isMonoField(el)) return
665
569
  el.setAttribute(SPELL_ON, '1')
666
570
  el.setAttribute(SPELL_WAS, el.getAttribute('spellcheck') ?? '')
@@ -691,18 +595,13 @@ if (spellObserver) return
691
595
  spellObserver = new MutationObserver((records) => {for (const record of records) {for (const node of record.addedNodes) {if (node.nodeType !== 1) continue
692
596
  if (node.matches(EDITABLE)) spellOn(node)
693
597
  node.querySelectorAll ? node.querySelectorAll(EDITABLE).forEach(spellOn) : null
694
- }
695
- }
696
- })
598
+ }}})
697
599
  spellObserver.observe(document.body, { childList: true, subtree: true })
698
- } catch (err) { }
699
- }
600
+ } catch (err) { }}
700
601
  const unsubscribeSpell = runtime.subscribe(syncSpell)
701
602
  ctx.effect(() => {return () => {unsubscribeSpell()
702
603
  if (spellObserver) spellObserver.disconnect()
703
- try { document.querySelectorAll('[' + SPELL_ON + ']').forEach(spellOff) } catch (err) { }
704
- }
705
- }, 'dsh-russian-lang: spellcheck')
604
+ try { document.querySelectorAll('[' + SPELL_ON + ']').forEach(spellOff) } catch (err) { }}}, 'dsh-russian-lang: spellcheck')
706
605
  syncSpell()
707
606
  const ZH_CJK = /[\u3400-\u9fff\uf900-\ufaff]/
708
607
  const ZH_RE_ESC = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
@@ -723,8 +622,7 @@ if (/\{[a-zA-Z_]\w*\}/.test(zhText)) {const parts = zhText.split(/\{[a-zA-Z_]\w*
723
622
  if (parts.some((p) => p.length === 0)) continue
724
623
  ZH_PATTERNS.push({ re: new RegExp(parts.map(ZH_RE_ESC).join('([\\s\\S]*?)')), ruParts: ruText.split(/\{[a-zA-Z_]\w*\}/g) })
725
624
  } else {ZH_EXACT.set(zhText, ruText)
726
- }
727
- }
625
+ }}
728
626
  ZH_PATTERNS.sort((a, b) => b.re.source.length - a.re.source.length)
729
627
  rebuildZhSorted()
730
628
  }
@@ -743,14 +641,12 @@ for (const p of ZH_PATTERNS) {const m = p.re.exec(trimmed)
743
641
  if (m && m[0] === trimmed) {let out = p.ruParts[0]
744
642
  for (let i = 1; i < p.ruParts.length; i++) out += m[i] + p.ruParts[i]
745
643
  return text.replace(trimmed, out)
746
- }
747
- }
644
+ }}
748
645
  let replaced = text
749
646
  let changed = false
750
647
  for (const [zhPhrase, ruPhrase] of zhSortedExact) {if (zhPhrase.length >= 2 && replaced.includes(zhPhrase)) {replaced = replaced.split(zhPhrase).join(ruPhrase)
751
648
  changed = true
752
- }
753
- }
649
+ }}
754
650
  if (changed) return replaced
755
651
  return null
756
652
  }
@@ -769,9 +665,7 @@ continue
769
665
  if (trimmed.length >= 1 && ZH_CJK.test(val)) {hits.push(node)
770
666
  } else if (trimmed.length >= 1 && DOM_EN_TEXT[trimmed]) {const p = node.parentElement
771
667
  if (p && !p.closest('code, pre, script, style, textarea, input, select, kbd, samp, [contenteditable="true"], [data-composer-input], [role="textbox"]')) {node.nodeValue = val.replace(trimmed, DOM_EN_TEXT[trimmed])
772
- }
773
- }
774
- }
668
+ }}}
775
669
  for (const node of hits) {const next = zhTranslateText(node.nodeValue)
776
670
  if (next && next !== node.nodeValue) node.nodeValue = next
777
671
  }
@@ -781,19 +675,12 @@ if (DOM_EN_TEXT[tr]) {el.setAttribute(attr, v.replace(tr, DOM_EN_TEXT[tr]))
781
675
  } else if (DOM_EN_ATTRS[tr]) {el.setAttribute(attr, v.replace(tr, DOM_EN_ATTRS[tr]))
782
676
  } else if (ZH_CJK.test(v)) {const next = zhTranslateText(v)
783
677
  if (next) el.setAttribute(attr, next)
784
- }
785
- }
786
- }
787
- }
678
+ }}}}
788
679
  if (root.getAttribute) {for (const attr of ['title', 'placeholder', 'aria-label']) {const v = root.getAttribute(attr)
789
680
  if (v) {const tr = v.trim()
790
681
  if (DOM_EN_TEXT[tr]) root.setAttribute(attr, v.replace(tr, DOM_EN_TEXT[tr]))
791
682
  else if (DOM_EN_ATTRS[tr]) root.setAttribute(attr, v.replace(tr, DOM_EN_ATTRS[tr]))
792
- }
793
- }
794
- }
795
- } catch (err) { }
796
- }
683
+ }}}} catch (err) { }}
797
684
  let zhObserver = null
798
685
  let zhDebounceTimer = null
799
686
  let zhWalking = false
@@ -803,8 +690,7 @@ if (zhWalking) return
803
690
  zhWalking = true
804
691
  try {ZH_WALKER(target || document.body)
805
692
  } finally {zhWalking = false
806
- }
807
- }, 120)
693
+ }}, 120)
808
694
  }
809
695
  const syncZhDom = () => {try {if (typeof document === 'undefined') return
810
696
  const ru = runtime.getLocale().active === 'ru'
@@ -815,30 +701,21 @@ return
815
701
  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
816
702
  queueZhWalk(node)
817
703
  return
818
- }
819
- }
820
- }
821
- }
822
- })
704
+ }}}}})
823
705
  zhObserver.observe(document.body, { childList: true, subtree: true })
824
706
  }
825
707
  queueZhWalk(document.body)
826
- } catch (err) { }
827
- }
708
+ } catch (err) { }}
828
709
  const unsubscribeZh = runtime.subscribe(syncZhDom)
829
710
  ctx.effect(() => {return () => {unsubscribeZh()
830
711
  if (zhObserver) zhObserver.disconnect()
831
- if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }
832
- }
833
- }, 'dsh-russian-lang: zh-dom')
712
+ if (zhDebounceTimer) { clearTimeout(zhDebounceTimer); zhDebounceTimer = null }}}, 'dsh-russian-lang: zh-dom')
834
713
  syncZhDom()
835
714
  const TYPO_YO_PAIRS = [["еще","ещё"],["ее","её"],["черный","чёрный"],["черная","чёрная"],["черные","чёрные"],["зеленый","зелёный"],["желтый","жёлтый"],["легкий","лёгкий"],["тяжелый","тяжёлый"],["надежный","надёжный"],["дешевый","дешёвый"],["идет","идёт"],["дает","даёт"],["ведет","ведёт"],["несет","несёт"],["живет","живёт"],["привел","привёл"],["шел","шёл"],["нее","неё"],["мое","моё"],["серьезно","серьёзно"],["придется","придётся"],["насчет","насчёт"],["твое","твоё"],["свое","своё"],["пойдем","пойдём"],["пришел","пришёл"],["нашел","нашёл"],["идем","идём"],["вперед","вперёд"],["пошел","пошёл"],["ребенка","ребёнка"],["ребенок","ребёнок"],["счет","счёт"],["своем","своём"],["придет","придёт"],["ушел","ушёл"],["ждет","ждёт"],["мертв","мёртв"],["твоем","твоём"],["вернется","вернётся"],["днем","днём"],["найдем","найдём"],["пойдет","пойдёт"],["начнем","начнём"],["вернемся","вернёмся"],["остается","остаётся"],["принес","принёс"],["идешь","идёшь"],["трех","трёх"],["ребенком","ребёнком"],["самолет","самолёт"],["определенно","определённо"],["пойдешь","пойдёшь"],["умрет","умрёт"],["прием","приём"],["прошел","прошёл"],["убьет","убьёт"],["тетя","тётя"],["провел","провёл"],["произойдет","произойдёт"],["семьей","семьёй"],["пойдемте","пойдёмте"],["пройдет","пройдёт"],["возьмем","возьмём"],["вернешься","вернёшься"],["найдешь","найдёшь"],["живешь","живёшь"],["отчет","отчёт"],["займет","займёт"],["ждем","ждём"],["сошел","сошёл"],["вдвоем","вдвоём"],["найдет","найдёт"],["вел","вёл"],["живем","живём"],["зашел","зашёл"],["начнется","начнётся"],["путем","путём"],["подойдет","подойдёт"],["режиссер","режиссёр"],["уйдет","уйдёт"],["придешь","придёшь"],["идемте","идёмте"],["ведешь","ведёшь"],["пес","пёс"],["умрешь","умрёшь"],["ждешь","ждёшь"],["найдете","найдёте"],["найдется","найдётся"],["клево","клёво"],["поймешь","поймёшь"],["разберемся","разберёмся"],["времен","времён"],["введен","введён"],["включен","включён"],["включенных","включённых"],["возьмется","возьмётся"],["завершен","завершён"],["завершенного","завершённого"],["задает","задаёт"],["задается","задаётся"],["заменен","заменён"],["звезд","звёзд"],["звезды","звёзды"],["изменен","изменён"],["истек","истёк"],["незавершенная","незавершённая"],["неподтвержденные","неподтверждённые"],["несохраненные","несохранённые"],["обновлен","обновлён"],["обновленные","обновлённые"],["определен","определён"],["отклонен","отклонён"],["отменен","отменён"],["очередность","очерёдность"],["поврежден","повреждён"],["повторен","повторён"],["подключен","подключён"],["подтвержденные","подтверждённые"],["приемки","приёмки"],["раздает","раздаёт"],["разрешенная","разрешённая"],["разрешенные","разрешённые"],["создаем","создаём"],["сойдется","сойдётся"],["сохранен","сохранён"],["сохраненный","сохранённый"],["счету","счёту"],["тяжелые","тяжёлые"],["темная","тёмная"],["уберет","уберёт"],["удален","удалён"],["учетные","учётные"],["учетных","учётных"],["емкости","ёмкости"],["емкость","ёмкость"]]
836
715
  const typoYo = makeTypoYo(TYPO_YO_PAIRS)
837
716
  const getTypoConf = () => {try {const t = scope.getSnapshot().value && scope.getSnapshot().value.typography
838
717
  if (!t || t.enabled === false) return null
839
- return { yo: t.yo === true }
840
- } catch (err) { return null }
841
- }
718
+ return { yo: t.yo === true }} catch (err) { return null }}
842
719
  const typoNode = (node, conf) => {const before = node.nodeValue
843
720
  if (!before || !before.match || (before.match(/[\u0400-\u04FF]/g) || []).length < 3) return
844
721
  if (node.parentElement && node.parentElement.closest('code, pre, a, script, style, textarea, input, select, button, kbd, samp, [contenteditable="true"], [data-composer-input], [role="textbox"]')) return
@@ -849,9 +726,7 @@ if (node.parentElement && node.parentElement.closest('[data-chat-flow-status="ru
849
726
  const sel = typeof window !== 'undefined' && window.getSelection && window.getSelection()
850
727
  if (sel && !sel.isCollapsed && sel.rangeCount > 0) {try {const range = sel.getRangeAt(0)
851
728
  if (range.intersectsNode ? range.intersectsNode(node) : (sel.containsNode && sel.containsNode(node, true))) {return
852
- }
853
- } catch (e) { void e; }
854
- }
729
+ }} catch (e) { void e; }}
855
730
  let after = typoQuotes(before)
856
731
  after = typoDash(after)
857
732
  after = typoPunct(after)
@@ -871,8 +746,7 @@ for (const root of typoPending) {if (root.nodeType === 3) typoNode(root, conf)
871
746
  else typoWalk(root, conf)
872
747
  }
873
748
  typoPending.clear()
874
- } catch (err) { }
875
- }
749
+ } catch (err) { }}
876
750
  const typoPending = new Set()
877
751
  const queueTypo = (roots) => {for (const r of roots) typoPending.add(r)
878
752
  if (!typoQueued) typoQueued = requestAnimationFrame(flushTypo)
@@ -885,19 +759,15 @@ if (typoObserver) return
885
759
  typoWalk(document.body, getTypoConf())
886
760
  typoObserver = new MutationObserver((records) => {const roots = []
887
761
  for (const record of records) {if (record.type === 'childList') {for (const added of record.addedNodes) {if (added.nodeType === 3 || added.nodeType === 1) roots.push(added)
888
- }
889
- }
890
- }
762
+ }}}
891
763
  if (roots.length > 0) queueTypo(roots)
892
764
  })
893
765
  typoObserver.observe(document.body, { childList: true, subtree: true })
894
- } catch (err) { }
895
- }
766
+ } catch (err) { }}
896
767
  const unsubscribeTypo = runtime.subscribe(syncTypo)
897
768
  ctx.effect(() => {return () => {unsubscribeTypo()
898
769
  if (typoObserver) typoObserver.disconnect()
899
- }
900
- }, 'dsh-russian-lang: typography')
770
+ }}, 'dsh-russian-lang: typography')
901
771
  syncTypo()
902
772
  const layout = makeLayout(new Set(["не","что","ты","это","на","он","мы","как","вы","да","мне","нет","меня","так","но","его","все","она","тебя","если","за","бы","тебе","они","чтобы","же","есть","просто","из","для"]), new Set())
903
773
  const layoutFixCandidate = layout.candidate
@@ -914,16 +784,13 @@ if (el._valueTracker) {el._valueTracker.setValue(value === '' ? '__force__' : ''
914
784
  }
915
785
  try {const propsKey = Object.keys(el).find((k) => k.startsWith('__reactProps$') || k.startsWith('__reactEventHandlers$'))
916
786
  if (propsKey && el[propsKey] && typeof el[propsKey].onChange === 'function') {el[propsKey].onChange({ target: el, currentTarget: el })
917
- }
918
- } catch (e) { void e; }
787
+ }} catch (e) { void e; }
919
788
  try {el.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }))
920
789
  } catch (e) {el.dispatchEvent(new Event('input', { bubbles: true }))
921
790
  }
922
791
  el.dispatchEvent(new Event('change', { bubbles: true }))
923
792
  if (typeof cursorStart === 'number' && typeof el.setSelectionRange === 'function') {const end = typeof cursorEnd === 'number' ? cursorEnd : cursorStart
924
- try { el.setSelectionRange(cursorStart, end) } catch (e) { void e; }
925
- }
926
- }
793
+ try { el.setSelectionRange(cursorStart, end) } catch (e) { void e; }}}
927
794
  let layoutHintEl = null
928
795
  const layoutCurrentInput = (ev) => {if (ev && ev.target) {const t = ev.target
929
796
  if (t.tagName === 'TEXTAREA' || (t.tagName === 'INPUT' && (t.type === 'text' || !t.type))) {return t
@@ -951,8 +818,7 @@ preCaretRange.selectNodeContents(root)
951
818
  preCaretRange.setEnd(range.startContainer, range.startOffset)
952
819
  return preCaretRange.toString().length
953
820
  } catch (e) {return -1
954
- }
955
- }
821
+ }}
956
822
  function setCaretCharacterOffset(root, offset) {if (!root || offset < 0) return
957
823
  const sel = typeof window !== 'undefined' && window.getSelection && window.getSelection()
958
824
  if (!sel) return
@@ -974,9 +840,7 @@ range.setStart(targetNode, targetOffset)
974
840
  range.collapse(true)
975
841
  sel.removeAllRanges()
976
842
  sel.addRange(range)
977
- }
978
- } catch (e) { void e; }
979
- }
843
+ }} catch (e) { void e; }}
980
844
  const isCaretInCode = (el, value, caretOffset) => {if (!el) return false
981
845
  if (caretOffset == null || caretOffset < 0) caretOffset = (value || '').length
982
846
  const sel = typeof window !== 'undefined' && window.getSelection && window.getSelection()
@@ -1013,10 +877,8 @@ sel.addRange(range)
1013
877
  document.execCommand('insertText', false, value)
1014
878
  const targetOffset = typeof cursorStart === 'number' ? cursorStart : (initialOffset >= 0 ? Math.min(initialOffset, value.length) : value.length)
1015
879
  setCaretCharacterOffset(host, targetOffset)
1016
- } catch (e) { void e; }
1017
- }
1018
- const layoutDismiss = () => {if (layoutHintEl) { layoutHintEl.remove(); layoutHintEl = null }
1019
- }
880
+ } catch (e) { void e; }}
881
+ const layoutDismiss = () => {if (layoutHintEl) { layoutHintEl.remove(); layoutHintEl = null }}
1020
882
  const layoutShowHint = (inputEl, converted, direction) => {layoutDismiss()
1021
883
  layoutHintEl = document.createElement('div')
1022
884
  layoutHintEl.dataset.russianLangLayout = '1'
@@ -1053,8 +915,7 @@ const v = getComposerText(el)
1053
915
  const c = layoutFixCandidate(v, 'lat2cyr') || layoutFixCandidate(v, 'cyr2lat')
1054
916
  if (c) {setComposerText(el, c.converted)
1055
917
  learnWords(c.converted)
1056
- }
1057
- })
918
+ }})
1058
919
  document.body.appendChild(layoutBadgeEl)
1059
920
  }
1060
921
  layoutBadgeEl.textContent = label
@@ -1062,8 +923,7 @@ const r = el.getBoundingClientRect()
1062
923
  layoutBadgeEl.style.left = (r.right - 36) + 'px'
1063
924
  layoutBadgeEl.style.top = (r.top - 22) + 'px'
1064
925
  }
1065
- const layoutBadgeHide = () => {if (layoutBadgeEl) { layoutBadgeEl.remove(); layoutBadgeEl = null }
1066
- }
926
+ const layoutBadgeHide = () => {if (layoutBadgeEl) { layoutBadgeEl.remove(); layoutBadgeEl = null }}
1067
927
  let isFormatting = false
1068
928
  const layoutOnInput = (ev) => {if (isFormatting) return
1069
929
  try {const el = layoutCurrentInput(ev)
@@ -1074,14 +934,11 @@ if (value.trim().length < 4) { layoutDismiss(); return }
1074
934
  const latCount = (value.match(/[a-z]/g) || []).length
1075
935
  const cyrCount = (value.match(/[\u0430-\u044f\u0451]/g) || []).length
1076
936
  if (latCount > cyrCount && cyrCount === 0) {const c = layoutFixCandidate(value, 'lat2cyr')
1077
- if (c) { layoutShowHint(el, c.converted, 'ru'); return }
1078
- }
937
+ if (c) { layoutShowHint(el, c.converted, 'ru'); return }}
1079
938
  if (cyrCount > 0 && latCount === 0 && value.trim().startsWith('/')) {const c = layoutFixCandidate(value, 'cyr2lat')
1080
- if (c) { layoutShowHint(el, c.converted, 'cmd'); return }
1081
- }
939
+ if (c) { layoutShowHint(el, c.converted, 'cmd'); return }}
1082
940
  layoutDismiss()
1083
- } catch (err) { }
1084
- }
941
+ } catch (err) { }}
1085
942
  const unsubscribeLayout = runtime.subscribe(layoutOnInput)
1086
943
  const layoutOnKeydown = (ev) => {const el = layoutCurrentInput(ev)
1087
944
  if (!el) return
@@ -1102,8 +959,7 @@ learnWords(c.converted)
1102
959
  } finally {isFormatting = false
1103
960
  }
1104
961
  return
1105
- }
1106
- }
962
+ }}
1107
963
  const c = layoutFixCandidate(value, 'lat2cyr') || layoutFixCandidate(value, 'cyr2lat')
1108
964
  if (c) {ev.preventDefault()
1109
965
  isFormatting = true
@@ -1116,8 +972,7 @@ setComposerText(el, c.converted, offset, offset)
1116
972
  }
1117
973
  learnWords(c.converted)
1118
974
  } finally {isFormatting = false
1119
- }
1120
- }
975
+ }}
1121
976
  return
1122
977
  }
1123
978
  if (ev.key === ' ' || ev.key === 'Enter') {if (value && value.startsWith('/') && typeof expandSlashAlias === 'function') {const expanded = expandSlashAlias(value)
@@ -1131,18 +986,12 @@ return
1131
986
  } else if (ev.key === 'Enter') {isFormatting = true
1132
987
  try {setComposerText(el, expanded, expanded.length, expanded.length)
1133
988
  } finally {isFormatting = false
1134
- }
1135
- }
1136
- }
1137
- }
1138
- }
989
+ }}}}}
1139
990
  if (ev.key === 'Enter' && !ev.shiftKey && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {const snapVal = scope ? (scope.getSnapshot().value || {}) : {}
1140
991
  const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
1141
992
  if (typoLive && typeof formatInputLive === 'function' && (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT')) {const formatted = formatInputLive(value)
1142
993
  if (formatted && formatted !== value) {setNativeInputValue(el, formatted, formatted.length, formatted.length)
1143
- }
1144
- }
1145
- }
994
+ }}}
1146
995
  try {const snapVal = scope ? (scope.getSnapshot().value || {}) : {}
1147
996
  const typoLive = snapVal.typography ? snapVal.typography.liveInput === true : false
1148
997
  if (typoLive && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {const isTextarea = el.tagName === 'TEXTAREA' || el.tagName === 'INPUT'
@@ -1166,12 +1015,7 @@ setNativeInputValue(el, nextVal, caretPos + 1, caretPos + 1)
1166
1015
  } finally {isFormatting = false
1167
1016
  }
1168
1017
  return
1169
- }
1170
- }
1171
- }
1172
- }
1173
- } catch (e) { void e; }
1174
- }
1018
+ }}}}} catch (e) { void e; }}
1175
1019
  const getHoverBox = () => {if (typeof document === 'undefined') return null
1176
1020
  if (!hoverBoxEl) {hoverBoxEl = document.createElement('div')
1177
1021
  hoverBoxEl.id = 'dsh-ru-inspector-hover'
@@ -1266,17 +1110,15 @@ const copyKeyBtn = modal.querySelector('.rl-copy-key-btn')
1266
1110
  if (copyKeyBtn) {copyKeyBtn.onclick = () => {try { navigator.clipboard.writeText(k || rawText) } catch (err) { void err; }
1267
1111
  copyKeyBtn.textContent = '✓'
1268
1112
  setTimeout(() => { copyKeyBtn.textContent = '📋' }, 1500)
1269
- }
1270
- }
1113
+ }}
1271
1114
  const copyReportBtn = modal.querySelector('.rl-copy-report-btn')
1272
1115
  if (copyReportBtn) {copyReportBtn.onclick = () => {const snippet = typeof generateBugReportSnippet === 'function'
1273
- ? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.9' })
1116
+ ? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.10' })
1274
1117
  : ('### Репорт: ' + (k || rawText) + '\n- Исходный: ' + (origText || rawText) + '\n- Перевод: ' + textarea.value.trim())
1275
1118
  try { navigator.clipboard.writeText(snippet) } catch (err) { void err; }
1276
1119
  copyReportBtn.textContent = '✓ Скопировано'
1277
1120
  setTimeout(() => { copyReportBtn.textContent = '📋 Скопировать репорт' }, 2000)
1278
- }
1279
- }
1121
+ }}
1280
1122
  const resetBtn = modal.querySelector('.rl-reset-override-btn')
1281
1123
  if (resetBtn) {resetBtn.onclick = () => {const targetKey = k || rawText
1282
1124
  const cur = getOverrides()
@@ -1285,8 +1127,7 @@ delete updated[targetKey]
1285
1127
  try { scope.set('overrides', updated) } catch (err) { void err; }
1286
1128
  modal.remove()
1287
1129
  if (typeof queueZhWalk === 'function') queueZhWalk()
1288
- }
1289
- }
1130
+ }}
1290
1131
  const saveBtn = modal.querySelector('.rl-save-btn')
1291
1132
  saveBtn.onclick = () => {const val = textarea.value.trim()
1292
1133
  const targetKey = k || rawText
@@ -1296,9 +1137,7 @@ const updated = Object.assign({}, cur, { [targetKey]: val })
1296
1137
  try { scope.set('overrides', updated) } catch (_e) { void _e }
1297
1138
  if (targetElement) {try {if (targetElement.childNodes && targetElement.childNodes.length === 1 && targetElement.childNodes[0].nodeType === 3) {targetElement.childNodes[0].nodeValue = val
1298
1139
  } else {targetElement.textContent = val
1299
- }
1300
- } catch (err) { void err; }
1301
- }
1140
+ }} catch (err) { void err; }}
1302
1141
  saveBtn.textContent = '✓ Применено'
1303
1142
  saveBtn.disabled = true
1304
1143
  if (typeof queueZhWalk === 'function') queueZhWalk()
@@ -1343,17 +1182,14 @@ else if (meta.source === 'untranslated_zh') title = '🔴 ZH'
1343
1182
  else if (meta.source === 'untranslated_en') title = '🔴 EN'
1344
1183
  }
1345
1184
  badge.textContent = title
1346
- }
1347
- }
1185
+ }}
1348
1186
  const inspectorOnKeyDown = (e) => {if (e.altKey && (e.key === 'i' || e.key === 'I' || e.code === 'KeyI')) {e.preventDefault()
1349
1187
  isInspectorActive = !isInspectorActive
1350
1188
  updateInspectorPill()
1351
1189
  if (!isInspectorActive && hoverBoxEl) hoverBoxEl.style.display = 'none'
1352
- }
1353
- }
1190
+ }}
1354
1191
  const inspectorOnKeyUp = (e) => {if (e.key === 'Alt' && !isInspectorActive) {if (hoverBoxEl) hoverBoxEl.style.display = 'none'
1355
- }
1356
- }
1192
+ }}
1357
1193
  ctx.effect(() => {document.addEventListener('click', inspectorOnClick, true)
1358
1194
  document.addEventListener('mousemove', inspectorOnMouseMove, true)
1359
1195
  document.addEventListener('keydown', inspectorOnKeyDown, true)
@@ -1365,9 +1201,7 @@ document.removeEventListener('keyup', inspectorOnKeyUp, true)
1365
1201
  const m = document.getElementById('dsh-ru-inspector-modal')
1366
1202
  if (m) m.remove()
1367
1203
  if (hoverBoxEl) { hoverBoxEl.remove(); hoverBoxEl = null }
1368
- if (pillEl) { pillEl.remove(); pillEl = null }
1369
- }
1370
- }, 'dsh-russian-lang: inspector')
1204
+ if (pillEl) { pillEl.remove(); pillEl = null }}}, 'dsh-russian-lang: inspector')
1371
1205
  runtime.findTranslationKey = (text) => (typeof findTranslationKey === 'function' ? findTranslationKey(text, RU, getOverrides(), ZH_RU) : null)
1372
1206
  runtime.openInspector = openInspectorModal
1373
1207
  runtime.translationRegistry = translationRegistry
@@ -1380,16 +1214,10 @@ if (node.matches && (node.matches('.dsw-toast, [role="alert"], .toast, .ant-mess
1380
1214
  if (/failed to fetch|network error|econnrefused|etimedout/i.test(text)) {const h = typeof humanizeError === 'function' ? humanizeError(text) : null
1381
1215
  if (h && h.title) {const targetEl = node.querySelector('.toast-body, .ant-message-custom-content') || node
1382
1216
  targetEl.textContent = h.title + ': ' + h.message
1383
- }
1384
- }
1385
- }
1386
- }
1387
- }
1388
- }) : null
1217
+ }}}}}}) : null
1389
1218
  ctx.effect(() => {if (toastObserver && typeof document !== 'undefined' && document.body) {toastObserver.observe(document.body, { childList: true, subtree: true })
1390
1219
  }
1391
- return () => { if (toastObserver) toastObserver.disconnect() }
1392
- }, 'dsh-russian-lang: toast-humanizer')
1220
+ return () => { if (toastObserver) toastObserver.disconnect() }}, 'dsh-russian-lang: toast-humanizer')
1393
1221
  ctx.effect(() => {document.addEventListener('input', layoutOnInput, true)
1394
1222
  document.addEventListener('keydown', layoutOnKeydown, true)
1395
1223
  return () => {unsubscribeLayout()
@@ -1397,13 +1225,11 @@ document.removeEventListener('input', layoutOnInput, true)
1397
1225
  document.removeEventListener('keydown', layoutOnKeydown, true)
1398
1226
  layoutDismiss()
1399
1227
  layoutBadgeHide()
1400
- }
1401
- }, 'dsh-russian-lang: layout')
1228
+ }}, 'dsh-russian-lang: layout')
1402
1229
  if (!ctx.slots || !React) return
1403
1230
  const toggleRu = (wantRu) => {try {if (runtime.getLocale().active === wantRu) return
1404
1231
  runtime.setLocale(wantRu ? 'ru' : 'en')
1405
- } catch (err) { console.warn('dsh-russian-lang: toggle failed', err) }
1406
- }
1232
+ } catch (err) { console.warn('dsh-russian-lang: toggle failed', err) }}
1407
1233
  try {ctx.slots.inject('plugins.item', () =>
1408
1234
  ctx.slots.register({name: 'plugins.item',id: SETTINGS_NS_NAME,order: 60,label: () => 'Russian language',locale: SETTINGS_NS_NAME,inject: () => ({ scope, runtime, toggleRu }),}, SettingsCard),)
1409
1235
  ctx.slots.inject('plugins.row.config', () =>
@@ -1419,15 +1245,13 @@ ctx.slots.register({name: 'conversation.chat.assistant-actions',id: 'dsh-russian
1419
1245
  } catch (err) { }
1420
1246
  try {ctx.slots.inject('conversation.session.header.utilities', () =>
1421
1247
  ctx.slots.register({name: 'conversation.session.header.utilities',id: 'dsh-russian-lang-md-export',order: 101,locale: SETTINGS_NS_NAME,inject: () => ({ runtime }),}, ExportMarkdownButton),)
1422
- } catch (err) { }
1423
- }
1248
+ } catch (err) { }}
1424
1249
  function QuickLangSwitch(props) {const inj = typeof props.inject === 'function' ? (props.inject() || {}) : (props.inject || {})
1425
1250
  const runtime = props.runtime || inj.runtime
1426
1251
  const toggleRu = props.toggleRu || inj.toggleRu
1427
1252
  const [locale, setLocaleState] = React.useState(runtime ? (runtime.getLocale().active || 'en') : 'ru')
1428
1253
  React.useEffect(() => {if (!runtime) return
1429
- return runtime.subscribe(() => {try { setLocaleState(runtime.getLocale().active || 'en') } catch (e) { }
1430
- })
1254
+ return runtime.subscribe(() => {try { setLocaleState(runtime.getLocale().active || 'en') } catch (e) { }})
1431
1255
  }, [runtime])
1432
1256
  const isRu = locale === 'ru'
1433
1257
  return React.createElement('button', {type: 'button',className: 'rl-lang-chip' + (isRu ? ' rl-lang-chip-active' : ''),title: isRu ? 'Интерфейс: Русский (нажмите для переключения на EN)' : 'Interface: English (click for RU)',onClick: () => { if (toggleRu) toggleRu(!isRu) },}, React.createElement('span', { className: 'rl-lang-text' }, isRu ? 'RU' : 'EN'))
@@ -1449,8 +1273,7 @@ if (text) messages.push({ role: 'user', content: text })
1449
1273
  clone.querySelectorAll('button, svg, [class*="actions"], [class*="Actions"], .rl-turn-translation').forEach((b) => b.remove())
1450
1274
  const text = clone.innerText.trim()
1451
1275
  if (text) messages.push({ role: 'assistant', content: text })
1452
- }
1453
- })
1276
+ }})
1454
1277
  }
1455
1278
  if (messages.length === 0) {const allElements = Array.from(document.querySelectorAll('[class*="userRow"], [class*="UserRow"], [class*="assistant-step"], [class*="AssistantMarkdown"], .dsw-turn-node, [data-role]'))
1456
1279
  const seen = new Set()
@@ -1477,24 +1300,18 @@ a.download = safeTitle + '-' + new Date().toISOString().slice(0, 10) + '.md'
1477
1300
  document.body.appendChild(a)
1478
1301
  a.click()
1479
1302
  setDone(true)
1480
- setTimeout(() => {try { document.body.removeChild(a); URL.revokeObjectURL(url) } catch (e) { void e; }
1481
- }, 30000)
1303
+ setTimeout(() => {try { document.body.removeChild(a); URL.revokeObjectURL(url) } catch (e) { void e; }}, 30000)
1482
1304
  setTimeout(() => setDone(false), 2000)
1483
1305
  } catch (err) {console.warn('dsh-russian-lang: export md failed', err)
1484
- }
1485
- }
1306
+ }}
1486
1307
  return React.createElement('button', {type: 'button',className: 'rl-lang-chip rl-export-md-btn',title: 'Экспорт диалога в Markdown (.md)',onClick: onExport,}, React.createElement('span', { className: 'rl-lang-text' }, done ? '✓ MD' : '📥 MD'))
1487
1308
  }
1488
1309
  async function translateTurnContent(text) {if (!text || typeof text !== 'string') return { error: 'Текст для перевода пуст.' }
1489
1310
  try {const res = await fetch('/api/dsh-russian-lang/translate', {method: 'POST',headers: { 'content-type': 'application/json', 'x-dsh-translator': '1' },body: JSON.stringify({ text })
1490
1311
  })
1491
1312
  const data = await res.json().catch(() => ({}))
1492
- if (!res.ok) {return { error: data.error || ('Ошибка перевода (HTTP ' + res.status + ')') }
1493
- }
1494
- return { translatedText: data.translatedText || text }
1495
- } catch (err) {return { error: 'Не удалось связаться с хостом DSH: ' + (err.message || err) }
1496
- }
1497
- }
1313
+ if (!res.ok) {return { error: data.error || ('Ошибка перевода (HTTP ' + res.status + ')') }}
1314
+ return { translatedText: data.translatedText || text }} catch (err) {return { error: 'Не удалось связаться с хостом DSH: ' + (err.message || err) }}}
1498
1315
  function TranslateTurnAction(props) {const t = typeof props.t === 'function' ? props.t : ((k) => k)
1499
1316
  const [loading, setLoading] = React.useState(false)
1500
1317
  const [open, setOpen] = React.useState(false)
@@ -1529,8 +1346,7 @@ rawText = targetSteps.map((s) => {const clone = s.cloneNode(true)
1529
1346
  clone.querySelectorAll('button, svg, [class*="actions"], [class*="Actions"]').forEach((b) => b.remove())
1530
1347
  return clone.innerText.trim()
1531
1348
  }).filter(Boolean).join('\n\n')
1532
- }
1533
- }
1349
+ }}
1534
1350
  if (!rawText) {const prose = tailFlowItem.querySelector('.dsw-prose, [data-block-kind="text"], .dsw-markdown-view, p')
1535
1351
  if (prose) rawText = prose.innerText.trim()
1536
1352
  }
@@ -1570,8 +1386,7 @@ if (parentContainer) {parentContainer.insertBefore(box, tailFlowItem)
1570
1386
  setOpen(true)
1571
1387
  } catch (err) {console.warn('dsh-russian-lang: translate turn failed', err)
1572
1388
  } finally {setLoading(false)
1573
- }
1574
- },}, React.createElement('span', null, loading ? '...' : (open ? 'RU ✓' : 'RU ↗')))
1389
+ }},}, React.createElement('span', null, loading ? '...' : (open ? 'RU ✓' : 'RU ↗')))
1575
1390
  }
1576
1391
  function SettingsCard(props) {const inj = typeof props.inject === 'function'
1577
1392
  ? (props.inject() || {})
@@ -1581,70 +1396,50 @@ const runtime = props.runtime || inj.runtime
1581
1396
  const toggleRu = props.toggleRu || inj.toggleRu
1582
1397
  const t = typeof props.t === 'function' ? props.t : ((k) => k)
1583
1398
  const [open, setOpen] = React.useState(false)
1584
- const [snap, setSnap] = React.useState(
1585
- () => (scope && scope.getSnapshot ? scope.getSnapshot() : { status: 'loading', value: {} }))
1586
- const [ruActive, setRuActive] = React.useState(
1587
- () => { try { return runtime.getLocale().active === 'ru' } catch (e) { return false } })
1399
+ const [snap, setSnap] = React.useState(() => (scope && scope.getSnapshot ? scope.getSnapshot() : { status: 'loading', value: {} }))
1400
+ const [ruActive, setRuActive] = React.useState(() => { try { return runtime.getLocale().active === 'ru' } catch (e) { return false } })
1588
1401
  const [typo, setTypoState] = React.useState(() =>
1589
1402
  (snap.value && snap.value.typography) || {})
1590
- const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.9',latestVersion: undefined,updateAvailable: false
1403
+ const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.10',latestVersion: undefined,updateAvailable: false
1591
1404
  })
1592
1405
  const [upLoading, setUpLoading] = React.useState(false)
1593
1406
  const [upMsg, setUpMsg] = React.useState(null)
1594
1407
  const checkUpdate = () => {setUpLoading(true)
1595
1408
  setUpMsg(null)
1596
- fetch('/api/dsh-russian-lang/update', {headers: { 'x-dsh-plugin-update': '1' }
1597
- })
1598
- .then((r) => r.json())
1599
- .then((data) => {setUpStatus(data)
1409
+ fetch('/api/dsh-russian-lang/update', {headers: { 'x-dsh-plugin-update': '1' }}).then((r) => r.json()).then((data) => {setUpStatus(data)
1600
1410
  setUpLoading(false)
1601
- })
1602
- .catch(() => {setUpLoading(false)
1411
+ }).catch(() => {setUpLoading(false)
1603
1412
  setUpMsg({ type: 'err', text: t('updaterFailed') })
1604
1413
  })
1605
1414
  }
1606
1415
  const triggerUpdate = () => {setUpLoading(true)
1607
1416
  setUpMsg(null)
1608
- fetch('/api/dsh-russian-lang/update', {method: 'POST',headers: { 'x-dsh-plugin-update': '1' }
1609
- })
1610
- .then((r) => r.json())
1611
- .then((data) => {setUpLoading(false)
1417
+ fetch('/api/dsh-russian-lang/update', {method: 'POST',headers: { 'x-dsh-plugin-update': '1' }}).then((r) => r.json()).then((data) => {setUpLoading(false)
1612
1418
  if (data.restartRequired || data.updatedVersion) {setUpStatus(data)
1613
1419
  setUpMsg({ type: 'ok', text: t('updaterSuccess') })
1614
1420
  } else if (data.error) {setUpMsg({ type: 'err', text: data.error })
1615
- }
1616
- })
1617
- .catch(() => {setUpLoading(false)
1421
+ }}).catch(() => {setUpLoading(false)
1618
1422
  setUpMsg({ type: 'err', text: t('updaterFailed') })
1619
1423
  })
1620
1424
  }
1621
1425
  const [transStatus, setTransStatus] = React.useState(null)
1622
1426
  const [transStarting, setTransStarting] = React.useState(false)
1623
1427
  const [transMsg, setTransMsg] = React.useState(null)
1624
- const fetchTransStatus = () => {fetch('/api/dsh-russian-lang/translator/status')
1625
- .then((r) => r.json())
1626
- .then((data) => setTransStatus(data))
1627
- .catch(() => setTransStatus(null))
1428
+ const fetchTransStatus = () => {fetch('/api/dsh-russian-lang/translator/status').then((r) => r.json()).then((data) => setTransStatus(data)).catch(() => setTransStatus(null))
1628
1429
  }
1629
1430
  const startLibreTranslate = () => {setTransStarting(true)
1630
1431
  setTransMsg(null)
1631
- fetch('/api/dsh-russian-lang/translator/setup', {method: 'POST',headers: { 'x-dsh-translator': '1' }
1632
- })
1633
- .then((r) => r.json())
1634
- .then((data) => {setTransStarting(false)
1432
+ fetch('/api/dsh-russian-lang/translator/setup', {method: 'POST',headers: { 'x-dsh-translator': '1' }}).then((r) => r.json()).then((data) => {setTransStarting(false)
1635
1433
  if (data.ok) {setTransMsg({ type: 'ok', text: data.message || 'Контейнер LibreTranslate запущен.' })
1636
1434
  fetchTransStatus()
1637
1435
  } else {setTransMsg({ type: 'err', text: data.error || 'Ошибка запуска контейнера.' })
1638
- }
1639
- })
1640
- .catch((err) => {setTransStarting(false)
1436
+ }}).catch((err) => {setTransStarting(false)
1641
1437
  setTransMsg({ type: 'err', text: 'Ошибка: ' + (err.message || err) })
1642
1438
  })
1643
1439
  }
1644
1440
  React.useEffect(() => {if (open || (props && props.view === 'page')) {checkUpdate()
1645
1441
  fetchTransStatus()
1646
- }
1647
- }, [open, props])
1442
+ }}, [open, props])
1648
1443
  React.useEffect(() => {if (!scope || !scope.subscribe) return undefined
1649
1444
  const un = scope.subscribe(() => {const s = scope.getSnapshot()
1650
1445
  setSnap(s)
@@ -1653,11 +1448,9 @@ if (s.value && s.value.typography) setTypoState(s.value.typography)
1653
1448
  setSnap(scope.getSnapshot())
1654
1449
  return un
1655
1450
  }, [scope])
1656
- React.useEffect(() => {try {const un = runtime.subscribe(() => {try { setRuActive(runtime.getLocale().active === 'ru') } catch (e) { }
1657
- })
1451
+ React.useEffect(() => {try {const un = runtime.subscribe(() => {try { setRuActive(runtime.getLocale().active === 'ru') } catch (e) { }})
1658
1452
  return un
1659
- } catch (e) { return undefined }
1660
- }, [runtime])
1453
+ } catch (e) { return undefined }}, [runtime])
1661
1454
  const status = snap.status || 'loading'
1662
1455
  const value = snap.value || {}
1663
1456
  const typography = typo
@@ -1671,8 +1464,7 @@ try {const r = scope.set('typography', next)
1671
1464
  if (r && typeof r.catch === 'function') r.catch((err) => {console.warn('dsh-russian-lang: scope.set typography failed', err && err.message || err)
1672
1465
  })
1673
1466
  } catch (err) {console.warn('dsh-russian-lang: scope.set typography sync threw', err && err.message || err)
1674
- }
1675
- }
1467
+ }}
1676
1468
  const onEnabled = (ev) => { if (toggleRu) toggleRu(ev.target.checked) }
1677
1469
  const checkbox = (checked, onChange, disabled) =>
1678
1470
  React.createElement('input', {type: 'checkbox', checked: !!checked, disabled: !!disabled,className: 'rl-check', onChange: (ev) => onChange(ev),})
@@ -1691,129 +1483,59 @@ const presetInfo = typeof SYSTEM_PROMPT_PRESETS !== 'undefined' ? SYSTEM_PROMPT_
1691
1483
  if (props && props.view === 'summary') {return React.createElement('div', { className: 'rl-sub' }, statusLine || t('cardSub'))
1692
1484
  }
1693
1485
  const page = !!(props && props.view === 'page')
1694
- return React.createElement('div', { className: page ? 'rl-page-seat' : 'rl-card' },React.createElement('button', {type: 'button',className: 'rl-head',style: page ? { display: 'none' } : undefined,'aria-expanded': page ? 'true' : String(open),onClick: () => setOpen(!open),},React.createElement('span', { className: 'rl-head-main' },React.createElement('div', { className: 'rl-title' },'🇷🇺 ' + t('cardTitle'),React.createElement('span', { className: 'rl-badge ' + (ruActive ? 'rl-badge-ok' : 'rl-badge-warn') },ruActive ? t('badgeRu') : t('badgeEn')),React.createElement('span', { className: 'rl-badge rl-badge-ok' }, t('badgeCoverage'))
1695
- ),React.createElement('div', { className: 'rl-sub' },statusLine || t('cardSub'))),React.createElement('span', {className: 'rl-chev' + (open ? ' rl-chev-open' : ''),}, React.createElement(Chevron, null))),(page || open) && React.createElement('div', { className: 'rl-body' },React.createElement('div', { className: 'rl-page' },React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secLanguage')),React.createElement('span', { className: 'rl-badge ' + (ruActive ? 'rl-badge-ok' : 'rl-badge-dim') },ruActive ? t('badgeRu') : t('badgeEn'))
1696
- ),React.createElement('div', { className: 'rl-section-desc' }, t('secLanguageDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(ruActive, onEnabled, disabled),t('enabled')
1697
- )
1698
- ),React.createElement('div', { className: 'rl-item-desc' }, t('enabledDesc'))
1699
- ),React.createElement('div', { className: 'rl-hint-text' }, t('quickSwitchNote')),overridesCount > 0 ? React.createElement('div', { className: 'rl-badge rl-badge-dim', style: { alignSelf: 'flex-start' } },t('overridesCount') + ': ' + overridesCount) : null
1700
- ),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secTypography')),React.createElement('span', { className: 'rl-badge rl-badge-ok' }, t('badgeSmartUx'))
1701
- ),React.createElement('div', { className: 'rl-section-desc' }, t('secTypographyDesc')),React.createElement('div', { className: 'rl-grid-2' },React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(typography.enabled !== false && ruActive,(ev) => setTypo({ enabled: ev.target.checked }), !ruActive || disabled),t('typography')
1702
- )
1703
- ),React.createElement('div', { className: 'rl-item-desc' }, t('typographyDesc'))
1704
- ),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(typography.liveInput !== false && ruActive,(ev) => setTypo({ liveInput: ev.target.checked }), !ruActive || disabled),t('liveInput')
1705
- )
1706
- ),React.createElement('div', { className: 'rl-item-desc' }, t('liveInputDesc'))
1707
- ),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(typography.yo === true && ruActive,(ev) => setTypo({ yo: ev.target.checked }), !ruActive || disabled),t('yo')
1708
- )
1709
- ),React.createElement('div', { className: 'rl-item-desc' }, t('yoDesc'))
1710
- ),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(value.slashAliases !== false && ruActive,(ev) => {try { scope.set('slashAliases', ev.target.checked) } catch (err) { void err; }
1711
- }, !ruActive || disabled),t('slashAliases')
1712
- )
1713
- ),React.createElement('div', { className: 'rl-item-desc' }, t('slashAliasesDesc'))
1714
- )
1715
- ),React.createElement('div', { className: 'rl-hotkey-box' },React.createElement('span', { className: 'rl-hotkey-tag' }, '⌨️ Alt+L'),React.createElement('span', null, t('altLHintText'))
1716
- )
1717
- ),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secAgentPrompt')),React.createElement('span', { className: 'rl-badge ' + (value.agentPrompt ? 'rl-badge-ok' : 'rl-badge-dim') },value.agentPrompt ? 'Активен' : 'Выключен')
1718
- ),React.createElement('div', { className: 'rl-section-desc' }, t('secAgentPromptDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(value.agentPrompt === true,(ev) => {try {const r = scope.set('agentPrompt', ev.target.checked)
1486
+ return React.createElement('div', { className: page ? 'rl-page-seat' : 'rl-card' },React.createElement('button', {type: 'button',className: 'rl-head',style: page ? { display: 'none' } : undefined,'aria-expanded': page ? 'true' : String(open),onClick: () => setOpen(!open),},React.createElement('span', { className: 'rl-head-main' },React.createElement('div', { className: 'rl-title' },'🇷🇺 ' + t('cardTitle'),React.createElement('span', { className: 'rl-badge ' + (ruActive ? 'rl-badge-ok' : 'rl-badge-warn') },ruActive ? t('badgeRu') : t('badgeEn')),React.createElement('span', { className: 'rl-badge rl-badge-ok' }, t('badgeCoverage'))),React.createElement('div', { className: 'rl-sub' },statusLine || t('cardSub'))),React.createElement('span', {className: 'rl-chev' + (open ? ' rl-chev-open' : ''),}, React.createElement(Chevron, null))),(page || open) && React.createElement('div', { className: 'rl-body' },React.createElement('div', { className: 'rl-page' },React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secLanguage')),React.createElement('span', { className: 'rl-badge ' + (ruActive ? 'rl-badge-ok' : 'rl-badge-dim') },ruActive ? t('badgeRu') : t('badgeEn'))),React.createElement('div', { className: 'rl-section-desc' }, t('secLanguageDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(ruActive, onEnabled, disabled),t('enabled'))),React.createElement('div', { className: 'rl-item-desc' }, t('enabledDesc'))),React.createElement('div', { className: 'rl-hint-text' }, t('quickSwitchNote')),overridesCount > 0 ? React.createElement('div', { className: 'rl-badge rl-badge-dim', style: { alignSelf: 'flex-start' } },t('overridesCount') + ': ' + overridesCount) : null
1487
+ ),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secTypography')),React.createElement('span', { className: 'rl-badge rl-badge-ok' }, t('badgeSmartUx'))),React.createElement('div', { className: 'rl-section-desc' }, t('secTypographyDesc')),React.createElement('div', { className: 'rl-grid-2' },React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(typography.enabled !== false && ruActive,(ev) => setTypo({ enabled: ev.target.checked }), !ruActive || disabled),t('typography'))),React.createElement('div', { className: 'rl-item-desc' }, t('typographyDesc'))),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(typography.liveInput !== false && ruActive,(ev) => setTypo({ liveInput: ev.target.checked }), !ruActive || disabled),t('liveInput'))),React.createElement('div', { className: 'rl-item-desc' }, t('liveInputDesc'))),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(typography.yo === true && ruActive,(ev) => setTypo({ yo: ev.target.checked }), !ruActive || disabled),t('yo'))),React.createElement('div', { className: 'rl-item-desc' }, t('yoDesc'))),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(value.slashAliases !== false && ruActive,(ev) => {try { scope.set('slashAliases', ev.target.checked) } catch (err) { void err; }}, !ruActive || disabled),t('slashAliases'))),React.createElement('div', { className: 'rl-item-desc' }, t('slashAliasesDesc')))),React.createElement('div', { className: 'rl-hotkey-box' },React.createElement('span', { className: 'rl-hotkey-tag' }, '⌨️ Alt+L'),React.createElement('span', null, t('altLHintText')))),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secAgentPrompt')),React.createElement('span', { className: 'rl-badge ' + (value.agentPrompt ? 'rl-badge-ok' : 'rl-badge-dim') },value.agentPrompt ? 'Активен' : 'Выключен')),React.createElement('div', { className: 'rl-section-desc' }, t('secAgentPromptDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { className: 'rl-item-head' },React.createElement('label', { className: 'rl-item-label' },checkbox(value.agentPrompt === true,(ev) => {try {const r = scope.set('agentPrompt', ev.target.checked)
1719
1488
  if (r && typeof r.catch === 'function') r.catch((err) => {console.warn('dsh-russian-lang: scope.set agentPrompt failed', err && err.message || err)
1720
1489
  })
1721
1490
  } catch (err) {console.warn('dsh-russian-lang: scope.set agentPrompt sync threw', err && err.message || err)
1722
- }
1723
- }, false),t('agentPrompt')
1724
- )
1725
- ),React.createElement('div', { className: 'rl-item-desc' }, t('agentPromptDesc'))
1726
- ),value.agentPrompt ? React.createElement(React.Fragment, null,React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },React.createElement('label', { className: 'rl-item-label', style: { fontWeight: 500 } }, t('agentPromptPreset')),React.createElement('select', {className: 'rl-select',value: value.agentPromptPreset || 'technical_expert',onChange: (ev) => {try {const val = ev.target.value
1491
+ }}, false),t('agentPrompt'))),React.createElement('div', { className: 'rl-item-desc' }, t('agentPromptDesc'))),value.agentPrompt ? React.createElement(React.Fragment, null,React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },React.createElement('label', { className: 'rl-item-label', style: { fontWeight: 500 } }, t('agentPromptPreset')),React.createElement('select', {className: 'rl-select',value: value.agentPromptPreset || 'technical_expert',onChange: (ev) => {try {const val = ev.target.value
1727
1492
  scope.set('agentPromptPreset', val)
1728
- } catch (err) { console.warn('dsh-russian-lang: set agentPromptPreset failed', err) }
1729
- }
1730
- },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'))
1731
- )
1732
- ),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
1493
+ } 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
1733
1494
  ) : null
1734
1495
  ) : null
1735
- ),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secUpdater')),React.createElement('span', { className: 'rl-badge ' + (upStatus.updateAvailable ? 'rl-badge-warn' : 'rl-badge-ok') },upStatus.updateAvailable ? t('badgeUpdateAvailable') : t('badgeUpToDate'))
1736
- ),React.createElement('div', { className: 'rl-section-desc' }, t('secUpdaterDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },React.createElement('div', null,React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },t('updaterCurrent').replace('{version}', upStatus.currentVersion || '0.3.9')),upStatus.updateAvailable
1496
+ ),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.10')),upStatus.updateAvailable
1737
1497
  ? React.createElement('div', { style: { color: 'var(--dsw-alias-state-warning-primary)', marginTop: '2px', fontWeight: 500 } },t('updaterLatest').replace('{version}', upStatus.latestVersion || ''))
1738
- : React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', marginTop: '2px' } },t('updaterUpToDate'))
1739
- ),upStatus.updateAvailable
1498
+ : React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', marginTop: '2px' } },t('updaterUpToDate'))),upStatus.updateAvailable
1740
1499
  ? React.createElement('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: upLoading,onClick: triggerUpdate,}, upLoading ? t('updaterUpdating') : t('updaterBtn').replace('{version}', upStatus.latestVersion || ''))
1741
- : React.createElement('button', {type: 'button',className: 'rl-btn',disabled: upLoading,onClick: checkUpdate,}, upLoading ? t('updaterChecking') : t('updaterCheckBtn'))
1742
- ),upMsg ? React.createElement('div', {style: {marginTop: '10px', padding: '8px 12px', borderRadius: '8px',background: upMsg.type === 'ok' ? 'color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)' : 'color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)',color: upMsg.type === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontSize: '12px', fontWeight: 500
1743
- }
1744
- }, upMsg.text) : null
1745
- )
1746
- ),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secTranslator')),React.createElement('span', {className: 'rl-badge ' + (value.translateEngine === 'local' ? 'rl-badge-ok' : (value.translateEngine === 'google' ? 'rl-badge-warn' : 'rl-badge-dim'))
1747
- }, value.translateEngine === 'local' ? 'Локально' : (value.translateEngine === 'google' ? 'Google' : 'Выключен'))
1748
- ),React.createElement('div', { className: 'rl-section-desc' }, t('secTranslatorDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },React.createElement('label', { className: 'rl-item-label', style: { fontWeight: 500 } }, t('translateEngine')),React.createElement('select', {className: 'rl-select',value: value.translateEngine || 'off',onChange: (ev) => {try {const val = ev.target.value
1500
+ : React.createElement('button', {type: 'button',className: 'rl-btn',disabled: upLoading,onClick: checkUpdate,}, upLoading ? t('updaterChecking') : t('updaterCheckBtn'))),upMsg ? React.createElement('div', {style: {marginTop: '10px', padding: '8px 12px', borderRadius: '8px',background: upMsg.type === 'ok' ? 'color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)' : 'color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)',color: upMsg.type === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontSize: '12px', fontWeight: 500
1501
+ }}, upMsg.text) : null
1502
+ )),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secTranslator')),React.createElement('span', {className: 'rl-badge ' + (value.translateEngine === 'local' ? 'rl-badge-ok' : (value.translateEngine === 'google' ? 'rl-badge-warn' : 'rl-badge-dim'))
1503
+ }, value.translateEngine === 'local' ? 'Локально' : (value.translateEngine === 'google' ? 'Google' : 'Выключен'))),React.createElement('div', { className: 'rl-section-desc' }, t('secTranslatorDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px' } },React.createElement('label', { className: 'rl-item-label', style: { fontWeight: 500 } }, t('translateEngine')),React.createElement('select', {className: 'rl-select',value: value.translateEngine || 'off',onChange: (ev) => {try {const val = ev.target.value
1749
1504
  scope.set('translateEngine', val)
1750
1505
  if (val === 'local') fetchTransStatus()
1751
- } catch (err) { console.warn('dsh-russian-lang: set translateEngine failed', err) }
1752
- }
1753
- },React.createElement('option', { value: 'off' }, t('engineOff')),React.createElement('option', { value: 'local' }, t('engineLocal')),React.createElement('option', { value: 'google' }, t('engineGoogle'))
1754
- )
1755
- ),value.translateEngine === 'google' ? React.createElement('div', {style: {marginTop: '10px', padding: '8px 12px', borderRadius: '8px',background: 'color-mix(in srgb, var(--dsw-alias-state-warning-primary) 12%, transparent)', border: '1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary) 30%, transparent)',color: 'var(--dsw-alias-state-warning-primary)', fontSize: '12px', fontWeight: 500, lineHeight: 1.4
1756
- }
1757
- }, t('googleWarn')) : null,value.translateEngine === 'local' ? React.createElement('div', {style: { marginTop: '10px', display: 'flex', flexDirection: 'column', gap: '8px' }
1758
- },React.createElement('div', { style: { fontSize: '12px', color: 'var(--dsw-alias-label-secondary)' } }, t('localInfo')),React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },React.createElement('span', {className: 'rl-badge ' + (transStatus && transStatus.running ? 'rl-badge-ok' : 'rl-badge-dim')
1506
+ } catch (err) { console.warn('dsh-russian-lang: set translateEngine failed', err) }}},React.createElement('option', { value: 'off' }, t('engineOff')),React.createElement('option', { value: 'local' }, t('engineLocal')),React.createElement('option', { value: 'google' }, t('engineGoogle')))),value.translateEngine === 'google' ? React.createElement('div', {style: {marginTop: '10px', padding: '8px 12px', borderRadius: '8px',background: 'color-mix(in srgb, var(--dsw-alias-state-warning-primary) 12%, transparent)', border: '1px solid color-mix(in srgb, var(--dsw-alias-state-warning-primary) 30%, transparent)',color: 'var(--dsw-alias-state-warning-primary)', fontSize: '12px', fontWeight: 500, lineHeight: 1.4
1507
+ }}, t('googleWarn')) : null,value.translateEngine === 'local' ? React.createElement('div', {style: { marginTop: '10px', display: 'flex', flexDirection: 'column', gap: '8px' }},React.createElement('div', { style: { fontSize: '12px', color: 'var(--dsw-alias-label-secondary)' } }, t('localInfo')),React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },React.createElement('span', {className: 'rl-badge ' + (transStatus && transStatus.running ? 'rl-badge-ok' : 'rl-badge-dim')
1759
1508
  }, transStatus && transStatus.running ? t('localStatusRunning') : t('localStatusStopped')),(!transStatus || !transStatus.running) ? React.createElement('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: transStarting,onClick: startLibreTranslate
1760
1509
  }, transStarting ? t('localStarting') : t('localStartBtn')) : null
1761
1510
  ),transMsg ? React.createElement('div', {style: {marginTop: '6px', padding: '8px 12px', borderRadius: '8px',background: transMsg.type === 'ok' ? 'color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)' : 'color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)',color: transMsg.type === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', fontSize: '12px', fontWeight: 500
1762
- }
1763
- }, transMsg.text) : null
1511
+ }}, transMsg.text) : null
1764
1512
  ) : null
1765
- )
1766
- ),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secOverrides')),React.createElement('span', { className: 'rl-badge rl-badge-dim' },String(overridesCount) + ' ' + (typeof plural === 'function' ? plural(overridesCount, ['запись', 'записи', 'записей']) : 'записей'))
1767
- ),React.createElement('div', { className: 'rl-section-desc' }, t('secOverridesDesc')),React.createElement('div', { style: { display: 'flex', gap: '8px', flexWrap: 'wrap' } },React.createElement('input', {className: 'rl-select',style: { flex: '1 1 180px', maxWidth: 'none' },placeholder: t('overrideKeyPlaceholder'),value: newKey,onChange: (e) => setNewKey(e.target.value),}),React.createElement('input', {className: 'rl-select',style: { flex: '2 1 240px', maxWidth: 'none' },placeholder: t('overrideValuePlaceholder'),value: newVal,onChange: (e) => setNewVal(e.target.value),}),React.createElement('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: !newKey.trim() || !newVal.trim(),onClick: () => {const k = newKey.trim()
1513
+ )),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secOverrides')),React.createElement('span', { className: 'rl-badge rl-badge-dim' },String(overridesCount) + ' ' + (typeof plural === 'function' ? plural(overridesCount, ['запись', 'записи', 'записей']) : 'записей'))),React.createElement('div', { className: 'rl-section-desc' }, t('secOverridesDesc')),React.createElement('div', { style: { display: 'flex', gap: '8px', flexWrap: 'wrap' } },React.createElement('input', {className: 'rl-select',style: { flex: '1 1 180px', maxWidth: 'none' },placeholder: t('overrideKeyPlaceholder'),value: newKey,onChange: (e) => setNewKey(e.target.value),}),React.createElement('input', {className: 'rl-select',style: { flex: '2 1 240px', maxWidth: 'none' },placeholder: t('overrideValuePlaceholder'),value: newVal,onChange: (e) => setNewVal(e.target.value),}),React.createElement('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: !newKey.trim() || !newVal.trim(),onClick: () => {const k = newKey.trim()
1768
1514
  const v = newVal.trim()
1769
1515
  if (!k || !v) return
1770
1516
  const next = Object.assign({}, overrides, { [k]: v })
1771
1517
  try { scope.set('overrides', next) } catch (_e) { void _e }
1772
1518
  setNewKey('')
1773
1519
  setNewVal('')
1774
- },}, t('overrideAddBtn'))
1775
- ),overridesCount === 0
1520
+ },}, t('overrideAddBtn'))),overridesCount === 0
1776
1521
  ? React.createElement('div', { className: 'rl-hint-text', style: { fontStyle: 'italic', padding: '4px 0' } }, t('overrideEmpty'))
1777
1522
  : React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: '6px', marginTop: '6px' } },Object.keys(overrides).map((k) =>
1778
- React.createElement('div', {key: k,style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '6px 10px', background: 'var(--dsw-alias-bg-layer-3)', borderRadius: '6px', border: '1px solid var(--dsw-alias-border-l2)', fontSize: '12px' },},React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', overflow: 'hidden' } },React.createElement('code', { style: { color: 'var(--dsw-alias-state-brand-primary)', fontWeight: 600 } }, k),React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)' } }, '→'),React.createElement('span', { style: { color: 'var(--dsw-alias-label-primary)' } }, overrides[k])
1779
- ),React.createElement('button', {type: 'button',className: 'rl-btn',style: { padding: '2px 8px', height: '24px', fontSize: '11px', color: 'var(--dsw-alias-state-error-primary)' },onClick: () => {const next = Object.assign({}, overrides)
1523
+ React.createElement('div', {key: k,style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '6px 10px', background: 'var(--dsw-alias-bg-layer-3)', borderRadius: '6px', border: '1px solid var(--dsw-alias-border-l2)', fontSize: '12px' },},React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', overflow: 'hidden' } },React.createElement('code', { style: { color: 'var(--dsw-alias-state-brand-primary)', fontWeight: 600 } }, k),React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)' } }, '→'),React.createElement('span', { style: { color: 'var(--dsw-alias-label-primary)' } }, overrides[k])),React.createElement('button', {type: 'button',className: 'rl-btn',style: { padding: '2px 8px', height: '24px', fontSize: '11px', color: 'var(--dsw-alias-state-error-primary)' },onClick: () => {const next = Object.assign({}, overrides)
1780
1524
  delete next[k]
1781
- try { scope.set('overrides', next) } catch (_e) { void _e }
1782
- },}, t('overrideDelete'))
1783
- )
1784
- )
1785
- ),React.createElement('div', { className: 'rl-hint-text', style: { marginTop: '8px' } }, t('overrideTip')),React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', flexWrap: 'wrap', marginTop: '10px' } },React.createElement('button', {type: 'button',className: 'rl-btn' + (isInspectorActive ? ' rl-btn-primary' : ''),onClick: () => {isInspectorActive = !isInspectorActive
1525
+ try { scope.set('overrides', next) } catch (_e) { void _e }},}, t('overrideDelete'))))),React.createElement('div', { className: 'rl-hint-text', style: { marginTop: '8px' } }, t('overrideTip')),React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', flexWrap: 'wrap', marginTop: '10px' } },React.createElement('button', {type: 'button',className: 'rl-btn' + (isInspectorActive ? ' rl-btn-primary' : ''),onClick: () => {isInspectorActive = !isInspectorActive
1786
1526
  updateInspectorPill()
1787
1527
  },}, isInspectorActive ? '🔍 Инспектор перевода (включён)' : '🔍 Включить инспектор перевода (Alt+I)'),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {try {navigator.clipboard.writeText(JSON.stringify(overrides, null, 2))
1788
1528
  alert('Оверрайды скопированы в буфер обмена как JSON!')
1789
- } catch (err) { void err; }
1790
- }
1791
- }, '📤 Экспорт JSON'),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {const input = prompt('Вставьте JSON с оверрайдами:')
1529
+ } catch (err) { void err; }}}, '📤 Экспорт JSON'),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {const input = prompt('Вставьте JSON с оверрайдами:')
1792
1530
  if (!input) return
1793
1531
  try {const parsed = JSON.parse(input)
1794
1532
  if (parsed && typeof parsed === 'object') {const merged = Object.assign({}, overrides, parsed)
1795
1533
  scope.set('overrides', merged)
1534
+ }} 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.10'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
1535
+ }, '💬 ' + t('reportIssue')),React.createElement('div', { className: 'rl-hint-text', style: { flex: 1 } },t('exportMdHint')))))))
1796
1536
  }
1797
- } catch (err) { alert('Ошибка разбора JSON: ' + (err && err.message || err)) }
1798
- }
1799
- }, '📥 Импорт JSON')
1800
- )
1801
- ),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%')
1802
- ),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'))
1803
- ),React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '1 225'),React.createElement('div', { className: 'rl-stat-label' }, t('statCoreKeys'))
1804
- ),React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '5 924'),React.createElement('div', { className: 'rl-stat-label' }, t('statPluginKeys'))
1805
- )
1806
- ),React.createElement('div', { className: 'rl-actions-row' },React.createElement('a', {href: makeIssueUrl({}, '0.3.9'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
1807
- }, '💬 ' + t('reportIssue')),React.createElement('div', { className: 'rl-hint-text', style: { flex: 1 } },t('exportMdHint')
1808
- )
1809
- )
1810
- )
1811
- )
1812
- )
1813
- )
1814
- }
1815
- const RL_CSS = [
1816
- '.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)}',].join('')
1537
+ 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)}'
1538
+ ].join('')
1817
1539
  const STYLE_ID = 'dsh-russian-lang-styles'
1818
1540
  if (typeof document !== 'undefined' && (document.getElementById && !document.getElementById(STYLE_ID))) {const tag = document.createElement('style')
1819
1541
  tag.id = STYLE_ID