dsh-think-translate 1.0.5 → 1.0.7

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
@@ -4,7 +4,7 @@
4
4
 
5
5
  ---
6
6
 
7
- Übersetzung auf Anzeigeebene für die Web-Oberfläche von [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): Die **Gedankenkette (Think-Zeile), Aufgabenkarten und der Antworttext** werden in der gewählten Zielsprache angezeigt, während die Originale im Verlauf unverändert bleiben.
7
+ Übersetzung auf Anzeigeebene für die Web-Oberfläche von [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): Die **Gedankenkette (Think-Zeile), Aufgabenkarten und der Antworttext** werden in der gewählten Zielsprache angezeigt, während die Originale im Verlauf unverändert bleiben und der übersetzte Text **nie in den Modellkontext gelangt**.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
@@ -12,14 +12,19 @@
12
12
 
13
13
  ## ✨ Funktionen
14
14
 
15
+ Modelle der DeepSeek-Familie denken oft auf Chinesisch — oder in der Sprache, in der sie gerade denken. dsh-think-translate zeigt die Think-Zeile, Aufgabenkarten und die Antwort live in *Ihrer* Sprache an, wie Untertitel für das Denken des Modells.
16
+
15
17
  - **8 Zielsprachen** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
18
  - **Einsprachige Oberfläche** — Einstellungsbereich, Denkzeilen und Aufgabenkarten folgen der Zielsprache (kein zh/en-Gemisch); die Auswahl bleibt erhalten
17
19
  - **Lokales Modell zuerst** — nutzt Ihr lokales Ollama-Modell (qwen usw.): privat, offline, kostenlos. Die erste Auswahl **startet den Download automatisch** mit Fortschrittsbalken; das Modell wird danach automatisch konfiguriert und aktiviert
20
+ - **🧠 Null Kontextkosten** — reine Anzeigeschicht: das Modell sieht weiterhin den Originaltext, und übersetzter Text verbraucht niemals das Kontextfenster
18
21
  - **Google / Bing-Fallback** — automatische Umschaltung, wenn das lokale Modell nicht verfügbar ist (google nutzt einen Node-CONNECT-Tunnel über den Systemproxy)
19
22
  - **Code-Artefakte übersprungen** — Pfade, Befehle, URLs, Regexes und reine Codezeilen werden nie übersetzt
20
23
  - **Satzweise Batch-Übersetzung** — lange Denkketten werden in kleinen Sätzen übersetzt, damit lokale kleine Modelle Qualität behalten
24
+ - **🧩 Absatz- und satzweise Zerlegung** — lange Denkketten werden an Leerzeilen geteilt (Absatzstruktur bleibt erhalten) und zusätzlich satzweise gebündelt, damit kleine lokale Modelle Qualität behalten
21
25
  - **Streaming-Ausgabe** — Übersetzungen erscheinen während des Denkens batchweise; Think-Zeile aufklappen zum Vergleich mit dem Original
22
26
  - **Robust** — Host-Anfragen mit Backoff (3×), Browser-Direkt-Fallback, Fehlschläge werden nie gecacht
27
+ - **🎚️ Einstellbarer Übersetzungszeitpunkt** — alles vorübersetzen / alte Ketten lazy laden (Standard) / nur beim Aufklappen
23
28
 
24
29
  ## 📦 Installation
25
30
 
@@ -51,6 +56,12 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
51
56
  - **google gtx / bing** — funktioniert sofort (Systemproxy / VPN automatisch)
52
57
  4. Nachricht senden und die Think-Zeile aufklappen, um die Übersetzung zu sehen
53
58
 
59
+ ## 🎬 Demo
60
+
61
+ ![dsh-think-translate demo](demo/demo.gif)
62
+
63
+ ![dsh-think-translate demo 2](demo/demo2.gif)
64
+
54
65
  ## ⚙️ Funktionsweise
55
66
 
56
67
  ```
package/README.es.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ---
6
6
 
7
- Traducción en la capa de visualización para la interfaz web de [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): la **cadena de pensamiento (fila Think), las tarjetas de tareas y el texto de respuesta** se muestran en el idioma de destino elegido, mientras los originales permanecen intactos en la transcripción.
7
+ Traducción en la capa de visualización para la interfaz web de [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): la **cadena de pensamiento (fila Think), las tarjetas de tareas y el texto de respuesta** se muestran en el idioma de destino elegido, mientras los originales permanecen intactos en la transcripción y el texto traducido **nunca entra en el contexto del modelo**.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
@@ -12,14 +12,19 @@ Traducción en la capa de visualización para la interfaz web de [DeepSeek Harne
12
12
 
13
13
  ## ✨ Características
14
14
 
15
+ Los modelos de la familia DeepSeek suelen razonar en chino — o en el idioma en que les da por pensar. dsh-think-translate muestra la fila Think, las tarjetas de tareas y la respuesta en *tu* idioma mientras miras, como subtítulos del pensamiento del modelo.
16
+
15
17
  - **8 idiomas de destino** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
18
  - **Interfaz en un solo idioma** — panel de ajustes, filas de pensamiento y tarjetas de tareas siguen el idioma de destino (sin mezclar zh/en); la elección persiste
17
19
  - **Modelo local primero** — usa tu modelo local de Ollama (qwen, etc.): privado, sin conexión, gratis. La primera selección **activa la descarga automática** con barra de progreso; el modelo se configura y habilita al terminar
20
+ - **🧠 Coste de contexto cero** — capa de visualización pura: el modelo sigue viendo el texto original y el texto traducido nunca consume la ventana de contexto
18
21
  - **Respaldo Google / Bing** — cambio automático si el modelo local no está disponible (google usa un túnel CONNECT de Node con el proxy del sistema)
19
22
  - **Artefactos de código omitidos** — rutas, comandos, URL, regex y líneas de código puro nunca se traducen
20
23
  - **Traducción por lotes de frases** — las cadenas largas se traducen en lotes pequeños para mantener la calidad en modelos locales pequeños
24
+ - **🧩 Fragmentación por párrafos y frases** — las cadenas largas se dividen por líneas en blanco (se conserva la estructura de párrafos) y luego por frases, para que un modelo local pequeño mantenga la calidad
21
25
  - **Salida en streaming** — las traducciones aparecen lote a lote mientras piensa; expande la fila Think para comparar con el original
22
26
  - **Resistente** — reintentos con backoff (3×), respaldo directo del navegador, los fallos nunca se cachean
27
+ - **🎚️ Momento de traducción ajustable** — pre-traducir todo / carga diferida de cadenas antiguas (por defecto) / solo al expandir
23
28
 
24
29
  ## 📦 Instalación
25
30
 
@@ -51,6 +56,12 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
51
56
  - **google gtx / bing** — funciona directamente (proxy del sistema / VPN automáticos)
52
57
  4. Envía un mensaje y expande la fila Think para ver la traducción
53
58
 
59
+ ## 🎬 Demo
60
+
61
+ ![dsh-think-translate demo](demo/demo.gif)
62
+
63
+ ![dsh-think-translate demo 2](demo/demo2.gif)
64
+
54
65
  ## ⚙️ Cómo funciona
55
66
 
56
67
  ```
