dsh-think-translate 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.de.md CHANGED
@@ -33,7 +33,7 @@ Modelle der DeepSeek-Familie denken oft auf Chinesisch — oder in der Sprache,
33
33
  - **🎚️ Einstellbarer Übersetzungszeitpunkt** — alles vorübersetzen / alte Ketten lazy laden (Standard) / nur beim Aufklappen
34
34
  - **🔗 Dynamische Anbieter-Kette** — die Listenreihenfolge ist die Ausführungsreihenfolge: ziehen zum Sortieren, jede Zeile einzeln ein-/ausschalten. Integriert google gtx / bing / lokales Ollama plus beliebige eigene Endpunkte
35
35
  - **🔌 Eigene Anbieter (OpenAI & Anthropic)** — im Panel jeden OpenAI-kompatiblen Endpunkt (`/v1/chat/completions`) oder die **Anthropic Messages API** (Claude) hinzufügen: Typ, Vorlage, Basis-URL, API-Schlüssel, Modell
36
- - **🪄 DSH-konfigurierte Anbieter übernehmen** — liest schreibgeschützte DSH-Zeilen aus `settings.yaml` (`llm-pi-ai.providers`); eine Schaltfläche liest neu ein und fügt alle der Kette hinzu. Der Schlüssel wird pro Anfrage aus `.credentials.yaml` aufgelöst und nie in der Plugin-Konfiguration gespeichert
36
+ - **🪄 DSH-konfigurierte Anbieter übernehmen** — liest schreibgeschützte DSH-Zeilen aus `settings.yaml` (`llm-pi-ai.providers`); eine Schaltfläche liest neu ein und fügt alle der Kette hinzu. Der Schlüssel wird pro Anfrage aus `.credentials.yaml` aufgelöst und nie in der Plugin-Konfiguration gespeichert. Auch die eigene Standardroute des Harness zählt: zeigt `agent-default-model` auf `deepseek-official`, erscheint die offizielle DeepSeek-API als weitere DSH-Zeile
37
37
  - **⏱️ Robust** — 3 Wiederholungen mit Backoff + direkter Browser-Fallback, Test-Schaltfläche pro Zeile, Fehlschläge werden nie zwischengespeichert
38
38
 
39
39
  ## 📦 Installation
package/README.es.md CHANGED
@@ -33,7 +33,7 @@ Los modelos de la familia DeepSeek suelen razonar en chino — o en el idioma en
33
33
  - **🎚️ Momento de traducción ajustable** — pre-traducir todo / carga diferida de cadenas antiguas (por defecto) / solo al expandir
34
34
  - **🔗 Cadena de proveedores dinámica** — el orden de la lista es el orden de uso: arrastra para reordenar y activa o desactiva cada fila. Integrados google gtx / bing / Ollama local más cualquier endpoint personalizado
35
35
  - **🔌 Proveedores personalizados (OpenAI y Anthropic)** — añade desde el panel cualquier endpoint compatible con OpenAI (`/v1/chat/completions`) o la **Anthropic Messages API** (Claude): tipo, preajuste, URL base, clave de API y modelo
36
- - **🪄 Hereda los proveedores configurados en DSH** — detecta filas DSH de solo lectura desde `settings.yaml` (`llm-pi-ai.providers`) y un botón reescanea y las añade todas a la cadena; la clave se resuelve en cada petición desde `.credentials.yaml` y nunca se guarda en la config del plugin
36
+ - **🪄 Hereda los proveedores configurados en DSH** — detecta filas DSH de solo lectura desde `settings.yaml` (`llm-pi-ai.providers`) y un botón reescanea y las añade todas a la cadena; la clave se resuelve en cada petición desde `.credentials.yaml` y nunca se guarda en la config del plugin. La ruta por defecto del propio harness también cuenta: si `agent-default-model` apunta a `deepseek-official`, la API oficial de DeepSeek aparece como una fila DSH más
37
37
  - **⏱️ Resistente** — 3 reintentos con backoff + respaldo directo del navegador, botón de prueba por fila, los fallos nunca se almacenan en caché
38
38
 
39
39
  ## 📦 Instalación
package/README.fr.md CHANGED
@@ -33,7 +33,7 @@ Les modèles de la famille DeepSeek raisonnent souvent en chinois — ou dans la
33
33
  - **🎚️ Moment de traduction réglable** — tout pré-traduire / chargement différé des anciennes chaînes (défaut) / uniquement à l'ouverture
34
34
  - **🔗 Chaîne de fournisseurs dynamique** — l'ordre de la liste est l'ordre d'exécution : glissez pour réordonner, activez ou désactivez chaque ligne. Intégrés google gtx / bing / Ollama local, plus tout endpoint personnalisé
35
35
  - **🔌 Fournisseurs personnalisés (OpenAI et Anthropic)** — ajoutez depuis le panneau tout endpoint compatible OpenAI (`/v1/chat/completions`) ou l'**Anthropic Messages API** (Claude) : type, préréglage, URL de base, clé API et modèle
