@lynn123411/dsh-chat-translate 1.8.0 → 2.0.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 +13 -14
- package/cordis.patch.yml +2 -1
- package/docs//351/205/215/347/275/256.md +22 -20
- package/lib/client.js +429 -1065
- package/lib/client.js.map +4 -4
- package/lib/index.js +265 -471
- package/lib/index.js.map +4 -4
- package/lib/types/client/index.d.ts +12 -11
- package/lib/types/client/locales.d.ts +8 -20
- package/lib/types/client/reading-settings-page.d.ts +7 -0
- package/lib/types/client/settings/store.d.ts +40 -32
- package/lib/types/client/settings/ui.d.ts +5 -4
- package/lib/types/client/translate/api.d.ts +5 -16
- package/lib/types/client/translate/client-cache.d.ts +1 -3
- package/lib/types/client/translate/lazy.d.ts +9 -7
- package/lib/types/client/translate/mount.d.ts +2 -6
- package/lib/types/client/translate/observer.d.ts +24 -24
- package/lib/types/client/translate/reply.d.ts +38 -0
- package/lib/types/index.d.ts +41 -12
- package/lib/types/server/cache.d.ts +13 -1
- package/lib/types/server/config.d.ts +81 -40
- package/lib/types/server/dispatcher.d.ts +25 -38
- package/lib/types/server/pipeline/{think.d.ts → blocks.d.ts} +9 -9
- package/lib/types/server/router.d.ts +6 -8
- package/lib/types/server/types.d.ts +2 -8
- package/package.json +14 -14
- package/lib/types/client/translate/think-button.d.ts +0 -19
- package/lib/types/client/translate/think.d.ts +0 -55
- package/lib/types/client/translate/viewport-observer.d.ts +0 -43
- package/lib/types/server/adapters/bing.d.ts +0 -15
|
@@ -1,39 +1,79 @@
|
|
|
1
|
+
import type { Volatile } from '@deepseek-ai/cordis';
|
|
1
2
|
import type { PluginConfig } from './types.ts';
|
|
2
3
|
import type { CredentialsReader } from './credentials.ts';
|
|
3
|
-
/** Hard cap for the translation concurrency pool. */
|
|
4
|
-
export declare const MAX_CONCURRENCY = 100;
|
|
5
|
-
/** Bounds for the AI channel request timeout. */
|
|
6
|
-
export declare const AI_TIMEOUT_MIN = 500;
|
|
7
|
-
export declare const AI_TIMEOUT_MAX = 120000;
|
|
8
4
|
/**
|
|
9
|
-
* Bounds for the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* Bounds for the AI channel request timeout. One request carries a packed run
|
|
6
|
+
* of whole reply blocks, and a local model can take minutes on that much
|
|
7
|
+
* prose, so the budget is long-form rather than the old short-text scale.
|
|
12
8
|
*/
|
|
13
|
-
export declare const
|
|
14
|
-
export declare const
|
|
15
|
-
/** Entries kept in the think-chain cache pool, separate from the title pool. */
|
|
16
|
-
export declare const THINK_CACHE_ENTRIES = 300;
|
|
9
|
+
export declare const AI_TIMEOUT_MIN = 500;
|
|
10
|
+
export declare const AI_TIMEOUT_MAX = 900000;
|
|
17
11
|
/**
|
|
18
|
-
* The settings namespace this plugin owns.
|
|
19
|
-
* the
|
|
20
|
-
*
|
|
12
|
+
* The settings namespace this plugin owns. Since 0.1.7 a namespace IS the
|
|
13
|
+
* Profile entry id, so the user-editable layer is the `config` row of
|
|
14
|
+
* `dsh-chat-translate` in the active profile's patch
|
|
15
|
+
* (`~/.dsh/profiles/<profile>/cordis.patch.yml`). The standalone
|
|
16
|
+
* ~/.dsh/dsh-chat-translate-config.json file is legacy (<=1.1) and is migrated
|
|
17
|
+
* once at boot.
|
|
21
18
|
*/
|
|
22
19
|
export declare const SETTINGS_NAMESPACE = "dsh-chat-translate";
|
|
23
20
|
export declare const DEFAULT_CONFIG: PluginConfig;
|
|
24
21
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* Config keys an earlier release declared and this one no longer reads. DSH
|
|
23
|
+
* resolves unknown keys through a non-strict schema and simply ignores them, so
|
|
24
|
+
* a leftover from an upgraded profile would linger in the patch forever unless
|
|
25
|
+
* it is actively unset once.
|
|
26
|
+
*
|
|
27
|
+
* `channels` predates 1.1 and was already retired then; it rides the same list
|
|
28
|
+
* so one pass cleans any generation of an old profile.
|
|
29
|
+
*/
|
|
30
|
+
export declare const RETIRED_CONFIG_KEYS: readonly ["channels", "concurrency", "timeoutMs", "thinkTimeoutMs", "aiEnabled", "bingEnabled", "thinkEnabled"];
|
|
31
|
+
/**
|
|
32
|
+
* Live read face of this plugin's own Config, as `ConfigManager` consumes it.
|
|
33
|
+
*
|
|
34
|
+
* Keeping this structural (instead of importing the DSH packages) lets tests
|
|
35
|
+
* inject an in-memory fake and keeps the host bundle free of service code. The
|
|
36
|
+
* plugin has no write face of its own: an accepted edit is written by the
|
|
37
|
+
* browser configuration form into the profile patch, and DSH commits the new
|
|
38
|
+
* value into the `Volatile` refs this source reads.
|
|
28
39
|
*/
|
|
29
|
-
export interface
|
|
40
|
+
export interface ConfigSourceLike {
|
|
30
41
|
/** Resolved value: schema defaults, then composition base, then user layer. */
|
|
31
42
|
get(): PluginConfig;
|
|
32
|
-
/** Observe
|
|
43
|
+
/** Observe committed live edits; returns the disposer. */
|
|
33
44
|
watch(listener: (config: PluginConfig) => void): () => void;
|
|
34
|
-
/** Merge a patch into the user layer and persist through the provider. */
|
|
35
|
-
update(patch: Partial<PluginConfig>): Promise<unknown>;
|
|
36
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Fields of this plugin's Config as the runtime resolved them. Every field is
|
|
48
|
+
* declared `volatile()`, so each one is a stable ref DSH commits a live edit
|
|
49
|
+
* into rather than a snapshot taken at apply time.
|
|
50
|
+
*/
|
|
51
|
+
export interface PluginConfigRefs {
|
|
52
|
+
readonly enabled: Volatile<boolean>;
|
|
53
|
+
readonly aiTimeoutMs: Volatile<number>;
|
|
54
|
+
readonly baseUrl: Volatile<string>;
|
|
55
|
+
readonly model: Volatile<string>;
|
|
56
|
+
readonly targetLang: Volatile<string>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Read the current committed value of every Config field.
|
|
60
|
+
* @param refs - the runtime-resolved Config object.
|
|
61
|
+
* @returns A detached plain config value.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readPluginConfig(refs: PluginConfigRefs): PluginConfig;
|
|
64
|
+
/**
|
|
65
|
+
* Adapt this plugin's runtime-resolved Config into the live source
|
|
66
|
+
* {@link ConfigManager} consumes.
|
|
67
|
+
*
|
|
68
|
+
* DSH commits an accepted live edit into the `Volatile` refs in place, then
|
|
69
|
+
* emits `loader/volatile-update` on the owning fiber — the listener is scoped
|
|
70
|
+
* to this plugin's own entry, so one subscription covers every field.
|
|
71
|
+
*
|
|
72
|
+
* @param onVolatileUpdate - subscribes to this plugin's volatile-commit event.
|
|
73
|
+
* @param refs - the runtime-resolved Config object.
|
|
74
|
+
* @returns The live read/watch source.
|
|
75
|
+
*/
|
|
76
|
+
export declare function createLiveConfigSource(onVolatileUpdate: (listener: () => void) => () => void, refs: PluginConfigRefs): ConfigSourceLike;
|
|
37
77
|
/**
|
|
38
78
|
* One path-addressed edit to a settings namespace's user section, matching
|
|
39
79
|
* DSH's `SettingsPathOp` wire shape.
|
|
@@ -47,9 +87,8 @@ export type SettingsPathOpLike = {
|
|
|
47
87
|
path: string[];
|
|
48
88
|
};
|
|
49
89
|
/**
|
|
50
|
-
* Provider-level write face the legacy migration needs:
|
|
51
|
-
* itself
|
|
52
|
-
* under one revision fence.
|
|
90
|
+
* Provider-level write face the legacy migration needs: DSH's `ctx.settings`
|
|
91
|
+
* service itself, whose `mutate` applies path ops under one revision fence.
|
|
53
92
|
*/
|
|
54
93
|
export interface SettingsMigrationTarget {
|
|
55
94
|
describe(): Array<{
|
|
@@ -60,29 +99,18 @@ export interface SettingsMigrationTarget {
|
|
|
60
99
|
mutate(ns: string, ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<unknown>;
|
|
61
100
|
}
|
|
62
101
|
/**
|
|
63
|
-
* Config facade over
|
|
102
|
+
* Config facade over this plugin's own live Config. No file I/O lives here
|
|
64
103
|
* anymore: persistence, atomic writes, external-edit hot reload and the
|
|
65
|
-
* browser-facing
|
|
104
|
+
* browser-facing edit API are all owned by DSH itself.
|
|
66
105
|
*/
|
|
67
106
|
export declare class ConfigManager {
|
|
68
|
-
private
|
|
107
|
+
private source;
|
|
69
108
|
private credentials;
|
|
70
|
-
constructor(
|
|
109
|
+
constructor(source: ConfigSourceLike, credentials: CredentialsReader);
|
|
71
110
|
getConfig(): PluginConfig;
|
|
72
111
|
/** Whether the AI channel has every required piece: baseUrl, model and key. */
|
|
73
112
|
isAiConfigured(): boolean;
|
|
74
113
|
onConfigChange(listener: (config: PluginConfig) => void): () => void;
|
|
75
|
-
/**
|
|
76
|
-
* Merge a partial update into the settings namespace. Values are sanitized
|
|
77
|
-
* here (bounds, trimming) so the schema's own constraints act as a second
|
|
78
|
-
* line of defence rather than the only one.
|
|
79
|
-
*
|
|
80
|
-
* @internal Test-only. No production path calls this: the host writes the
|
|
81
|
-
* settings namespace from the browser through the DSH settings service, and
|
|
82
|
-
* this facade is only driven by `scripts/test-*.mjs` (regression suite).
|
|
83
|
-
* Kept (with this marker) so those tests keep exercising the sanitizer.
|
|
84
|
-
*/
|
|
85
|
-
updateConfig(partial: Partial<PluginConfig>): Promise<PluginConfig>;
|
|
86
114
|
}
|
|
87
115
|
/**
|
|
88
116
|
* Coerce a raw record (legacy config file, HTTP-era partials) into a
|
|
@@ -91,6 +119,19 @@ export declare class ConfigManager {
|
|
|
91
119
|
* so one bad field never takes down a whole migration.
|
|
92
120
|
*/
|
|
93
121
|
export declare function sanitizePatch(input: Record<string, unknown>): Partial<PluginConfig>;
|
|
122
|
+
/**
|
|
123
|
+
* One-shot retirement of config keys an earlier release declared. DSH hands
|
|
124
|
+
* unknown keys through rather than rejecting them, so without this pass a
|
|
125
|
+
* profile that predates the reply-body rewrite keeps dead keys forever.
|
|
126
|
+
*
|
|
127
|
+
* A tolerant, per-key unset: only keys the user layer actually carries are
|
|
128
|
+
* written, and a failed write is logged and retried on the next boot rather
|
|
129
|
+
* than taking down plugin load.
|
|
130
|
+
*
|
|
131
|
+
* @param settings - DSH's own settings service (the `mutate` write face).
|
|
132
|
+
* @returns whether any retired key was removed.
|
|
133
|
+
*/
|
|
134
|
+
export declare function retireRemovedConfigKeys(settings: SettingsMigrationTarget): Promise<boolean>;
|
|
94
135
|
/**
|
|
95
136
|
* One-shot migration from the pre-1.2 standalone config file. Runs only while
|
|
96
137
|
* the settings namespace has no user layer yet, so values the user edited
|
|
@@ -1,66 +1,53 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReplyBlockResult } from './types.ts';
|
|
2
2
|
import type { ConfigManager } from './config.ts';
|
|
3
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;
|
|
10
8
|
private credentials;
|
|
11
9
|
private masking;
|
|
12
10
|
private adapters;
|
|
13
11
|
private circuitStates;
|
|
14
|
-
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
/** 思考链请求的串行队列尾,保证同时最多一个在途请求。 */
|
|
18
|
-
private thinkTail;
|
|
19
|
-
constructor(configManager: ConfigManager, cache: LruDiskCache, credentials?: KeyReader, thinkCache?: LruDiskCache);
|
|
12
|
+
/** 正文请求的串行队列尾,保证同时最多一个在途请求。 */
|
|
13
|
+
private replyTail;
|
|
14
|
+
constructor(configManager: ConfigManager, cache: LruDiskCache, credentials?: KeyReader);
|
|
20
15
|
private registerAdapter;
|
|
21
16
|
/**
|
|
22
|
-
*
|
|
17
|
+
* 翻译回答正文的块:本次唯一的翻译入口。
|
|
23
18
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* - AI on + NOT configured + Bing off -> [] (no translation)
|
|
28
|
-
* - AI off + Bing on -> [bing]
|
|
29
|
-
* - AI off + Bing off -> [] (no translation)
|
|
30
|
-
*/
|
|
31
|
-
private computeChannels;
|
|
32
|
-
translateBatch(texts: string[], forceRefresh?: boolean): Promise<TranslateItemResult[]>;
|
|
33
|
-
translateOne(rawText: string, forceRefresh?: boolean): Promise<TranslateItemResult>;
|
|
34
|
-
/**
|
|
35
|
-
* 翻译思考正文的块。整条链路与工具标题翻译分离:只走 AI 通道、独立串行
|
|
36
|
-
* 队列、独立超时、独立缓存池,Bing 从不参与。
|
|
19
|
+
* 每个块先按输入上限切成片段,再各自掩码、相邻片段打包成一个请求;整批失败
|
|
20
|
+
* 时退回逐片段单发。调用方传进来的每个块要么整块译出、要么整块保持原文:
|
|
21
|
+
* 任一片段缺失(含掩码还原不通过)都让该块作废,避免半中半英的段落。
|
|
37
22
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* 避免半中半英的段落。
|
|
23
|
+
* 客户端按 2048 估算 token 分块,所以一个 markdown 块可能跨多次调用;每次
|
|
24
|
+
* 调用都独立决定成败,不会出现「前一段已挂译文、后一段失败」的半截结果。
|
|
41
25
|
*/
|
|
42
|
-
|
|
26
|
+
translateReplyBlocks(blocks: string[]): Promise<ReplyBlockResult[]>;
|
|
43
27
|
/**
|
|
44
28
|
* 一整批一次请求;失败则该批逐片段单发重试一次,仍失败的片段直接放弃
|
|
45
|
-
*
|
|
29
|
+
* (保留原文),不在界面上留提示。失败的片段不进缓存,因此下一次滚回视口
|
|
30
|
+
* 时会自然重试。
|
|
31
|
+
*
|
|
32
|
+
* 熔断记账按「批」算:一次批请求失败记一次,逐片段重试失败再各记一次。
|
|
46
33
|
*/
|
|
47
|
-
private
|
|
48
|
-
/**
|
|
49
|
-
private
|
|
50
|
-
/**
|
|
51
|
-
private
|
|
34
|
+
private translateReplyBatch;
|
|
35
|
+
/** 发出一次正文请求并把结果还原成每个片段的最终译文。 */
|
|
36
|
+
private requestReplyBatch;
|
|
37
|
+
/** 串行执行器:同时最多一个在途请求。 */
|
|
38
|
+
private runReplySerial;
|
|
52
39
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
40
|
+
* 通道探针:设置面板的「测试 AI 通道」按钮走这里。探测文本用一句英文正文,
|
|
41
|
+
* 与真实请求同形。
|
|
42
|
+
*
|
|
43
|
+
* 探针是用户主动发起的连通性检查,因此不写也不读熔断账本:连点几次测试
|
|
44
|
+
* 不该把正文翻译挡在冷却期外,正文的失败也不该让按钮变哑。
|
|
55
45
|
*/
|
|
56
|
-
private isChannelCoolingDown;
|
|
57
46
|
testChannel(channelId: string): Promise<{
|
|
58
47
|
ok: boolean;
|
|
59
48
|
latencyMs: number;
|
|
60
49
|
error?: string;
|
|
61
50
|
}>;
|
|
62
|
-
private enqueueTask;
|
|
63
|
-
private processNext;
|
|
64
51
|
private isCircuitOpen;
|
|
65
52
|
private recordSuccess;
|
|
66
53
|
private recordFailure;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 回答正文翻译的请求预算。每次请求的输入估算 token 上限,与本地服务的
|
|
3
3
|
* prefill 批次(MAX_NUM_BATCHED_TOKENS)保持同一量级;输出上限写进请求体的
|
|
4
4
|
* max_tokens,避免长文本把剩余上下文全部花在生成上。
|
|
5
5
|
*/
|
|
6
|
-
export declare const
|
|
7
|
-
export declare const
|
|
6
|
+
export declare const REPLY_MAX_INPUT_TOKENS = 4096;
|
|
7
|
+
export declare const REPLY_MAX_OUTPUT_TOKENS = 8192;
|
|
8
8
|
/**
|
|
9
9
|
* 保守估算一段文本占用的 token 数:汉字一字一 token,其余每三个字符一 token。
|
|
10
10
|
* 实测本机 hy-mt2-1.8b 的英文语料约为每 3.3 字符一 token,这里取 3 作为上界,
|
|
@@ -17,7 +17,7 @@ export declare function estimateTokens(text: string): number;
|
|
|
17
17
|
*/
|
|
18
18
|
export declare function splitOversizedBlock(text: string, maxTokens?: number): string[];
|
|
19
19
|
/** 一个待翻译的片段:属于哪个块、块内第几段、以及该片段的掩码结果。 */
|
|
20
|
-
export interface
|
|
20
|
+
export interface ReplyPieceShell<TMask> {
|
|
21
21
|
block: number;
|
|
22
22
|
index: number;
|
|
23
23
|
text: string;
|
|
@@ -31,19 +31,19 @@ export declare function packPieces<T extends {
|
|
|
31
31
|
* 块分隔标记:`⟪<4 letters><index>⟫`。外括号刻意与掩码占位符的 ⟦⟧ 不同形,
|
|
32
32
|
* 掩码残留检测(只认 ⟦⟧)因此不会把这个标记当成泄漏。
|
|
33
33
|
*/
|
|
34
|
-
export interface
|
|
34
|
+
export interface BatchFormat {
|
|
35
35
|
id: string;
|
|
36
36
|
token: (index: number) => string;
|
|
37
37
|
}
|
|
38
|
-
export declare function
|
|
38
|
+
export declare function createBatchFormat(): BatchFormat;
|
|
39
39
|
/** 每个片段以单独一行标记开头,段与段之间空一行。 */
|
|
40
|
-
export declare function buildBatchPayload(pieces: string[], format:
|
|
40
|
+
export declare function buildBatchPayload(pieces: string[], format: BatchFormat): string;
|
|
41
41
|
/**
|
|
42
42
|
* 按标记把整批译文切回每个片段。
|
|
43
43
|
*
|
|
44
44
|
* 序号必须恰好出现一次、按 0..count-1 升序、每段非空,且不得混入其它批次
|
|
45
45
|
* 的标记;任何一条不满足都返回 null,由调用方作废整批并退回单块重试。
|
|
46
46
|
*/
|
|
47
|
-
export declare function splitBatchTranslation(translated: string, format:
|
|
47
|
+
export declare function splitBatchTranslation(translated: string, format: BatchFormat, count: number): string[] | null;
|
|
48
48
|
/** 翻译结果里是否还留着块标记(用于判定整批作废)。 */
|
|
49
|
-
export declare function
|
|
49
|
+
export declare function hasBatchResidue(text: string): boolean;
|
|
@@ -5,17 +5,15 @@ import type { TranslationDispatcher } from './dispatcher.ts';
|
|
|
5
5
|
* the shared `/api` channel.
|
|
6
6
|
*
|
|
7
7
|
* Config and credentials have no HTTP endpoints: since 1.2 the settings panel
|
|
8
|
-
* reads and writes through DSH's own channels — the
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* channel probe.
|
|
8
|
+
* reads and writes through DSH's own channels — the shared configuration form
|
|
9
|
+
* (`ctx.configForms`) over the profile entry, and the `credentials` Remote API
|
|
10
|
+
* for the key — so the plugin owns exactly two routes: reply-body block
|
|
11
|
+
* translation, and the channel probe.
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
14
|
-
export declare const
|
|
13
|
+
/** Reply-body block translation route path. */
|
|
14
|
+
export declare const REPLY_ROUTE_PATH = "/api/dsh-chat-translate/translate-reply";
|
|
15
15
|
/** Single-channel probe route path. */
|
|
16
16
|
export declare const TEST_CHANNEL_ROUTE_PATH = "/api/dsh-chat-translate/test-channel";
|
|
17
|
-
/** Think-chain block translation route path. */
|
|
18
|
-
export declare const THINK_ROUTE_PATH = "/api/dsh-chat-translate/translate-think";
|
|
19
17
|
/**
|
|
20
18
|
* Build the plugin's exact Fetch routes.
|
|
21
19
|
* @param dispatcher - translation coordinator the routes proxy to.
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
export interface PluginConfig {
|
|
2
2
|
enabled: boolean;
|
|
3
|
-
concurrency: number;
|
|
4
|
-
timeoutMs: number;
|
|
5
3
|
aiTimeoutMs: number;
|
|
6
|
-
thinkTimeoutMs: number;
|
|
7
|
-
aiEnabled: boolean;
|
|
8
|
-
bingEnabled: boolean;
|
|
9
|
-
thinkEnabled: boolean;
|
|
10
4
|
baseUrl: string;
|
|
11
5
|
model: string;
|
|
12
6
|
targetLang: string;
|
|
@@ -35,8 +29,8 @@ export interface ITranslationAdapter {
|
|
|
35
29
|
isAvailable(config: PluginConfig): boolean;
|
|
36
30
|
translate(text: string, signal: AbortSignal, config: PluginConfig, options?: TranslateAdapterOptions): Promise<string>;
|
|
37
31
|
}
|
|
38
|
-
/** One
|
|
39
|
-
export interface
|
|
32
|
+
/** One reply block's outcome, aligned by index with the request's block list. */
|
|
33
|
+
export interface ReplyBlockResult {
|
|
40
34
|
original: string;
|
|
41
35
|
translated: string;
|
|
42
36
|
ok: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lynn123411/dsh-chat-translate",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Assistant reply-body translation for the DeepSeek Harness Web UI (OpenAI-compatible channel)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "lynn123411",
|
|
7
7
|
"repository": {
|
|
@@ -61,23 +61,23 @@
|
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "node build.mjs",
|
|
63
63
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
64
|
-
"test": "node scripts/test-client-wiring.mjs && node scripts/test-regression-suite.mjs && node scripts/test-channel-logic.mjs && node scripts/test-store.mjs && node scripts/test-toggle-restore.mjs && node scripts/test-
|
|
64
|
+
"test": "node scripts/test-client-wiring.mjs && node scripts/test-regression-suite.mjs && node scripts/test-channel-logic.mjs && node scripts/test-store.mjs && node scripts/test-toggle-restore.mjs && node scripts/test-reply-pipeline.mjs && node scripts/test-reply-client.mjs",
|
|
65
65
|
"assert:lib-untracked": "node scripts/assert-lib-untracked.mjs",
|
|
66
66
|
"check": "pnpm typecheck && pnpm build && pnpm test && pnpm assert:lib-untracked"
|
|
67
67
|
},
|
|
68
68
|
"engines": {
|
|
69
69
|
"node": "^22.19 || >=24",
|
|
70
|
-
"dsh": "0.1.
|
|
70
|
+
"dsh": "0.1.7-rc.2"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@deepseek-ai/cordis": "4.0.
|
|
74
|
-
"@deepseek-ai/dsh-client-connection": "0.1.
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.1.
|
|
76
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.
|
|
77
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.
|
|
78
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.
|
|
79
|
-
"@deepseek-ai/dsh-home-paths": "0.1.
|
|
80
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
73
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
74
|
+
"@deepseek-ai/dsh-client-connection": "0.1.7-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-home-paths": "0.1.7-rc.2",
|
|
80
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
81
81
|
"@eslint/js": "^9.30.0",
|
|
82
82
|
"@types/node": "^22.19.0",
|
|
83
83
|
"@types/react": "^19.2.18",
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
"typescript": "^6.0.0"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
|
-
"@deepseek-ai/dsh-client-ui-primitives": "
|
|
90
|
-
"@deepseek-ai/dsh-home-paths": "0.1.
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-home-paths": "0.1.7-rc.2"
|
|
91
91
|
},
|
|
92
92
|
"peerDependenciesMeta": {
|
|
93
93
|
"@deepseek-ai/dsh-client-ui-primitives": {
|
|
@@ -1,19 +0,0 @@
|
|
|
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;
|
|
@@ -1,55 +0,0 @@
|
|
|
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
|
-
/** 折叠或停用时撤掉按钮,卡片的翻译状态不变。 */
|
|
38
|
-
private removeButton;
|
|
39
|
-
/** 插件停用:撤掉按钮并还原这张卡片上的全部译文。 */
|
|
40
|
-
private discardCard;
|
|
41
|
-
/** 卡片当前显示的是译文(至少有一个译文块可见)。 */
|
|
42
|
-
private isShowingTranslation;
|
|
43
|
-
private bodyOf;
|
|
44
|
-
/** 展开时若整条都命中缓存,直接显示中文,不点按钮也不发请求。 */
|
|
45
|
-
private showCachedWhenComplete;
|
|
46
|
-
/** 按钮被点击;按钮只在展开的卡片上存在。 */
|
|
47
|
-
private activate;
|
|
48
|
-
/** 翻译这张卡片的全部单位:命中缓存的先挂上,其余按块请求。 */
|
|
49
|
-
private translateCard;
|
|
50
|
-
private translateUnits;
|
|
51
|
-
/** 把译文挂到单位上;期间文本或节点已经变动的单位直接放弃(结果已进缓存)。 */
|
|
52
|
-
private applyUnit;
|
|
53
|
-
}
|
|
54
|
-
export declare const thinkTranslator: ThinkTranslateController;
|
|
55
|
-
export {};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* StreamDebounceViewportObserver
|
|
3
|
-
*
|
|
4
|
-
* Combines:
|
|
5
|
-
* 1. True IntersectionObserver for viewport visibility detection (rootMargin: '150px 0px').
|
|
6
|
-
* 2. Typing/streaming debounce (default 400ms) to avoid translating partial streaming sentences.
|
|
7
|
-
* 3. Batch queuing to group multiple visible elements into efficient batch requests.
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
export interface ViewportObserverOptions {
|
|
11
|
-
rootMargin?: string;
|
|
12
|
-
debounceMs?: number;
|
|
13
|
-
onVisibleBatch: (items: Array<{
|
|
14
|
-
element: HTMLElement;
|
|
15
|
-
text: string;
|
|
16
|
-
}>) => void;
|
|
17
|
-
}
|
|
18
|
-
export declare class StreamDebounceViewportObserver {
|
|
19
|
-
private intersectionObserver;
|
|
20
|
-
/**
|
|
21
|
-
* Pending per-element streaming debounce timers. A Map (not a WeakMap)
|
|
22
|
-
* because disconnect() must enumerate and clear every pending timer: a
|
|
23
|
-
* WeakMap cannot be iterated, so a debounce armed just before the switch was
|
|
24
|
-
* turned off would still fire register() afterwards.
|
|
25
|
-
*/
|
|
26
|
-
private streamingTimers;
|
|
27
|
-
private pendingQueue;
|
|
28
|
-
private batchFlushTimer;
|
|
29
|
-
private options;
|
|
30
|
-
constructor(options: ViewportObserverOptions);
|
|
31
|
-
private initIntersectionObserver;
|
|
32
|
-
/**
|
|
33
|
-
* Observe an element with streaming debounce.
|
|
34
|
-
* If streaming updates characterData repeatedly within debounceMs, the timer resets.
|
|
35
|
-
*/
|
|
36
|
-
observeWithDebounce(element: HTMLElement, text: string, immediate?: boolean): void;
|
|
37
|
-
private armDebounce;
|
|
38
|
-
private register;
|
|
39
|
-
private enqueueBatch;
|
|
40
|
-
private flushQueue;
|
|
41
|
-
unobserve(element: HTMLElement): void;
|
|
42
|
-
disconnect(): void;
|
|
43
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { ITranslationAdapter, PluginConfig } from './base.ts';
|
|
2
|
-
interface BingTokens {
|
|
3
|
-
ig: string;
|
|
4
|
-
key: string;
|
|
5
|
-
token: string;
|
|
6
|
-
}
|
|
7
|
-
export declare function fetchTokens(signal: AbortSignal, forceRefresh?: boolean): Promise<BingTokens>;
|
|
8
|
-
export declare class BingWebAdapter implements ITranslationAdapter {
|
|
9
|
-
readonly id = "bing";
|
|
10
|
-
readonly name = "\u5FAE\u8F6F Bing \u7F51\u9875\u7FFB\u8BD1 (\u514DKey\u76F4\u8FDE)";
|
|
11
|
-
isAvailable(_config: PluginConfig): boolean;
|
|
12
|
-
translate(text: string, signal: AbortSignal, config: PluginConfig): Promise<string>;
|
|
13
|
-
private executeTranslate;
|
|
14
|
-
}
|
|
15
|
-
export {};
|