@lynn123411/dsh-chat-translate 1.3.2 → 1.5.0
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.md +12 -8
- package/docs//351/205/215/347/275/256.md +11 -1
- package/lib/client.js +945 -344
- package/lib/client.js.map +4 -4
- package/lib/index.js +565 -173
- package/lib/index.js.map +4 -4
- package/lib/types/client/index.d.ts +6 -5
- package/lib/types/client/locales.d.ts +51 -4
- package/lib/types/client/reading-settings-page.d.ts +1 -1
- package/lib/types/client/settings/store.d.ts +17 -4
- package/lib/types/client/settings/ui.d.ts +5 -3
- package/lib/types/client/translate/api.d.ts +13 -0
- package/lib/types/client/translate/client-cache.d.ts +6 -1
- package/lib/types/client/translate/mount.d.ts +22 -0
- package/lib/types/client/translate/observer.d.ts +17 -0
- package/lib/types/client/translate/think-button.d.ts +19 -0
- package/lib/types/client/translate/think.d.ts +52 -0
- package/lib/types/index.d.ts +11 -18
- package/lib/types/server/adapters/base.d.ts +1 -1
- package/lib/types/server/adapters/openai.d.ts +2 -2
- package/lib/types/server/cache.d.ts +5 -1
- package/lib/types/server/config.d.ts +35 -7
- package/lib/types/server/dispatcher.d.ts +30 -3
- package/lib/types/server/pipeline/mask-tokens.d.ts +54 -48
- package/lib/types/server/pipeline/masking.d.ts +35 -12
- package/lib/types/server/pipeline/think.d.ts +49 -0
- package/lib/types/server/router.d.ts +24 -3
- package/lib/types/server/types.d.ts +18 -1
- package/package.json +23 -10
|
@@ -3,10 +3,11 @@ export declare const name = "dsh-chat-translate";
|
|
|
3
3
|
/**
|
|
4
4
|
* Declared services: slots for the shared 「阅读体验」 settings page (page claim
|
|
5
5
|
* plus the card registration), settingsScope for the per-namespace settings
|
|
6
|
-
* mirror,
|
|
7
|
-
* namespace
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
6
|
+
* mirror, the remote + remote.credentials pair for the credentials Remote
|
|
7
|
+
* namespace, and locale for the card's own dictionary. The runtime withholds any
|
|
8
|
+
* service not declared here; `locale` is declared rather than sampled because
|
|
9
|
+
* `ctx.get('locale')` at apply time can beat the locale plugin's `provide`,
|
|
10
|
+
* which silently left the card's dictionary unregistered.
|
|
10
11
|
*/
|
|
11
12
|
export declare const inject: string[];
|
|
12
13
|
interface ClientContext {
|
|
@@ -20,7 +21,7 @@ interface ClientContext {
|
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
|
-
* Mount the tool-call / think-
|
|
24
|
+
* Mount the tool-call / think-chain translation observer and the settings UI
|
|
24
25
|
* card inside the shared reading-settings page.
|
|
25
26
|
* @param ctx - DSH browser client context.
|
|
26
27
|
*/
|
|
@@ -1,12 +1,59 @@
|
|
|
1
1
|
/** Locale bundles for the chat-translate card inside the shared 「阅读体验」 page. */
|
|
2
2
|
/** Dictionary namespace owned by this plugin's settings card. */
|
|
3
3
|
export declare const NS = "settings.chatTranslate";
|
|
4
|
+
/**
|
|
5
|
+
* Chinese copy — the single source of truth for the key set. `{name}` markers
|
|
6
|
+
* are either template params filled through `t(key, params)` / `.replace()`, or
|
|
7
|
+
* inline technical samples rendered by `withSamples` at the call site.
|
|
8
|
+
*/
|
|
9
|
+
export declare const zh: {
|
|
10
|
+
readonly pageNav: "阅读体验";
|
|
11
|
+
readonly title: "聊天翻译";
|
|
12
|
+
readonly masterTitle: "翻译总开关";
|
|
13
|
+
readonly enableTranslation: "启用翻译";
|
|
14
|
+
readonly masterDesc: "自动将当前会话中工具调用标题(如 {example})翻译为中文,点击译文可原地切换原文/译文。思考正文默认不翻译,由 AI 卡片里的「显示思考链翻译按钮」控制:打开后每张思考卡右侧出现按钮,点击才翻译。思考卡折叠起来的那一行摘要永不翻译。仅作用于当前查看的会话,回答正文永不翻译。";
|
|
15
|
+
readonly aiTitle: "AI 翻译(OpenAI 兼容协议)";
|
|
16
|
+
readonly enableAi: "启用 AI 翻译通道";
|
|
17
|
+
readonly badgeConfigured: "已配置";
|
|
18
|
+
readonly badgeUnconfigured: "未配置";
|
|
19
|
+
readonly aiFallbackNote: "未配置时 AI 通道自动跳过,由 Bing 兜底。";
|
|
20
|
+
readonly apiKeyTitle: "API Key";
|
|
21
|
+
readonly apiKeyDesc: "保存至 {home} 的 {ref},保存后立即生效;留空保存 = 清除";
|
|
22
|
+
readonly apiKeyPlaceholderConfigured: "已配置(如需更换请直接输入)";
|
|
23
|
+
readonly save: "保存";
|
|
24
|
+
readonly saving: "保存中…";
|
|
25
|
+
readonly keySaved: "已保存到 {home},立即生效";
|
|
26
|
+
readonly keyCleared: "已清除 API Key(AI 通道将不可用,Bing 兜底)";
|
|
27
|
+
readonly saveFailed: "保存失败:{error}";
|
|
28
|
+
readonly unknownError: "未知错误";
|
|
29
|
+
readonly baseUrlTitle: "Base URL";
|
|
30
|
+
readonly baseUrlDesc: "任意 OpenAI 兼容服务端点,如 {url1}、{url2}";
|
|
31
|
+
readonly modelTitle: "模型";
|
|
32
|
+
readonly modelDesc: "如 {model1}、{model2};留空视为未配置";
|
|
33
|
+
readonly test: "测试 AI 通道";
|
|
34
|
+
readonly testing: "测试中…";
|
|
35
|
+
readonly testOk: "连接正常,延迟 {latency}ms";
|
|
36
|
+
readonly testFail: "失败:{error}";
|
|
37
|
+
readonly thinkEnableTitle: "显示思考链翻译按钮";
|
|
38
|
+
readonly thinkEnableDesc: "在每张思考卡的「Think」右侧显示一个按钮,点击才翻译这条思考正文(不会自动翻译);翻译中按钮转圈,翻好后实心高亮,再点一下整条切回原文(正文里的点击不切换)。代码块与折叠摘要不动,Bing 通道不参与。关闭此开关会撤掉按钮并还原已翻译内容。";
|
|
39
|
+
readonly thinkTimeoutTitle: "思考链翻译超时";
|
|
40
|
+
readonly thinkTimeoutDesc: "单位毫秒,范围 500-900000。整块思考可能要几分钟,默认 600000。";
|
|
41
|
+
readonly bingTitle: "Bing 网页翻译(免 Key 兜底)";
|
|
42
|
+
readonly enableBing: "启用 Bing 翻译通道";
|
|
43
|
+
readonly bingDesc: "内置免 Key 翻译通道,无需任何配置,只翻译工具调用标题。AI 未配置或请求失败时自动兜底;思考链翻译永远不走这条通道。AI 与 Bing 同时关闭则工具调用标题也不翻译。";
|
|
44
|
+
readonly behaviorTitle: "通道行为";
|
|
45
|
+
readonly behavior1: "AI 开启且已配置 → AI 优先翻译,失败自动降级 Bing";
|
|
46
|
+
readonly behavior2: "AI 开启但未配置 + Bing 开启 → 由 Bing 翻译";
|
|
47
|
+
readonly behavior3: "AI 开启但未配置 + Bing 关闭 → 不翻译";
|
|
48
|
+
readonly behavior4: "AI 关闭 + Bing 开启 → 直接使用 Bing";
|
|
49
|
+
readonly behavior5: "AI 关闭 + Bing 关闭 → 不翻译";
|
|
50
|
+
readonly concurrencyTitle: "最大翻译并发数";
|
|
51
|
+
readonly concurrencyDesc: "控制视口滚动与多工具卡片时的最大并行请求数(范围 1-100,推荐 3)。";
|
|
52
|
+
};
|
|
4
53
|
/** Locale keys this plugin renders. */
|
|
5
|
-
export type ChatTranslateLocaleKey =
|
|
6
|
-
/** English copy. */
|
|
54
|
+
export type ChatTranslateLocaleKey = keyof typeof zh;
|
|
55
|
+
/** English copy; the same key set as {@link zh}. */
|
|
7
56
|
export declare const en: Record<ChatTranslateLocaleKey, string>;
|
|
8
|
-
/** Chinese copy. */
|
|
9
|
-
export declare const zh: Record<ChatTranslateLocaleKey, string>;
|
|
10
57
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
11
58
|
interface LocaleNamespaceMap {
|
|
12
59
|
'settings.chatTranslate': ChatTranslateLocaleKey;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* mounted but hidden, so a card's local state survives a tab switch.
|
|
20
20
|
*
|
|
21
21
|
* Keep this file identical across the participating plugins
|
|
22
|
-
* (`dsh-smooth-stream`, `dsh-
|
|
22
|
+
* (`dsh-smooth-stream`, `dsh-chat-translate`).
|
|
23
23
|
* Participants own their own card component, locale dictionaries, settings
|
|
24
24
|
* namespace and Host half — only the page shell below is shared, because
|
|
25
25
|
* cross-plugin value imports are forbidden by the client bundle purity gate.
|
|
@@ -3,10 +3,15 @@ export interface ClientSettingsState {
|
|
|
3
3
|
concurrency: number;
|
|
4
4
|
aiEnabled: boolean;
|
|
5
5
|
bingEnabled: boolean;
|
|
6
|
+
thinkEnabled: boolean;
|
|
7
|
+
thinkTimeoutMs: number;
|
|
6
8
|
baseUrl: string;
|
|
7
9
|
model: string;
|
|
8
10
|
aiConfigured: boolean;
|
|
9
11
|
}
|
|
12
|
+
/** 思考链翻译超时的取值范围,与宿主半边保持一致。 */
|
|
13
|
+
export declare const THINK_TIMEOUT_MIN = 500;
|
|
14
|
+
export declare const THINK_TIMEOUT_MAX = 900000;
|
|
10
15
|
/** Settings namespace + credentials ref, mirroring the host constants. */
|
|
11
16
|
export declare const SETTINGS_NAMESPACE = "dsh-chat-translate";
|
|
12
17
|
export declare const TRANSLATE_API_KEY_REF = "TRANSLATE_API_KEY";
|
|
@@ -16,6 +21,14 @@ export declare const TRANSLATE_API_KEY_REF = "TRANSLATE_API_KEY";
|
|
|
16
21
|
* `credentials` Remote namespace. Keeping them structural keeps this bundle
|
|
17
22
|
* free of host-service imports and lets tests inject fakes.
|
|
18
23
|
*/
|
|
24
|
+
export type SettingsPathOpLike = {
|
|
25
|
+
op: 'set';
|
|
26
|
+
path: string[];
|
|
27
|
+
value: unknown;
|
|
28
|
+
} | {
|
|
29
|
+
op: 'unset';
|
|
30
|
+
path: string[];
|
|
31
|
+
};
|
|
19
32
|
export interface SettingsScopeLike {
|
|
20
33
|
getSnapshot(): {
|
|
21
34
|
status: string;
|
|
@@ -23,8 +36,7 @@ export interface SettingsScopeLike {
|
|
|
23
36
|
writable: boolean;
|
|
24
37
|
};
|
|
25
38
|
subscribe(listener: () => void): () => void;
|
|
26
|
-
|
|
27
|
-
unset(field: string): Promise<unknown>;
|
|
39
|
+
mutate(ops: readonly SettingsPathOpLike[]): Promise<unknown>;
|
|
28
40
|
}
|
|
29
41
|
/** Shape of every DSH client Remote call: {ok, value} / {ok, error} wrapper. */
|
|
30
42
|
export type RemoteResult<T> = {
|
|
@@ -52,8 +64,9 @@ export interface CredentialsRemoteLike {
|
|
|
52
64
|
*
|
|
53
65
|
* Since 1.2 there is no localStorage overlay and no custom config HTTP
|
|
54
66
|
* endpoint: the store derives from the bound `settingsScope` (which mirrors
|
|
55
|
-
* the host document and folds every write answer back), and
|
|
56
|
-
*
|
|
67
|
+
* the host document and folds every write answer back), and each debounced
|
|
68
|
+
* flush commits its touched fields as one `scope.mutate` batch — one
|
|
69
|
+
* revision fence, serialized and persisted by DSH.
|
|
57
70
|
* Without a bound scope (e.g. non-loopback pages, unit tests) it degrades to
|
|
58
71
|
* an in-memory store with the same semantics DSH itself uses for memory
|
|
59
72
|
* persistence.
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
+
import { NS } from '../locales.ts';
|
|
3
4
|
/**
|
|
4
5
|
* Chat-translate's card inside the shared 「阅读体验」 settings page. The card
|
|
5
6
|
* keeps its own chrome (title rows, switches, credential form); the shared page
|
|
6
7
|
* only stacks the participants' cards.
|
|
7
|
-
* @param
|
|
8
|
+
* @param props - renderer-bound seat of the `reading.settings.item` slot plus
|
|
9
|
+
* the `t` seat its `locale: NS` registration injects.
|
|
8
10
|
* @returns The card element.
|
|
9
11
|
*/
|
|
10
|
-
export declare function TidySettingsPanel(
|
|
12
|
+
export declare function TidySettingsPanel(props: PropsRuntime<'reading.settings.item'> & PropsLocale<typeof NS>): React.ReactElement;
|
|
11
13
|
/**
|
|
12
14
|
* Bind the settings store to DSH's native settings/credentials services and
|
|
13
15
|
* join the shared 「阅读体验」 settings page: whichever participant activates
|
|
@@ -8,7 +8,20 @@ export interface TranslateBatchOptions {
|
|
|
8
8
|
signal?: AbortSignal;
|
|
9
9
|
timeoutMs?: number;
|
|
10
10
|
}
|
|
11
|
+
/** 思考正文一个块的翻译结果,与请求的块列表按下标对齐。 */
|
|
12
|
+
export interface ThinkBlockResult {
|
|
13
|
+
original: string;
|
|
14
|
+
translated: string;
|
|
15
|
+
ok: boolean;
|
|
16
|
+
cached: boolean;
|
|
17
|
+
channel: string;
|
|
18
|
+
}
|
|
11
19
|
export declare function requestTranslateBatch(texts: string[], options?: TranslateBatchOptions): Promise<TranslateItemResult[]>;
|
|
20
|
+
/**
|
|
21
|
+
* 请求翻译思考正文的块。宿主侧按输入上限打包、串行发送,客户端超时交给
|
|
22
|
+
* 宿主(思考块可能要几分钟),这里不设自己的截止时间。
|
|
23
|
+
*/
|
|
24
|
+
export declare function requestTranslateThink(blocks: string[]): Promise<ThinkBlockResult[]>;
|
|
12
25
|
export declare function testServerChannel(channel: string): Promise<{
|
|
13
26
|
ok: boolean;
|
|
14
27
|
latencyMs: number;
|
|
@@ -2,7 +2,9 @@ export declare class ClientCache {
|
|
|
2
2
|
private memCache;
|
|
3
3
|
private dirty;
|
|
4
4
|
private saveTimer;
|
|
5
|
-
|
|
5
|
+
private storageKey;
|
|
6
|
+
private maxEntries;
|
|
7
|
+
constructor(storageKey?: string, maxEntries?: number);
|
|
6
8
|
private load;
|
|
7
9
|
get(text: string): string | undefined;
|
|
8
10
|
set(text: string, translated: string): void;
|
|
@@ -11,4 +13,7 @@ export declare class ClientCache {
|
|
|
11
13
|
clear(): void;
|
|
12
14
|
size(): number;
|
|
13
15
|
}
|
|
16
|
+
/** 工具标题译文缓存。 */
|
|
14
17
|
export declare const clientCache: ClientCache;
|
|
18
|
+
/** 思考正文译文缓存:独立存储键与容量,不挤占工具标题的缓存。 */
|
|
19
|
+
export declare const thinkClientCache: ClientCache;
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
export declare const CLASS_ORIGINAL_HIDDEN = "dsh-tidy-original-hidden";
|
|
12
12
|
export declare const CLASS_ORIGINAL_SHOWN = "dsh-tidy-original-shown";
|
|
13
13
|
export declare const CLASS_TRANSLATED_BLOCK = "dsh-tidy-translated-block";
|
|
14
|
+
/** 混合块里的行内片段容器(我们自建、自拆)。 */
|
|
15
|
+
export declare const CLASS_RUN = "dsh-tidy-run";
|
|
14
16
|
export interface MountOptions {
|
|
15
17
|
originalText?: string;
|
|
16
18
|
interactive?: boolean;
|
|
@@ -20,6 +22,26 @@ export declare class NonDestructiveTranslationMount {
|
|
|
20
22
|
* Mounts a translated string onto the target element non-destructively.
|
|
21
23
|
*/
|
|
22
24
|
static mount(element: HTMLElement, translated: string, options?: MountOptions): void;
|
|
25
|
+
/**
|
|
26
|
+
* 把一段连续的行内节点包进自建容器后挂载译文。
|
|
27
|
+
*
|
|
28
|
+
* 混合块(列表项文字 + 子列表 / 代码块)里的行内片段没有自己的元素,
|
|
29
|
+
* 只能先包一层再走同一条非侵入式挂载;嵌套的块级子节点留在容器之外,
|
|
30
|
+
* 排版原样保留。
|
|
31
|
+
* @returns 自建容器;节点不再相邻或没有共同父节点时返回 null。
|
|
32
|
+
*/
|
|
33
|
+
static mountRun(nodes: Node[], translated: string, options?: MountOptions): HTMLElement | null;
|
|
34
|
+
/** 还原行内片段并撤掉自建容器。 */
|
|
35
|
+
static unmountRun(run: HTMLElement): void;
|
|
36
|
+
/** 还原一个作用域内我们挂过的全部译文(元素单位与行内片段都算)。 */
|
|
37
|
+
static restore(scope: ParentNode): void;
|
|
38
|
+
/** 我们自己的挂载容器与还原容器,观察器必须跳过它们。 */
|
|
39
|
+
static isOwnNode(element: HTMLElement): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* 把一个作用域里的全部挂载译文一起翻面。思考链只由卡片标题右侧的按钮调用
|
|
42
|
+
* 它,正文里的点击不再改变原文与译文。
|
|
43
|
+
*/
|
|
44
|
+
static toggleGroup(scope: ParentNode, showOriginal: boolean): void;
|
|
23
45
|
/**
|
|
24
46
|
* Unmounts translation and restores original DOM nodes completely.
|
|
25
47
|
*/
|
|
@@ -3,9 +3,21 @@ export declare class ChatTranslateObserver {
|
|
|
3
3
|
private rootElement;
|
|
4
4
|
private rootCheckTimer;
|
|
5
5
|
private isEnabled;
|
|
6
|
+
/** 设置里的思考链开关;与总开关一起决定按钮是否存在。 */
|
|
7
|
+
private thinkEnabled;
|
|
6
8
|
constructor();
|
|
7
9
|
setEnabled(enabled: boolean): void;
|
|
8
10
|
private restoreOriginals;
|
|
11
|
+
/**
|
|
12
|
+
* 设置里的思考链开关:关掉就撤掉所有翻译按钮并还原思考卡里的译文,工具标题
|
|
13
|
+
* 译文不动。
|
|
14
|
+
*/
|
|
15
|
+
setThinkEnabled(enabled: boolean): void;
|
|
16
|
+
/**
|
|
17
|
+
* AI 通道的可用性(通道开关打开且 Key / Base URL / 模型齐全)。不可用时同样
|
|
18
|
+
* 不注入按钮:思考链翻译只能走这条通道。
|
|
19
|
+
*/
|
|
20
|
+
setThinkConfigured(configured: boolean): void;
|
|
9
21
|
/**
|
|
10
22
|
* The active session's scroll container. Only the currently-viewed session
|
|
11
23
|
* is translated; switching sessions replaces this subtree and the observer
|
|
@@ -19,6 +31,11 @@ export declare class ChatTranslateObserver {
|
|
|
19
31
|
private handleMutations;
|
|
20
32
|
private scanContainer;
|
|
21
33
|
private scanNode;
|
|
34
|
+
/**
|
|
35
|
+
* 交给思考正文翻译控制器:命中节点所在的卡片,以及它下面(或它就是)的
|
|
36
|
+
* 全部思考卡片。
|
|
37
|
+
*/
|
|
38
|
+
private scanThink;
|
|
22
39
|
private processSpan;
|
|
23
40
|
disconnect(): void;
|
|
24
41
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 思考卡标题右侧的翻译按钮。
|
|
3
|
+
*
|
|
4
|
+
* 三态:idle(描边「译」字,点击开始翻译)、working(圈内圆环旋转,点击忽略)、
|
|
5
|
+
* translated(实心高亮,点击整条切回原文)。样式只在首次使用注入一次,配色沿用
|
|
6
|
+
* 内核的 dsw-* 变量;系统开启「减弱动态效果」时圆环不旋转。
|
|
7
|
+
*/
|
|
8
|
+
export type ThinkButtonState = 'idle' | 'working' | 'translated';
|
|
9
|
+
export declare const THINK_BUTTON_CLASS = "dsh-tidy-think-button";
|
|
10
|
+
/** 注入按钮样式,重复调用无副作用。 */
|
|
11
|
+
export declare function ensureThinkButtonStyles(): void;
|
|
12
|
+
export interface ThinkButtonHandle {
|
|
13
|
+
element: HTMLButtonElement;
|
|
14
|
+
getState(): ThinkButtonState;
|
|
15
|
+
setState(state: ThinkButtonState): void;
|
|
16
|
+
setDisabled(disabled: boolean, reason: string): void;
|
|
17
|
+
}
|
|
18
|
+
/** 创建一个按钮实例;onActivate 由调用方决定点下去做什么。 */
|
|
19
|
+
export declare function createThinkButton(onActivate: () => void): ThinkButtonHandle;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** 稳定的翻译单位:一块级元素,或一段行内节点。 */
|
|
2
|
+
export interface ThinkUnit {
|
|
3
|
+
element?: HTMLElement;
|
|
4
|
+
nodes?: Node[];
|
|
5
|
+
text: string;
|
|
6
|
+
anchor: Node;
|
|
7
|
+
}
|
|
8
|
+
/** 收集正文里全部可翻译单位,跳过已挂译文的部分与代码块。 */
|
|
9
|
+
export declare function collectThinkUnits(body: HTMLElement): ThinkUnit[];
|
|
10
|
+
/** 按估算 token 把单位切成若干次客户端请求,顺序不变。 */
|
|
11
|
+
export declare function chunkUnits(units: ThinkUnit[], maxTokens?: number): ThinkUnit[][];
|
|
12
|
+
declare class ThinkTranslateController {
|
|
13
|
+
/** 总开关与设置里的思考链开关同时打开。 */
|
|
14
|
+
private enabled;
|
|
15
|
+
/** AI 通道开关打开且已配置;缺任何一条都不注入按钮。 */
|
|
16
|
+
private configured;
|
|
17
|
+
private states;
|
|
18
|
+
private syncTimers;
|
|
19
|
+
/** 只处理当前会话的容器;由观察器在会话切换时更新。 */
|
|
20
|
+
private scope;
|
|
21
|
+
/** 观察器把当前会话的根交给控制器,插入按钮时只看这一棵子树。 */
|
|
22
|
+
setScope(scope: ParentNode): void;
|
|
23
|
+
isEnabled(): boolean;
|
|
24
|
+
setEnabled(enabled: boolean): void;
|
|
25
|
+
setConfigured(configured: boolean): void;
|
|
26
|
+
/** 按钮是否可以存在:两个开关与 AI 通道配置缺一不可。 */
|
|
27
|
+
private usable;
|
|
28
|
+
private applyVisibility;
|
|
29
|
+
/** 扫描当前会话里的全部思考卡(开关、配置或会话变化后调用)。 */
|
|
30
|
+
syncAll(): void;
|
|
31
|
+
/** 防抖后同步一张卡片。 */
|
|
32
|
+
syncCard(card: HTMLElement): void;
|
|
33
|
+
private stateFor;
|
|
34
|
+
private runSync;
|
|
35
|
+
private updateDisabled;
|
|
36
|
+
private ensureButton;
|
|
37
|
+
private removeButton;
|
|
38
|
+
/** 卡片当前显示的是译文(至少有一个译文块可见)。 */
|
|
39
|
+
private isShowingTranslation;
|
|
40
|
+
private bodyOf;
|
|
41
|
+
/** 展开时若整条都命中缓存,直接显示中文,不点按钮也不发请求。 */
|
|
42
|
+
private showCachedWhenComplete;
|
|
43
|
+
/** 按钮被点击。 */
|
|
44
|
+
private activate;
|
|
45
|
+
/** 翻译这张卡片的全部单位:命中缓存的先挂上,其余按块请求。 */
|
|
46
|
+
private translateCard;
|
|
47
|
+
private translateUnits;
|
|
48
|
+
/** 把译文挂到单位上;期间文本或节点已经变动的单位直接放弃(结果已进缓存)。 */
|
|
49
|
+
private applyUnit;
|
|
50
|
+
}
|
|
51
|
+
export declare const thinkTranslator: ThinkTranslateController;
|
|
52
|
+
export {};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,30 +1,23 @@
|
|
|
1
|
+
import type { HostConnectionHandle } from '@deepseek-ai/dsh-client-connection';
|
|
2
|
+
import { type SettingsMigrationTarget } from './server/config.ts';
|
|
1
3
|
/** Stable Cordis loader name. */
|
|
2
4
|
export declare const name = "dsh-chat-translate";
|
|
3
5
|
/**
|
|
4
|
-
* Hard dependencies:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Hard dependencies: settings and credentials are the DSH-owned config/secret
|
|
7
|
+
* surfaces this plugin rides on (no standalone config file since 1.2). The
|
|
8
|
+
* translation routes register through the connection service when a Web
|
|
9
|
+
* carrier composes it, so apply takes that service through ctx.inject and the
|
|
10
|
+
* plugin still loads in compositions without one.
|
|
7
11
|
*/
|
|
8
12
|
export declare const inject: string[];
|
|
9
13
|
interface HostContext {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
kind: 'prefix' | 'exact';
|
|
13
|
-
path: string;
|
|
14
|
-
handler: (req: any, res: any) => Promise<void> | void;
|
|
15
|
-
}): () => void;
|
|
16
|
-
};
|
|
17
|
-
settings: {
|
|
14
|
+
connection: HostConnectionHandle;
|
|
15
|
+
settings: SettingsMigrationTarget & {
|
|
18
16
|
register(ns: string, schema: unknown): {
|
|
19
17
|
get(): any;
|
|
20
18
|
watch(listener: (config: any) => void): () => void;
|
|
21
19
|
update(patch: Record<string, unknown>): Promise<unknown>;
|
|
22
20
|
};
|
|
23
|
-
describe(): Array<{
|
|
24
|
-
ns: string;
|
|
25
|
-
user?: unknown;
|
|
26
|
-
}>;
|
|
27
|
-
update(ns: string, patch: Record<string, unknown>): Promise<unknown>;
|
|
28
21
|
};
|
|
29
22
|
credentials: {
|
|
30
23
|
resolve(ref: string): Promise<{
|
|
@@ -40,8 +33,8 @@ interface HostContext {
|
|
|
40
33
|
unset(ref: string): Promise<void>;
|
|
41
34
|
};
|
|
42
35
|
on(event: string, listener: (...args: any[]) => void): () => void;
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
/** Optional-service injection: the callback runs once connection is present. */
|
|
37
|
+
inject(names: string[], callback: (ctx: HostContext) => void | (() => void)): void;
|
|
45
38
|
}
|
|
46
39
|
/** Mount the host half; provides the translation proxy and rides DSH config. */
|
|
47
40
|
export declare function apply(ctx: HostContext): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { ITranslationAdapter, PluginConfig } from '../types.ts';
|
|
1
|
+
export type { ITranslationAdapter, PluginConfig, TranslateAdapterOptions, } from '../types.ts';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ITranslationAdapter, PluginConfig } from './base.ts';
|
|
1
|
+
import type { ITranslationAdapter, PluginConfig, TranslateAdapterOptions } from './base.ts';
|
|
2
2
|
import type { KeyReader } from '../credentials.ts';
|
|
3
3
|
export declare class OpenAiCompatibleAdapter implements ITranslationAdapter {
|
|
4
4
|
readonly id = "openai";
|
|
@@ -6,5 +6,5 @@ export declare class OpenAiCompatibleAdapter implements ITranslationAdapter {
|
|
|
6
6
|
private credentials;
|
|
7
7
|
constructor(credentials: KeyReader);
|
|
8
8
|
isAvailable(config: PluginConfig): boolean;
|
|
9
|
-
translate(text: string, signal: AbortSignal, config: PluginConfig): Promise<string>;
|
|
9
|
+
translate(text: string, signal: AbortSignal, config: PluginConfig, options?: TranslateAdapterOptions): Promise<string>;
|
|
10
10
|
}
|
|
@@ -6,8 +6,12 @@ export declare class LruDiskCache {
|
|
|
6
6
|
private dirty;
|
|
7
7
|
/** Legacy root-level cache file (<=1.1); moved under the plugin subdir. */
|
|
8
8
|
private legacyPath;
|
|
9
|
-
|
|
9
|
+
/** Only the original title cache carries a pre-1.2 root-level predecessor. */
|
|
10
|
+
private hasLegacyPredecessor;
|
|
11
|
+
constructor(maxEntries?: number, fileName?: string);
|
|
10
12
|
init(): Promise<void>;
|
|
13
|
+
/** Load one cache document into memory, dropping poisoned or malformed entries. */
|
|
14
|
+
private loadFile;
|
|
11
15
|
get(key: string): string | undefined;
|
|
12
16
|
set(key: string, value: string): void;
|
|
13
17
|
private scheduleSave;
|
|
@@ -5,6 +5,15 @@ export declare const MAX_CONCURRENCY = 100;
|
|
|
5
5
|
/** Bounds for the AI channel request timeout. */
|
|
6
6
|
export declare const AI_TIMEOUT_MIN = 500;
|
|
7
7
|
export declare const AI_TIMEOUT_MAX = 120000;
|
|
8
|
+
/**
|
|
9
|
+
* Bounds for the think-chain request timeout. A full reasoning block can take
|
|
10
|
+
* minutes on a local model, so this budget is far larger than the short-text
|
|
11
|
+
* AI timeout and is bounded separately.
|
|
12
|
+
*/
|
|
13
|
+
export declare const THINK_TIMEOUT_MIN = 500;
|
|
14
|
+
export declare const THINK_TIMEOUT_MAX = 900000;
|
|
15
|
+
/** Entries kept in the think-chain cache pool, separate from the title pool. */
|
|
16
|
+
export declare const THINK_CACHE_ENTRIES = 300;
|
|
8
17
|
/**
|
|
9
18
|
* The settings namespace this plugin owns. The user-editable layer lives in
|
|
10
19
|
* the DSH-managed document (~/.dsh/settings.yaml) under this key; the
|
|
@@ -25,6 +34,31 @@ export interface SettingsScopeLike {
|
|
|
25
34
|
/** Merge a patch into the user layer and persist through the provider. */
|
|
26
35
|
update(patch: Partial<PluginConfig>): Promise<unknown>;
|
|
27
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* One path-addressed edit to a settings namespace's user section, matching
|
|
39
|
+
* DSH's `SettingsPathOp` wire shape.
|
|
40
|
+
*/
|
|
41
|
+
export type SettingsPathOpLike = {
|
|
42
|
+
op: 'set';
|
|
43
|
+
path: string[];
|
|
44
|
+
value: unknown;
|
|
45
|
+
} | {
|
|
46
|
+
op: 'unset';
|
|
47
|
+
path: string[];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Provider-level write face the legacy migration needs: the settings service
|
|
51
|
+
* itself (not the per-namespace owner scope), whose `mutate` applies path ops
|
|
52
|
+
* under one revision fence.
|
|
53
|
+
*/
|
|
54
|
+
export interface SettingsMigrationTarget {
|
|
55
|
+
describe(): Array<{
|
|
56
|
+
ns: string;
|
|
57
|
+
user?: unknown;
|
|
58
|
+
revision?: number;
|
|
59
|
+
}>;
|
|
60
|
+
mutate(ns: string, ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<unknown>;
|
|
61
|
+
}
|
|
28
62
|
/**
|
|
29
63
|
* Config facade over the DSH `ctx.settings` service. No file I/O lives here
|
|
30
64
|
* anymore: persistence, atomic writes, external-edit hot reload and the
|
|
@@ -64,10 +98,4 @@ export declare function sanitizePatch(input: Record<string, unknown>): Partial<P
|
|
|
64
98
|
* or not a migration happened.
|
|
65
99
|
* @returns whether any legacy values were migrated.
|
|
66
100
|
*/
|
|
67
|
-
export declare function migrateLegacyConfigFile(settings:
|
|
68
|
-
describe(): Array<{
|
|
69
|
-
ns: string;
|
|
70
|
-
user?: unknown;
|
|
71
|
-
}>;
|
|
72
|
-
update(ns: string, patch: Record<string, unknown>): Promise<unknown>;
|
|
73
|
-
}, legacyPath: string): Promise<boolean>;
|
|
101
|
+
export declare function migrateLegacyConfigFile(settings: SettingsMigrationTarget, legacyPath: string): Promise<boolean>;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { TranslateItemResult } from './types.ts';
|
|
1
|
+
import type { ThinkBlockResult, TranslateItemResult } from './types.ts';
|
|
2
2
|
import type { ConfigManager } from './config.ts';
|
|
3
|
-
import
|
|
3
|
+
import { LruDiskCache } from './cache.ts';
|
|
4
4
|
import type { KeyReader } from './credentials.ts';
|
|
5
5
|
export declare class TranslationDispatcher {
|
|
6
6
|
private configManager;
|
|
7
7
|
private cache;
|
|
8
|
+
/** 思考正文译文的独立缓存池,与工具标题的池互不挤占。 */
|
|
9
|
+
private thinkCache;
|
|
8
10
|
private credentials;
|
|
9
11
|
private masking;
|
|
10
12
|
private adapters;
|
|
@@ -12,7 +14,9 @@ export declare class TranslationDispatcher {
|
|
|
12
14
|
private inFlightMap;
|
|
13
15
|
private activeCount;
|
|
14
16
|
private queue;
|
|
15
|
-
|
|
17
|
+
/** 思考链请求的串行队列尾,保证同时最多一个在途请求。 */
|
|
18
|
+
private thinkTail;
|
|
19
|
+
constructor(configManager: ConfigManager, cache: LruDiskCache, credentials?: KeyReader, thinkCache?: LruDiskCache);
|
|
16
20
|
private registerAdapter;
|
|
17
21
|
/**
|
|
18
22
|
* Decide which channels are active for the current config, in priority order.
|
|
@@ -27,6 +31,29 @@ export declare class TranslationDispatcher {
|
|
|
27
31
|
private computeChannels;
|
|
28
32
|
translateBatch(texts: string[], forceRefresh?: boolean): Promise<TranslateItemResult[]>;
|
|
29
33
|
translateOne(rawText: string, forceRefresh?: boolean): Promise<TranslateItemResult>;
|
|
34
|
+
/**
|
|
35
|
+
* 翻译思考正文的块。整条链路与工具标题翻译分离:只走 AI 通道、独立串行
|
|
36
|
+
* 队列、独立超时、独立缓存池,Bing 从不参与。
|
|
37
|
+
*
|
|
38
|
+
* 每个块先按输入上限切成片段,再各自掩码、相邻片段打包成一个请求;整批
|
|
39
|
+
* 失败时退回逐片段单发,仍失败的片段保留原文。块内任一片段失败即整块不译,
|
|
40
|
+
* 避免半中半英的段落。
|
|
41
|
+
*/
|
|
42
|
+
translateThinkBlocks(blocks: string[]): Promise<ThinkBlockResult[]>;
|
|
43
|
+
/**
|
|
44
|
+
* 一整批一次请求;失败则该批逐片段单发重试一次,仍失败的片段直接放弃
|
|
45
|
+
* (保留原文),不在界面上留提示。
|
|
46
|
+
*/
|
|
47
|
+
private translateThinkBatch;
|
|
48
|
+
/** 发出一次思考链请求并把结果还原成每个片段的最终译文。 */
|
|
49
|
+
private requestThinkBatch;
|
|
50
|
+
/** 串行执行器:同时最多一个在途请求,与工具标题的并发池完全独立。 */
|
|
51
|
+
private runThinkSerial;
|
|
52
|
+
/**
|
|
53
|
+
* 只读判断某通道是否还在熔断冷却期。思考链翻译只借用这个判断避开已经
|
|
54
|
+
* 出问题的服务,不改动熔断状态本身(那是工具标题通道的记账)。
|
|
55
|
+
*/
|
|
56
|
+
private isChannelCoolingDown;
|
|
30
57
|
testChannel(channelId: string): Promise<{
|
|
31
58
|
ok: boolean;
|
|
32
59
|
latencyMs: number;
|