36
- - **🪄 Hérite des fournisseurs configurés dans DSH** — détecte les lignes DSH en lecture seule depuis `settings.yaml` (`llm-pi-ai.providers`), et un bouton relance l'analyse et les ajoute toutes à la chaîne ; la clé est résolue à chaque requête depuis `.credentials.yaml` et n'est jamais écrite dans la config du plugin
36
+ - **🪄 Hérite des fournisseurs configurés dans DSH** — détecte les lignes DSH en lecture seule depuis `settings.yaml` (`llm-pi-ai.providers`), et un bouton relance l'analyse et les ajoute toutes à la chaîne ; la clé est résolue à chaque requête depuis `.credentials.yaml` et n'est jamais écrite dans la config du plugin. La route par défaut du harness compte aussi : si `agent-default-model` désigne `deepseek-official`, l'API officielle DeepSeek apparaît comme une ligne DSH de plus
37
37
  - **⏱️ Résilient** — 3 tentatives avec backoff + secours direct du navigateur, bouton de test par ligne, les échecs ne sont jamais mis en cache
38
38
 
39
39
  ## 📦 Installation
package/README.ja.md CHANGED
@@ -33,7 +33,7 @@ DeepSeek 系モデルは中国語で考えることが多く、あるいはた
33
33
  - **🎚️ 翻訳タイミングを調整可能** — すべて事前翻訳 / 履歴を遅延ロード(既定)/ 展開時のみ翻訳
34
34
  - **🔗 動的プロバイダーチェーン** — 一覧の順序がそのまま実行順。ドラッグで並べ替え、行ごとにオン/オフ。組み込みの google gtx / bing / ローカル Ollama に加えて任意のカスタム端点
35
35
  - **🔌 カスタムプロバイダー(OpenAI / Anthropic)** — 設定パネルから任意の OpenAI 互換端点(`/v1/chat/completions`)や **Anthropic Messages API**(Claude)を追加:種類・プリセット・ベース URL・API キー・モデル
36
- - **🪄 DSH 設定のプロバイダーを引き継ぐ** — `settings.yaml`(`llm-pi-ai.providers`)から読み取り専用の DSH 行を自動検出し、ボタン1つで再スキャンしてまとめてチェーンに追加。キーは `.credentials.yaml` からリクエスト時に解決され、プラグインの設定には保存されません
36
+ - **🪄 DSH 設定のプロバイダーを引き継ぐ** — `settings.yaml`(`llm-pi-ai.providers`)から読み取り専用の DSH 行を自動検出し、ボタン1つで再スキャンしてまとめてチェーンに追加。キーは `.credentials.yaml` からリクエスト時に解決され、プラグインの設定には保存されません。harness 自身の既定ルートも対象です:`agent-default-model` が `deepseek-official` を指していれば、DeepSeek の公式 API も DSH 行として提供されます
37
37
  - **⏱️ 失敗に強い** — host は3回バックオフ再試行 + ブラウザ直接フォールバック、行ごとのテストボタン、失敗結果はキャッシュしない
38
38
 
39
39
  ## 📦 インストール
package/README.ko.md CHANGED
@@ -33,7 +33,7 @@ DeepSeek 계열 모델은 중국어로, 또는 우연히 생각에 쓰는 언어
33
33
  - **🎚️ 번역 시점 조절** — 모두 사전 번역 / 이전 체인 지연 로딩(기본)/ 펼친 체인만 번역
34
34
  - **🔗 동적 제공자 체인** — 목록 순서가 곧 실행 순서. 끌어서 정렬하고 행마다 켜고 끄기. 내장 google gtx / bing / 로컬 Ollama에 더해 임의의 사용자 지정 엔드포인트
35
35
  - **🔌 사용자 지정 제공자(OpenAI / Anthropic)** — 설정 패널에서 OpenAI 호환 엔드포인트(`/v1/chat/completions`)나 **Anthropic Messages API**(Claude)를 추가: 유형, 프리셋, 베이스 URL, API 키, 모델
36
- - **🪄 DSH 설정의 제공자 상속** — `settings.yaml`(`llm-pi-ai.providers`)에서 읽기 전용 DSH 행을 자동 발견하고, 버튼 한 번으로 다시 검사해 모두 체인에 추가. 키는 요청 시 `.credentials.yaml`에서 해석되며 플러그인 설정에 저장되지 않음
36
+ - **🪄 DSH 설정의 제공자 상속** — `settings.yaml`(`llm-pi-ai.providers`)에서 읽기 전용 DSH 행을 자동 발견하고, 버튼 한 번으로 다시 검사해 모두 체인에 추가. 키는 요청 시 `.credentials.yaml`에서 해석되며 플러그인 설정에 저장되지 않음. harness 자체의 기본 경로도 포함됩니다: `agent-default-model`이 `deepseek-official`을 가리키면 DeepSeek 공식 API도 DSH 행으로 제공됩니다
37
37
  - **⏱️ 실패에 강함** — host 3회 백오프 재시도 + 브라우저 직접 폴백, 행별 테스트 버튼, 실패 결과는 캐시하지 않음
38
38
 
39
39
  ## 📦 설치
package/README.md CHANGED
@@ -24,7 +24,7 @@ DeepSeek-class models often reason in Chinese — or in whatever language they h
24
24
  - **🌍 8 languages, one consistent UI** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский; the settings panel, thinking rows and task cards all follow your choice, and it persists across reloads
