dsh-think-translate 1.0.4 → 1.0.6
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 +26 -2
- package/lib/index.js +22 -12
- package/package.json +4 -1
package/lib/client.js
CHANGED
|
@@ -6,6 +6,14 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
var react = require("react");
|
|
8
8
|
var React = react.default !== undefined ? react.default : react;
|
|
9
|
+
// Official Markdown renderer (loaded via the client module graph, declared
|
|
10
|
+
// in dsh.client.external). Using it keeps official formatting — code-block
|
|
11
|
+
// copy, syntax highlighting, tables, footnotes, file mentions — while this
|
|
12
|
+
// plugin only supplies translated text. Falls back to the local Markdownish
|
|
13
|
+
// if the dependency is unavailable.
|
|
14
|
+
var primitives = null;
|
|
15
|
+
try { primitives = require("@deepseek-ai/dsh-client-ui-primitives"); } catch (e) { primitives = null; }
|
|
16
|
+
var MarkdownText = primitives && primitives.MarkdownText ? primitives.MarkdownText : null;
|
|
9
17
|
var createElement = function (type, props) {
|
|
10
18
|
var children = [];
|
|
11
19
|
for (var i = 2; i < arguments.length; i++) children.push(arguments[i]);
|
|
@@ -135,6 +143,7 @@ window.__ModuleLoader__.load({
|
|
|
135
143
|
providerUnavailable: "翻译服务未加载:插件的 host 半边未生效(常见于刚改过配置或更新后)。请重启 Web 再试——重启后翻译与设置将恢复正常。",
|
|
136
144
|
providerDesc: "Google / Bing / 本地模型(自动选择)",
|
|
137
145
|
labels: { google: "google gtx(免费)", bing: "bing(免费)", openai: "本地部署模型(Ollama)" },
|
|
146
|
+
copy: "复制", copied: "已复制",
|
|
138
147
|
note: "界面翻译:思考链、任务卡片、回答正文会显示为你选的语言,原文保留在会话记录中可随时对照。优先使用本地模型,其次 Google/Bing;文件路径、代码等自动跳过。",
|
|
139
148
|
},
|
|
140
149
|
"en": {
|
|
@@ -149,6 +158,7 @@ window.__ModuleLoader__.load({
|
|
|
149
158
|
providerUnavailable: "Translation service not loaded: the plugin host half is inactive (common after config changes or updates). Restart the web app to restore translation and settings.",
|
|
150
159
|
providerDesc: "Google / Bing / local model (auto)",
|
|
151
160
|
labels: { google: "google gtx (free)", bing: "bing (free)", openai: "Local model (Ollama)" },
|
|
161
|
+
copy: "Copy", copied: "Copied",
|
|
152
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.",
|
|
153
163
|
},
|
|
154
164
|
"ja": {
|
|
@@ -720,11 +730,25 @@ window.__ModuleLoader__.load({
|
|
|
720
730
|
}
|
|
721
731
|
function TextBlock(props) {
|
|
722
732
|
var s = useStore();
|
|
733
|
+
var tt = UI_TEXT[s.lang] || UI_TEXT.en;
|
|
723
734
|
var tr = useXlate(props.text, s.ans && !props.streaming);
|
|
724
735
|
var hasTr = !!(tr && tr.ok && !tr.skipped && tr.text !== props.text);
|
|
736
|
+
// Official MarkdownText when available: keeps the full official
|
|
737
|
+
// formatting (code copy, highlighting, tables...). Translated text
|
|
738
|
+
// replaces the body when answer translation is on; original stays in
|
|
739
|
+
// a collapsible details block.
|
|
740
|
+
var codeLabels = MarkdownText ? { copyLabel: tt.copy, copiedLabel: tt.copied } : undefined;
|
|
741
|
+
var MDT = MarkdownText || Markdownish;
|
|
742
|
+
var body = hasTr ? tr.text : props.text;
|
|
725
743
|
return createElement("div", null,
|
|
726
|
-
createElement(
|
|
727
|
-
|
|
744
|
+
createElement(MDT, MarkdownText
|
|
745
|
+
? { text: body, streaming: props.streaming, codeLabels: codeLabels }
|
|
746
|
+
: { text: body, streaming: props.streaming }),
|
|
747
|
+
hasTr ? createElement("details", { className: "xl-orig" },
|
|
748
|
+
createElement("summary", null, tt.original),
|
|
749
|
+
createElement("div", null, createElement(MDT, MarkdownText
|
|
750
|
+
? { text: props.text, codeLabels: codeLabels }
|
|
751
|
+
: { text: props.text }))) : null);
|
|
728
752
|
}
|
|
729
753
|
|
|
730
754
|
function AssistantView(props) {
|
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): 这个 -> この; 那个 -> その; 方案 -> 案/プラン; 关键 -> 要点/鍵; 针对 -> 対して; 进行 -> 行う; 通过 -> 通じて; 以及 -> および; 对于 -> に対して; 需要 -> 必要がある; 我们 -> 私たち; 可以 -> できる; 问题 -> 問題; 情况 -> 状況; 内容 -> 内容; 相关 -> 関連; 以下 -> 以下; 上述 -> 前述; 当前 -> 現在; 整体 -> 全体; 主要 -> 主に; 具体 -> 具体的に; 方面 -> 面/点; 部分 -> 部分; 实现 -> 実装/実現; 使用 -> 使用する; 提供 -> 提供する; 支持 -> サポートする; 处理 -> 処理する; 解决 -> 解決する; 提高 -> 向上させる; 降低 -> 低下させる; 增加 -> 増加させる; 减少 ->
|
|
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
|
-
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
cur = ''
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
|
|
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.
|
|
4
|
+
"version": "1.0.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
"immediately": true,
|
|
28
28
|
"inject": [
|
|
29
29
|
"@deepseek-ai/dsh-client-runtime"
|
|
30
|
+
],
|
|
31
|
+
"external": [
|
|
32
|
+
"@deepseek-ai/dsh-client-ui-primitives"
|
|
30
33
|
]
|
|
31
34
|
},
|
|
32
35
|
"bundle": {
|