package/README.fr.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ---
6
6
 
7
- Traduction au niveau de l'affichage pour l'interface web de [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) : la **chaîne de réflexion (ligne Think), les cartes de tâches et le texte de réponse** s'affichent dans la langue cible choisie, tandis que les originaux restent intacts dans la transcription.
7
+ Traduction au niveau de l'affichage pour l'interface web de [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) : la **chaîne de réflexion (ligne Think), les cartes de tâches et le texte de réponse** s'affichent dans la langue cible choisie, tandis que les originaux restent intacts dans la transcription et que le texte traduit **n'entre jamais dans le contexte du modèle**.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
@@ -12,14 +12,19 @@ Traduction au niveau de l'affichage pour l'interface web de [DeepSeek Harness](h
12
12
 
13
13
  ## ✨ Fonctionnalités
14
14
 
15
+ Les modèles de la famille DeepSeek raisonnent souvent en chinois — ou dans la langue qu'ils utilisent pour penser. dsh-think-translate affiche la ligne Think, les cartes de tâches et la réponse dans *votre* langue en direct, comme des sous-titres pour la réflexion du modèle.
16
+
15
17
  - **8 langues cibles** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
18
  - **Interface monolingue** — panneau de réglages, lignes de réflexion et cartes de tâches suivent la langue cible (pas de mélange zh/en) ; le choix persiste
17
19
  - **Modèle local d'abord** — utilise votre modèle Ollama local (qwen, etc.) : privé, hors ligne, gratuit. La première sélection **déclenche le téléchargement automatique** avec barre de progression ; le modèle est configuré et activé à la fin
20
+ - **🧠 Coût de contexte nul** — couche d'affichage pure : le modèle voit toujours le texte original et le texte traduit ne consomme jamais la fenêtre de contexte
18
21
  - **Repli Google / Bing** — bascule automatique si le modèle local est indisponible (google passe par un tunnel CONNECT Node avec le proxy système)
19
22
  - **Artefacts de code ignorés** — chemins, commandes, URL, regex et lignes de code pur ne sont jamais traduits
20
23
  - **Traduction par lots de phrases** — les chaînes longues sont traduites en petits lots pour garder la qualité sur les petits modèles locaux
24
+ - **🧩 Découpage par paragraphes et phrases** — les longues chaînes sont découpées sur les lignes vides (structure de paragraphes préservée) puis par phrases, pour garder la qualité sur un petit modèle local
21
25
  - **Sortie en streaming** — les traductions apparaissent lot par lot pendant la réflexion ; dépliez la ligne Think pour comparer avec l'original
22
26
  - **Résilient** — nouvelles tentatives avec backoff (3×), repli direct navigateur, échecs jamais mis en cache
27
+ - **🎚️ Moment de traduction réglable** — tout pré-traduire / chargement différé des anciennes chaînes (défaut) / uniquement à l'ouverture
23
28
 
24
29
  ## 📦 Installation
25
30
 
@@ -51,6 +56,12 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
51
56
  - **google gtx / bing** — fonctionne directement (proxy système / VPN automatiques)
52
57
  4. Envoyez un message et dépliez la ligne Think pour voir la traduction
53
58
 
59
+ ## 🎬 Démo
60
+
61
+ ![dsh-think-translate demo](demo/demo.gif)
62
+
63
+ ![dsh-think-translate demo 2](demo/demo2.gif)
64
+
54
65
  ## ⚙️ Fonctionnement
55
66
 
56
67
  ```
package/README.ja.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ---
6
6
 
7
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI の**表示層翻訳**プラグイン:**思考チェーン(Think 行)、タスクカード、回答本文**を選択した対象言語で表示します。原文は会話履歴に完全に残ります。
7
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI の**表示層翻訳**プラグイン:**思考チェーン(Think 行)、タスクカード、回答本文**を選択した対象言語で表示します。原文は会話履歴に完全に残り、訳文は**モデルコンテキストに一切入りません**。
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
@@ -12,14 +12,19 @@
12
12
 
13
13
  ## ✨ 特徴
14
14
 
15
+ DeepSeek 系モデルは中国語で考えることが多く、あるいはたまたま思考に使う言語で考えます。dsh-think-translate は、モデルの思考に字幕を付けるように、Think 行・タスクカード・回答を*あなたの*言語でリアルタイム表示します。
16
+
15
17
  - **8 つの対象言語** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
18
  - **単一言語 UI** — 設定パネル・思考行・タスクカードがすべて対象言語に追従(中英混在なし)、選択は永続化
17
19
  - **ローカルモデル優先** — ローカル Ollama モデル(qwen など)を優先:プライベート・オフライン・無料。初回選択時に**自動ダウンロード**(リアルタイム進捗バー)、完了後自動で設定・有効化
20
+ - **🧠 コンテキスト消費ゼロ** — 純表示層:モデルは原文のまま見ており、訳文はコンテキストウィンドウを一切消費しません
18
21
  - **Google / Bing フォールバック** — ローカルモデルが使えないとき自動切替(google は Node CONNECT トンネルでシステムプロキシ経由、アンチボット回避)
19
22
  - **コード類は自動スキップ** — ファイルパス・コマンド・URL・正規表現・純コード行は翻訳しない
20
23
  - **文単位バッチ翻訳** — 長い思考チェーンを短文バッチで逐次翻訳し、ローカル小モデルでも品質を維持
24
+ - **🧩 段落・文単位のチャンク分割** — 長い思考チェーンを空行で分割(段落構造を保持)し、さらに文単位でバッチ化。ローカル小モデルでも品質を維持
21
25
  - **ストリーミング出力** — 思考中に訳文がバッチ単位で表示され、Think 行を開いて原文と比較可能
22
26
  - **耐障害性** — host リクエストはバックオフ付き 3 回リトライ、ブラウザ直接フォールバック、失敗結果はキャッシュしない
27
+ - **🎚️ 翻訳タイミングを調整可能** — すべて事前翻訳 / 履歴を遅延ロード(既定)/ 展開時のみ翻訳
23
28
 
24
29
  ## 📦 インストール
25
30
 
@@ -51,6 +56,12 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
51
56
  - **google gtx / bing**:そのまま使える(システムプロキシ / VPN を自動利用)
52
57
  4. メッセージを送信し、Think 行を展開して訳文を確認
53
58
 
59
+ ## 🎬 デモ
60
+
61
+ ![dsh-think-translate demo](demo/demo.gif)
62
+
63
+ ![dsh-think-translate demo 2](demo/demo2.gif)
64
+
54
65
  ## ⚙️ 仕組み
55
66
 
56
67
  ```
package/README.ko.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ---
6
6
 
7
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI용 **표시 계층 번역** 플러그인: **사고 체인(Think 행), 작업 카드, 답변 본문**을 선택한 대상 언어로 표시합니다. 원문은 대화 기록에 완전히 보존됩니다.
7
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI용 **표시 계층 번역** 플러그인: **사고 체인(Think 행), 작업 카드, 답변 본문**을 선택한 대상 언어로 표시합니다. 원문은 대화 기록에 완전히 보존되며, 번역문은 **모델 컨텍스트에 전혀 들어가지 않습니다**.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
@@ -12,14 +12,19 @@
12
12
 
13
13
  ## ✨ 기능
14
14
 
15
+ DeepSeek 계열 모델은 중국어로, 또는 우연히 생각에 쓰는 언어로 추론하는 경우가 많습니다. dsh-think-translate는 모델의 사고에 자막을 다는 것처럼 Think 행·작업 카드·답변을 *여러분의* 언어로 실시간 표시합니다.
16
+
15
17
  - **8개 대상 언어** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
18
  - **단일 언어 UI** — 설정 패널, 사고 행, 작업 카드가 모두 대상 언어를 따름(중·영 혼용 없음), 선택 영구 저장
17
19
  - **로컬 모델 우선** — 로컬 Ollama 모델(qwen 등) 우선: 프라이빗·오프라인·무료. 첫 선택 시 **자동 다운로드**(실시간 진행률 표시), 완료 후 자동 설정·활성화
20
+ - **🧠 컨텍스트 비용 0** — 순수 표시 계층: 모델은 여전히 원문을 보고, 번역문은 컨텍스트 창을 전혀 소비하지 않음
18
21
  - **Google / Bing 폴백** — 로컬 모델 사용 불가 시 자동 전환(google은 Node CONNECT 터널로 시스템 프록시 경유, 안티봇 우회)
19
22
  - **코드류 자동 스킵** — 파일 경로, 명령어, URL, 정규식, 순수 코드 줄은 번역하지 않음
20
23
  - **문장 배치 번역** — 긴 사고 체인을 짧은 문장 배치로 순차 번역하여 로컬 소형 모델도 품질 유지
24
+ - **🧩 문단·문장 인식 분할** — 긴 사고 체인을 빈 줄로 분할(문단 구조 유지)하고 다시 문장 단위로 배치 처리해 로컬 소형 모델도 품질 유지
21
25
  - **스트리밍 출력** — 사고 중 번역이 배치 단위로 표시, Think 행을 펼쳐 원문과 비교
22
26
  - **내구성** — host 요청 백오프 3회 재시도, 브라우저 직접 폴백, 실패 결과는 캐시하지 않음
27
+ - **🎚️ 번역 시점 조절** — 모두 사전 번역 / 이전 체인 지연 로딩(기본)/ 펼친 체인만 번역
23
28
 
24
29
  ## 📦 설치
25
30
 
@@ -51,6 +56,12 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
51
56
  - **google gtx / bing** — 바로 사용 가능(시스템 프록시/VPN 자동 이용)
52
57
  4. 메시지를 보내고 Think 행을 펼쳐 번역 확인
53
58
 
59
+ ## 🎬 데모
60
+
61
+ ![dsh-think-translate demo](demo/demo.gif)
62
+
63
+ ![dsh-think-translate demo 2](demo/demo2.gif)
64
+
54
65
  ## ⚙️ 작동 원리
55
66
 
56
67
  ```
package/README.md CHANGED
@@ -4,22 +4,25 @@
4
4
 
5
5
  ---
6
6
 
7
- Display-layer translation for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI. The **thinking chain (Think row), task cards and answer text** are displayed in your chosen target language — while the originals stay intact in the transcript.
7
+ Translate the **reasoning / thinking chain (chain-of-thought), task cards and answers** of the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI into one of **8 target languages** — in real time, on the display layer only. The originals stay untouched in the transcript, and the translated text **never enters the model context**.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
11
11
  [![dsh](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-ai/deepseek-harness)
12
12
 
13
- ## ✨ Features
13
+ ## ✨ Why dsh-think-translate
14
14
 
15
- - **8 target languages** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
- - **Single-language UI** — settings panel, thinking rows and task cards all follow the target language (no mixed zh/en); your choice persists across reloads
17
- - **Local model first** — uses your local Ollama model (qwen, etc.): private, offline, free. First local-model selection **auto-triggers the download** with a live progress bar; the model is configured and enabled automatically when done
18
- - **Google / Bing fallback** — automatic switch when the local model is unavailable (google goes through a Node CONNECT tunnel using the system proxy, bypassing anti-bot blocks)
19
- - **Code artifacts skipped** — file paths, commands, URLs, regexes and pure-code lines are never translated
20
- - **Sentence-batched translation** — long thinking chains are translated in small sentence batches so local small models keep quality
21
- - **Streaming output** — translations appear batch by batch while thinking; expand the Think row to compare with the original
22
- - **Resilient** — host requests retry with backoff (3×), browser-direct fallback, failed results are never cached
15
+ DeepSeek-class models often reason in Chinese — or in whatever language they happen to think in. dsh-think-translate renders the **Think row, task cards and answer** in *your* language while you watch, like subtitles for the model's thinking.
16
+
17
+ - **🕵️ Read any thinking chain** — reasoning, chain-of-thought, task cards and answers translated in real time, streamed batch by batch
18
+ - **🌍 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
19
+ - **🔒 Private & offline-first** — local Ollama (qwen2.5:7b / 14b or custom) is the default 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
20
+ - **🧠 Zero context cost** — pure display layer: the model still sees the original text, and translated text never consumes the context window
21
+ - **☁️ Google / Bing fallback** — automatic switch when the local model is unavailable (google goes through a Node CONNECT tunnel using the system proxy, bypassing anti-bot blocks)
22
+ - **🛡️ Code-safe** — file paths, commands, URLs, regexes and pure-code lines are never translated
23
+ - **🧩 Paragraph & sentence-aware chunking** — long thinking chains are split on blank lines (paragraph structure preserved) and further batched by sentence, so even a small local model keeps quality
24
+ - **⏱️ Resilient** — 3× backoff retries, browser-direct fallback, failed results never cached
25
+ - **🎚️ Adjustable translation timing** — pre-translate everything, lazy-load historical chains (default), or translate only the expanded chain
23
26
 
24
27
  ## 📦 Installation
25
28
 
@@ -49,7 +52,13 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
49
52
  3. Pick the **preferred provider**:
50
53
  - **Local model (Ollama)** — on first selection a download prompt appears (qwen2.5:7b / 14b or custom); it auto-enables when finished. The "+" button next to the model picker downloads more models anytime
51
54
  - **google gtx / bing** — works out of the box (auto system proxy / VPN)
52
- 4. Send a message and expand the Think row to see the translation
55
+ 4. Send a message that makes the model think, then expand the **Think row** to read the translation and compare with the original
56
+
57
+ ## 🎬 Demo
58
+
59
+ ![dsh-think-translate demo](demo/demo.gif)
60
+
61
+ ![dsh-think-translate demo 2](demo/demo2.gif)
53
62
 
54
63
  ## ⚙️ How it works
55
64
 
@@ -63,7 +72,7 @@ browser → POST /_xlate/translate (same-origin, no CORS)
63
72
  ```
64
73
 
65
74
  - **Host half** (`lib/index.js`): provider adapters, LRU cache (600), `/_xlate/models` listing, `/_xlate/model/pull` + `pull-status` model download management (auto-configures on completion)
66
- - **Client half** (`lib/client.js`): 8-language UI, sentence-batched translation, streaming Think rows, localStorage persistence
75
+ - **Client half** (`lib/client.js`): 8-language UI, sentence/paragraph-batched translation, streaming Think rows, localStorage persistence (settings + translation cache)
67
76
  - Pure display layer: originals remain in the transcript and model context
68
77
 
69
78
  ## 🛠 Development
package/README.ru.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ---
6
6
 
7
- Перевод на уровне отображения для веб-интерфейса [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): **цепочка размышлений (строка Think), карточки задач и текст ответа** отображаются на выбранном целевом языке, а оригиналы полностью сохраняются в истории.
7
+ Перевод на уровне отображения для веб-интерфейса [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): **цепочка размышлений (строка Think), карточки задач и текст ответа** отображаются на выбранном целевом языке, а оригиналы полностью сохраняются в истории; переведённый текст **никогда не попадает в контекст модели**.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
@@ -12,14 +12,19 @@
12
12
 
13
13
  ## ✨ Возможности
14
14
 
15
+ Модели семейства DeepSeek часто рассуждают на китайском — или на том языке, на котором им случилось думать. dsh-think-translate показывает строку Think, карточки задач и ответ на *вашем* языке в реальном времени — как субтитры к размышлениям модели.
16
+
15
17
  - **8 целевых языков** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
18
  - **Одноязычный интерфейс** — панель настроек, строки размышлений и карточки задач следуют целевому языку (без смешения zh/en); выбор сохраняется
17
19
  - **Локальная модель в приоритете** — использует вашу локальную модель Ollama (qwen и др.): приватно, офлайн, бесплатно. При первом выборе **автоматически запускается загрузка** с индикатором прогресса; модель настраивается и активируется по завершении
20
+ - **🧠 Нулевая стоимость контекста** — чистый слой отображения: модель по-прежнему видит исходный текст, а переведённый текст никогда не расходует окно контекста
18
21
  - **Запасной вариант Google / Bing** — автоматическое переключение, если локальная модель недоступна (google идёт через Node CONNECT-туннель с системным прокси)
19
22
  - **Кодовые артефакты пропускаются** — пути, команды, URL, регулярные выражения и чистые строки кода не переводятся
20
23
  - **Пакетный перевод по предложениям** — длинные цепочки переводятся небольшими пакетами, чтобы локальные малые модели сохраняли качество
24
+ - **🧩 Разбивка по абзацам и предложениям** — длинные цепочки делятся по пустым строкам (структура абзацев сохраняется) и дополнительно по предложениям, чтобы локальные малые модели сохраняли качество
21
25
  - **Потоковый вывод** — переводы появляются пакет за пакетом во время размышления; разверните строку Think для сравнения с оригиналом
22
26
  - **Устойчивость** — повторные попытки с backoff (3×), прямой запасной вариант из браузера, неудачи не кэшируются
27
+ - **🎚️ Настраиваемый момент перевода** — переводить всё / ленивая загрузка старых цепочек (по умолчанию) / только при раскрытии
23
28
 
24
29
  ## 📦 Установка
25
30
 
@@ -51,6 +56,12 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
51
56
  - **google gtx / bing** — работает сразу (автоматический системный прокси / VPN)
52
57
  4. Отправьте сообщение и разверните строку Think, чтобы увидеть перевод
53
58
 
59
+ ## 🎬 Демо
60
+
61
+ ![dsh-think-translate demo](demo/demo.gif)
62
+
63
+ ![dsh-think-translate demo 2](demo/demo2.gif)
64
+
54
65
  ## ⚙️ Как это работает
55
66
 
56
67
  ```
package/README.zh-CN.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ---
6
6
 
7
- 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI 提供**显示层翻译**:把界面上的**思考链(Think 行)、任务卡片、回答正文**翻译为你选择的目标语言,原文完整保留在会话记录中。
7
+ 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI 提供**显示层翻译**:把界面上的**思考链(Think 行)、任务卡片、回答正文**翻译为你选择的目标语言,原文完整保留在会话记录中,译文**绝不进入模型上下文**。
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/dsh-think-translate?color=4D6BFE&label=npm)](https://www.npmjs.com/package/dsh-think-translate)
10
10
  [![license](https://img.shields.io/npm/l/dsh-think-translate?color=4D6BFE)](LICENSE)
@@ -12,14 +12,19 @@
12
12
 
13
13
  ## ✨ 特性
14
14
 
15
+ DeepSeek 系模型经常用中文思考——或者用它们碰巧习惯的语言。dsh-think-translate 在你观看时把 Think 行、任务卡片和回答渲染成*你的*语言,就像给模型的思考配上字幕。
16
+
15
17
  - **8 种目标语言** — 中文 / English / 日本語 / 한국어 / Español / Français / Deutsch / Русский
16
18
  - **单一语言界面** — 设置面板、思考行、任务卡片全部跟随目标语言(不混中英),选择持久化
17
19
  - **本地模型为主力** — 优先使用本地 Ollama 模型(qwen 等),隐私离线免费;首次选择本地模型时**自动触发下载**(实时进度条),完成后自动配置启用
20
+ - **🧠 零上下文成本** — 纯显示层:模型看到的仍是原文,译文绝不占用上下文窗口
18
21
  - **Google / Bing 兜底** — 本地模型不可用时自动切换(google 通过 Node CONNECT 隧道走系统代理,绕过反爬)
19
22
  - **代码工件自动跳过** — 文件路径、命令、URL、正则、纯代码行不翻译
20
23
  - **句子分批翻译** — 长思考链按句子分批串行翻译,本地小模型也能保持质量
24
+ - **🧩 段落与句子感知切分** — 长思考链按空行切分(保留段落结构)再按句分批,本地小模型也能保持质量
21
25
  - **流式输出** — 思考过程中译文逐批出现,展开 Think 行可对照原文
22
26
  - **失败韧性** — host 请求 3 次退避重试 + 浏览器直连兜底,失败结果不缓存
27
+ - **🎚️ 可调翻译时机** — 三档:全部预翻译 / 懒加载历史(默认)/ 仅展开时翻译
23
28
 
24
29
  ## 📦 安装
25
30
 
@@ -51,6 +56,12 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
51
56
  - **google gtx / bing**:开箱即用(自动走系统代理/VPN)
52
57
  4. 发消息让模型思考,展开 Think 行查看译文
53
58
 
59
+ ## 🎬 演示
60
+
61
+ ![dsh-think-translate demo](demo/demo.gif)
62
+
63
+ ![dsh-think-translate demo 2](demo/demo2.gif)
64
+
54
65
  ## ⚙️ 工作原理
55
66
 
56
67
  ```
@@ -63,7 +74,7 @@ New-Item -ItemType Junction -Path "$HOME\.dsh\profiles\node_modules\dsh-think-tr
63
74
  ```
64
75
 
65
76
  - **host 半边**(`lib/index.js`):供应商适配器、LRU 缓存(600)、`/_xlate/models` 模型列表、`/_xlate/model/pull` + `pull-status` 模型下载管理(完成后自动配置启用)
66
- - **client 半边**(`lib/client.js`):8 语言 UI、句子分批翻译、流式 Think 行、localStorage 持久化
77
+ - **client 半边**(`lib/client.js`):8 语言 UI、段落/句子分批翻译、流式 Think 行、设置与译文缓存持久化(localStorage)
67
78
  - 纯显示层:原文完整保留在会话日志与模型上下文中
68
79
 
69
80
  ## 🛠 开发
package/demo/demo.gif ADDED
Binary file
package/demo/demo2.gif ADDED
Binary file
package/lib/client.js CHANGED
@@ -109,7 +109,7 @@ window.__ModuleLoader__.load({
109
109
  }
110
110
  var store = {
111
111
  lang: loadPref("lang", "zh-CN"), think: loadPref("think", "true") !== "false",
112
- todo: loadPref("todo", "true") !== "false", ans: loadPref("ans", "false") === "true",
112
+ todo: loadPref("todo", "true") !== "false", ans: loadPref("ans", "false") === "true", mode: loadPref("mode", "lazy"),
113
113
  listeners: new Set(),
114
114
  set: function (patch) {
115
115
  var changed = false;
@@ -132,7 +132,7 @@ window.__ModuleLoader__.load({
132
132
  // Settings UI strings, keyed by target language (follows store.lang).
133
133
  var UI_TEXT = {
134
134
  "zh-CN": {
135
- targetLang: "目标语言", think: "翻译思考链(Think 行)", todo: "翻译任务卡片条目", ans: "翻译回答正文(追加译文)",
135
+ targetLang: "目标语言", think: "翻译思考链(Think 行)", todo: "翻译任务卡片条目", ans: "翻译回答正文(追加译文)", mode: "翻译时机", modeEager: "全部预翻译(最高负荷)", modeLazy: "懒加载(推荐)", modeExpand: "仅展开时翻译(最低负荷)",
136
136
  sectionTitle: "思考链翻译", thinking: "思考中", thinkTitle: "思考", original: "原文", transFail: "翻译失败·原文",
137
137
  tasks: "任务", taskList: "任务清单", doneFmt: "{d} 已完成 · {t} 项", stopped: "已停止",
138
138
  noLocalModel: "本地模型未安装", downloadModel: "下载模型", downloading: "下载中", verifying: "校验中", pullDone: "已安装并启用", pullError: "下载失败",
@@ -147,7 +147,7 @@ window.__ModuleLoader__.load({
147
147
  note: "界面翻译:思考链、任务卡片、回答正文会显示为你选的语言,原文保留在会话记录中可随时对照。优先使用本地模型,其次 Google/Bing;文件路径、代码等自动跳过。",
148
148
  },
149
149
  "en": {
150
- targetLang: "Target language", think: "Translate thinking chain", todo: "Translate task card items", ans: "Translate answer text (append translation)",
150
+ targetLang: "Target language", think: "Translate thinking chain", todo: "Translate task card items", ans: "Translate answer text (append translation)", mode: "When to translate", modeEager: "Pre-translate all (highest load)", modeLazy: "Lazy-load (recommended)", modeExpand: "Only on expand (lowest load)",
151
151
  sectionTitle: "Think Translation", thinking: "Thinking", thinkTitle: "Think", original: "Original", transFail: "untranslated",
152
152
  tasks: "Tasks", taskList: "Tasks", doneFmt: "{d}/{t} done", stopped: "stopped",
153
153
  noLocalModel: "No local model installed", downloadModel: "Download model", downloading: "Downloading", verifying: "Verifying", pullDone: "Installed and enabled", pullError: "Download failed",
@@ -162,7 +162,7 @@ window.__ModuleLoader__.load({
162
162
  note: "UI translation: the thinking chain, task cards and answer text display in your chosen language; originals stay in the transcript for comparison. Local model first, then Google/Bing; file paths and code are skipped.",
163
163
  },
164
164
  "ja": {
165
- targetLang: "対象言語", think: "思考チェーンを翻訳(Think 行)", todo: "タスクカード項目を翻訳", ans: "回答本文を翻訳(訳文を追加)",
165
+ targetLang: "対象言語", think: "思考チェーンを翻訳(Think 行)", todo: "タスクカード項目を翻訳", ans: "回答本文を翻訳(訳文を追加)", mode: "翻訳タイミング", modeEager: "すべて事前翻訳(最大負荷)", modeLazy: "遅延ロード(推奨)", modeExpand: "展開時のみ翻訳(最小負荷)",
166
166
  sectionTitle: "思考チェーン翻訳", thinking: "思考中", thinkTitle: "思考", original: "原文", transFail: "翻訳失敗・原文",
167
167
  tasks: "タスク", taskList: "タスク一覧", doneFmt: "{d} 完了 · 全{t}件", stopped: "停止済み",
168
168
  noLocalModel: "ローカルモデル未インストール", downloadModel: "モデルをダウンロード", downloading: "ダウンロード中", verifying: "検証中", pullDone: "インストール済み・有効", pullError: "ダウンロード失敗",
@@ -176,7 +176,7 @@ window.__ModuleLoader__.load({
176
176
  note: "UI 翻訳:思考チェーン・タスクカード・回答本文が選んだ言語で表示されます。原文は会話履歴に残り、いつでも比較できます。優先はローカルモデル、次に Google/Bing。ファイルパスやコードは自動的にスキップされます。",
177
177
  },
178
178
  "ko": {
179
- targetLang: "대상 언어", think: "사고 체인 번역(Think 행)", todo: "작업 카드 항목 번역", ans: "답변 본문 번역(번역 추가)",
179
+ targetLang: "대상 언어", think: "사고 체인 번역(Think 행)", todo: "작업 카드 항목 번역", ans: "답변 본문 번역(번역 추가)", mode: "번역 시점", modeEager: "모두 사전 번역(최대 부하)", modeLazy: "지연 로딩(권장)", modeExpand: "펼칠 때만 번역(최소 부하)",
180
180
  sectionTitle: "사고 체인 번역", thinking: "생각 중", thinkTitle: "생각", original: "원문", transFail: "번역 실패·원문",
181
181
  tasks: "작업", taskList: "작업 목록", doneFmt: "{d}/{t} 완료", stopped: "중지됨",
182
182
  noLocalModel: "로컬 모델 미설치", downloadModel: "모델 다운로드", downloading: "다운로드 중", verifying: "검증 중", pullDone: "설치 완료·활성화", pullError: "다운로드 실패",
@@ -190,7 +190,7 @@ window.__ModuleLoader__.load({
190
190
  note: "UI 번역: 사고 체인, 작업 카드, 답변 본문이 선택한 언어로 표시됩니다. 원문은 대화 기록에 남아 언제든 비교할 수 있습니다. 우선 로컬 모델, 다음 Google/Bing. 파일 경로와 코드는 자동으로 건너뜁니다.",
191
191
  },
192
192
  "es": {
193
- targetLang: "Idioma de destino", think: "Traducir cadena de pensamiento", todo: "Traducir elementos de tarjeta de tareas", ans: "Traducir texto de respuesta",
193
+ targetLang: "Idioma de destino", think: "Traducir cadena de pensamiento", todo: "Traducir elementos de tarjeta de tareas", ans: "Traducir texto de respuesta", mode: "Cuándo traducir", modeEager: "Pre-traducir todo (carga máxima)", modeLazy: "Carga diferida (recomendado)", modeExpand: "Solo al expandir (carga mínima)",
194
194
  sectionTitle: "Traducción de cadena de pensamiento", thinking: "Pensando…", thinkTitle: "Pensar", original: "Original", transFail: "Error de traducción",
195
195
  tasks: "Tareas", taskList: "Lista de tareas", doneFmt: "{d}/{t} completado", stopped: "detenido",
196
196
  noLocalModel: "Sin modelo local instalado", downloadModel: "Descargar modelo", downloading: "Descargando", verifying: "Verificando", pullDone: "Instalado y activado", pullError: "Error de descarga",
@@ -204,7 +204,7 @@ window.__ModuleLoader__.load({
204
204
  note: "Traducción de la interfaz: la cadena de pensamiento, las tarjetas de tareas y el texto de respuesta se muestran en el idioma elegido; el original permanece en el historial. Modelo local primero, luego Google/Bing; rutas y código se omiten.",
205
205
  },
206
206
  "fr": {
207
- targetLang: "Langue cible", think: "Traduire la chaîne de réflexion", todo: "Traduire les éléments de carte de tâches", ans: "Traduire le texte de réponse",
207
+ targetLang: "Langue cible", think: "Traduire la chaîne de réflexion", todo: "Traduire les éléments de carte de tâches", ans: "Traduire le texte de réponse", mode: "Quand traduire", modeEager: "Tout pré-traduire (charge maximale)", modeLazy: "Chargement différé (recommandé)", modeExpand: "Uniquement à l'ouverture (charge minimale)",
208
208
  sectionTitle: "Traduction de chaîne de réflexion", thinking: "Réflexion…", thinkTitle: "Réflexion", original: "Original", transFail: "Échec de traduction",
209
209
  tasks: "Tâches", taskList: "Liste des tâches", doneFmt: "{d}/{t} terminé", stopped: "arrêté",
210
210
  noLocalModel: "Aucun modèle local installé", downloadModel: "Télécharger le modèle", downloading: "Téléchargement…", verifying: "Vérification…", pullDone: "Installé et activé", pullError: "Échec du téléchargement",
@@ -218,7 +218,7 @@ window.__ModuleLoader__.load({
218
218
  note: "Traduction de l'interface : la chaîne de réflexion, les cartes de tâches et le texte de réponse s'affichent dans la langue choisie ; l'original reste dans l'historique. Modèle local d'abord, puis Google/Bing ; chemins et code ignorés.",
219
219
  },
220
220
  "de": {
221
- targetLang: "Zielsprache", think: "Gedankenkette übersetzen", todo: "Aufgabenkartenelemente übersetzen", ans: "Antworttext übersetzen",
221
+ targetLang: "Zielsprache", think: "Gedankenkette übersetzen", todo: "Aufgabenkartenelemente übersetzen", ans: "Antworttext übersetzen", mode: "Wann übersetzen", modeEager: "Alles vorübersetzen (höchste Last)", modeLazy: "Lazy laden (empfohlen)", modeExpand: "Nur beim Aufklappen (geringste Last)",
222
222
  sectionTitle: "Übersetzung der Gedankenkette", thinking: "Denkt…", thinkTitle: "Denken", original: "Original", transFail: "Übersetzung fehlgeschlagen",
223
223
  tasks: "Aufgaben", taskList: "Aufgabenliste", doneFmt: "{d}/{t} erledigt", stopped: "gestoppt",
224
224
  noLocalModel: "Kein lokales Modell installiert", downloadModel: "Modell herunterladen", downloading: "Lade herunter…", verifying: "Prüfe…", pullDone: "Installiert und aktiviert", pullError: "Download fehlgeschlagen",
@@ -232,7 +232,7 @@ window.__ModuleLoader__.load({
232
232
  note: "UI-Übersetzung: Gedankenkette, Aufgabenkarten und Antworttext erscheinen in der gewählten Sprache; das Original bleibt im Verlauf. Erst lokales Modell, dann Google/Bing; Pfade und Code werden übersprungen.",
233
233
  },
234
234
  "ru": {
235
- targetLang: "Целевой язык", think: "Переводить цепочку размышлений", todo: "Переводить элементы карточек задач", ans: "Переводить текст ответа",
235
+ targetLang: "Целевой язык", think: "Переводить цепочку размышлений", todo: "Переводить элементы карточек задач", ans: "Переводить текст ответа", mode: "Когда переводить", modeEager: "Переводить всё заранее (макс. нагрузка)", modeLazy: "Ленивая загрузка (рекомендуется)", modeExpand: "Только при раскрытии (мин. нагрузка)",
236
236
  sectionTitle: "Перевод цепочки размышлений", thinking: "Размышление…", thinkTitle: "Размышление", original: "Оригинал", transFail: "Ошибка перевода",
237
237
  tasks: "Задачи", taskList: "Список задач", doneFmt: "{d}/{t} выполнено", stopped: "остановлено",
238
238
  noLocalModel: "Локальная модель не установлена", downloadModel: "Скачать модель", downloading: "Загрузка…", verifying: "Проверка…", pullDone: "Установлено и включено", pullError: "Ошибка загрузки",
@@ -279,7 +279,7 @@ window.__ModuleLoader__.load({
279
279
  var stats = { ok: 0, fail: 0, lastError: null, lastProvider: null, lastModel: null };
280
280
  // Diagnostic ledger surfaced in the settings panel (2026-08-24): bundle
281
281
  // version, apply execution, per-slot registration outcome, current lang.
282
- var diag = { version: "v3-priority-1", applied: false, lang: "?", slots: {}, lastLangChange: 0 };
282
+ var diag = { version: "v5-mode", applied: false, lang: "?", slots: {}, lastLangChange: 0 };
283
283
 
284
284
  function fnv(s) {
285
285
  var h = 0x811c9dc5;
@@ -509,6 +509,7 @@ window.__ModuleLoader__.load({
509
509
  }
510
510
  var XL_STREAM_MARGIN = 120;
511
511
  var XL_STREAM_MIN_BATCH = 24;
512
+ var XL_BATCH_CHARS = 900;
512
513
  function ThinkRow(props) {
513
514
  var text = props.text;
514
515
  var running = props.running;
@@ -516,7 +517,7 @@ window.__ModuleLoader__.load({
516
517
  var pairOpen = useState(false);
517
518
  var open = pairOpen[0], setOpen = pairOpen[1];
518
519
  var enabled = s.think;
519
- var lang = s.lang;
520
+ var lang = s.lang; var eager = props.eager !== false; var mode = s.mode || "lazy";
520
521
  var refPair = useState(function () { return { current: { segIdx: 0, transText: "", done: false, failed: false, busy: false } }; });
521
522
  var tRef = refPair[0];
522
523
  var tickPair = useState(0);
@@ -538,7 +539,7 @@ window.__ModuleLoader__.load({
538
539
  var stateRef = useState(function () { return { current: {} }; })[0];
539
540
  stateRef.current = { text: text, running: running };
540
541
  useEffect(function () {
541
- if (!enabled) return undefined;
542
+ if (!enabled) return undefined; if (!(mode === "eager" || (mode === "lazy" && eager)) && !open) return undefined;
542
543
  var alive = true;
543
544
  (async function () {
544
545
  while (alive) {
@@ -563,7 +564,7 @@ window.__ModuleLoader__.load({
563
564
  batch += segs[i].text;
564
565
  nextIdx = i + 1;
565
566
  count++;
566
- if (batch.length >= 180) break;
567
+ if (batch.length >= XL_BATCH_CHARS) break;
567
568
  }
568
569
  if (count === 0 || (isRunning && batch.trim().length < XL_STREAM_MIN_BATCH)) {
569
570
  await sleepMs(200);
@@ -595,8 +596,8 @@ window.__ModuleLoader__.load({
595
596
  });
596
597
  }
597
598
  })();
598
- return function () { alive = false; };
599
- }, [lang, enabled]);
599
+ return function () { alive = false; tRef.current = Object.assign({}, tRef.current, { busy: false }); };
600
+ }, [lang, enabled, mode, eager, open]);
600
601
  var t = tRef.current;
601
602
  var tt = UI_TEXT[s.lang] || UI_TEXT.en;
602
603
  var hasTr = !!(enabled && t.transText && !t.failed);
@@ -765,7 +766,7 @@ window.__ModuleLoader__.load({
765
766
  }
766
767
  var mentions = owner !== undefined && props.fileMentions ? props.fileMentions(owner) : undefined;
767
768
  var streaming = data.status === "running";
768
- var interrupted = data.status === "interrupted";
769
+ var interrupted = data.status === "interrupted"; var isLatest = !streaming && data.finalNode !== undefined && tail && tail.closing && tail.closing.finalNode && tail.closing.finalNode.seq === data.finalNode.seq; var eager = streaming || isLatest;
769
770
  var blocks = data.blocks || [];
770
771
  var hasVisible = streaming || interrupted === true || blocks.some(function (b) { return b.kind !== "tool-call"; });
771
772
  if (!hasVisible) return null;
@@ -777,7 +778,7 @@ window.__ModuleLoader__.load({
777
778
  if (block.kind === "text") {
778
779
  rendered.push(createElement(TextBlock, { key: "t" + i, text: block.text, streaming: streaming }));
779
780
  } else if (block.kind === "reasoning") {
780
- rendered.push(createElement(ThinkRow, { key: "r" + i, text: block.text, running: streaming && i === last }));
781
+ rendered.push(createElement(ThinkRow, { key: "r" + i, text: block.text, running: streaming && i === last, eager: eager }));
781
782
  } else if (block.kind === "image") {
782
783
  var start = i;
783
784
  var group = [block];
@@ -1119,6 +1120,15 @@ window.__ModuleLoader__.load({
1119
1120
  createElement("label", null,
1120
1121
  createElement("input", { type: "checkbox", checked: s.think, onChange: function (e) { store.set({ think: e.target.checked }); } }),
1121
1122
  createElement("span", null, t.think)),
1123
+ createElement("label", null,
1124
+ createElement("span", null, t.mode),
1125
+ createElement("select", {
1126
+ className: "xl-sel", value: s.mode || "lazy",
1127
+ onChange: function (e) { store.set({ mode: e.target.value }); }
1128
+ },
1129
+ createElement("option", { value: "eager" }, t.modeEager),
1130
+ createElement("option", { value: "lazy" }, t.modeLazy),
1131
+ createElement("option", { value: "expand" }, t.modeExpand))),
1122
1132
  createElement("label", null,
1123
1133
  createElement("input", { type: "checkbox", checked: s.todo, onChange: function (e) { store.set({ todo: e.target.checked }); } }),
1124
1134
  createElement("span", null, t.todo)),
package/lib/index.js CHANGED
@@ -247,7 +247,7 @@ async function viaBing(text, to) {
247
247
  // common Chinese words untranslated (这个/方案/关键/针对...). Giving explicit
248
248
  // mappings (zh -> ja) turns near-miss output into clean translations; the
249
249
  // "never leave source words" rule applies to every target language.
250
- const OPENAI_SYSTEM = 'You are a translation engine. Translate the user text into the requested target language. Output ONLY the translation — no explanations, no quotes, no trailing commentary. Never keep a source-language word untranslated in the output. Common mappings (Chinese -> Japanese): 这个 -> この; 那个 -> その; 方案 -> 案/プラン; 关键 -> 要点/鍵; 针对 -> 対して; 进行 -> 行う; 通过 -> 通じて; 以及 -> および; 对于 -> に対して; 需要 -> 必要がある; 我们 -> 私たち; 可以 -> できる; 问题 -> 問題; 情况 -> 状況; 内容 -> 内容; 相关 -> 関連; 以下 -> 以下; 上述 -> 前述; 当前 -> 現在; 整体 -> 全体; 主要 -> 主に; 具体 -> 具体的に; 方面 -> 面/点; 部分 -> 部分; 实现 -> 実装/実現; 使用 -> 使用する; 提供 -> 提供する; 支持 -> サポートする; 处理 -> 処理する; 解决 -> 解決する; 提高 -> 向上させる; 降低 -> 低下させる; 增加 -> 増加させる; 减少 -> 減らす. Keep code blocks, commands, file paths, URLs, and identifiers verbatim.'
250
+ const OPENAI_SYSTEM = 'You are a translation engine. Translate the user text into the requested target language. Output ONLY the translation — no explanations, no quotes, no trailing commentary. Never keep a source-language word untranslated in the output. Common mappings (Chinese -> Japanese): 然后 -> その後/そして; 所以 -> だから; 因为 -> なぜなら; 因此 -> そのため; 这个 -> この; 那个 -> その; 方案 -> 案/プラン; 关键 -> 要点/鍵; 针对 -> 対して; 进行 -> 行う; 通过 -> 通じて; 以及 -> および; 对于 -> に対して; 需要 -> 必要がある; 我们 -> 私たち; 可以 -> できる; 问题 -> 問題; 情况 -> 状況; 内容 -> 内容; 相关 -> 関連; 以下 -> 以下; 上述 -> 前述; 当前 -> 現在; 整体 -> 全体; 主要 -> 主に; 具体 -> 具体的に; 方面 -> 面/点; 部分 -> 部分; 实现 -> 実装/実現; 使用 -> 使用する; 提供 -> 提供する; 支持 -> サポートする; 处理 -> 処理する; 解决 -> 解決する; 提高 -> 向上させる; 降低 -> 低下させる; 增加 -> 増加させる; 减少 -> 減らす; 性能瓶颈 -> パフォーマンスのボトルネック; 优化方案 -> 最適化案; 优化 -> 最適化; 数据流 -> データフロー; 方向 -> 方向; 架构 -> アーキテクチャ; 模块 -> モジュール; 系统 -> システム; 结果 -> 結果; 评估 -> 評価する; 分析 -> 分析する; 提出 -> 提案する; 实施 -> 実施する; 选择 -> 選択する; 比较 -> 比較する. Keep code blocks, commands, file paths, URLs, and identifiers verbatim.'
251
251
 
252
252
  async function viaOpenAI(text, to, cfg) {
253
253
  if (!cfg.apiKey) throw new Error('openai: missing apiKey')
@@ -420,19 +420,27 @@ function needsTranslate(text, target) {
420
420
  }
421
421
  function chunkText(text) {
422
422
  if (text.length <= 1200) return [text]
423
- const sentences = text.match(/[\s\S]*?[.。!?!?;\n]|[\s\S]+$/g) || [text]
423
+ // Split on blank-line paragraphs first so each translated chunk keeps its
424
+ // own paragraph; join('\n\n') on the caller side restores the paragraph
425
+ // breaks that sentence-level chunking used to flatten (2026-08-26).
426
+ // A single oversized paragraph is sub-split on sentence boundaries.
427
+ const paras = text.split(/\n{2,}/).filter(function (p) { return p.trim() })
424
428
  const chunks = []
425
- let cur = ''
426
- for (const sn of sentences) {
427
- if (cur && cur.length + sn.length > 1200) { chunks.push(cur); cur = '' }
428
- if (sn.length > 1200) {
429
- for (let i = 0; i < sn.length; i += 1100) chunks.push(sn.slice(i, i + 1100))
430
- cur = ''
431
- } else {
432
- cur += sn
429
+ for (const para of paras) {
430
+ if (para.length <= 1200) { chunks.push(para); continue }
431
+ const sentences = para.match(/[\s\S]*?[.。!?!?;\n]|[\s\S]+$/g) || [para]
432
+ let cur = ''
433
+ for (const sn of sentences) {
434
+ if (cur && cur.length + sn.length > 1200) { chunks.push(cur); cur = '' }
435
+ if (sn.length > 1200) {
436
+ for (let i = 0; i < sn.length; i += 1100) chunks.push(sn.slice(i, i + 1100))
437
+ cur = ''
438
+ } else {
439
+ cur += sn
440
+ }
433
441
  }
442
+ if (cur.trim()) chunks.push(cur)
434
443
  }
435
- if (cur.trim()) chunks.push(cur)
436
444
  return chunks.length ? chunks : [text]
437
445
  }
438
446
 
@@ -452,7 +460,9 @@ async function translateViaChain(text, target, cfg) {
452
460
  state.lastError = null
453
461
  // Carry the concrete model name for openai (local Ollama) so the UI can
454
462
  // show e.g. "local model: qwen2.5:14b" instead of the bare provider id.
455
- return { ok: true, text: outs.join(''), provider: id, ...(id === 'openai' ? { model: cfg.providers.openai.model } : {}) }
463
+ // Join chunks with a blank line so paragraph structure survives
464
+ // chunking (sentence-level chunking used to collapse them).
465
+ return { ok: true, text: outs.join('\n\n'), provider: id, ...(id === 'openai' ? { model: cfg.providers.openai.model } : {}) }
456
466
  } catch (e) {
457
467
  lastErr = e
458
468
  if (id === 'bing') bingCache = null
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 / Google / Bing, fail-open)",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -12,7 +12,8 @@
12
12
  "files": [
13
13
  "lib/index.js",
14
14
  "lib/client.js",
15
- "cordis.patch.yml"
15
+ "cordis.patch.yml",
16
+ "demo"
16
17
  ],
17
18
  "repository": {
18
19
  "type": "git",