25
25
  - **🔗 Dynamic provider chain** — order providers by drag-and-drop and enable/disable each one. Built-ins (google gtx, bing, local Ollama) plus any number of custom providers; the chain already ends with the free providers, so a second fallback pass is a config-file option, not a UI one
26
26
  - **🔌 Custom providers (OpenAI & Anthropic)** — add arbitrary OpenAI-compatible endpoints (any `/v1/chat/completions` gateway) or native **Anthropic Messages API** endpoints (Claude) from the settings panel: name, type, base URL, API key, model
27
- - **🪄 DSH provider discovery** — providers already configured in DSH's `settings.yaml` (`llm-pi-ai.providers`, e.g. linuxdo-hub, coding-hub) are auto-discovered and appear in the chain as read-only "DSH" entries, and **one button in settings rescans and adds them all at once**; keys are resolved from `.credentials.yaml` at runtime and never written to the plugin's config
27
+ - **🪄 DSH provider discovery** — providers already configured in DSH's `settings.yaml` (`llm-pi-ai.providers`, e.g. linuxdo-hub, coding-hub) are auto-discovered and appear in the chain as read-only "DSH" entries, and **one button in settings rescans and adds them all at once**; keys are resolved from `.credentials.yaml` at runtime and never written to the plugin's config. The harness's own default route counts too: when `agent-default-model` points at `deepseek-official`, DeepSeek's API is offered as one more DSH entry
28
28
  - **🔒 Private & offline-first** — local Ollama (qwen2.5:7b / 14b or custom) is a first-class provider: free, unlimited, nothing leaves your machine. First local-model selection **auto-downloads** the model with a live progress bar and enables it when done
29
29
  - **🧠 Zero context cost** — pure display layer: the model still sees the original text, and translated text never consumes the context window
30
30
  - **☁️ Google / Bing fallback** — automatic switch when other providers are unavailable (google goes through a Node CONNECT tunnel using the system proxy, bypassing anti-bot blocks)
package/README.ru.md CHANGED
@@ -33,7 +33,7 @@
33
33
  - **🎚️ Настраиваемый момент перевода** — переводить всё / ленивая загрузка старых цепочек (по умолчанию) / только при раскрытии
34
34
  - **🔗 Динамическая цепочка провайдеров** — порядок списка и есть порядок обращения: перетаскивайте для сортировки, включайте и выключайте каждую строку. Встроенные google gtx / bing / локальный Ollama плюс любые свои эндпоинты
35
35
  - **🔌 Свои провайдеры (OpenAI и Anthropic)** — из панели можно добавить любой OpenAI-совместимый эндпоинт (`/v1/chat/completions`) или **Anthropic Messages API** (Claude): тип, шаблон, базовый URL, ключ API, модель
36
- - **🪄 Наследование провайдеров из DSH** — читает из `settings.yaml` (`llm-pi-ai.providers`) строки DSH только для чтения; одна кнопка заново сканирует и добавляет их все в цепочку. Ключ разрешается на каждый запрос из `.credentials.yaml` и никогда не попадает в конфиг плагина
36
+ - **🪄 Наследование провайдеров из DSH** — читает из `settings.yaml` (`llm-pi-ai.providers`) строки DSH только для чтения; одна кнопка заново сканирует и добавляет их все в цепочку. Ключ разрешается на каждый запрос из `.credentials.yaml` и никогда не попадает в конфиг плагина. Собственный маршрут харнесса тоже учитывается: если `agent-default-model` указывает на `deepseek-official`, официальный API DeepSeek появляется как ещё одна строка DSH
37
37
  - **⏱️ Устойчивость** — 3 повтора с backoff + прямой запрос из браузера, кнопка проверки в каждой строке, ошибки не кэшируются
38
38
 
39
39
  ## 📦 Установка
package/README.zh-CN.md CHANGED
@@ -33,7 +33,7 @@ DeepSeek 系模型经常用中文思考——或者用它们碰巧习惯的语
33
33
  - **🎚️ 可调翻译时机** — 三档:全部预翻译 / 懒加载历史(默认)/ 仅展开时翻译
34
34
  - **🔗 动态提供方链** — 列表顺序就是投递顺序:拖动排序、逐行开关;内置 google gtx / bing / 本地 Ollama,外加任意数量的自定义端点
35
35
  - **🔌 自定义提供方(OpenAI 与 Anthropic)** — 设置面板里可添加任意 OpenAI 兼容端点(任何 `/v1/chat/completions` 网关)或原生 **Anthropic Messages API**(Claude):类型、预设、接口地址、API 密钥、模型
36
- - **🪄 继承 DSH 已配置的提供方** — 自动发现 `settings.yaml`(`llm-pi-ai.providers`)里的只读 DSH 行,一个按钮重新扫描并全部加入链;密钥在请求时从 `.credentials.yaml` 解析,绝不写入插件配置
36
+ - **🪄 继承 DSH 已配置的提供方** — 自动发现 `settings.yaml`(`llm-pi-ai.providers`)里的只读 DSH 行,一个按钮重新扫描并全部加入链;密钥在请求时从 `.credentials.yaml` 解析,绝不写入插件配置;harness 自己的默认路线也算:`agent-default-model` 指向 `deepseek-official` 时,DeepSeek 官方 API 也会作为一条 DSH 条目提供
37
37
  - **⏱️ 失败韧性** — host 请求 3 次退避重试 + 浏览器直连兜底、每行独立的测试按钮、失败结果不缓存
