@lynn123411/dsh-chat-translate 3.1.0 → 3.3.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 +6 -6
- package/assets/icon.svg +6 -0
- package/docs//351/205/215/347/275/256.md +6 -5
- package/lib/client.js +181 -291
- package/lib/client.js.map +4 -4
- package/lib/index.js +1 -1
- package/lib/index.js.map +3 -3
- package/lib/types/client/chat/assistant-step.d.ts +6 -3
- package/lib/types/client/chat/click-guard.d.ts +10 -9
- package/lib/types/client/chat/row-plan.d.ts +3 -2
- package/lib/types/client/chat/styles.d.ts +41 -5
- package/lib/types/client/chat/translate-store.d.ts +1 -1
- package/lib/types/client/index.d.ts +11 -12
- package/lib/types/client/locales.d.ts +11 -5
- package/lib/types/client/settings/plugin-page.d.ts +18 -0
- package/lib/types/client/settings/ui.d.ts +12 -9
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +23 -14
- package/lib/types/client/reading-settings-page.d.ts +0 -92
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
* markdown 交给宿主公开基线组件 `MarkdownText` 重渲染:格式保真由官方
|
|
8
8
|
* 渲染器负责,本文件不产生任何自拼 markup。
|
|
9
9
|
* 2. 「已翻译/没译成」是渲染状态:落定、开关开启、进入视口后逐行首跑;成功
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 的块(包括模型认为原样最好的块)挂译文、左缘蓝线;失败的块保持原文、
|
|
11
|
+
* 左缘挂红实线;已登记在途而尚无结果的块显灰脉动。
|
|
12
|
+
* 3. 切换与补跑都在**左缘热区**上,不在正文上:正文整块可点会让「选中一段
|
|
13
|
+
* 文字复制」与「切换译文/原文」抢同一次点击;热区收成贴着那条线的窄带后,
|
|
14
|
+
* 正文的单击落字、拖选复制都不再误触。失败块的 ↻ 常驻在红线旁,点它整行
|
|
15
|
+
* 补跑,无限次、无自动重试。
|
|
13
16
|
*
|
|
14
17
|
* 除正文外的行内容与宿主逐分支等价:reasoning 行走折叠(含 Turn-process
|
|
15
18
|
* 隐藏与 beforematch 揭示)、连续 image 组交回 owner 的 renderMessageImages、
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 左缘热区的点击判定:只有「按下与松手都落在热区上」的干净点击才切换/补跑。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 热区没有子元素,所以浏览器派发过来的 click 若 `target === currentTarget`,
|
|
5
|
+
* 就说明按下与松手都在热区里;反之(在热区按下、往右拖进正文选字再松手)click
|
|
6
|
+
* 会派发给两者的公共祖先,target 不是热区,天然判为拖选。这不是启发式,是
|
|
7
|
+
* click 事件的分派语义——正文整块不再是点击目标,误触从根上消失。
|
|
8
|
+
* 纯 DOM 判定,不依赖 React,可独立测试。
|
|
6
9
|
*/
|
|
7
10
|
interface MinimalPointerEvent {
|
|
8
11
|
/** 点击命中的最深元素。 */
|
|
9
|
-
target:
|
|
10
|
-
|
|
11
|
-
} | null;
|
|
12
|
-
/** 挂了本守卫的已翻译块容器。 */
|
|
12
|
+
target: unknown;
|
|
13
|
+
/** 挂了本守卫的热区元素本身。 */
|
|
13
14
|
currentTarget: unknown;
|
|
14
15
|
}
|
|
15
16
|
interface MinimalWindow {
|
|
@@ -21,7 +22,7 @@ interface MinimalWindow {
|
|
|
21
22
|
/**
|
|
22
23
|
* @param event - click 事件(结构面:target/currentTarget)。
|
|
23
24
|
* @param view - 可注入的 window(测试传假对象;运行时传全局 window)。
|
|
24
|
-
* @returns
|
|
25
|
+
* @returns 该点击是否应当触发切换/补跑。
|
|
25
26
|
*/
|
|
26
|
-
export declare function
|
|
27
|
+
export declare function isCleanHotspotClick(event: MinimalPointerEvent, view: MinimalWindow | undefined): boolean;
|
|
27
28
|
export {};
|
|
@@ -36,7 +36,7 @@ export type RowPlanEntry =
|
|
|
36
36
|
text: string;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* 正文块:mark
|
|
39
|
+
* 正文块:mark 即左缘线状态(线型、脉动、热区动作全由它决定);
|
|
40
40
|
* translated 仅在成功时非 null,供 mark==='translated' 时换源文本渲染;
|
|
41
41
|
* fail 仅在 mark==='failed' 时非 null——服务端的技术一句,Tooltip 悬停
|
|
42
42
|
* 报因用,缺省时只报「翻译失败」。线型不从这里出:失败一律同一条红实线。
|
|
@@ -83,7 +83,8 @@ export interface RowPlanInput {
|
|
|
83
83
|
outcomes?: readonly (BlockOutcome | null)[];
|
|
84
84
|
/** 池里这行的状态:pending 时未落定的块显灰脉动,未登记什么都不显。 */
|
|
85
85
|
rowStatus?: 'pending' | 'settled';
|
|
86
|
-
/** 用户点了「读原文」的正文块(按条目 key):把译文态折成 original-view。
|
|
86
|
+
/** 用户点了「读原文」的正文块(按条目 key):把译文态折成 original-view。
|
|
87
|
+
* 触发它的是左缘热区上的切换,不是点正文。 */
|
|
87
88
|
originalKeys?: ReadonlySet<number>;
|
|
88
89
|
}
|
|
89
90
|
export declare function planAssistantRow(input: RowPlanInput): RowPlan;
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 助手行接管所需的样式:与宿主 `AssistantMarkdown` / `ReasoningRow` 逐条对齐
|
|
3
3
|
* 的等价规则(字号、行距、gap、粘性折叠头、running 微光、summary 遮罩等),
|
|
4
|
-
*
|
|
4
|
+
* 加上本插件唯一新增的视觉词汇——正文块左缘的线与贴着它的那条热区:
|
|
5
5
|
* 蓝粗线(1px 主色)=正在读译文;灰细线(0.5px 中性)=有译文但正在读原文
|
|
6
6
|
* (再点即切回);红实线(1px error 色)=翻译失败(通道伤:超时、断流、空
|
|
7
|
-
*
|
|
8
|
-
* 无线=没送过模型(含空白块)或开关关闭。
|
|
7
|
+
* 返回);灰脉动=整行在途、这块尚无结果;无线=没送过模型(含空白块)或开关关闭。
|
|
9
8
|
* 译文的结构漂移不再挂红线——由宿主按原文修回、重掷或照收,红线只剩通道伤一种。
|
|
10
9
|
*
|
|
10
|
+
* 点击不落在正文上:`dsh-ct-hotspot` 是贴着左缘线的窄热区(线左侧 4px + 线
|
|
11
|
+
* 右侧那 12px 缩进,随块高铺满),切换与补跑都由它承载,正文只做选中/复制。
|
|
12
|
+
* 悬停不改任何视觉——只有鼠标在热区上变手型;键盘聚焦(且仅键盘聚焦)时线
|
|
13
|
+
* 加亮,粗细始终只表示译文/原文。失败块的 ↻ 常驻在红线旁,是补跑的可见入口。
|
|
14
|
+
*
|
|
11
15
|
* 全部颜色与尺寸走 `--dsw-alias-*` / `--dsh-*` 主题别名,中性平边按官方规则
|
|
12
16
|
* 画 0.5px hairline;动画声明尊重 `prefers-reduced-motion`。样式随 bundle 注入
|
|
13
17
|
* 一次(`<style data-plugin-css>`),类名用固定前缀,不依赖宿主哈希类名。
|
|
@@ -19,10 +23,42 @@ export declare const ASSISTANT_CSS: string;
|
|
|
19
23
|
* 失败只有 `failed` 一种:同一条红实线,败因走悬停文案。
|
|
20
24
|
*/
|
|
21
25
|
export type ProseMark = 'translated' | 'original-view' | 'failed' | 'inflight' | null;
|
|
22
|
-
/** mark →
|
|
26
|
+
/** mark → 热区动作单点:切换、重试、没有热区三选一(渲染层照此接线)。 */
|
|
23
27
|
export type ProseAction = 'toggle' | 'retry' | null;
|
|
28
|
+
/**
|
|
29
|
+
* mark → 正文块类名与热区动作的单点表。
|
|
30
|
+
*
|
|
31
|
+
* 一张表而不是两个并列 switch:线型(类名)与可点性(动作)从同一行读出来,
|
|
32
|
+
* 加一种 mark 时不可能只改一处、留下「有线但点不动」或「可点但没有线」的
|
|
33
|
+
* 半截状态。`action` 为 null 表示没有热区——没线的块(没送过模型、开关关闭、
|
|
34
|
+
* 通道没配好、空白块)没有可切换的东西。
|
|
35
|
+
*/
|
|
36
|
+
interface ProsePresentation {
|
|
37
|
+
/** 正文块的状态类名;null 的块只有基础类。 */
|
|
38
|
+
className: string | null;
|
|
39
|
+
/** 热区动作:切换、补跑、或没有热区。 */
|
|
40
|
+
action: ProseAction;
|
|
41
|
+
}
|
|
42
|
+
/** 每个正文块都挂的基础类:定位父级(热区以它为基准绝对定位)。 */
|
|
43
|
+
export declare const PROSE_BASE_CLASS = "dsh-ct-prose";
|
|
44
|
+
export declare function prosePresentation(mark: ProseMark): ProsePresentation;
|
|
45
|
+
/** mark → 热区动作({@link prosePresentation} 的窄读法,测试与渲染层共用)。 */
|
|
24
46
|
export declare function proseAction(mark: ProseMark): ProseAction;
|
|
25
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* mark → 正文块类名单点:只有线型。点击不在这层——正文整块不可点,切换与
|
|
49
|
+
* 补跑由左缘热区({@link HOTSPOT_CLASS})承载,所以这里不再有可点态/光标类。
|
|
50
|
+
*/
|
|
26
51
|
export declare function proseClassNames(mark: ProseMark): string | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* 缩进单点:能翻译(开关开 + 通道齐备)时所有正文块一律左缩进,与「这块是否
|
|
54
|
+
* 已挂线」无关——译文落定的那一刻正文不右移,块与块左缘也对齐;关掉开关或
|
|
55
|
+
* 通道没配好时不缩进,排版回到宿主原样(此时正文会横移一次,是刻意的取舍)。
|
|
56
|
+
*/
|
|
57
|
+
export declare function proseIndentClassName(canTranslate: boolean): string | undefined;
|
|
58
|
+
/** 左缘热区的类名单点:切换/补跑的唯一点击目标。 */
|
|
59
|
+
export declare const HOTSPOT_CLASS = "dsh-ct-hotspot";
|
|
60
|
+
/** 在途热区:仍在,但按下去是空操作,光标不再变手型。 */
|
|
61
|
+
export declare const HOTSPOT_IDLE_ATTR = "data-idle";
|
|
27
62
|
/** 注入接管样式;重复调用是空操作。 */
|
|
28
63
|
export declare function ensureAssistantStyles(): void;
|
|
64
|
+
export {};
|
|
@@ -57,7 +57,7 @@ export declare class ChatTranslateStore {
|
|
|
57
57
|
getState(rowKey: string): RowState | undefined;
|
|
58
58
|
/**
|
|
59
59
|
* 确保该行按当前文本被翻译。
|
|
60
|
-
* @param manual -
|
|
60
|
+
* @param manual - 用户点击失败块的左缘热区发起的补跑:已落定的同代行只有这条路会重发。
|
|
61
61
|
* @returns 是否发出了新的请求。
|
|
62
62
|
*/
|
|
63
63
|
ensure(rowKey: string, texts: readonly string[], manual?: boolean): boolean;
|
|
@@ -1,16 +1,15 @@
|
|
|
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 keyed assistant-row takeover,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* unregistered.
|
|
4
|
+
* Declared services: slots for the keyed assistant-row takeover, this plugin's
|
|
5
|
+
* configuration page on the **Plugins** page, and the composer dock pill;
|
|
6
|
+
* configForms for this plugin's own configuration form plus the host `ui-chat`
|
|
7
|
+
* entry whose `transcriptView` field drives the reasoning-row presentation
|
|
8
|
+
* policy; the remote + remote.credentials pair for the credentials Remote
|
|
9
|
+
* namespace; and locale for the card's own dictionary. The runtime withholds
|
|
10
|
+
* any service not declared here; `locale` is declared rather than sampled
|
|
11
|
+
* because `ctx.get('locale')` at apply time can beat the locale plugin's
|
|
12
|
+
* `provide`, which silently left the card's dictionary unregistered.
|
|
14
13
|
*/
|
|
15
14
|
export declare const inject: string[];
|
|
16
15
|
interface ClientContext {
|
|
@@ -30,8 +29,8 @@ interface ClientContext {
|
|
|
30
29
|
}
|
|
31
30
|
/**
|
|
32
31
|
* Mount the assistant-row takeover (keyed `conversation.chat.node` /
|
|
33
|
-
* `assistant-step`),
|
|
34
|
-
*
|
|
32
|
+
* `assistant-step`), this plugin's configuration page on the **Plugins** page,
|
|
33
|
+
* and the translation toggle in the composer dock.
|
|
35
34
|
* @param ctx - DSH browser client context.
|
|
36
35
|
*/
|
|
37
36
|
export declare function apply(ctx: ClientContext): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Locale bundles for the chat-translate card
|
|
1
|
+
/** Locale bundles for the chat-translate card on the DSH Plugins page. */
|
|
2
2
|
/** Dictionary namespace owned by this plugin's settings card. */
|
|
3
3
|
export declare const NS = "settings.chatTranslate";
|
|
4
4
|
/**
|
|
@@ -7,11 +7,9 @@ export declare const NS = "settings.chatTranslate";
|
|
|
7
7
|
* inline technical samples rendered by `withSamples` at the call site.
|
|
8
8
|
*/
|
|
9
9
|
export declare const zh: {
|
|
10
|
-
readonly pageNav: "阅读体验";
|
|
11
|
-
readonly title: "正文翻译";
|
|
12
10
|
readonly masterTitle: "翻译总开关";
|
|
13
11
|
readonly enableTranslation: "启用翻译";
|
|
14
|
-
readonly masterDesc: "自动把助手回答正文翻成中文(如 {example}):折叠块里的过程正文与最终汇总一视同仁,整段中文的块也照送,由模型一并改写成自然中文、顺掉机器腔。Think
|
|
12
|
+
readonly masterDesc: "自动把助手回答正文翻成中文(如 {example}):折叠块里的过程正文与最终汇总一视同仁,整段中文的块也照送,由模型一并改写成自然中文、顺掉机器腔。Think 卡与工具调用行不翻,代码块原样保留。正文块的左缘一条竖线报出状态:正在读译文是蓝线,备着译文正读原文是灰细线,没译成是红实线(鼠标悬停热区报出通道细节——超时、断流或空返回;译文的结构漂移由宿主自动修回或重掷,不再因格式问题挂红线),正在请求是灰脉动线,没送过模型或开关关闭没有线。切换不在正文上:贴着那条线的窄带才是操作区,鼠标移到它上面变手型,点一下在译文与原文间切换——正文整块不再响应点击,选字、拖选复制都不会误触;模型认为原样最好、返回一模一样的文字时同样算翻译过、同样可切。挂红线的块右侧常驻一个小 ↻,点那条窄带即整行重新翻译,已成功的块保持显示不打扰。通道失败不自动重试,恢复后由你点那条窄带救活。译文按原 markdown 格式重新渲染,表格、列表、加粗与行内代码保持结构。回答还在流式输出时不翻,落定后按阅读顺序逐段出中文。只作用于当前查看的会话,译文不写回会话上下文。输入框下方那一行有同款开关,两处状态同步。";
|
|
15
13
|
readonly aiTitle: "AI 翻译(OpenAI 兼容协议)";
|
|
16
14
|
readonly aiRule: "只有这一条通道:Key / Base URL / 模型齐备才会翻译,缺任何一项都保持原文。";
|
|
17
15
|
readonly badgeConfigured: "已配置";
|
|
@@ -41,7 +39,10 @@ export declare const zh: {
|
|
|
41
39
|
readonly dockOn: "正文翻译已开启,点击关闭";
|
|
42
40
|
readonly dockOff: "正文翻译已关闭,点击开启";
|
|
43
41
|
readonly failTitle: "翻译失败";
|
|
44
|
-
readonly retryAria: "
|
|
42
|
+
readonly retryAria: "翻译失败,点左缘那条窄带可按 Enter 重试";
|
|
43
|
+
readonly toggleToOriginalAria: "查看原文";
|
|
44
|
+
readonly toggleToTranslatedAria: "查看译文";
|
|
45
|
+
readonly inflightAria: "翻译中,此处暂无动作";
|
|
45
46
|
};
|
|
46
47
|
/** Locale keys this plugin renders. */
|
|
47
48
|
export type ChatTranslateLocaleKey = keyof typeof zh;
|
|
@@ -57,6 +58,11 @@ export declare function bindRowCopy(locale: {
|
|
|
57
58
|
} | null | undefined): void;
|
|
58
59
|
export declare function rowCopy(): {
|
|
59
60
|
retryAria: string;
|
|
61
|
+
/** 热区动作的读屏标签:读译文时提示可切回原文,反之亦然。 */
|
|
62
|
+
toggleToOriginalAria: string;
|
|
63
|
+
toggleToTranslatedAria: string;
|
|
64
|
+
/** 在途热区的读屏标签:说清此刻按下去没有动作。 */
|
|
65
|
+
inflightAria: string;
|
|
60
66
|
/** 失败块悬停文案:本地化标签 + 服务端原样带来的技术一句(通道伤)。 */
|
|
61
67
|
failTitle: (detail?: string | null) => string;
|
|
62
68
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** The Plugins page slot a bundle's own configuration registers into. */
|
|
2
|
+
export declare const BUNDLE_CONFIG_SLOT = "plugins.bundle.config";
|
|
3
|
+
/** This plugin's own config key: the bundle's package name. */
|
|
4
|
+
export declare const BUNDLE_CONFIG_KEY = "@lynn123411/dsh-chat-translate";
|
|
5
|
+
/** Services this registration needs from the browser runtime. */
|
|
6
|
+
interface PluginPageSlots {
|
|
7
|
+
inject(name: string, callback: () => unknown): () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Contribute this plugin's configuration page to the Plugins page.
|
|
11
|
+
* @param slots - the client slot registry.
|
|
12
|
+
* @param register - registers the page component; returns its disposer.
|
|
13
|
+
* @returns A disposer releasing the contribution, or a no-op when the browser
|
|
14
|
+
* runtime hands no slot registry at all (the plugin's other seats stay mounted
|
|
15
|
+
* either way). A throwing `inject` is a real contract violation and propagates.
|
|
16
|
+
*/
|
|
17
|
+
export declare function setupPluginPage(slots: PluginPageSlots | null | undefined, register: () => () => void): () => void;
|
|
18
|
+
export {};
|
|
@@ -2,19 +2,22 @@ import * as React from 'react';
|
|
|
2
2
|
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
3
|
import { NS } from '../locales.ts';
|
|
4
4
|
/**
|
|
5
|
-
* Chat-translate's
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Chat-translate's own configuration page inside the DSH **Plugins** page.
|
|
6
|
+
*
|
|
7
|
+
* The host's Plugins page owns per-plugin configuration: it draws the icon, the
|
|
8
|
+
* title and the crumb, and renders a `plugins.bundle.config` entry ON the
|
|
9
|
+
* bundle's own page, under its description and above its rows. The card keeps
|
|
10
|
+
* its own chrome — the switches, the credential form, the section order — so the
|
|
11
|
+
* move from the Settings page changed where it is reached from, not what it does.
|
|
12
|
+
* @param props - renderer-bound seat of the `plugins.bundle.config` slot: the
|
|
13
|
+
* `view` asked for plus the `t` seat its `locale: NS` registration injects.
|
|
10
14
|
* @returns The card element.
|
|
11
15
|
*/
|
|
12
|
-
export declare function TidySettingsPanel(props: PropsRuntime<'
|
|
16
|
+
export declare function TidySettingsPanel(props: PropsRuntime<'plugins.bundle.config'> & PropsLocale<typeof NS>): React.ReactElement;
|
|
13
17
|
/**
|
|
14
18
|
* Bind the settings store to this plugin's own configuration form and the
|
|
15
|
-
* credentials Remote API, then
|
|
16
|
-
*
|
|
17
|
-
* card into its child slot.
|
|
19
|
+
* credentials Remote API, then contribute this plugin's configuration page to
|
|
20
|
+
* the DSH **Plugins** page.
|
|
18
21
|
* @param ctx - DSH browser client context.
|
|
19
22
|
*/
|
|
20
23
|
export declare function setupSettingsUi(ctx: any): void;
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lynn123411/dsh-chat-translate",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Assistant reply-body translation for the DeepSeek Harness Web UI (OpenAI-compatible channel)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "lynn123411",
|
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
"default": "./lib/client.js"
|
|
39
39
|
},
|
|
40
40
|
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
41
|
+
"./locale/en.json": "./locale/en.json",
|
|
42
|
+
"./locale/zh.json": "./locale/zh.json",
|
|
41
43
|
"./package.json": "./package.json"
|
|
42
44
|
},
|
|
43
45
|
"dsh": {
|
|
@@ -48,50 +50,57 @@
|
|
|
48
50
|
"platform": "web",
|
|
49
51
|
"immediately": true,
|
|
50
52
|
"inject": [
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-primitives"
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-primitives",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager"
|
|
52
55
|
]
|
|
53
56
|
}
|
|
54
57
|
},
|
|
55
58
|
"files": [
|
|
56
59
|
"lib",
|
|
57
60
|
"docs",
|
|
61
|
+
"assets",
|
|
62
|
+
"locale",
|
|
58
63
|
"cordis.patch.yml",
|
|
59
64
|
"README.md"
|
|
60
65
|
],
|
|
61
66
|
"scripts": {
|
|
62
67
|
"build": "node build.mjs",
|
|
63
68
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
64
|
-
"test": "node scripts/test-client-wiring.mjs && node scripts/test-chat-translate.mjs && node scripts/test-regression-suite.mjs && node scripts/test-channel-logic.mjs && node scripts/test-store.mjs && node scripts/test-reply-pipeline.mjs",
|
|
69
|
+
"test": "node scripts/test-client-wiring.mjs && node scripts/test-render.mjs && node scripts/test-chat-translate.mjs && node scripts/test-regression-suite.mjs && node scripts/test-channel-logic.mjs && node scripts/test-store.mjs && node scripts/test-reply-pipeline.mjs",
|
|
65
70
|
"assert:lib-untracked": "node scripts/assert-lib-untracked.mjs",
|
|
66
71
|
"check": "pnpm typecheck && pnpm build && pnpm test && pnpm assert:lib-untracked"
|
|
67
72
|
},
|
|
68
73
|
"engines": {
|
|
69
74
|
"node": "^22.19 || >=24",
|
|
70
|
-
"dsh": ">=0.
|
|
75
|
+
"dsh": ">=0.2.0-rc.2"
|
|
71
76
|
},
|
|
72
77
|
"devDependencies": {
|
|
73
78
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
74
|
-
"@deepseek-ai/dsh-client-connection": ">=0.
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-primitives": ">=0.
|
|
76
|
-
"@deepseek-ai/dsh-client-ui-renderer": ">=0.
|
|
77
|
-
"@deepseek-ai/dsh-client-ui-settings": ">=0.
|
|
78
|
-
"@deepseek-ai/dsh-client-ui-slots": ">=0.
|
|
79
|
-
"@deepseek-ai/dsh-home-paths": ">=0.
|
|
79
|
+
"@deepseek-ai/dsh-client-connection": ">=0.2.0-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.2.0-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.2.0-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.2.0-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-home-paths": ">=0.2.0-rc.2",
|
|
80
85
|
"@deepseek-ai/schemastery": "^3.18.4",
|
|
81
86
|
"@eslint/js": "^9.30.0",
|
|
82
87
|
"@types/node": "^22.19.0",
|
|
83
88
|
"@types/react": "^19.2.18",
|
|
84
89
|
"esbuild": "^0.25.0",
|
|
85
90
|
"jsdom": "^26.1.0",
|
|
86
|
-
"
|
|
91
|
+
"react": "^18.3.1",
|
|
92
|
+
"react-dom": "^18.3.1",
|
|
93
|
+
"typescript": "^6.0.0",
|
|
94
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": ">=0.2.0-rc.2"
|
|
87
95
|
},
|
|
88
96
|
"peerDependencies": {
|
|
89
|
-
"@deepseek-ai/dsh-client-ui-primitives": ">=0.
|
|
90
|
-
"@deepseek-ai/dsh-home-paths": ">=0.
|
|
97
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.2.0-rc.2",
|
|
98
|
+
"@deepseek-ai/dsh-home-paths": ">=0.2.0-rc.2"
|
|
91
99
|
},
|
|
92
100
|
"peerDependenciesMeta": {
|
|
93
101
|
"@deepseek-ai/dsh-client-ui-primitives": {
|
|
94
102
|
"optional": true
|
|
95
103
|
}
|
|
96
|
-
}
|
|
104
|
+
},
|
|
105
|
+
"icon": "./assets/icon.svg"
|
|
97
106
|
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The shared 「阅读体验」 settings page.
|
|
3
|
-
*
|
|
4
|
-
* Several plugins contribute their configuration to ONE settings page, but the
|
|
5
|
-
* kernel cannot declare that page jointly: `settings.section` is a list slot
|
|
6
|
-
* that rejects a duplicate `id` at the same priority ("already has an entry
|
|
7
|
-
* with id"), and a child slot may be declared exactly once ("slot … is already
|
|
8
|
-
* declared"). Composing several cards into one page therefore takes one
|
|
9
|
-
* declarer, so every participating plugin carries this same shell and the
|
|
10
|
-
* FIRST one to activate claims the page; the others register their card into
|
|
11
|
-
* {@link READING_ITEM_SLOT} and wait for the winner's declaration through
|
|
12
|
-
* `slots.inject`. Uninstalling the winner promotes another participant on the
|
|
13
|
-
* next boot, so no participant is a fixed owner.
|
|
14
|
-
*
|
|
15
|
-
* The page renders ONE TAB PER REGISTERED CARD: the tab roster comes from the
|
|
16
|
-
* child slot's own registrations (id + `label` + `order`, the same shape the
|
|
17
|
-
* kernel's own Plugins page uses for its tabs), and each panel dispatches
|
|
18
|
-
* through `renderSlot(READING_ITEM_SLOT, {}, { only: id })`. Every panel stays
|
|
19
|
-
* mounted but hidden, so a card's local state survives a tab switch.
|
|
20
|
-
*
|
|
21
|
-
* Keep this file identical across the participating plugins
|
|
22
|
-
* (`dsh-smooth-stream`, `dsh-chat-translate`).
|
|
23
|
-
* Participants own their own card component, locale dictionaries, settings
|
|
24
|
-
* namespace and Host half — only the page shell below is shared, because
|
|
25
|
-
* cross-plugin value imports are forbidden by the client bundle purity gate.
|
|
26
|
-
* It imports nothing but `react` plus type-only DSH packages on purpose, so
|
|
27
|
-
* every participant's build configuration compiles it unchanged.
|
|
28
|
-
*
|
|
29
|
-
* Settings storage belongs to the card, not to this shell: since 0.1.7 a
|
|
30
|
-
* participant reads and writes its own Config through
|
|
31
|
-
* `ctx.configForms.get(<profile entry id>)` and gates the registration on
|
|
32
|
-
* `ctx.configForms.whileServed(...)`. The shell only stacks cards — it holds no
|
|
33
|
-
* settings service of its own, which is why it needs nothing beyond
|
|
34
|
-
* `ctx.slots` and the optional `locale`.
|
|
35
|
-
*/
|
|
36
|
-
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
37
|
-
import type { InjectFace, LocaleNamespaceMap, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
38
|
-
/** Page id claimed by the first participating plugin to activate. */
|
|
39
|
-
export declare const READING_PAGE_ID = "reading";
|
|
40
|
-
/** Sidebar position of the shared page; own plugins start at 110. */
|
|
41
|
-
export declare const READING_PAGE_ORDER = 110;
|
|
42
|
-
/** The page's one child slot: every participant's card registers here. */
|
|
43
|
-
export declare const READING_ITEM_SLOT = "reading.settings.item";
|
|
44
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
45
|
-
interface SlotMap {
|
|
46
|
-
/** One participating plugin's card inside the shared reading page. */
|
|
47
|
-
'reading.settings.item': {
|
|
48
|
-
kind: 'list';
|
|
49
|
-
scope: 'root';
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
/** One participant's tab, derived from its card registration. */
|
|
54
|
-
export interface ReadingTab {
|
|
55
|
-
/** Registration id — the participant's Host settings namespace. */
|
|
56
|
-
id: string;
|
|
57
|
-
/** Registration order, ascending in the tab bar. */
|
|
58
|
-
order: number;
|
|
59
|
-
/** Registration label, already resolved for the active locale. */
|
|
60
|
-
label: string;
|
|
61
|
-
}
|
|
62
|
-
/** Live tab roster handed to the page component through its inject face. */
|
|
63
|
-
export interface ReadingTabsFace {
|
|
64
|
-
getSnapshot(): readonly ReadingTab[];
|
|
65
|
-
subscribe(listener: () => void): () => void;
|
|
66
|
-
}
|
|
67
|
-
/** Inject face of the shared page. */
|
|
68
|
-
export interface ReadingPageFace {
|
|
69
|
-
readingTabs: ReadingTabsFace;
|
|
70
|
-
}
|
|
71
|
-
type ReadingPageProps = PropsRuntime<'settings.section'> & PropsRenderSlots<typeof READING_ITEM_SLOT> & InjectFace<ReadingPageFace>;
|
|
72
|
-
/**
|
|
73
|
-
* Page body: one tab per registered card, plus the selected card's panel.
|
|
74
|
-
* The shell supplies the section's own seats and `renderSlot` bound to the
|
|
75
|
-
* child slot declared at registration time.
|
|
76
|
-
*/
|
|
77
|
-
export declare function ReadingSettingsSection({ renderSlot, readingTabs }: ReadingPageProps): import("react").DetailedReactHTMLElement<import("react").HTMLAttributes<HTMLElement>, HTMLElement> | null;
|
|
78
|
-
/** Whether a participant already holds the shared page. */
|
|
79
|
-
export declare function readingPageClaimed(ctx: ClientContext): boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Claim the shared page when no participant holds it yet. Call inside
|
|
82
|
-
* `ctx.slots.inject('settings.section', …)`: injection order decides the
|
|
83
|
-
* winner, and the losers stay silent instead of colliding with the kernel's
|
|
84
|
-
* duplicate-id and duplicate-declaration guards.
|
|
85
|
-
* @param ctx - browser context carrying the slot registry.
|
|
86
|
-
* @param label - page label, re-read by the shell on every projection.
|
|
87
|
-
* @param locale - locale namespace the label thunk translates through.
|
|
88
|
-
* @returns The page registration's disposer, or a no-op when another
|
|
89
|
-
* participant already holds the page.
|
|
90
|
-
*/
|
|
91
|
-
export declare function claimReadingSettingsPage(ctx: ClientContext, label: () => string, locale: keyof LocaleNamespaceMap & string): () => void;
|
|
92
|
-
export {};
|