@jaychang1989/dsh-webchat 0.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/LICENSE +201 -0
- package/NOTICE +18 -0
- package/README.md +78 -0
- package/README.zh.md +83 -0
- package/cordis.patch.yml +22 -0
- package/lib/client.js +1725 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +3887 -0
- package/lib/types/client/api.d.ts +74 -0
- package/lib/types/client/controller.d.ts +19 -0
- package/lib/types/client/index.d.ts +30 -0
- package/lib/types/client/locales.d.ts +169 -0
- package/lib/types/client/mount.d.ts +23 -0
- package/lib/types/client/panel/Markdown.d.ts +15 -0
- package/lib/types/client/panel/WebChatPanel.d.ts +28 -0
- package/lib/types/client/sidebar-entry-core.d.ts +60 -0
- package/lib/types/client/sidebar-entry.d.ts +22 -0
- package/lib/types/engine/engine.d.ts +247 -0
- package/lib/types/engine/html-md.d.ts +23 -0
- package/lib/types/index.d.ts +63 -0
- package/lib/types/protocol.d.ts +107 -0
- package/lib/types/routes.d.ts +22 -0
- package/lib/types/store.d.ts +69 -0
- package/lib/types/tools.d.ts +27 -0
- package/lib/types/transfer.d.ts +138 -0
- package/package.json +96 -0
- package/src/client/api.ts +90 -0
- package/src/client/controller.ts +43 -0
- package/src/client/css-modules.d.ts +5 -0
- package/src/client/index.ts +75 -0
- package/src/client/locales.ts +172 -0
- package/src/client/mount.tsx +125 -0
- package/src/client/panel/Markdown.tsx +251 -0
- package/src/client/panel/WebChatPanel.tsx +760 -0
- package/src/client/panel/panel.module.css +936 -0
- package/src/client/sidebar-entry-core.ts +207 -0
- package/src/client/sidebar-entry.ts +49 -0
- package/src/engine/engine.ts +1385 -0
- package/src/engine/html-md.ts +126 -0
- package/src/index.ts +212 -0
- package/src/protocol.ts +116 -0
- package/src/routes.ts +307 -0
- package/src/store.ts +217 -0
- package/src/tools.ts +257 -0
- package/src/transfer.ts +602 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side API client for the /api/dsh-webchat route family. The only
|
|
3
|
+
* data access path the panel components use — plain fetch, same origin.
|
|
4
|
+
*/
|
|
5
|
+
import { type TransferMode, type WebChatState } from '../protocol.ts';
|
|
6
|
+
/** Shape every /api/dsh-webchat response carries: ok plus optional error. */
|
|
7
|
+
interface ApiResult {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Endpoint payloads always extend ApiResult. */
|
|
12
|
+
type EndpointResult<T> = T & ApiResult;
|
|
13
|
+
export declare class WebChatApi {
|
|
14
|
+
state(): Promise<EndpointResult<WebChatState>>;
|
|
15
|
+
openLogin(): Promise<EndpointResult<{
|
|
16
|
+
ok: boolean;
|
|
17
|
+
}>>;
|
|
18
|
+
closeBrowser(): Promise<EndpointResult<{
|
|
19
|
+
ok: boolean;
|
|
20
|
+
}>>;
|
|
21
|
+
newChat(): Promise<EndpointResult<{
|
|
22
|
+
ok: boolean;
|
|
23
|
+
chatId?: string;
|
|
24
|
+
}>>;
|
|
25
|
+
send(text: string, images?: string[]): Promise<EndpointResult<{
|
|
26
|
+
ok: boolean;
|
|
27
|
+
chatId?: string;
|
|
28
|
+
}>>;
|
|
29
|
+
stop(): Promise<EndpointResult<{
|
|
30
|
+
ok: boolean;
|
|
31
|
+
}>>;
|
|
32
|
+
setDeepThink(enabled: boolean): Promise<EndpointResult<{
|
|
33
|
+
ok: boolean;
|
|
34
|
+
}>>;
|
|
35
|
+
setSearch(enabled: boolean): Promise<EndpointResult<{
|
|
36
|
+
ok: boolean;
|
|
37
|
+
}>>;
|
|
38
|
+
transfer(chatId: string, cwd?: string, mode?: TransferMode, workspaceId?: string, targetSessionId?: string): Promise<EndpointResult<{
|
|
39
|
+
ok: boolean;
|
|
40
|
+
sessionId?: string;
|
|
41
|
+
distilled?: boolean;
|
|
42
|
+
attached?: boolean;
|
|
43
|
+
continued?: boolean;
|
|
44
|
+
workspaceId?: string;
|
|
45
|
+
}>>;
|
|
46
|
+
exportFile(chatId: string, cwd?: string): Promise<EndpointResult<{
|
|
47
|
+
ok: boolean;
|
|
48
|
+
filePath?: string;
|
|
49
|
+
}>>;
|
|
50
|
+
renameChat(chatId: string, title: string): Promise<EndpointResult<{
|
|
51
|
+
ok: boolean;
|
|
52
|
+
}>>;
|
|
53
|
+
deleteChat(chatId: string): Promise<EndpointResult<{
|
|
54
|
+
ok: boolean;
|
|
55
|
+
}>>;
|
|
56
|
+
clearChats(): Promise<EndpointResult<{
|
|
57
|
+
ok: boolean;
|
|
58
|
+
count?: number;
|
|
59
|
+
}>>;
|
|
60
|
+
webChats(): Promise<EndpointResult<{
|
|
61
|
+
ok: boolean;
|
|
62
|
+
web: Array<{
|
|
63
|
+
title: string;
|
|
64
|
+
}>;
|
|
65
|
+
missing: string[];
|
|
66
|
+
}>>;
|
|
67
|
+
recover(title: string): Promise<EndpointResult<{
|
|
68
|
+
ok: boolean;
|
|
69
|
+
chatId?: string;
|
|
70
|
+
title?: string;
|
|
71
|
+
created?: boolean;
|
|
72
|
+
}>>;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel controller: minimal external state the sidebar entry and the panel
|
|
3
|
+
* share (open/closed), plus a tiny pub/sub. The chat data itself lives in the
|
|
4
|
+
* panel component's polling loop.
|
|
5
|
+
*/
|
|
6
|
+
export interface PanelSnapshot {
|
|
7
|
+
readonly panelOpen: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Plain pub/sub store (no React dependency — used by the DOM sidebar entry). */
|
|
10
|
+
export declare class PanelController {
|
|
11
|
+
private snapshot;
|
|
12
|
+
private readonly listeners;
|
|
13
|
+
getSnapshot(): PanelSnapshot;
|
|
14
|
+
subscribe(listener: () => void): () => void;
|
|
15
|
+
private set;
|
|
16
|
+
open(): void;
|
|
17
|
+
close(): void;
|
|
18
|
+
toggle(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-half entry for the dsh-webchat plugin — runs inside the dsh web GUI.
|
|
3
|
+
*
|
|
4
|
+
* Registers the locale dictionaries and mounts the two DOM surfaces: the
|
|
5
|
+
* sidebar entry row (toggles the panel) and the web-chat panel in the center
|
|
6
|
+
* column. Failure policy: DOM mounting problems are logged, never thrown —
|
|
7
|
+
* the web shell fails the whole boot when a plugin apply throws, and an
|
|
8
|
+
* external plugin must not take the GUI down.
|
|
9
|
+
*
|
|
10
|
+
* Export discipline (packages/client rule): the /client surface carries what
|
|
11
|
+
* cordis loading needs plus types only — all value exports stay internal.
|
|
12
|
+
*/
|
|
13
|
+
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
14
|
+
import { type WebChatKey } from './locales.ts';
|
|
15
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
16
|
+
interface LocaleNamespaceMap {
|
|
17
|
+
/** dsh-webchat surface copy. */
|
|
18
|
+
'dsh-webchat': WebChatKey;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Required services (fiber inject waiting — the runtime must be up first). */
|
|
22
|
+
export declare const inject: string[];
|
|
23
|
+
/** Type-only surface (export discipline: no value exports beyond the plugin contract). */
|
|
24
|
+
export type { WebChatPanelProps } from './panel/WebChatPanel.tsx';
|
|
25
|
+
export type { WebChatKey } from './locales.ts';
|
|
26
|
+
/**
|
|
27
|
+
* Mount the web-chat panel.
|
|
28
|
+
* @param ctx - client root context (locale + sessions services).
|
|
29
|
+
*/
|
|
30
|
+
export declare function apply(ctx: ClientContext): void;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-webchat surface copy: zh is the key source, en mirrors every key. The
|
|
3
|
+
* key union (not an object shape) is what the locale machinery wants —
|
|
4
|
+
* LocaleKeysOf<N> intersects the namespace with `string`, so the namespace
|
|
5
|
+
* value must itself be the key union (same pattern as dsh-ssh).
|
|
6
|
+
*/
|
|
7
|
+
export declare const zh: {
|
|
8
|
+
readonly 'entry.label': "网页聊天";
|
|
9
|
+
readonly 'entry.tooltip': "DeepSeek 网页端聊天(deepseek-chat,Codex ChatGPT 模式)";
|
|
10
|
+
readonly 'panel.title': "DeepSeek 网页端聊天";
|
|
11
|
+
readonly 'status.engine.stopped': "浏览器未启动";
|
|
12
|
+
readonly 'status.engine.launching': "浏览器启动中…";
|
|
13
|
+
readonly 'status.engine.ready': "浏览器就绪";
|
|
14
|
+
readonly 'status.engine.error': "浏览器错误";
|
|
15
|
+
readonly 'status.loggedIn': "已登录";
|
|
16
|
+
readonly 'status.notLoggedIn': "未登录";
|
|
17
|
+
readonly 'status.unknown': "状态未知";
|
|
18
|
+
readonly 'action.openLogin': "打开登录窗口";
|
|
19
|
+
readonly 'action.closeBrowser': "关闭浏览器";
|
|
20
|
+
readonly 'action.newChat': "新对话";
|
|
21
|
+
readonly 'action.recover': "从网页恢复";
|
|
22
|
+
readonly 'action.refresh': "刷新";
|
|
23
|
+
readonly 'action.send': "发送";
|
|
24
|
+
readonly 'action.stop': "停止生成";
|
|
25
|
+
readonly 'action.transferToHarness': "转移到 Harness";
|
|
26
|
+
readonly 'action.exportFile': "导出为文件";
|
|
27
|
+
readonly 'transfer.mode.distill': "蒸馏为任务简报";
|
|
28
|
+
readonly 'transfer.mode.raw': "原始记录";
|
|
29
|
+
readonly 'transfer.mode.hint': "蒸馏:用 harness LLM 把对话提炼成可执行任务简报;原始记录:原样放入新会话首条消息";
|
|
30
|
+
readonly 'transfer.workspace.label': "目标工作区";
|
|
31
|
+
readonly 'transfer.workspace.ungrouped': "未分组";
|
|
32
|
+
readonly 'transfer.workspace.new': "新建工作区…";
|
|
33
|
+
readonly 'transfer.workspace.hint': "转移到所选工作区;未分组则不归入任何工作区";
|
|
34
|
+
readonly 'transfer.workspace.created': "已创建并选中工作区 {title}";
|
|
35
|
+
readonly 'transfer.workspace.createFailed': "创建工作区失败:{error}";
|
|
36
|
+
readonly 'transfer.session.label': "延续到会话";
|
|
37
|
+
readonly 'transfer.session.hint': "选择已有 harness 会话以延续(把简报作为新消息追加);默认新建会话";
|
|
38
|
+
readonly 'transfer.session.new': "新建会话";
|
|
39
|
+
readonly 'transfer.attached.failed': "会话已创建({sessionId}),但归入工作区失败,现为未分组";
|
|
40
|
+
readonly 'handoff.eyebrow': "交付到 Harness";
|
|
41
|
+
readonly 'toggle.deepThink': "深度思考";
|
|
42
|
+
readonly 'toggle.search': "智能搜索";
|
|
43
|
+
readonly 'toggle.deepThink.hint': "开启 R1 深度思考";
|
|
44
|
+
readonly 'toggle.search.hint': "开启联网检索";
|
|
45
|
+
readonly 'sidebar.empty': "暂无对话";
|
|
46
|
+
readonly 'chats.count': "{count} 条";
|
|
47
|
+
readonly 'role.you': "你";
|
|
48
|
+
readonly 'chat.rename': "重命名";
|
|
49
|
+
readonly 'chat.delete': "删除";
|
|
50
|
+
readonly 'chat.delete.confirm': "确认删除?";
|
|
51
|
+
readonly 'chat.clearAll': "清空全部";
|
|
52
|
+
readonly 'chat.clearAll.confirm': "确认清空全部?";
|
|
53
|
+
readonly 'chat.rename.placeholder': "输入新标题…";
|
|
54
|
+
readonly 'chat.rename.ok': "确定";
|
|
55
|
+
readonly 'chat.rename.cancel': "取消";
|
|
56
|
+
readonly 'chat.manage.hint': "管理本地保存的聊天记录(不影响网页端)";
|
|
57
|
+
readonly 'toast.rename.done': "已重命名";
|
|
58
|
+
readonly 'toast.rename.failed': "重命名失败:{error}";
|
|
59
|
+
readonly 'toast.delete.done': "已删除该对话";
|
|
60
|
+
readonly 'toast.delete.failed': "删除失败:{error}";
|
|
61
|
+
readonly 'toast.clear.done': "已清空全部对话";
|
|
62
|
+
readonly 'toast.clear.failed': "清空失败:{error}";
|
|
63
|
+
readonly 'model.deepseek-chat': "DeepSeek";
|
|
64
|
+
readonly 'model.deepseek-reasoner': "DeepSeek";
|
|
65
|
+
readonly 'composer.placeholder': "给 DeepSeek 发送消息…(将发送到网页端)";
|
|
66
|
+
readonly 'composer.hint': "Enter 发送 · Shift+Enter 换行";
|
|
67
|
+
readonly 'composer.busy': "正在生成回复…";
|
|
68
|
+
readonly 'composer.notLoggedIn': "请先登录 DeepSeek 网页端";
|
|
69
|
+
readonly 'composer.attach': "图片";
|
|
70
|
+
readonly 'composer.attach.hint': "添加本地图片(绝对路径,逗号/换行分隔)";
|
|
71
|
+
readonly 'composer.attach.placeholder': "图片绝对路径,逗号或换行分隔";
|
|
72
|
+
readonly 'empty.eyebrow': "DeepSeek 网页端 · 桥接开发";
|
|
73
|
+
readonly 'empty.title': "先聊,再交付";
|
|
74
|
+
readonly 'empty.body': "登录网页端即可开始对话;任何对话都能蒸馏成任务简报,转入 Harness 会话继续开发。";
|
|
75
|
+
readonly 'transfer.done': "已创建 Harness 会话 {sessionId},正在为你打开…";
|
|
76
|
+
readonly 'transfer.continued': "已延续 Harness 会话 {sessionId},正在为你打开…";
|
|
77
|
+
readonly 'transfer.failed': "转移到 Harness 失败:{error}";
|
|
78
|
+
readonly 'export.done': "已导出到工作区文件:{filePath}";
|
|
79
|
+
readonly 'export.failed': "导出失败:{error}";
|
|
80
|
+
readonly 'recover.hint': "列出网页端尚未导入本地的会话";
|
|
81
|
+
readonly 'recover.empty': "网页端没有未导入的会话";
|
|
82
|
+
readonly 'recover.done': "已恢复「{title}」";
|
|
83
|
+
readonly 'msg.empty': "(空)";
|
|
84
|
+
readonly 'msg.error': "(该回复可能不完整:{error})";
|
|
85
|
+
readonly streaming: "生成中";
|
|
86
|
+
};
|
|
87
|
+
/** Bilingual balance enforced: en must cover every zh key. */
|
|
88
|
+
export declare const en: {
|
|
89
|
+
'entry.label': string;
|
|
90
|
+
'entry.tooltip': string;
|
|
91
|
+
'panel.title': string;
|
|
92
|
+
'status.engine.stopped': string;
|
|
93
|
+
'status.engine.launching': string;
|
|
94
|
+
'status.engine.ready': string;
|
|
95
|
+
'status.engine.error': string;
|
|
96
|
+
'status.loggedIn': string;
|
|
97
|
+
'status.notLoggedIn': string;
|
|
98
|
+
'status.unknown': string;
|
|
99
|
+
'action.openLogin': string;
|
|
100
|
+
'action.closeBrowser': string;
|
|
101
|
+
'action.newChat': string;
|
|
102
|
+
'action.recover': string;
|
|
103
|
+
'action.refresh': string;
|
|
104
|
+
'action.send': string;
|
|
105
|
+
'action.stop': string;
|
|
106
|
+
'action.transferToHarness': string;
|
|
107
|
+
'action.exportFile': string;
|
|
108
|
+
'transfer.mode.distill': string;
|
|
109
|
+
'transfer.mode.raw': string;
|
|
110
|
+
'transfer.mode.hint': string;
|
|
111
|
+
'transfer.workspace.label': string;
|
|
112
|
+
'transfer.workspace.ungrouped': string;
|
|
113
|
+
'transfer.workspace.new': string;
|
|
114
|
+
'transfer.workspace.hint': string;
|
|
115
|
+
'transfer.workspace.created': string;
|
|
116
|
+
'transfer.workspace.createFailed': string;
|
|
117
|
+
'transfer.session.label': string;
|
|
118
|
+
'transfer.session.hint': string;
|
|
119
|
+
'transfer.session.new': string;
|
|
120
|
+
'transfer.attached.failed': string;
|
|
121
|
+
'handoff.eyebrow': string;
|
|
122
|
+
'toggle.deepThink': string;
|
|
123
|
+
'toggle.search': string;
|
|
124
|
+
'toggle.deepThink.hint': string;
|
|
125
|
+
'toggle.search.hint': string;
|
|
126
|
+
'sidebar.empty': string;
|
|
127
|
+
'chats.count': string;
|
|
128
|
+
'role.you': string;
|
|
129
|
+
'chat.rename': string;
|
|
130
|
+
'chat.delete': string;
|
|
131
|
+
'chat.delete.confirm': string;
|
|
132
|
+
'chat.clearAll': string;
|
|
133
|
+
'chat.clearAll.confirm': string;
|
|
134
|
+
'chat.rename.placeholder': string;
|
|
135
|
+
'chat.rename.ok': string;
|
|
136
|
+
'chat.rename.cancel': string;
|
|
137
|
+
'chat.manage.hint': string;
|
|
138
|
+
'toast.rename.done': string;
|
|
139
|
+
'toast.rename.failed': string;
|
|
140
|
+
'toast.delete.done': string;
|
|
141
|
+
'toast.delete.failed': string;
|
|
142
|
+
'toast.clear.done': string;
|
|
143
|
+
'toast.clear.failed': string;
|
|
144
|
+
'model.deepseek-chat': string;
|
|
145
|
+
'model.deepseek-reasoner': string;
|
|
146
|
+
'composer.placeholder': string;
|
|
147
|
+
'composer.hint': string;
|
|
148
|
+
'composer.busy': string;
|
|
149
|
+
'composer.notLoggedIn': string;
|
|
150
|
+
'composer.attach': string;
|
|
151
|
+
'composer.attach.hint': string;
|
|
152
|
+
'composer.attach.placeholder': string;
|
|
153
|
+
'empty.eyebrow': string;
|
|
154
|
+
'empty.title': string;
|
|
155
|
+
'empty.body': string;
|
|
156
|
+
'transfer.done': string;
|
|
157
|
+
'transfer.continued': string;
|
|
158
|
+
'transfer.failed': string;
|
|
159
|
+
'export.done': string;
|
|
160
|
+
'export.failed': string;
|
|
161
|
+
'recover.hint': string;
|
|
162
|
+
'recover.empty': string;
|
|
163
|
+
'recover.done': string;
|
|
164
|
+
'msg.empty': string;
|
|
165
|
+
'msg.error': string;
|
|
166
|
+
streaming: string;
|
|
167
|
+
};
|
|
168
|
+
/** Key union of the web-chat surface copy. */
|
|
169
|
+
export type WebChatKey = keyof typeof zh;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ISessions, IWorkspaces } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
|
+
import type { WebChatApi } from './api.ts';
|
|
3
|
+
import type { PanelController } from './controller.ts';
|
|
4
|
+
import type { WebChatKey } from './locales.ts';
|
|
5
|
+
/** The injected panel container (kept in the DOM, hidden when inactive). */
|
|
6
|
+
export declare const PANEL_VIEW_SELECTOR = "[data-dsh-webchat-view]";
|
|
7
|
+
export interface MountPanelDeps {
|
|
8
|
+
sessions: ISessions;
|
|
9
|
+
workspaces: IWorkspaces;
|
|
10
|
+
api: WebChatApi;
|
|
11
|
+
controller: PanelController;
|
|
12
|
+
/** Locale accessor bound to the plugin's registered dictionaries. */
|
|
13
|
+
tt: (key: WebChatKey) => string;
|
|
14
|
+
/** Resolve the current session's cwd (for transfers / exports). */
|
|
15
|
+
currentCwd: () => string | undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Mount the panel React tree into the center column and bind its visibility
|
|
19
|
+
* to the controller's panelOpen state.
|
|
20
|
+
* @param deps - sessions, api, controller, locale accessor.
|
|
21
|
+
* @returns disposer unmounting the tree and restoring the column.
|
|
22
|
+
*/
|
|
23
|
+
export declare function mountPanel(deps: MountPanelDeps): () => void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal markdown renderer for the web-chat panel. Deliberately small and
|
|
3
|
+
* dependency-free: headings, paragraphs, bold/italic, inline + fenced code,
|
|
4
|
+
* links, DeepSeek `[citation:N]` markers (rendered as superscript badges),
|
|
5
|
+
* unordered/ordered lists, blockquotes, GFM tables, horizontal rules, and
|
|
6
|
+
* <details> passthrough (used for DeepSeek R1 thinking blocks). Anything
|
|
7
|
+
* unrecognized renders as plain text — never raw HTML.
|
|
8
|
+
*/
|
|
9
|
+
import { type ReactNode } from 'react';
|
|
10
|
+
/** Render one block of markdown text into React nodes. */
|
|
11
|
+
export declare function renderMarkdown(source: string): ReactNode[];
|
|
12
|
+
/** The markdown renderer component. */
|
|
13
|
+
export declare function Markdown({ source }: {
|
|
14
|
+
source: string;
|
|
15
|
+
}): ReactNode;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The web-chat panel: engine/login status, a conversation sidebar (the local
|
|
3
|
+
* transcript list + management), the chat transcript with markdown rendering,
|
|
4
|
+
* streaming updates (polled), the composer with deep-think / internet-search
|
|
5
|
+
* toggles, and the transfer-to-Harness handoff. All data flows through the
|
|
6
|
+
* /api/dsh-webchat routes; the panel is a plain React root in the center column.
|
|
7
|
+
*
|
|
8
|
+
* Layout mirrors chat.deepseek.com: a left conversation sidebar (new chat +
|
|
9
|
+
* history + per-item rename/delete + clear-all) beside the main chat column
|
|
10
|
+
* (messages → composer with 深度思考 / 智能搜索 toggles → handoff dock). The
|
|
11
|
+
* mono register stays reserved for machine things — timestamps, counts,
|
|
12
|
+
* status, and the handoff eyebrow.
|
|
13
|
+
*/
|
|
14
|
+
import type { ISessions, IWorkspaces } from '@deepseek-ai/dsh-client-runtime/client';
|
|
15
|
+
import type { WebChatApi } from '../api.ts';
|
|
16
|
+
import type { WebChatKey } from '../locales.ts';
|
|
17
|
+
export interface WebChatPanelProps {
|
|
18
|
+
api: WebChatApi;
|
|
19
|
+
/** Locale accessor (the plugin registers its dictionaries on ctx.locale). */
|
|
20
|
+
tt: (key: WebChatKey) => string;
|
|
21
|
+
/** Client sessions service (ctx.sessions) for opening transferred sessions. */
|
|
22
|
+
sessions: ISessions;
|
|
23
|
+
/** Client workspaces service (ctx.workspaces) for the transfer target picker. */
|
|
24
|
+
workspaces: IWorkspaces;
|
|
25
|
+
/** Resolve the current workspace directory (cwd for new sessions / exports). */
|
|
26
|
+
currentCwd: () => string | undefined;
|
|
27
|
+
}
|
|
28
|
+
export declare function WebChatPanel({ api, tt, sessions, workspaces, currentCwd }: WebChatPanelProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared sidebar entry injection core.
|
|
3
|
+
*
|
|
4
|
+
* dsh's sidebar shell exposes no slot an external plugin can register into,
|
|
5
|
+
* so the entry row is injected between the shell's New Session button and the
|
|
6
|
+
* workspace browser. The injection self-heals: a MutationObserver watches the
|
|
7
|
+
* sidebar root and re-inserts the row whenever a React re-render displaces it
|
|
8
|
+
* (re-insertion happens in the same frame, before paint, so no flicker).
|
|
9
|
+
*
|
|
10
|
+
* The row is plain DOM (no React tree) so it can never disturb the shell's
|
|
11
|
+
* reconciliation; the view it toggles is a separate root owned by the caller.
|
|
12
|
+
*
|
|
13
|
+
* Packages receive this file as a generated copy via scripts/sync-shared.mjs;
|
|
14
|
+
* edit the shared source and re-run the sync instead of editing a copy.
|
|
15
|
+
*/
|
|
16
|
+
/** Per-package configuration for one sidebar entry row. */
|
|
17
|
+
export interface SidebarEntryOptions {
|
|
18
|
+
/** Full attribute name identifying the injected row (idempotency key), e.g. 'data-dsh-ssh-entry'. */
|
|
19
|
+
rowAttribute: string;
|
|
20
|
+
/** CSS selector matching the injected row, e.g. '[data-dsh-ssh-entry]'. */
|
|
21
|
+
rowSelector: string;
|
|
22
|
+
/**
|
|
23
|
+
* L2 semantic-attribute plugin id (issue #506, enum table:
|
|
24
|
+
* skins/skin-center/contracts/semantic-attrs-v1.md). When set, the row also
|
|
25
|
+
* outputs data-dsh-plugin="<id>" and data-dsh-part="sidebar-entry"; unset
|
|
26
|
+
* leaves the row without semantic attributes.
|
|
27
|
+
*/
|
|
28
|
+
plugin?: string;
|
|
29
|
+
/** Inline icon markup (matches the shell's 16px nav-icon look). */
|
|
30
|
+
icon: string;
|
|
31
|
+
/** CSS module class names for the row and its two spans (entry / entryIcon / entryLabel). */
|
|
32
|
+
css: Record<string, string>;
|
|
33
|
+
/** Localized row label (aria-label + visible text). */
|
|
34
|
+
label(): string;
|
|
35
|
+
/** Optional localized tooltip (title attribute). */
|
|
36
|
+
tooltip?(): string;
|
|
37
|
+
/** Click action (open/toggle the owning panel). */
|
|
38
|
+
onToggle(): void;
|
|
39
|
+
/** Family-block position: 'before' inserts ahead of sibling plugin rows, 'after' behind them. */
|
|
40
|
+
position: 'before' | 'after';
|
|
41
|
+
/**
|
|
42
|
+
* Selectors of the sibling plugin entry rows this package orders against
|
|
43
|
+
* (its own row included — the placement guard excludes a row that is
|
|
44
|
+
* already inside the root). Each package passes the same list it used
|
|
45
|
+
* before the consolidation so the rendered order stays stable.
|
|
46
|
+
*/
|
|
47
|
+
familySelectors: readonly string[];
|
|
48
|
+
/** Optional active-state bridge; highlights the row while the panel is open. */
|
|
49
|
+
active?: {
|
|
50
|
+
subscribe(listener: () => void): () => void;
|
|
51
|
+
isOpen(): boolean;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
56
|
+
* on later React re-renders.
|
|
57
|
+
* @param options - the row's attribute/icon/copy/action/ordering configuration.
|
|
58
|
+
* @returns disposer removing the entry and its observers.
|
|
59
|
+
*/
|
|
60
|
+
export declare function mountSidebarEntry(options: SidebarEntryOptions): () => void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar entry injection — package-specific wiring over the shared core
|
|
3
|
+
* (sidebar-entry-core.ts). The row is plain DOM so it can never disturb the
|
|
4
|
+
* shell's reconciliation; the panel view it toggles is a separate React root
|
|
5
|
+
* mounted in the center column (see mount.tsx).
|
|
6
|
+
*/
|
|
7
|
+
import type { PanelController } from './controller.ts';
|
|
8
|
+
import type { WebChatKey } from './locales.ts';
|
|
9
|
+
/** Stable data attribute identifying the injected entry row. */
|
|
10
|
+
export declare const ENTRY_SELECTOR = "[data-dsh-webchat-entry]";
|
|
11
|
+
export interface SidebarEntryDeps {
|
|
12
|
+
controller: PanelController;
|
|
13
|
+
/** Locale accessor (spelled here to keep the core dependency-free). */
|
|
14
|
+
tt: (key: WebChatKey) => string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
18
|
+
* on later React re-renders.
|
|
19
|
+
* @param deps - controller and copy accessor.
|
|
20
|
+
* @returns disposer removing the entry and its observers.
|
|
21
|
+
*/
|
|
22
|
+
export declare function mountSidebarEntry(deps: SidebarEntryDeps): () => void;
|