38
38
 
39
39
  ## 📦 安装
package/lib/client.js CHANGED
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
23
23
  // there yet (an older process still serving the port), and the contract test
24
24
  // pins PLUGIN_VERSION to package.json so the two can never drift.
25
25
  var PLUGIN_NAME = "dsh-think-translate";
26
- var PLUGIN_VERSION = "1.2.1";
26
+ var PLUGIN_VERSION = "1.2.2";
27
27
  var PLUGIN_REPO = "https://github.com/UncleK/dsh-think-translate";
28
28
  // The official MarkdownText contract changed in 0.1.2-alpha: it now takes a
29
29
  // nested `labels` object and dereferences it without defaults inside its code
@@ -142,7 +142,7 @@ window.__ModuleLoader__.load({
142
142
  ".xl-provider-row:active{opacity:.55}",
143
143
  ".xl-provider-type{font-size:11px;opacity:.6;flex:0 0 auto}",
144
144
  ".xl-provider-name{font-size:12px;font-weight:500;flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
145
- ".xl-badge{font-size:9px;padding:1px 5px;border-radius:4px;background:var(--dsw-alias-brand-primary,#4c9aff);color:#fff;font-weight:600}",
145
+ ".xl-badge{font-size:10px;line-height:16px;padding:0 6px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.45));background:transparent;color:inherit;opacity:.7;font-weight:500;white-space:nowrap}",
146
146
  ".xl-add-form{display:flex;flex-direction:column;gap:6px;padding:8px;background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.08));border-radius:8px;margin:4px 0}",
147
147
  ".xl-form-row{display:flex;align-items:center;gap:8px}",
148
148
  ".xl-form-label{flex:0 0 62px;font-size:11.5px;opacity:.65}",
@@ -155,7 +155,6 @@ window.__ModuleLoader__.load({
155
155
  ".xl-menu-item:hover{background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.14))}",
156
156
  ".xl-menu-item-active{color:var(--dsw-alias-brand-primary,#4c9aff);font-weight:600}",
157
157
  ".xl-menu-note{font-size:11px;opacity:.55;padding:4px 8px 2px;margin-top:2px;border-top:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.2))}",
158
- ".xl-dsh-menu{margin:4px 0;padding:6px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.35));border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.06))}",
159
158
  ".xl-caret:disabled{opacity:.35;cursor:default}",
160
159
  ".xl-unused{opacity:.85}",
161
160
  ".xl-kv-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
@@ -170,6 +169,7 @@ window.__ModuleLoader__.load({
170
169
  ".xl-model-item-active{border-color:var(--dsw-alias-brand-primary,#4c9aff);color:var(--dsw-alias-brand-primary,#4c9aff)}",
171
170
  ".xl-x-btn{border:0;background:none;color:inherit;opacity:.5;cursor:pointer;font-size:15px;line-height:1;padding:0 6px;border-radius:4px}",
172
171
  ".xl-x-btn:hover{opacity:1;background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.12))}",
172
+ ".xl-add-btn{padding:1px 7px;font-size:14px;line-height:1.35;font-weight:600}",
173
173
  ".xl-provider-row .xl-sel{max-width:260px}",
174
174
  ".xl-model-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap}",
175
175
  ".xl-model-row .xl-sel{max-width:220px}",
@@ -260,9 +260,12 @@ window.__ModuleLoader__.load({
260
260
  providerBaseURL: "接口地址",
261
261
  providerApiKey: "API 密钥",
262
262
  footnotes: "脚注",
263
- dshImport: "继承 DSH 已配置的 API",
263
+ dshImport: "重新扫描 DSH 配置",
264
264
  dshImportAll: "全部加入链",
265
265
  dshImportEmpty: "DSH 里没有发现已配置的 provider",
266
+ dshRescanning: "正在重新扫描 DSH 配置…",
267
+ dshRescanDone: "重新扫描完成",
268
+ providerDshList: "DSH 已配置提供方",
266
269
  starCta: "给个星星鼓励一下",
267
270
  providerPreset: "提供方",
268
271
  currentInUse: "当前使用",
@@ -281,7 +284,7 @@ window.__ModuleLoader__.load({
281
284
  providerOff: "关闭",
282
285
  providerSaved: "已保存",
283
286
  providerDeleted: "已删除",
284
- providerDrag: "拖动排序",
287
+ providerDrag: "翻译优先级(拖动排序)",
285
288
  providerCancel: "取消",
286
289
  providerSave: "保存",
287
290
  providerSaveError: "保存失败",
@@ -308,9 +311,12 @@ window.__ModuleLoader__.load({
308
311
  providerBaseURL: "Base URL",
309
312
  providerApiKey: "API key",
310
313
  footnotes: "Footnotes",
311
- dshImport: "Import from DSH config",
314
+ dshImport: "Rescan DSH config",
312
315
  dshImportAll: "Add all to chain",
313
316
  dshImportEmpty: "No provider configured in DSH",
317
+ dshRescanning: "Rescanning the DSH config…",
318
+ dshRescanDone: "Rescan complete",
319
+ providerDshList: "Configured in DSH",
314
320
  starCta: "Star it on GitHub",
315
321
  providerPreset: "Provider",
316
322
  currentInUse: "In use",
@@ -329,7 +335,7 @@ window.__ModuleLoader__.load({
329
335
  providerOff: "off",
330
336
  providerSaved: "Saved",
331
337
  providerDeleted: "Deleted",
332
- providerDrag: "Drag to reorder",
338
+ providerDrag: "Translation priority (drag to reorder)",
333
339
  providerCancel: "Cancel",
334
340
  providerSave: "Save",
335
341
  providerSaveError: "Save failed",
@@ -356,9 +362,12 @@ window.__ModuleLoader__.load({
356
362
  providerApiKey: "API キー",
357
363
  copy: "コピー", copied: "コピー済み",
358
364
  footnotes: "脚注",
359
- dshImport: "DSH の設定から取り込む",
365
+ dshImport: "DSH 設定を再スキャン",
360
366
  dshImportAll: "すべてチェーンに追加",
361
367
  dshImportEmpty: "DSH に設定済みプロバイダーがありません",
368
+ dshRescanning: "DSH 設定を再スキャン中…",
369
+ dshRescanDone: "再スキャン完了",
370
+ providerDshList: "DSH に設定済みの提供元",
362
371
  starCta: "GitHub でスターを",
363
372
  providerPreset: "提供元",
364
373
  currentInUse: "使用中",
@@ -377,7 +386,7 @@ window.__ModuleLoader__.load({
377
386
  providerOff: "オフ",
378
387
  providerSaved: "保存しました",
379
388
  providerDeleted: "削除しました",
380
- providerDrag: "ドラッグで並べ替え",
389
+ providerDrag: "翻訳の優先順位(ドラッグで並べ替え)",
381
390
  providerCancel: "キャンセル",
382
391
  providerSave: "保存",
383
392
  providerSaveError: "保存に失敗",
@@ -404,9 +413,12 @@ window.__ModuleLoader__.load({
404
413
  providerApiKey: "API 키",
405
414
  copy: "복사", copied: "복사됨",
406
415
  footnotes: "각주",
407
- dshImport: "DSH 설정에서 가져오기",
416
+ dshImport: "DSH 설정 다시 검사",
408
417
  dshImportAll: "모두 체인에 추가",
409
418
  dshImportEmpty: "DSH에 구성된 제공자가 없습니다",
419
+ dshRescanning: "DSH 설정을 다시 검사하는 중…",
420
+ dshRescanDone: "다시 검사 완료",
421
+ providerDshList: "DSH에 설정된 제공자",
410
422
  starCta: "GitHub에서 별 주기",
411
423
  providerPreset: "제공자",
412
424
  currentInUse: "사용 중",
@@ -425,7 +437,7 @@ window.__ModuleLoader__.load({
425
437
  providerOff: "끄기",
426
438
  providerSaved: "저장됨",
427
439
  providerDeleted: "삭제됨",
428
- providerDrag: "끌어서 순서 변경",
440
+ providerDrag: "번역 우선순위(끌어서 정렬)",
429
441
  providerCancel: "취소",
430
442
  providerSave: "저장",
431
443
  providerSaveError: "저장 실패",
@@ -452,9 +464,12 @@ window.__ModuleLoader__.load({
452
464
  providerApiKey: "Clave de API",
453
465
  copy: "Copiar", copied: "Copiado",
454
466
  footnotes: "Notas al pie",
455
- dshImport: "Importar desde la config de DSH",
467
+ dshImport: "Reescanear la config de DSH",
456
468
  dshImportAll: "Añadir todo a la cadena",
457
469
  dshImportEmpty: "No hay proveedores configurados en DSH",
470
+ dshRescanning: "Reescaneando la config de DSH…",
471
+ dshRescanDone: "Reescaneo completado",
472
+ providerDshList: "Configurados en DSH",
458
473
  starCta: "Danos una estrella",
459
474
  providerPreset: "Proveedor",
460
475
  currentInUse: "En uso",
@@ -473,7 +488,7 @@ window.__ModuleLoader__.load({
473
488
  providerOff: "Desactivado",
474
489
  providerSaved: "Guardado",
475
490
  providerDeleted: "Eliminado",
476
- providerDrag: "Arrastra para reordenar",
491
+ providerDrag: "Prioridad de traducción (arrastra para reordenar)",
477
492
  providerCancel: "Cancelar",
478
493
  providerSave: "Guardar",
479
494
  providerSaveError: "Error al guardar",
@@ -500,9 +515,12 @@ window.__ModuleLoader__.load({
500
515
  providerApiKey: "Clé API",
501
516
  copy: "Copier", copied: "Copié",
502
517
  footnotes: "Notes de bas de page",
503
- dshImport: "Importer depuis la config DSH",
518
+ dshImport: "Réanalyser la config DSH",
504
519
  dshImportAll: "Tout ajouter à la chaîne",
505
520
  dshImportEmpty: "Aucun fournisseur configuré dans DSH",
521
+ dshRescanning: "Réanalyse de la config DSH…",
522
+ dshRescanDone: "Réanalyse terminée",
523
+ providerDshList: "Configurés dans DSH",
506
524
  starCta: "Mettre une étoile",
507
525
  providerPreset: "Fournisseur",
508
526
  currentInUse: "En cours d'utilisation",
@@ -521,7 +539,7 @@ window.__ModuleLoader__.load({
521
539
  providerOff: "Désactivé",
522
540
  providerSaved: "Enregistré",
523
541
  providerDeleted: "Supprimé",
524
- providerDrag: "Glisser pour réordonner",
542
+ providerDrag: "Priorité de traduction (glisser pour réordonner)",
525
543
  providerCancel: "Annuler",
526
544
  providerSave: "Enregistrer",
527
545
  providerSaveError: "Échec de l'enregistrement",
@@ -548,9 +566,12 @@ window.__ModuleLoader__.load({
548
566
  providerApiKey: "API-Schlüssel",
549
567
  copy: "Kopieren", copied: "Kopiert",
550
568
  footnotes: "Fußnoten",
551
- dshImport: "Aus DSH-Konfiguration übernehmen",
569
+ dshImport: "DSH-Konfiguration neu einlesen",
552
570
  dshImportAll: "Alle zur Kette hinzufügen",
553
571
  dshImportEmpty: "Kein konfigurierter Anbieter in DSH",
572
+ dshRescanning: "DSH-Konfiguration wird neu eingelesen…",
573
+ dshRescanDone: "Neu eingelesen",
574
+ providerDshList: "In DSH konfiguriert",
554
575
  starCta: "Mit einem Stern danken",
555
576
  providerPreset: "Anbieter",
556
577
  currentInUse: "In Benutzung",
@@ -569,7 +590,7 @@ window.__ModuleLoader__.load({
569
590
  providerOff: "Aus",
570
591
  providerSaved: "Gespeichert",
571
592
  providerDeleted: "Gelöscht",
572
- providerDrag: "Zum Sortieren ziehen",
593
+ providerDrag: "Übersetzungspriorität (zum Sortieren ziehen)",
573
594
  providerCancel: "Abbrechen",
574
595
  providerSave: "Speichern",
575
596
  providerSaveError: "Speichern fehlgeschlagen",
@@ -596,9 +617,12 @@ window.__ModuleLoader__.load({
596
617
  providerApiKey: "Ключ API",
597
618
  copy: "Копировать", copied: "Скопировано",
598
619
  footnotes: "Сноски",
599
- dshImport: "Импортировать из конфигурации DSH",
620
+ dshImport: "Пересканировать конфиг DSH",
600
621
  dshImportAll: "Добавить все в цепочку",
601
622
  dshImportEmpty: "В DSH нет настроенных провайдеров",
623
+ dshRescanning: "Пересканирую конфиг DSH…",
624
+ dshRescanDone: "Сканирование завершено",
625
+ providerDshList: "Настроены в DSH",
602
626
  starCta: "Поставить звезду",
603
627
  providerPreset: "Провайдер",
604
628
  currentInUse: "Используется",
@@ -617,7 +641,7 @@ window.__ModuleLoader__.load({
617
641
  providerOff: "Выкл",
618
642
  providerSaved: "Сохранено",
619
643
  providerDeleted: "Удалено",
620
- providerDrag: "Перетащите для порядка",
644
+ providerDrag: "Приоритет перевода (перетащите для порядка)",
621
645
  providerCancel: "Отмена",
622
646
  providerSave: "Сохранить",
623
647
  providerSaveError: "Не удалось сохранить",
@@ -1974,34 +1998,30 @@ window.__ModuleLoader__.load({
1974
1998
  // URL or a key per provider.
1975
1999
  var dshAll = Object.keys(provs).filter(function (id) { return provs[id] && provs[id].source === "dsh"; });
1976
2000
  var dshAvailable = dshAll.filter(function (id) { return chain.indexOf(id) < 0; });
1977
- var openDshImport = function () {
2001
+ // The host already merges the harness's providers into the config it serves,
2002
+ // so they show up in the "available providers" list without being asked for.
2003
+ // What the button adds is the RE-READ: editing ~/.dsh/settings.yaml (or
2004
+ // fixing the home the host looks at) otherwise needs a web restart.
2005
+ var rescanDsh = function () {
2006
+ setMsg(t.dshRescanning);
1978
2007
  fetch("/_xlate/dsh-scan").then(function (r) { return r.json(); })
1979
2008
  .then(function (c) {
1980
- if (c && c.providers) { cfgRef.current = c; setCfg(c); }
1981
- setAdding(false);
1982
- setEditingId(null);
1983
- setDshMenu(true);
2009
+ if (!c || !c.providers) throw new Error("bad scan response");
2010
+ cfgRef.current = c;
2011
+ setCfg(c);
2012
+ var d = c.dshScan || {};
2013
+ var found = (d.providers || []).length;
2014
+ setMsg(found
2015
+ ? t.dshRescanDone + " · " + found
2016
+ : t.dshImportEmpty + (d.home ? " · " + d.home + "/settings.yaml" : ""));
1984
2017
  })
1985
2018
  .catch(function (e) { setMsg(t.connFail + ": " + (e && e.message ? e.message : e)); });
1986
2019
  };
1987
- // Where the host looked for the harness settings, and what it found there:
1988
- // an empty scan used to be a dead end, so the panel now names the file it
1989
- // read (the usual cause is a home directory the plugin guessed wrong).
1990
- var dshScanNote = function () {
1991
- var d = cfg && cfg.dshScan;
1992
- if (!d || !d.home) return null;
1993
- var where = d.home + "/settings.yaml";
1994
- var detail = d.settingsFound
1995
- ? (d.providers && d.providers.length ? d.providers.join(", ") : t.dshImportEmpty)
1996
- : (t.dshImportEmpty + " · " + where);
1997
- return createElement("div", { className: "xl-note", style: { marginTop: "4px" } }, detail);
1998
- };
1999
2020
  var addAllDsh = function () {
2000
2021
  var next = chain.slice();
2001
2022
  dshAvailable.forEach(function (id) { if (next.indexOf(id) < 0) next.push(id); });
2002
2023
  applyLocal({ chain: next });
2003
2024
  saveCfg({ chain: next });
2004
- setDshMenu(false);
2005
2025
  };
2006
2026
 
2007
2027
  // Local-model menu: opened by the chip on the endpoint's own row and
@@ -2164,10 +2184,23 @@ window.__ModuleLoader__.load({
2164
2184
  var unused = Object.keys(provs).filter(function (id) {
2165
2185
  return chain.indexOf(id) < 0;
2166
2186
  });
2187
+ var unusedDsh = unused.filter(function (id) { return provs[id] && provs[id].source === "dsh"; });
2167
2188
  var unusedArea = null;
2168
2189
  if (unused.length > 0) {
2190
+ // The heading names what the rows are — in practice the DSH entries the
2191
+ // host discovered; the generic wording stays for a custom provider that
2192
+ // was saved without a chain reference.
2193
+ var allDsh = unusedDsh.length === unused.length;
2169
2194
  unusedArea = createElement("div", { className: "xl-unused", style: { marginTop: "8px" } },
2170
- createElement("div", { className: "xl-note", style: { fontWeight: 600 } }, t.providerUnused),
2195
+ // The heading row carries the bulk action: the rows are right here, so
2196
+ // this is the one place that lists them (the old separate "inherit"
2197
+ // menu listed the same providers a second time).
2198
+ createElement("div", { style: { display: "flex", alignItems: "center", gap: "6px" } },
2199
+ createElement("div", { className: "xl-note", style: { fontWeight: 600, flex: "1 1 auto" } },
2200
+ allDsh ? t.providerDshList : t.providerUnused),
2201
+ unusedDsh.length > 1 ? createElement("button", {
2202
+ className: "xl-btn", type: "button", onClick: addAllDsh
2203
+ }, t.dshImportAll) : null),
2171
2204
  unused.map(function (id) {
2172
2205
  var up = provs[id] || {};
2173
2206
  var isDsh = up.source === "dsh";
@@ -2175,7 +2208,13 @@ window.__ModuleLoader__.load({
2175
2208
  createElement("span", { className: "xl-provider-type" }, providerLabel(id, up, t)),
2176
2209
  createElement("span", { className: "xl-provider-name" }, id),
2177
2210
  isDsh ? createElement("span", { className: "xl-badge" }, t.providerDsh) : null,
2178
- createElement("button", { className: "xl-btn", type: "button", onClick: function () { actions.addToChain(id); } }, "+ " + t.providerAddToChain));
2211
+ // A bare "+" with the wording in the tooltip: one narrow control
2212
+ // at the end of the row, same as the ↑↓/测试 cluster above.
2213
+ createElement("button", {
2214
+ className: "xl-btn xl-add-btn", type: "button",
2215
+ title: t.providerAddToChain, "aria-label": t.providerAddToChain,
2216
+ onClick: function () { actions.addToChain(id); }
2217
+ }, "+"));
2179
2218
  }));
2180
2219
  }
2181
2220
 
@@ -2187,29 +2226,8 @@ window.__ModuleLoader__.load({
2187
2226
  : createElement("button", { className: "xl-btn", type: "button", onClick: startAdd, style: { marginTop: "4px" } },
2188
2227
  "+ " + t.providerAdd),
2189
2228
  adding || editingId ? null
2190
- : createElement("button", { className: "xl-btn", type: "button", onClick: openDshImport, style: { marginTop: "4px", marginLeft: "6px" } },
2191
- "+ " + t.dshImport + (dshAvailable.length ? " · " + dshAvailable.length : "")),
2192
- dshMenu ? createElement("div", { className: "xl-dsh-menu" },
2193
- dshAvailable.length === 0
2194
- ? createElement("div", null,
2195
- createElement("div", { className: "xl-note" }, t.dshImportEmpty),
2196
- dshScanNote())
2197
- : createElement("div", null,
2198
- dshAvailable.length > 1 ? createElement("button", {
2199
- className: "xl-btn", type: "button", style: { marginBottom: "4px" }, onClick: addAllDsh
2200
- }, t.dshImportAll) : null,
2201
- dshAvailable.map(function (id) {
2202
- var dp = provs[id] || {};
2203
- return createElement("div", { key: id, className: "xl-provider-row" },
2204
- createElement("span", { className: "xl-badge" }, t.providerDsh),
2205
- createElement("span", { className: "xl-provider-name" }, id),
2206
- createElement("span", { className: "xl-provider-type" }, providerLabel(id, dp, t)),
2207
- dp.model ? createElement("span", { className: "xl-provider-type" }, dp.model) : null,
2208
- createElement("button", {
2209
- className: "xl-btn", type: "button",
2210
- onClick: function () { actions.addToChain(id); }
2211
- }, "+ " + t.providerAddToChain));
2212
- }))) : null,
2229
+ : createElement("button", { className: "xl-btn", type: "button", onClick: rescanDsh, style: { marginTop: "4px", marginLeft: "6px" } },
2230
+ "↻ " + t.dshImport),
2213
2231
  unusedArea,
2214
2232
  msg ? createElement("div", { className: "xl-note", style: { marginTop: "6px" } }, msg) : null);
2215
2233
  }
package/lib/index.js CHANGED
@@ -771,6 +771,38 @@ function extractDshProviders(txt) {
771
771
  return providers
772
772
  }
773
773
 
774
+ // The harness ALSO has one built-in provider route that never shows up in
775
+ // llm-pi-ai.providers: `deepseek-official`, registered by llm-deepseek and
776
+ // configured through `agent-default-model` (provider + model) plus a credential
777
+ // ref. Its transport is OpenAI-compatible — llm-deepseek itself posts to
778
+ // `${baseURL}/chat/completions` with PUBLIC_BASE_URL = https://api.deepseek.com
779
+ // and the DEEPSEEK_API_KEY credential — so this plugin can drive it with the
780
+ // same adapter, which is what lets "inherit from DSH" offer DeepSeek's own API
781
+ // and not just the custom endpoints someone declared by hand.
782
+ const BUILTIN_HARNESS_ROUTES = {
783
+ 'deepseek-official': { type: 'openai', baseURL: 'https://api.deepseek.com', apiKeyEnv: 'DEEPSEEK_API_KEY' },
784
+ }
785
+
786
+ // Returns { id, entry } for the harness's default model route when the plugin
787
+ // knows how to speak to it, else null. `refs` is the credential map, used only to
788
+ // confirm the key exists somewhere.
789
+ function extractHarnessDefaultRoute(txt, refs) {
790
+ const block = txt.match(/^agent-default-model:[^\n]*\n((?:[ \t]+[^\n]*\n?)*)/m)
791
+ if (!block) return null
792
+ const provider = (block[1].match(/^[ \t]+provider:[ \t]*(\S+)/m) || [])[1]
793
+ const model = (block[1].match(/^[ \t]+model:[ \t]*(\S+)/m) || [])[1]
794
+ if (!provider || !model) return null
795
+ const route = BUILTIN_HARNESS_ROUTES[provider]
796
+ if (!route) return null
797
+ const entry = { type: route.type, baseURL: route.baseURL, model: model }
798
+ // The ref is the route's documented credential; carry it even when the user
799
+ // keeps the key in the environment instead of .credentials.yaml, because the
800
+ // host resolves apiKeyEnv from both.
801
+ if (route.apiKeyEnv) entry.apiKeyEnv = route.apiKeyEnv
802
+ void refs
803
+ return { id: provider, entry: entry }
804
+ }
805
+
774
806
  // (2026-09-01) Parse .credentials.yaml refs. Returns { envName: value }.
775
807
  function extractCredRefs(txt) {
776
808
  const refs = {}
@@ -836,6 +868,13 @@ async function discoverDshProviders() {
836
868
  out[name] = entry
837
869
  diag.providers.push(name)
838
870
  }
871
+ // The harness's built-in route (deepseek-official): never in llm-pi-ai.providers,
872
+ // configured by agent-default-model. An id declared by hand wins.
873
+ const def = extractHarnessDefaultRoute(settingsTxt, refs)
874
+ if (def && !out[def.id]) {
875
+ out[def.id] = Object.assign({ source: 'dsh', enabled: false }, def.entry)
876
+ diag.providers.push(def.id)
877
+ }
839
878
  } catch (e) {
840
879
  diag.error = e instanceof Error ? e.message : String(e)
841
880
  }
@@ -1430,7 +1469,7 @@ export function apply(ctx) {
1430
1469
  // plugin system (which only reads name, inject, apply) ignores.
1431
1470
  export {
1432
1471
  buildChainFromPriority, migrateConfig, normalizeConfigPatch,
1433
- applyFullProviders, extractDshProviders, extractCredRefs,
1472
+ applyFullProviders, extractDshProviders, extractCredRefs, extractHarnessDefaultRoute,
1434
1473
  runChainFor, translateViaChain, removeFromChain,
1435
1474
  needsTranslate, hasProse, chunkText, fnv, deepMerge,
1436
1475
  structuredCloneSafe, ADAPTERS,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-think-translate",
3
3
  "description": "Display-layer translation for the DSH web app: thinking chain, task cards, and optional answer text, in 8 target languages (local Ollama / any OpenAI-compatible or Anthropic endpoint / Google / Bing, fail-open)",
4
- "version": "1.2.1",
4
+ "version": "1.2.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {