@lynn123411/dsh-chat-translate 2.0.0 → 3.0.1
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 -12
- package/docs//351/205/215/347/275/256.md +9 -13
- package/lib/client.js +813 -837
- package/lib/client.js.map +4 -4
- package/lib/index.js +100 -264
- package/lib/index.js.map +4 -4
- package/lib/types/client/chat/assistant-step.d.ts +79 -0
- package/lib/types/client/chat/click-guard.d.ts +27 -0
- package/lib/types/client/chat/presentation.d.ts +51 -0
- package/lib/types/client/chat/row-plan.d.ts +72 -0
- package/lib/types/client/chat/styles.d.ts +19 -0
- package/lib/types/client/chat/translate-store.d.ts +73 -0
- package/lib/types/client/composer/dock-toggle.d.ts +26 -0
- package/lib/types/client/index.d.ts +26 -17
- package/lib/types/client/locales.d.ts +5 -2
- package/lib/types/index.d.ts +8 -11
- package/lib/types/server/cache.d.ts +4 -19
- package/lib/types/server/config.d.ts +6 -72
- package/lib/types/server/credentials.d.ts +3 -3
- package/lib/types/server/dispatcher.d.ts +6 -0
- package/lib/types/server/pipeline/mask-tokens.d.ts +4 -22
- package/lib/types/server/pipeline/masking.d.ts +7 -12
- package/lib/types/server/prompt-revision.d.ts +14 -0
- package/lib/types/server/router.d.ts +2 -2
- package/lib/types/server/types.d.ts +0 -1
- package/package.json +11 -11
- package/lib/types/client/translate/client-cache.d.ts +0 -17
- package/lib/types/client/translate/lazy.d.ts +0 -16
- package/lib/types/client/translate/mount.d.ts +0 -57
- package/lib/types/client/translate/observer.d.ts +0 -52
- package/lib/types/client/translate/reply.d.ts +0 -38
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 助手行渲染器:经 keyed slot `conversation.chat.node`(key `assistant-step`)
|
|
3
|
+
* 替换宿主默认的行渲染。
|
|
4
|
+
*
|
|
5
|
+
* 接管换来两件事——
|
|
6
|
+
* 1. 正文块从数据层拿**原始 markdown**(`AssistantBlock.text`),译文同样按
|
|
7
|
+
* markdown 交给宿主公开基线组件 `MarkdownText` 重渲染:格式保真由官方
|
|
8
|
+
* 渲染器负责,本文件不产生任何自拼 markup。
|
|
9
|
+
* 2. 「已翻译」是渲染状态:落定、开关开启、进入视口后逐行请求;成功的块
|
|
10
|
+
* (包括模型认为原样最好的块)挂译文、左缘一条细线、点击在译文与原文间
|
|
11
|
+
* 切换;失败的块保持原文、无标记,下一次条件满足自然重试。
|
|
12
|
+
*
|
|
13
|
+
* 除正文外的行内容与宿主逐分支等价:reasoning 行走折叠(含 Turn-process
|
|
14
|
+
* 隐藏与 beforematch 揭示)、连续 image 组交回 owner 的 renderMessageImages、
|
|
15
|
+
* 未知块落 JsonBlock、tool-call 在助手行内跳过(工具行由宿主独立渲染)、
|
|
16
|
+
* 中断前缀行尾带「已停止」。props 形状是宿主 contract 的结构性镜像:值边
|
|
17
|
+
* 不跨包(客户端 bundle 纯净门禁),类型注释指明对应声明。
|
|
18
|
+
* 本渲染器抛错时按 keyed slot 的退位语义退出该 cell,宿主原渲染器回位。
|
|
19
|
+
*/
|
|
20
|
+
import * as React from 'react';
|
|
21
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
22
|
+
import type { MarkdownFileMentions } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
23
|
+
import type { AssistantBlockLike } from './row-plan.ts';
|
|
24
|
+
interface DisclosureState {
|
|
25
|
+
readonly expanded: boolean;
|
|
26
|
+
readonly setExpanded: (open: boolean) => void;
|
|
27
|
+
readonly toggle: () => void;
|
|
28
|
+
}
|
|
29
|
+
interface PresentationLike {
|
|
30
|
+
readonly settledReasoningPreview: boolean;
|
|
31
|
+
}
|
|
32
|
+
interface NodeDataLike {
|
|
33
|
+
readonly status: string;
|
|
34
|
+
readonly turn: number;
|
|
35
|
+
readonly step: number;
|
|
36
|
+
readonly blocks: readonly AssistantBlockLike[];
|
|
37
|
+
readonly finalNode?: {
|
|
38
|
+
readonly seq: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
interface TurnProcessLike {
|
|
42
|
+
readonly foldable: boolean;
|
|
43
|
+
readonly open: boolean;
|
|
44
|
+
readonly spec: {
|
|
45
|
+
readonly answerStep: number;
|
|
46
|
+
readonly inlineReasoning: boolean;
|
|
47
|
+
};
|
|
48
|
+
readonly setOpen: (open: boolean) => void;
|
|
49
|
+
}
|
|
50
|
+
export interface AssistantStepProps {
|
|
51
|
+
readonly node: {
|
|
52
|
+
readonly data: NodeDataLike;
|
|
53
|
+
readonly location?: {
|
|
54
|
+
readonly kind?: string;
|
|
55
|
+
readonly turn?: {
|
|
56
|
+
readonly status?: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
readonly anchorSeq?: number;
|
|
60
|
+
};
|
|
61
|
+
readonly groupPart?: string;
|
|
62
|
+
readonly useDisclosure: () => DisclosureState;
|
|
63
|
+
readonly useTurnData: (key: string) => unknown;
|
|
64
|
+
readonly turnProcess?: TurnProcessLike;
|
|
65
|
+
readonly openFile: (path: string, options?: {
|
|
66
|
+
line?: number;
|
|
67
|
+
}) => void;
|
|
68
|
+
readonly renderMessageImages: (owner: {
|
|
69
|
+
readonly images: readonly {
|
|
70
|
+
readonly attachment: unknown;
|
|
71
|
+
}[];
|
|
72
|
+
readonly align?: string;
|
|
73
|
+
}) => ReactNode;
|
|
74
|
+
readonly fileMentions: (owner: unknown) => MarkdownFileMentions | undefined;
|
|
75
|
+
readonly usePresentation: <R>(select: (policy: PresentationLike) => R) => R;
|
|
76
|
+
readonly t: (key: string, params?: Record<string, unknown>) => string;
|
|
77
|
+
}
|
|
78
|
+
export declare const AssistantStepView: React.MemoExoticComponent<(props: AssistantStepProps) => ReactElement | null>;
|
|
79
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 已翻译块的点击守卫:切换只认「落在块空白正文上的裸点击」。
|
|
3
|
+
*
|
|
4
|
+
* 块内含链接、代码块复制钮等交互元素——它们的点击归自己,不连带切块;
|
|
5
|
+
* 拖选译文松手产生的 click 同样忽略。纯 DOM 判定,不依赖 React,可独立测试。
|
|
6
|
+
*/
|
|
7
|
+
interface MinimalPointerEvent {
|
|
8
|
+
/** 点击命中的最深元素。 */
|
|
9
|
+
target: {
|
|
10
|
+
closest?(selector: string): unknown;
|
|
11
|
+
} | null;
|
|
12
|
+
/** 挂了本守卫的已翻译块容器。 */
|
|
13
|
+
currentTarget: unknown;
|
|
14
|
+
}
|
|
15
|
+
interface MinimalWindow {
|
|
16
|
+
getSelection?(): {
|
|
17
|
+
isCollapsed?: boolean;
|
|
18
|
+
toString?(): string;
|
|
19
|
+
} | null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* @param event - click 事件(结构面:target/currentTarget)。
|
|
23
|
+
* @param view - 可注入的 window(测试传假对象;运行时传全局 window)。
|
|
24
|
+
* @returns 该点击是否应当触发原文/译文切换。
|
|
25
|
+
*/
|
|
26
|
+
export declare function isBareBlockClick(event: MinimalPointerEvent, view: MinimalWindow | undefined): boolean;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 助手行渲染所需的「工作细节」呈现策略。
|
|
3
|
+
*
|
|
4
|
+
* 宿主把这一策略派生自 `ui-chat` 配置条目的 `transcriptView` 字段;本插件接管
|
|
5
|
+
* 助手行渲染后,经 `ctx.configForms.get('ui-chat')` 读同一个字段,映射到与宿主
|
|
6
|
+
* 同形、同值的策略表。策略对象按模式常驻(同一模式恒等返回同一实例),
|
|
7
|
+
* 选择器订阅看到的是稳定引用。
|
|
8
|
+
*/
|
|
9
|
+
/** 与宿主 ChatPresentationPolicy 同形的结构性镜像。 */
|
|
10
|
+
export interface ChatPresentationPolicy {
|
|
11
|
+
readonly mode: string;
|
|
12
|
+
readonly foldCompletedTurns: boolean;
|
|
13
|
+
readonly stepGrouping: 'collapsed' | 'history' | 'none';
|
|
14
|
+
readonly liveProcessDetail: boolean;
|
|
15
|
+
readonly settledReasoningPreview: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 模式 → 策略常量表,逐行对齐宿主 ui-chat 的实现(含两代 legacy 存储值:
|
|
19
|
+
* `normal` 按 standard 读、`expanded` 按 detailed 读,从不写回)。
|
|
20
|
+
* 渲染器只选择单个字段,不按 mode 分支。
|
|
21
|
+
*/
|
|
22
|
+
export declare const POLICY_BY_MODE: Readonly<Record<string, ChatPresentationPolicy>>;
|
|
23
|
+
/** 宿主在用户没有显式选择时使用的模式。 */
|
|
24
|
+
export declare const DEFAULT_MODE = "standard";
|
|
25
|
+
/** 本模块只用到配置面的这一小片形状(结构性镜像,值边不跨包)。 */
|
|
26
|
+
interface ModeFormLike {
|
|
27
|
+
getSnapshot(): {
|
|
28
|
+
status: string;
|
|
29
|
+
value?: Record<string, unknown>;
|
|
30
|
+
};
|
|
31
|
+
subscribe(listener: () => void): () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 跟随 `ui-chat` 配置的活策略源。attach 之后:读一次、订一次;配置面缺席
|
|
35
|
+
* (宿主半边未服务该条目、内存模式)时停在 standard,不发订阅。
|
|
36
|
+
*/
|
|
37
|
+
export declare class ChatPresentation {
|
|
38
|
+
private policy;
|
|
39
|
+
private listeners;
|
|
40
|
+
private form;
|
|
41
|
+
private unsubscribe;
|
|
42
|
+
attach(form: ModeFormLike | null | undefined): void;
|
|
43
|
+
detach(): void;
|
|
44
|
+
getSnapshot: () => ChatPresentationPolicy;
|
|
45
|
+
subscribe: (listener: () => void) => (() => void);
|
|
46
|
+
private read;
|
|
47
|
+
}
|
|
48
|
+
export declare function createChatPresentation(): ChatPresentation;
|
|
49
|
+
/** 渲染器与配置面之间的进程内单例:在 client/index.ts 接线一次。 */
|
|
50
|
+
export declare const chatPresentation: ChatPresentation;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 助手行的渲染计划:纯函数,不含 React,无浏览器依赖。
|
|
3
|
+
*
|
|
4
|
+
* 计划层承担两类判断——
|
|
5
|
+
* 1. 与宿主 `AssistantMarkdown` 逐条等价的分支:什么时候整行不渲染、
|
|
6
|
+
* groupPart 怎么过滤、tool-call 块为什么在这里被跳过(工具行是 flow 里
|
|
7
|
+
* 独立的行,由宿主自己渲染)、连续 image 怎么合成一组、未知块落 JSON、
|
|
8
|
+
* 「已停止」标记的出现条件。
|
|
9
|
+
* 2. 翻译的呈现判断:正文块在什么状态下显示译文并带「已翻译」标记。标准只有
|
|
10
|
+
* 一条——该块的翻译成功落定。「模型认为原样最好、返回同样的文字」同样是
|
|
11
|
+
* 成功:照常挂载、照常标记,标记传达的是「这段过了模型」,不是「这段变过」。
|
|
12
|
+
*
|
|
13
|
+
* 渲染层(assistant-step.tsx)把计划变成 JSX;改动这里的等价分支必须同时对照
|
|
14
|
+
* 宿主实现。
|
|
15
|
+
*/
|
|
16
|
+
import type { BlockOutcome } from './translate-store.ts';
|
|
17
|
+
/** 助手行块数据的结构性镜像(值边不跨包,type-only 世界)。 */
|
|
18
|
+
export interface AssistantBlockLike {
|
|
19
|
+
readonly kind: string;
|
|
20
|
+
readonly text?: string;
|
|
21
|
+
readonly attachment?: unknown;
|
|
22
|
+
readonly block?: unknown;
|
|
23
|
+
}
|
|
24
|
+
export type RowPlanEntry =
|
|
25
|
+
/** 思考块:blockIndex 供「流式尾块」判定,key 是条目在计划里的位置(React 键)。 */
|
|
26
|
+
{
|
|
27
|
+
type: 'reasoning';
|
|
28
|
+
key: number;
|
|
29
|
+
blockIndex: number;
|
|
30
|
+
text: string;
|
|
31
|
+
}
|
|
32
|
+
/** 正文块:translated 非 null 时显示该译文并带「已翻译」标记,否则显示 text。 */
|
|
33
|
+
| {
|
|
34
|
+
type: 'prose';
|
|
35
|
+
key: number;
|
|
36
|
+
text: string;
|
|
37
|
+
translated: string | null;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'images';
|
|
40
|
+
key: number;
|
|
41
|
+
attachments: readonly unknown[];
|
|
42
|
+
} | {
|
|
43
|
+
type: 'unknown';
|
|
44
|
+
key: number;
|
|
45
|
+
block: unknown;
|
|
46
|
+
} | {
|
|
47
|
+
type: 'stopped';
|
|
48
|
+
key: number;
|
|
49
|
+
};
|
|
50
|
+
export interface RowPlan {
|
|
51
|
+
/** null = 整行不渲染(与宿主的空行守卫一致)。 */
|
|
52
|
+
entries: RowPlanEntry[] | null;
|
|
53
|
+
/** 送翻译池的正文文本,按正文块出现顺序,与结果下标对齐。 */
|
|
54
|
+
texts: string[];
|
|
55
|
+
}
|
|
56
|
+
export interface RowPlanInput {
|
|
57
|
+
blocks: readonly AssistantBlockLike[];
|
|
58
|
+
/** 行在流式中:流式期间一律原文,落定后才有译文。 */
|
|
59
|
+
streaming: boolean;
|
|
60
|
+
/** 中断前缀:行尾带「已停止」标记。 */
|
|
61
|
+
interrupted: boolean;
|
|
62
|
+
/** flow 分组:'reasoning' 只要思考块,'response' 排除思考块,缺省全要。 */
|
|
63
|
+
groupPart?: string;
|
|
64
|
+
/** 可译门(enabled 且通道三要素齐备):false 时一切按原文呈现,不挂线。 */
|
|
65
|
+
canTranslate: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* 该行正文块的翻译结果,与 texts 同下标;null = 该批尚未落定(逐批出文的
|
|
68
|
+
* 中间态)。未登记或换代时整个缺省。
|
|
69
|
+
*/
|
|
70
|
+
outcomes?: readonly (BlockOutcome | null)[];
|
|
71
|
+
}
|
|
72
|
+
export declare function planAssistantRow(input: RowPlanInput): RowPlan;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 助手行接管所需的样式:与宿主 `AssistantMarkdown` / `ReasoningRow` 逐条对齐
|
|
3
|
+
* 的等价规则(字号、行距、gap、粘性折叠头、running 微光、summary 遮罩等),
|
|
4
|
+
* 加上本插件唯一新增的视觉词汇——左缘线的色相与粗细标记:蓝粗线(1px)=正在
|
|
5
|
+
* 读译文,灰细线(0.5px)=有译文但正在读原文(再点即切回),无线=没译成。
|
|
6
|
+
*
|
|
7
|
+
* 全部颜色与尺寸走 `--dsw-alias-*` / `--dsh-*` 主题别名,中性平边按官方规则
|
|
8
|
+
* 画 0.5px hairline;动画声明尊重 `prefers-reduced-motion`。样式随 bundle 注入
|
|
9
|
+
* 一次(`<style data-plugin-css>`),类名用固定前缀,不依赖宿主哈希类名。
|
|
10
|
+
*/
|
|
11
|
+
export declare const ASSISTANT_CSS: string;
|
|
12
|
+
/**
|
|
13
|
+
* 正文块的标记类名单点:有译文才可点(常驻内缩 + 光标);左缘线的色相与粗细
|
|
14
|
+
* 随显示态——译文挂 1px 蓝线、原文态挂 0.5px 灰线(保留「这块有译文、点了能
|
|
15
|
+
* 切回」的线索),没译无线。线宽只差 0.5px,切换不跳字。
|
|
16
|
+
*/
|
|
17
|
+
export declare function proseClassNames(processed: boolean, showOriginal: boolean): string | undefined;
|
|
18
|
+
/** 注入接管样式;重复调用是空操作。 */
|
|
19
|
+
export declare function ensureAssistantStyles(): void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type ReplyBlockResult } from '../translate/api.ts';
|
|
2
|
+
/** 一行正文的按块翻译结果,与请求的 texts 数组按下标对齐。 */
|
|
3
|
+
export interface BlockOutcome {
|
|
4
|
+
/** 模型产出的中文(Markdown 源文本);失败时等于原文。 */
|
|
5
|
+
translated: string;
|
|
6
|
+
/** 该块是否翻译成功。失败的块界面保持原文、不标记。 */
|
|
7
|
+
ok: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 一行正文的翻译状态。outcomes 与 texts 同下标,null = 该块所在批尚未落定:
|
|
11
|
+
* 每批返回就地填入并通知,长回答按阅读顺序逐段出中文,不憋到最后一批。
|
|
12
|
+
* 全部批落定后:无失败即 done;有失败标 partial——partial 行允许后续 ensure
|
|
13
|
+
* 重走(渲染层的下一次触发是滚出视口再回来、或行重挂载),失败的块因此
|
|
14
|
+
* 自然重试,成功的块由宿主磁盘缓存直接命中、不再花模型调用。重走有上限
|
|
15
|
+
* (MAX_ROW_ATTEMPTS):确定性失败(如弱模型丢占位符)不该被滚动无限续杯,
|
|
16
|
+
* 封顶后保持原文直到文本换代重置。
|
|
17
|
+
*/
|
|
18
|
+
export interface RowState {
|
|
19
|
+
status: 'pending' | 'partial' | 'done';
|
|
20
|
+
texts: readonly string[];
|
|
21
|
+
outcomes: readonly (BlockOutcome | null)[];
|
|
22
|
+
/** 这一代文本已经完整跑过几轮(首跑计 1)。 */
|
|
23
|
+
attempts: number;
|
|
24
|
+
}
|
|
25
|
+
/** 送译一行正文的取数面:默认走宿主路由,测试注入假实现。 */
|
|
26
|
+
export type ReplyFetcher = (texts: string[]) => Promise<ReplyBlockResult[]>;
|
|
27
|
+
/** 同时在池的行数上限:LRU 淘汰最久未读的整行;行文本本身仍归宿主会话持有。 */
|
|
28
|
+
export declare const MAX_TRANSLATED_ROWS = 200;
|
|
29
|
+
/** 同一代文本最多完整跑几轮;封顶后不再打通道,直到文本换代重置额度。 */
|
|
30
|
+
export declare const MAX_ROW_ATTEMPTS = 3;
|
|
31
|
+
/**
|
|
32
|
+
* 一次 HTTP 请求装载的估算 token 上限:与宿主的打包输入上限同值同源
|
|
33
|
+
* (blocks.ts 的 REPLY_MAX_INPUT_TOKENS)——客户端按宿主的打包窗口切批,
|
|
34
|
+
* 每批在宿主侧通常正好落进一次模型调用。
|
|
35
|
+
*/
|
|
36
|
+
export declare const REQUEST_TOKEN_BUDGET = 4096;
|
|
37
|
+
/** 按出现顺序把整行文本切成不超预算的批;单块超预算时独自成批。 */
|
|
38
|
+
export declare function chunkTexts(texts: readonly string[]): string[][];
|
|
39
|
+
/**
|
|
40
|
+
* 助手行的翻译池:按行键存「该行全部正文块的翻译结果」。
|
|
41
|
+
*
|
|
42
|
+
* 行键由渲染层给出(会话内锚点序号),文本数组参与一致性判断:同键不同文本
|
|
43
|
+
* 视为行换了一代内容,整行重新请求——这保证重渲染或会话切换后不会把旧译文
|
|
44
|
+
* 配到新文本上。请求幂等:同键同文本、且上一代已落定成功或仍在途的 ensure
|
|
45
|
+
* 不再发请求;只有 partial(有失败块)的同代 ensure 会重走,且同代封顶三次。在途期间换代,
|
|
46
|
+
* 迟到的旧代结果直接丢弃。
|
|
47
|
+
*
|
|
48
|
+
* 本模块不依赖 React:渲染层用 useSyncExternalStore 订阅版本,再读 getState。
|
|
49
|
+
*/
|
|
50
|
+
export declare class ChatTranslateStore {
|
|
51
|
+
private rows;
|
|
52
|
+
private listeners;
|
|
53
|
+
private version;
|
|
54
|
+
private fetch;
|
|
55
|
+
constructor(fetcher: ReplyFetcher);
|
|
56
|
+
/** 单调版本号:任何状态变化(含单批落定)都前进一位,供渲染层判等重渲。 */
|
|
57
|
+
getVersion(): number;
|
|
58
|
+
subscribe: (listener: () => void) => (() => void);
|
|
59
|
+
/** 读一行状态,并把该行刷成最近使用。 */
|
|
60
|
+
getState(rowKey: string): RowState | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* 确保该行按当前文本被翻译。
|
|
63
|
+
* @returns 是否发出了新的请求(同代 done/pending 与换代前的登记之外都是 false)。
|
|
64
|
+
*/
|
|
65
|
+
ensure(rowKey: string, texts: readonly string[]): boolean;
|
|
66
|
+
private run;
|
|
67
|
+
private prune;
|
|
68
|
+
private bump;
|
|
69
|
+
}
|
|
70
|
+
/** 供测试注入取数面创建独立池。 */
|
|
71
|
+
export declare function createTranslateStore(fetcher: ReplyFetcher): ChatTranslateStore;
|
|
72
|
+
/** 正文翻译池:客户端半边唯一的按行译文登记处。 */
|
|
73
|
+
export declare const chatTranslate: ChatTranslateStore;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
+
import { NS } from '../locales.ts';
|
|
4
|
+
/**
|
|
5
|
+
* `t` 由注册项的 `locale: NS` 注入,类型走共享的 `PropsLocale`。owner props
|
|
6
|
+
* (`PropsRuntime<'conversation.composer.dock'>`)的类型随
|
|
7
|
+
* `@deepseek-ai/dsh-client-ui-conversation` 声明,本包不依赖它(与 dsh-a6api
|
|
8
|
+
* 同法),组件也不读那些 prop。
|
|
9
|
+
*/
|
|
10
|
+
export type DockToggleProps = PropsLocale<typeof NS>;
|
|
11
|
+
/**
|
|
12
|
+
* 对话输入框下方那一行的翻译开关:一个胶囊按钮,点亮表示正文翻译开着。
|
|
13
|
+
* 它与设置页「总开关」是同一个 `enabled` 字段——点击写 profile 配置,两处
|
|
14
|
+
* 状态由同一个 store 同步;关闭与重开的行为(还原英文、重扫补翻)也走
|
|
15
|
+
* 同一条链路。Key 没配齐时照常可切换:缺配置只是不产生翻译,界面无特殊态。
|
|
16
|
+
*/
|
|
17
|
+
export declare function ComposerTranslateToggle(props: DockToggleProps): ReactElement;
|
|
18
|
+
/**
|
|
19
|
+
* 把开关注册进 `conversation.composer.dock`——ui-conversation 声明的
|
|
20
|
+
* 「输入框卡片下方那一行」。该 slot 只在会话形态渲染,新建会话的 Hero 形态
|
|
21
|
+
* 没有这一行,按钮随之不出现。`slots.inject` 等声明落地再注册,声明塌缩时
|
|
22
|
+
* 注册一起退场。
|
|
23
|
+
* @param ctx - DSH browser client context(`slots` 已在客户端半边声明注入)。
|
|
24
|
+
* @returns 撤销本次贡献的 disposer。
|
|
25
|
+
*/
|
|
26
|
+
export declare function setupComposerToggle(ctx: any): () => void;
|
|
@@ -1,36 +1,45 @@
|
|
|
1
1
|
/** Client plugin name, shared with the browser bundle id. */
|
|
2
2
|
export declare const name = "dsh-chat-translate";
|
|
3
3
|
/**
|
|
4
|
-
* Declared services: slots for the
|
|
5
|
-
* plus the card registration)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
4
|
+
* Declared services: slots for the keyed assistant-row takeover, the shared
|
|
5
|
+
* 「阅读体验」 settings page (page claim plus the card registration) and the
|
|
6
|
+
* composer dock pill; configForms for this plugin's own configuration form
|
|
7
|
+
* plus the host `ui-chat` entry whose `transcriptView` field drives the
|
|
8
|
+
* reasoning-row presentation policy; the remote + remote.credentials pair for
|
|
9
|
+
* the credentials Remote namespace; and locale for the card's own dictionary.
|
|
10
|
+
* The runtime withholds any service not declared here; `locale` is declared
|
|
11
|
+
* rather than sampled because `ctx.get('locale')` at apply time can beat the
|
|
12
|
+
* locale plugin's `provide`, which silently left the card's dictionary
|
|
13
|
+
* unregistered.
|
|
12
14
|
*/
|
|
13
15
|
export declare const inject: string[];
|
|
14
16
|
interface ClientContext {
|
|
15
17
|
effect(factory: () => void | (() => void), label?: string): void;
|
|
16
18
|
get?(serviceName: string): any;
|
|
17
|
-
slots?:
|
|
18
|
-
|
|
19
|
+
slots?: {
|
|
20
|
+
inject(name: string, register: () => unknown): () => void;
|
|
21
|
+
register(options: Record<string, unknown>, component: unknown): () => void;
|
|
22
|
+
};
|
|
23
|
+
configForms?: {
|
|
24
|
+
get(namespace: string): any;
|
|
25
|
+
};
|
|
19
26
|
locale?: any;
|
|
20
27
|
remote?: {
|
|
21
28
|
credentials?: any;
|
|
22
29
|
};
|
|
23
30
|
}
|
|
24
31
|
/**
|
|
25
|
-
* Mount the
|
|
26
|
-
* shared reading-settings
|
|
32
|
+
* Mount the assistant-row takeover (keyed `conversation.chat.node` /
|
|
33
|
+
* `assistant-step`), the settings UI card inside the shared reading-settings
|
|
34
|
+
* page, and the translation toggle in the composer dock.
|
|
27
35
|
* @param ctx - DSH browser client context.
|
|
28
36
|
*/
|
|
29
37
|
export declare function apply(ctx: ClientContext): void;
|
|
30
|
-
export {
|
|
31
|
-
export {
|
|
38
|
+
export { AssistantStepView } from './chat/assistant-step.tsx';
|
|
39
|
+
export { planAssistantRow } from './chat/row-plan.ts';
|
|
40
|
+
export { chatTranslate, createTranslateStore } from './chat/translate-store.ts';
|
|
41
|
+
export { chatPresentation, createChatPresentation, POLICY_BY_MODE } from './chat/presentation.ts';
|
|
42
|
+
export { ensureAssistantStyles } from './chat/styles.ts';
|
|
32
43
|
export { setupSettingsUi } from './settings/ui.tsx';
|
|
33
|
-
export {
|
|
34
|
-
export { clientCache } from './translate/client-cache.ts';
|
|
35
|
-
export { lazyQueue } from './translate/lazy.ts';
|
|
44
|
+
export { setupComposerToggle, ComposerTranslateToggle } from './composer/dock-toggle.tsx';
|
|
36
45
|
export { settingsStore } from './settings/store.ts';
|
|
@@ -11,7 +11,7 @@ export declare const zh: {
|
|
|
11
11
|
readonly title: "正文翻译";
|
|
12
12
|
readonly masterTitle: "翻译总开关";
|
|
13
13
|
readonly enableTranslation: "启用翻译";
|
|
14
|
-
readonly masterDesc: "
|
|
14
|
+
readonly masterDesc: "自动把助手回答正文翻成中文(如 {example}):折叠块里的过程正文与最终汇总一视同仁,整段中文的块也照送,由模型一并改写成自然中文、顺掉机器腔。Think 卡与工具调用行不翻,代码块原样保留。翻译过的块左缘有一条竖线:显示译文时是稍粗的蓝线,点回原文变细灰线(提示这里备着译文,再点即切回),没译成的没有线——线的颜色与粗细一眼分清「有没有译」和「正在读哪一版」。点击已翻译的块可在译文与原文间切换,模型认为原样最好、返回一模一样的文字时同样算翻译过、同样可切。译文按原 markdown 格式重新渲染,表格、列表、加粗与行内代码保持结构。回答还在流式输出时不翻,落定后按阅读顺序逐段出中文。只作用于当前查看的会话,译文不写回会话上下文。输入框下方那一行有同款开关,两处状态同步。";
|
|
15
15
|
readonly aiTitle: "AI 翻译(OpenAI 兼容协议)";
|
|
16
16
|
readonly aiRule: "只有这一条通道:Key / Base URL / 模型齐备才会翻译,缺任何一项都保持原文。";
|
|
17
17
|
readonly badgeConfigured: "已配置";
|
|
@@ -36,7 +36,10 @@ export declare const zh: {
|
|
|
36
36
|
readonly testOk: "连接正常,延迟 {latency}ms";
|
|
37
37
|
readonly testFail: "失败:{error}";
|
|
38
38
|
readonly behaviorTitle: "翻译范围";
|
|
39
|
-
readonly behaviorRule: "
|
|
39
|
+
readonly behaviorRule: "助手回答正文一律翻译,包括最终汇总与整段中文的块:中文块同样交给模型改写成自然中文,意义、术语与数字不变。思考链正文、工具调用标题与折叠摘要不翻译,代码块及其内部原样保留。";
|
|
40
|
+
readonly dockLabel: "译";
|
|
41
|
+
readonly dockOn: "正文翻译已开启,点击关闭";
|
|
42
|
+
readonly dockOff: "正文翻译已关闭,点击开启";
|
|
40
43
|
};
|
|
41
44
|
/** Locale keys this plugin renders. */
|
|
42
45
|
export type ChatTranslateLocaleKey = keyof typeof zh;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type { HostConnectionHandle } from '@deepseek-ai/dsh-client-connection';
|
|
2
2
|
import z from '@deepseek-ai/schemastery';
|
|
3
|
-
import { type PluginConfigRefs
|
|
3
|
+
import { type PluginConfigRefs } from './server/config.ts';
|
|
4
4
|
/** Stable Cordis loader name. */
|
|
5
5
|
export declare const name = "dsh-chat-translate";
|
|
6
6
|
/**
|
|
7
|
-
* Hard dependencies:
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* without one.
|
|
7
|
+
* Hard dependencies: credentials is the DSH-owned secret surface this plugin
|
|
8
|
+
* rides on, and settings carries the entry's automatic-page policy. The
|
|
9
|
+
* plugin's own values arrive as its resolved Config. The translation routes
|
|
10
|
+
* register through the connection service when a Web carrier composes it, so
|
|
11
|
+
* apply takes that service through ctx.inject and the plugin still loads in
|
|
12
|
+
* compositions without one.
|
|
14
13
|
*/
|
|
15
14
|
export declare const inject: string[];
|
|
16
15
|
/**
|
|
@@ -25,18 +24,16 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
25
24
|
aiTimeoutMs: z<number, number, "volatile-defined">;
|
|
26
25
|
baseUrl: z<string, string, "volatile-defined">;
|
|
27
26
|
model: z<string, string, "volatile-defined">;
|
|
28
|
-
targetLang: z<string, string, "volatile-defined">;
|
|
29
27
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
30
28
|
enabled: z<boolean, boolean, "volatile-defined">;
|
|
31
29
|
aiTimeoutMs: z<number, number, "volatile-defined">;
|
|
32
30
|
baseUrl: z<string, string, "volatile-defined">;
|
|
33
31
|
model: z<string, string, "volatile-defined">;
|
|
34
|
-
targetLang: z<string, string, "volatile-defined">;
|
|
35
32
|
}>>, "plain">;
|
|
36
33
|
interface HostContext {
|
|
37
34
|
connection: HostConnectionHandle;
|
|
38
35
|
fiber: unknown;
|
|
39
|
-
settings:
|
|
36
|
+
settings: {
|
|
40
37
|
/** Register this entry's automatic-page policy; `auto: false` opts out. */
|
|
41
38
|
configure(presentation: {
|
|
42
39
|
auto?: boolean;
|
|
@@ -2,31 +2,16 @@ export declare class LruDiskCache {
|
|
|
2
2
|
private cache;
|
|
3
3
|
private maxEntries;
|
|
4
4
|
private filePath;
|
|
5
|
+
private revision;
|
|
5
6
|
private saveTimer;
|
|
6
7
|
private dirty;
|
|
7
|
-
|
|
8
|
-
private legacyPath;
|
|
9
|
-
/** Only the original cache file carries a pre-1.2 root-level predecessor. */
|
|
10
|
-
private hasLegacyPredecessor;
|
|
11
|
-
constructor(maxEntries?: number, fileName?: string);
|
|
8
|
+
constructor(maxEntries?: number, fileName?: string, revision?: string);
|
|
12
9
|
init(): Promise<void>;
|
|
13
|
-
/**
|
|
14
|
-
private
|
|
10
|
+
/** Adopt loaded entries, dropping poisoned or malformed ones. */
|
|
11
|
+
private loadEntries;
|
|
15
12
|
get(key: string): string | undefined;
|
|
16
13
|
set(key: string, value: string): void;
|
|
17
14
|
private scheduleSave;
|
|
18
15
|
flush(): Promise<void>;
|
|
19
16
|
dispose(): Promise<void>;
|
|
20
17
|
}
|
|
21
|
-
/**
|
|
22
|
-
* One-shot retirement of store files an earlier release owned. Repo rule: a
|
|
23
|
-
* capability's leftovers go away in the change that removes the capability, so
|
|
24
|
-
* a retired pool file is deleted rather than left to rot in the user's home.
|
|
25
|
-
*
|
|
26
|
-
* Absent files and unreadable paths both produce `false`; this never throws —
|
|
27
|
-
* a failed cleanup must not stop the plugin from loading.
|
|
28
|
-
*
|
|
29
|
-
* @param fileNames - file names under the plugin's own home directory.
|
|
30
|
-
* @returns whether at least one file was removed.
|
|
31
|
-
*/
|
|
32
|
-
export declare function retireStoreFiles(fileNames: readonly string[]): Promise<boolean>;
|
|
@@ -4,30 +4,17 @@ import type { CredentialsReader } from './credentials.ts';
|
|
|
4
4
|
/**
|
|
5
5
|
* Bounds for the AI channel request timeout. One request carries a packed run
|
|
6
6
|
* of whole reply blocks, and a local model can take minutes on that much
|
|
7
|
-
* prose, so the budget is long-form
|
|
7
|
+
* prose, so the budget is sized for long-form output.
|
|
8
8
|
*/
|
|
9
9
|
export declare const AI_TIMEOUT_MIN = 500;
|
|
10
10
|
export declare const AI_TIMEOUT_MAX = 900000;
|
|
11
11
|
/**
|
|
12
|
-
* The settings namespace this plugin owns.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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.
|
|
12
|
+
* The settings namespace this plugin owns. A namespace IS the Profile entry
|
|
13
|
+
* id, so the user-editable layer is the `config` row of `dsh-chat-translate`
|
|
14
|
+
* in the active profile's patch (`~/.dsh/profiles/<profile>/cordis.patch.yml`).
|
|
18
15
|
*/
|
|
19
16
|
export declare const SETTINGS_NAMESPACE = "dsh-chat-translate";
|
|
20
17
|
export declare const DEFAULT_CONFIG: PluginConfig;
|
|
21
|
-
/**
|
|
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
18
|
/**
|
|
32
19
|
* Live read face of this plugin's own Config, as `ConfigManager` consumes it.
|
|
33
20
|
*
|
|
@@ -53,7 +40,6 @@ export interface PluginConfigRefs {
|
|
|
53
40
|
readonly aiTimeoutMs: Volatile<number>;
|
|
54
41
|
readonly baseUrl: Volatile<string>;
|
|
55
42
|
readonly model: Volatile<string>;
|
|
56
|
-
readonly targetLang: Volatile<string>;
|
|
57
43
|
}
|
|
58
44
|
/**
|
|
59
45
|
* Read the current committed value of every Config field.
|
|
@@ -75,32 +61,8 @@ export declare function readPluginConfig(refs: PluginConfigRefs): PluginConfig;
|
|
|
75
61
|
*/
|
|
76
62
|
export declare function createLiveConfigSource(onVolatileUpdate: (listener: () => void) => () => void, refs: PluginConfigRefs): ConfigSourceLike;
|
|
77
63
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*/
|
|
81
|
-
export type SettingsPathOpLike = {
|
|
82
|
-
op: 'set';
|
|
83
|
-
path: string[];
|
|
84
|
-
value: unknown;
|
|
85
|
-
} | {
|
|
86
|
-
op: 'unset';
|
|
87
|
-
path: string[];
|
|
88
|
-
};
|
|
89
|
-
/**
|
|
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.
|
|
92
|
-
*/
|
|
93
|
-
export interface SettingsMigrationTarget {
|
|
94
|
-
describe(): Array<{
|
|
95
|
-
ns: string;
|
|
96
|
-
user?: unknown;
|
|
97
|
-
revision?: number;
|
|
98
|
-
}>;
|
|
99
|
-
mutate(ns: string, ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<unknown>;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Config facade over this plugin's own live Config. No file I/O lives here
|
|
103
|
-
* anymore: persistence, atomic writes, external-edit hot reload and the
|
|
64
|
+
* Config facade over this plugin's own live Config. This module does no file
|
|
65
|
+
* I/O: persistence, atomic writes, external-edit hot reload and the
|
|
104
66
|
* browser-facing edit API are all owned by DSH itself.
|
|
105
67
|
*/
|
|
106
68
|
export declare class ConfigManager {
|
|
@@ -112,31 +74,3 @@ export declare class ConfigManager {
|
|
|
112
74
|
isAiConfigured(): boolean;
|
|
113
75
|
onConfigChange(listener: (config: PluginConfig) => void): () => void;
|
|
114
76
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Coerce a raw record (legacy config file, HTTP-era partials) into a
|
|
117
|
-
* validated partial config patch. Unknown fields are dropped, type-mismatched
|
|
118
|
-
* values are skipped (the schema default wins), and numerics are clamped —
|
|
119
|
-
* so one bad field never takes down a whole migration.
|
|
120
|
-
*/
|
|
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>;
|
|
135
|
-
/**
|
|
136
|
-
* One-shot migration from the pre-1.2 standalone config file. Runs only while
|
|
137
|
-
* the settings namespace has no user layer yet, so values the user edited
|
|
138
|
-
* after upgrading are never overwritten. The legacy file is removed whether
|
|
139
|
-
* or not a migration happened.
|
|
140
|
-
* @returns whether any legacy values were migrated.
|
|
141
|
-
*/
|
|
142
|
-
export declare function migrateLegacyConfigFile(settings: SettingsMigrationTarget, legacyPath: string): Promise<boolean>;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* API-key access through the DSH `ctx.credentials` service.
|
|
3
3
|
*
|
|
4
4
|
* The service owns ~/.dsh/.credentials.yaml (refs section, 0600 perms, env
|
|
5
|
-
* shadowing, cross-process locking)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* shadowing, cross-process locking): reads go through `resolve`, writes through
|
|
6
|
+
* `set`/`unset`, and the host's `credentials/reference-updated` event keeps
|
|
7
|
+
* the sync cache warm.
|
|
8
8
|
*/
|
|
9
9
|
/** Refs key that holds the translation API key. */
|
|
10
10
|
export declare const TRANSLATE_API_KEY_REF = "TRANSLATE_API_KEY";
|