@morlay/ui-conversation-manager 0.1.0 → 0.1.2
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 +20 -97
- package/dist/client.cjs +6 -51
- package/dist/client.d.cts +0 -32
- package/dist/client.d.mts +0 -32
- package/dist/index.d.cts +0 -1
- package/dist/index.d.mts +0 -1
- package/dist/index.mjs +0 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +13 -13
- package/src/client/ConversationManagerIcon.tsx +1 -1
- package/src/client/ConversationManagerPage.styles.ts +3 -4
- package/src/client/ConversationManagerPage.tsx +24 -26
- package/src/client/controller.ts +29 -36
- package/src/client/format.ts +1 -4
- package/src/client/index.ts +4 -4
- package/src/client/locales.ts +4 -4
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -1,102 +1,25 @@
|
|
|
1
1
|
# @morlay/ui-conversation-manager
|
|
2
2
|
|
|
3
|
-
「对话管理」全局面板页(**浏览器半**):`sidebar.panellist` nav 行 + `main` keyed 面板(与官方
|
|
4
|
-
ui-plugin-manager
|
|
5
|
-
|
|
6
|
-
孤儿数据 GC;统计视图给出全部对话的 token 用量。
|
|
7
|
-
|
|
8
|
-
会话列表取 `POST /api/session.rows` 的完整语料(含归档),按最近活动在前;搜索(标题或工作区名)、
|
|
9
|
-
子代理开关与翻页都是**后端**请求(前端只渲染当前页,输入停 250ms 再发);**已归档**的行带标记,
|
|
10
|
-
也只有这些行的「删除」可用
|
|
11
|
-
(未归档行提供「归档」,两者互斥)。子代理派生会话(`origin: 'subagent'`)默认不列(它们既不可删
|
|
12
|
-
也多数无意义,实测在真实库里占七成),勾选「显示子代理会话」即真全量。
|
|
13
|
-
|
|
14
|
-
统计视图先按**时间范围**过滤(默认**本日**;**本周** / 近 7 天 / 近 30 天 / 近 90 天 / 最后是**全部**;
|
|
15
|
-
换范围会向 host 重新聚合一次。**本日 / 本周**按 host 的本地时区算,本周从**周一 00:00** 起),
|
|
16
|
-
二层 Tabs 再切维度:**总览**(「全部」与「其中子代理」两行)、**按模型**、**按会话**(用量降序取前 20)。
|
|
17
|
-
时间范围取代了原来的「按天」一栏。
|
|
18
|
-
|
|
19
|
-
每一行都是同一个形态:**label 在上,下面横向排布各个单项**,单项内部是「标签在上、值在下」。
|
|
20
|
-
单项依次是:**输入(含缓存输入)· 缓存输入 · 缓存命中率 · 输出 · 推理 · 事件**
|
|
21
|
-
——缓存输入占总输入(含缓存)的比例即命中率。**没有单独的合计项**;排序按「输入(含缓存)+ 输出」(不显示)。
|
|
3
|
+
「对话管理」全局面板页(**浏览器半**):`sidebar.panellist` 的 nav 行 + `main` keyed 面板(与官方
|
|
4
|
+
ui-plugin-manager 同一种注册方式)。一层 Tabs 在**会话**与**统计**之间切换:会话视图做全量会话的搜索、分页与
|
|
5
|
+
归档 / 取消归档 / 导出 / 删除(确认弹窗),并带导入为新会话与孤儿数据 GC;统计视图给出全部对话的 token 用量。
|
|
22
6
|
|
|
23
7
|
## 用法
|
|
24
8
|
|
|
25
|
-
页面是 profile 的一行(由 `@morlay/better-session` 的 patch
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
归档 / 取消归档 / 删除 / 导入 / GC 成功后重拉会话行;host 拒绝(未归档 / 正在使用 / 不存在)
|
|
43
|
-
时按错误码给出可读文案。列表每页 20 条,搜索框复用官方 `Input`(连同官方图标与焦点样式)。
|
|
44
|
-
|
|
45
|
-
GC 是唯一会**停止所有运行中 agent** 的动作:确认后进入不可关闭的等待弹窗,避免用户在 VACUUM 期间
|
|
46
|
-
做别的操作。
|
|
47
|
-
|
|
48
|
-
## 数据位标注(`data-*`)
|
|
49
|
-
|
|
50
|
-
页面每个数据位带稳定的 `data-*` 标注,沟通时可以直接指着它说(例如
|
|
51
|
-
`data-usage-key=2026-09-14` 那行、`data-session-id=…` 那行的 `data-action=remove`)。
|
|
52
|
-
只标"最小身份 + DOM 读不出的状态":视图/维度由 `data-view`、`data-tab`、`data-usage-tab` 表达,
|
|
53
|
-
行内合计与明细、行标题这类页面上直接可见的内容不再重复标注。
|
|
54
|
-
|
|
55
|
-
时间范围的按钮与维度 tab 共用**同一套官方 tab 条视觉**(文字 13/16 wt500、选中态品牌蓝 + 2px 下划线,
|
|
56
|
-
取自会话头部的 `ConversationRoot.module.css`),只在其语义上用 `radiogroup`(单选筛选)区别于维度的 `tab`;
|
|
57
|
-
**不用 `Pill`**——官方 `ui-primitives` README 里 `Pill` 是「视图切换与筛选用法的可选中胶囊」(只读 capsule 是
|
|
58
|
-
`Tag`),官方那件互斥择一的选择件是 `SegmentedControl`(受控 tab 形态 `SegmentedTabs`);我们这里是**单选筛选**:
|
|
59
|
-
视觉取 tab 条、语义用 `radiogroup`。
|
|
60
|
-
|
|
61
|
-
统计的每一行(总览与列表)是同一形态:行 label + 横向排布的单项,所以读法一致——
|
|
62
|
-
「`data-usage-key=2026-09-14` 行里 `data-usage-cell=input` 的 `data-usage-value`」。
|
|
63
|
-
|
|
64
|
-
| 标注 | 位置 | 含义 |
|
|
65
|
-
| ----------------------------------------------------------- | ------------------ | --------------------------------------------------------------- |
|
|
66
|
-
| `data-view` | 页面根 | `sessions` / `usage` |
|
|
67
|
-
| `data-tab` | 一层 tab 按钮 | 该按钮切到的视图 |
|
|
68
|
-
| `data-action` | 按钮 | `import` / `gc` / `archive` / `unarchive` / `export` / `remove` |
|
|
69
|
-
| `data-filter` | 过滤器 | `search` / `subagents` |
|
|
70
|
-
| `data-scroll` | 滚动容器 | `page`(会话列表与统计各一处) |
|
|
71
|
-
| `data-session-id` / `data-archived` / `data-subagent` | 会话行 | 该行的会话 id 与状态(`true` / `false`) |
|
|
72
|
-
| `data-pagination` / `data-page-current` / `data-page-total` | 分页条 | 当前页与总页数 |
|
|
73
|
-
| `data-notice` / `data-failure` / `data-status` | 顶部提示与空态 | 结果、错误、`empty` / `empty-search` |
|
|
74
|
-
| `data-usage-view` / `data-usage-tab` / `data-usage-range` | 统计视图与二级 tab | 当前维度(`overview` / `models` / `sessions`)与时间范围 |
|
|
75
|
-
| `data-range` | 时间范围选项 | 该选项对应的 range 值 |
|
|
76
|
-
| `data-usage-cell` / `data-usage-value` | 总览格子 | 维度键与原始数值(未格式化) |
|
|
77
|
-
| `data-usage-key` | 统计划表行 | 维度键(日期 / `provider / model` / 会话 id / `subagent`) |
|
|
78
|
-
| `data-usage-status` | 统计状态行 | `loading` / `error` / `empty` |
|
|
79
|
-
|
|
80
|
-
## 已知限制
|
|
81
|
-
|
|
82
|
-
- 列表是全量会话:未归档行提供**归档**、已归档行提供**取消归档**(同一位置二选一,互斥);
|
|
83
|
-
**删除只对已归档会话开放**(未归档行按钮禁用;host 侧仍返回 409),沿用
|
|
84
|
-
[ADR-会话删除仅限已归档且硬删](../session-rdb/.agents/adrs/20260917-会话删除仅限已归档且硬删.md)。
|
|
85
|
-
- 只有「最近活动在前」一种排序,没有分组与批量操作;分页在后端(每次只取当前页 20 条,翻页与搜索都重新请求)。
|
|
86
|
-
- 子代理派生会话默认不列(`origin: 'subagent'`):勾选「显示子代理会话」后出现并带「子代理」标记,
|
|
87
|
-
但这些行不可删除(它们未归档);归档 / 取消归档仍可用。
|
|
88
|
-
- 没有 summary 的成员(例如已从 host 消失的归档 id)不产生行。
|
|
89
|
-
- 删除不再顺带清孤儿:库体积要等一次 GC 才下降,见
|
|
90
|
-
[ADR-删除不再清孤儿与vacuum独立成gc通道](../session-rdb/.agents/adrs/20260918-删除不再清孤儿与vacuum独立成gc通道.md)。
|
|
91
|
-
GC 同时回收**父已不存在的 subagent 会话**(它们是删除父会话后的孤儿);父还在的子会话不动。
|
|
92
|
-
- 导入只接受含会话日志 artifact 的 zip;导出只导出该 artifact(不含附件等旁路数据)。
|
|
93
|
-
- 统计是一次全库聚合(真实库实测约 140ms),只在进入统计视图时请求一次;口径见
|
|
94
|
-
[ADR-用量统计走专用用量日志表](../session-rdb/.agents/adrs/20260918-用量统计走专用用量日志表.md)。
|
|
95
|
-
按会话的行是各会话自己的日志口径(fork 子会话含继承前缀),所以各行之和 ≥ 总量;「其中子代理」与另一侧
|
|
96
|
-
相加正好等于总量。
|
|
97
|
-
- 统计只反映**库里现有**的用量:删掉的会话与 GC 回收掉的孤儿行都不再计入。
|
|
98
|
-
|
|
99
|
-
## 文档
|
|
100
|
-
|
|
101
|
-
- 决策:[ADR-禁用上游已归档入口改由对话管理页承载](../../bundles/better-session/.agents/adrs/20260918-禁用上游已归档入口改由对话管理页承载.md)
|
|
102
|
-
- 测试落点:[`src/__tests__/`](./src/__tests__)(注册面 / 页面 / 注入面 / 数字格式化四条 spec)
|
|
9
|
+
页面是 profile 的一行(由 `@morlay/better-session` 的 patch 插入),装好即在侧栏出现「对话管理」。它不新增
|
|
10
|
+
host 面,只读既有服务与路由:
|
|
11
|
+
|
|
12
|
+
| 动作 | 接缝 |
|
|
13
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
14
|
+
| 列表与标题 | `POST /api/morlay/v1/session/rows`(`@morlay/session-rdb`:完整语料 + 标题 + 最后活动时间 + 工作区归属;搜索 / 子代理过滤 / 分页都在后端) |
|
|
15
|
+
| 归档 | `ctx.uiWorkspace.archiveSession`(上游 ui-workspace,未归档行提供) |
|
|
16
|
+
| 取消归档 | `ctx.uiWorkspace.unarchiveSession`(上游 ui-workspace,已归档行提供) |
|
|
17
|
+
| 导出 | `POST /api/morlay/v1/session/export`(`@morlay/session-rdb`,直接下载 zip) |
|
|
18
|
+
| 删除 | `POST /api/morlay/v1/session/delete`(`@morlay/session-rdb`,仅已归档行可用) |
|
|
19
|
+
| 导入为新会话 | `POST /api/morlay/v1/session/import`(`@morlay/session-rdb`,不带 `sessionId` = 新建会话) |
|
|
20
|
+
| 清理孤儿数据 | `POST /api/morlay/v1/session/gc`(停 agent → 回收孤儿 subagent 会话 → 回收孤儿事件行 → VACUUM;执行期间阻塞界面) |
|
|
21
|
+
| 用量统计 | `POST /api/morlay/v1/session/usage`(`@morlay/session-rdb` 读专用用量表聚合;进入统计视图时拉一次,维度切换本地折叠,范围切换重新请求) |
|
|
22
|
+
|
|
23
|
+
归档 / 取消归档 / 删除 / 导入 / GC 成功后重拉会话行;host 拒绝(未归档 / 正在使用 / 不存在)时按错误码给出可读
|
|
24
|
+
文案。删除只对已归档会话开放;子代理派生会话(`origin: 'subagent'`)默认不列,勾选「显示子代理会话」即真
|
|
25
|
+
全量。
|
package/dist/client.cjs
CHANGED
|
@@ -9,18 +9,12 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
let _morlay_dsh_client_ui_primitives_client = require("@morlay/dsh-client-ui-primitives/client");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
11
|
//#region src/client/controller.ts
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
/**
|
|
19
|
-
* 管理面的会话行(**完整语料,含归档**):我们自己的路由,与官方 `session/list` 分开——那条按部署策略
|
|
20
|
-
* 默认排除归档(给上游 UI 用),归档集的管理动作需要完整集合。
|
|
21
|
-
*/
|
|
22
|
-
const SESSION_ROWS_PATH = "/api/session.rows";
|
|
23
|
-
/** 带 host 错误码的请求失败:页面据此选本地化文案。 */
|
|
12
|
+
const SESSION_DELETE_PATH = "/api/morlay/v1/session/delete";
|
|
13
|
+
const SESSION_IMPORT_PATH = "/api/morlay/v1/session/import";
|
|
14
|
+
const SESSION_EXPORT_PATH = "/api/morlay/v1/session/export";
|
|
15
|
+
const SESSION_GC_PATH = "/api/morlay/v1/session/gc";
|
|
16
|
+
const SESSION_USAGE_PATH = "/api/morlay/v1/session/usage";
|
|
17
|
+
const SESSION_ROWS_PATH = "/api/morlay/v1/session/rows";
|
|
24
18
|
var ConversationManagerRequestError = class extends Error {
|
|
25
19
|
code;
|
|
26
20
|
constructor(message, code) {
|
|
@@ -56,7 +50,6 @@ window.__ModuleLoader__.load({
|
|
|
56
50
|
const comma = dataUrl.indexOf(",");
|
|
57
51
|
return comma < 0 ? dataUrl : dataUrl.slice(comma + 1);
|
|
58
52
|
}
|
|
59
|
-
/** 页面的动作:host 交互收在这里,页面只见数据与回调。 */
|
|
60
53
|
var ConversationManagerController = class {
|
|
61
54
|
ports;
|
|
62
55
|
face;
|
|
@@ -118,21 +111,15 @@ window.__ModuleLoader__.load({
|
|
|
118
111
|
stoppedAgents: typeof value["stoppedAgents"] === "number" ? value["stoppedAgents"] : 0
|
|
119
112
|
};
|
|
120
113
|
}
|
|
121
|
-
/**
|
|
122
|
-
* 用量统计:host 侧聚合,前端各维度本地折叠。
|
|
123
|
-
* @param range - 时间范围语义键(`all` 不限、`day`/`week` 自然日/周、其余最近 N 天)。
|
|
124
|
-
*/
|
|
125
114
|
async loadUsage(range) {
|
|
126
115
|
const report = await postJson(SESSION_USAGE_PATH, { range });
|
|
127
116
|
if (report.totals === void 0 || !Array.isArray(report.buckets) || !Array.isArray(report.sessions)) throw new ConversationManagerRequestError("用量统计响应不可用", void 0);
|
|
128
117
|
return report;
|
|
129
118
|
}
|
|
130
119
|
};
|
|
131
|
-
/** 导出文件名优先取 host 给的 Content-Disposition。 */
|
|
132
120
|
function filenameOf(disposition, sessionId) {
|
|
133
121
|
return (disposition === null ? null : /filename="([^"]+)"/u.exec(disposition))?.[1] ?? `${sessionId}.zip`;
|
|
134
122
|
}
|
|
135
|
-
/** 浏览器下载:blob URL + 一次性 anchor;URL 在下一轮事件循环回收。 */
|
|
136
123
|
function downloadBlob(blob, filename) {
|
|
137
124
|
const url = URL.createObjectURL(blob);
|
|
138
125
|
const anchor = document.createElement("a");
|
|
@@ -148,11 +135,9 @@ window.__ModuleLoader__.load({
|
|
|
148
135
|
}
|
|
149
136
|
//#endregion
|
|
150
137
|
//#region src/client/format.ts
|
|
151
|
-
/** 一位小数;三位数以上不留小数。 */
|
|
152
138
|
function trim(value) {
|
|
153
139
|
return value >= 100 ? String(Math.round(value)) : value.toFixed(1).replace(/\.0$/u, "");
|
|
154
140
|
}
|
|
155
|
-
/** @param value - token 数(非负整数)。 @returns 紧凑文本,例如 `1.2K`、`44.2B`。 */
|
|
156
141
|
function formatTokens(value) {
|
|
157
142
|
if (!Number.isFinite(value) || value <= 0) return "0";
|
|
158
143
|
if (value < 1e3) return String(Math.round(value));
|
|
@@ -160,20 +145,14 @@ window.__ModuleLoader__.load({
|
|
|
160
145
|
if (value < 1e9) return `${trim(value / 1e6)}M`;
|
|
161
146
|
return `${trim(value / 1e9)}B`;
|
|
162
147
|
}
|
|
163
|
-
/** @param value - 计数(轮次 / 步骤 / 用户输入 / 工具调用)。 @returns 千分位原文,例如 `1,024`。 */
|
|
164
148
|
function formatCount(value) {
|
|
165
149
|
if (!Number.isFinite(value) || value <= 0) return "0";
|
|
166
150
|
return Math.round(value).toLocaleString("en-US");
|
|
167
151
|
}
|
|
168
|
-
/** @param value - 百分点(0~100)。 @returns 紧凑百分比,例如 `98.4%`、`100%`。 */
|
|
169
152
|
function formatPercent(value) {
|
|
170
153
|
if (!Number.isFinite(value) || value <= 0) return "0%";
|
|
171
154
|
return `${value >= 100 ? Math.round(value) : Math.round(value * 10) / 10}%`;
|
|
172
155
|
}
|
|
173
|
-
/** 「对话管理」页面的样式表(官方 --dsw-* 变量 + 我们的 css-in-js 层)。
|
|
174
|
-
* 页面根是纵向 flex 且**自己不滚**:页头(标题 / tab 条 / 导入 / 清理)与搜索行固定在顶部,
|
|
175
|
-
* 只有 `scroll` 那层随内容滚。除滚动层外的项都 `flex: none`,否则内容变长时 flex 会把这些项
|
|
176
|
-
* 压到 min-content(官方 Input 的 32px 高会被压成一行文字高)。 */
|
|
177
156
|
const styles = {
|
|
178
157
|
page: {
|
|
179
158
|
display: "flex",
|
|
@@ -418,16 +397,12 @@ window.__ModuleLoader__.load({
|
|
|
418
397
|
};
|
|
419
398
|
//#endregion
|
|
420
399
|
//#region src/client/ConversationManagerPage.tsx
|
|
421
|
-
/** 一页的行数(会话列表)。 */
|
|
422
400
|
const PAGE_SIZE = 20;
|
|
423
|
-
/** 统计视图按会话列出时的行数上限。 */
|
|
424
401
|
const USAGE_SESSION_ROWS = 20;
|
|
425
|
-
/** 行上显示的紧凑相对时间。 */
|
|
426
402
|
function timeLabel(updatedAt, now, t) {
|
|
427
403
|
const { unit, n } = (0, _deepseek_ai_dsh_client_ui_primitives.relativeTime)(updatedAt, now);
|
|
428
404
|
return unit === "now" ? t("time.now") : t(`time.${unit}`, { n });
|
|
429
405
|
}
|
|
430
|
-
/** host 错误码 → 可读文案;没有码时保留原文。 */
|
|
431
406
|
function failureText(error, t) {
|
|
432
407
|
const code = error instanceof ConversationManagerRequestError ? error.code : void 0;
|
|
433
408
|
if (code === "SESSION_NOT_ARCHIVED") return t("failure.notArchived");
|
|
@@ -868,7 +843,6 @@ window.__ModuleLoader__.load({
|
|
|
868
843
|
]
|
|
869
844
|
});
|
|
870
845
|
}
|
|
871
|
-
/** 时间范围选项:默认本日,其后是本周(周一起算)与最近 N 天,「全部」放在最后。 */
|
|
872
846
|
const USAGE_RANGES = [
|
|
873
847
|
"day",
|
|
874
848
|
"week",
|
|
@@ -877,7 +851,6 @@ window.__ModuleLoader__.load({
|
|
|
877
851
|
"90d",
|
|
878
852
|
"all"
|
|
879
853
|
];
|
|
880
|
-
/** 范围按钮的文案。 */
|
|
881
854
|
function rangeLabel(range, t) {
|
|
882
855
|
switch (range) {
|
|
883
856
|
case "all": return t("usage.range.all");
|
|
@@ -888,17 +861,11 @@ window.__ModuleLoader__.load({
|
|
|
888
861
|
case "90d": return t("usage.range.days", { n: 90 });
|
|
889
862
|
}
|
|
890
863
|
}
|
|
891
|
-
/** 缓存命中率(百分点):缓存输入占总输入(含缓存)的比例。 */
|
|
892
864
|
function cacheHitPercent(totals) {
|
|
893
865
|
const total = totals.inputTokens + totals.cacheReadTokens;
|
|
894
866
|
if (total <= 0) return 0;
|
|
895
867
|
return totals.cacheReadTokens / total * 100;
|
|
896
868
|
}
|
|
897
|
-
/**
|
|
898
|
-
* 显示口径的单项:输入(含缓存输入)、缓存输入、缓存命中率、输出、推理,
|
|
899
|
-
* 以及活动计数(轮次 / 步骤 / 用户输入 / 工具调用)——没有合计项。
|
|
900
|
-
* 活动计数在按模型的行上不显示(事件没有模型归属,见 `withActivity`)。
|
|
901
|
-
*/
|
|
902
869
|
function usageMetrics(totals, t, options = {}) {
|
|
903
870
|
return [
|
|
904
871
|
{
|
|
@@ -959,7 +926,6 @@ window.__ModuleLoader__.load({
|
|
|
959
926
|
]
|
|
960
927
|
];
|
|
961
928
|
}
|
|
962
|
-
/** 折叠行的排序口径(不显示):输入(含缓存)+ 输出。 */
|
|
963
929
|
function sortWeight(totals) {
|
|
964
930
|
return totals.inputTokens + totals.cacheReadTokens + totals.outputTokens;
|
|
965
931
|
}
|
|
@@ -976,7 +942,6 @@ window.__ModuleLoader__.load({
|
|
|
976
942
|
totalTokens: 0
|
|
977
943
|
};
|
|
978
944
|
}
|
|
979
|
-
/** 把桶按一个键折叠成行并按用量降序(按天 / 按模型都用它)。 */
|
|
980
945
|
function foldBuckets(buckets, keyOf) {
|
|
981
946
|
const folded = /* @__PURE__ */ new Map();
|
|
982
947
|
for (const bucket of buckets) {
|
|
@@ -995,7 +960,6 @@ window.__ModuleLoader__.load({
|
|
|
995
960
|
}
|
|
996
961
|
return [...folded.values()].sort((left, right) => sortWeight(right.totals) - sortWeight(left.totals));
|
|
997
962
|
}
|
|
998
|
-
/** 一行用量:label 在上,下面是横向排布的单项。 */
|
|
999
963
|
function UsageRow({ rowKey, label, totals, t, withActivity = true }) {
|
|
1000
964
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1001
965
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.usageRow),
|
|
@@ -1037,7 +1001,6 @@ window.__ModuleLoader__.load({
|
|
|
1037
1001
|
}, row.key))
|
|
1038
1002
|
});
|
|
1039
1003
|
}
|
|
1040
|
-
/** 总览:全部与「其中子代理」两行,与列表行同形。 */
|
|
1041
1004
|
function UsageOverview({ report, t }) {
|
|
1042
1005
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
1043
1006
|
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.usageList),
|
|
@@ -1054,7 +1017,6 @@ window.__ModuleLoader__.load({
|
|
|
1054
1017
|
})]
|
|
1055
1018
|
});
|
|
1056
1019
|
}
|
|
1057
|
-
/** 统计视图:时间范围过滤 + 二层维度切换(总览 / 按模型 / 按会话)。 */
|
|
1058
1020
|
function UsageView({ report, loading, error, tab, onTab, range, onRange, t }) {
|
|
1059
1021
|
const items = [
|
|
1060
1022
|
"overview",
|
|
@@ -1136,8 +1098,6 @@ window.__ModuleLoader__.load({
|
|
|
1136
1098
|
}
|
|
1137
1099
|
//#endregion
|
|
1138
1100
|
//#region src/client/locales.ts
|
|
1139
|
-
/** 「对话管理」页面的文案字典。 */
|
|
1140
|
-
/** 简体中文是 key 真源。 */
|
|
1141
1101
|
const zh = {
|
|
1142
1102
|
panel: "对话管理",
|
|
1143
1103
|
title: "对话管理",
|
|
@@ -1212,7 +1172,6 @@ window.__ModuleLoader__.load({
|
|
|
1212
1172
|
"time.months": "{n}个月",
|
|
1213
1173
|
"time.years": "{n}年"
|
|
1214
1174
|
};
|
|
1215
|
-
/** 英文对照表。 */
|
|
1216
1175
|
const en = {
|
|
1217
1176
|
panel: "Conversations",
|
|
1218
1177
|
title: "Conversations",
|
|
@@ -1289,13 +1248,9 @@ window.__ModuleLoader__.load({
|
|
|
1289
1248
|
};
|
|
1290
1249
|
//#endregion
|
|
1291
1250
|
//#region src/client/index.ts
|
|
1292
|
-
/** 本包字典的 namespace。 */
|
|
1293
1251
|
const NS = "conversationManager";
|
|
1294
|
-
/** nav 行与主面板共用的 id。 */
|
|
1295
1252
|
const PANEL_ID = "conversations";
|
|
1296
|
-
/** nav 行的位置:紧邻官方 Plugins 行(order 0)。 */
|
|
1297
1253
|
const PANEL_ORDER = 1;
|
|
1298
|
-
/** 页面用到的服务。 */
|
|
1299
1254
|
const inject = [
|
|
1300
1255
|
"slots",
|
|
1301
1256
|
"locale",
|
package/dist/client.d.cts
CHANGED
|
@@ -3,8 +3,6 @@ import { MainPanelId } from "@deepseek-ai/dsh-client-ui-layout/client";
|
|
|
3
3
|
import { InjectFace, PropsLocale, PropsRuntime } from "@deepseek-ai/dsh-client-ui-slots";
|
|
4
4
|
import "react";
|
|
5
5
|
//#region src/client/locales.d.ts
|
|
6
|
-
/** 「对话管理」页面的文案字典。 */
|
|
7
|
-
/** 简体中文是 key 真源。 */
|
|
8
6
|
declare const zh: {
|
|
9
7
|
panel: string;
|
|
10
8
|
title: string;
|
|
@@ -79,7 +77,6 @@ declare const zh: {
|
|
|
79
77
|
"time.months": string;
|
|
80
78
|
"time.years": string;
|
|
81
79
|
};
|
|
82
|
-
/** 「对话管理」页面字典的 key 集合。 */
|
|
83
80
|
type ConversationManagerKey = keyof typeof zh;
|
|
84
81
|
//#endregion
|
|
85
82
|
//#region ../../../vendor/deepseek-harness/packages/core/scope/lib/types/index.d.ts
|
|
@@ -3196,7 +3193,6 @@ declare class SessionStore extends Service {
|
|
|
3196
3193
|
}
|
|
3197
3194
|
//#endregion
|
|
3198
3195
|
//#region src/client/controller.d.ts
|
|
3199
|
-
/** 一行会话:标题、origin、最后活动时间、归档标记与工作区归属都由 host 给出。 */
|
|
3200
3196
|
interface SessionRowRecord {
|
|
3201
3197
|
sessionId: string;
|
|
3202
3198
|
title: string | null;
|
|
@@ -3207,7 +3203,6 @@ interface SessionRowRecord {
|
|
|
3207
3203
|
archived: boolean;
|
|
3208
3204
|
workspace: string | null;
|
|
3209
3205
|
}
|
|
3210
|
-
/** 列表请求:搜索、子代理过滤与分页都在后端做(前端分页等于每次拉全量)。 */
|
|
3211
3206
|
interface SessionRowsQuery {
|
|
3212
3207
|
query?: string;
|
|
3213
3208
|
includeSubagents?: boolean;
|
|
@@ -3216,24 +3211,17 @@ interface SessionRowsQuery {
|
|
|
3216
3211
|
}
|
|
3217
3212
|
interface SessionRowsPage {
|
|
3218
3213
|
items: SessionRowRecord[];
|
|
3219
|
-
/** 过滤后的总数(分页前),页面据此算页数。 */
|
|
3220
3214
|
total: number;
|
|
3221
3215
|
page: number;
|
|
3222
3216
|
pageSize: number;
|
|
3223
3217
|
}
|
|
3224
|
-
/**
|
|
3225
|
-
* 时间范围的语义键(与 session-rdb `./usage` 的 `UsageRangeKey` 镜像):`day` / `week` 是本地自然日 /
|
|
3226
|
-
* 自然周,`7d` / `30d` / `90d` 是最近 N 个自然日(含今天)——边界由 host 按本地时区算,客户端只传语义。
|
|
3227
|
-
*/
|
|
3228
3218
|
type UsageRangeKey = "all" | "day" | "week" | "7d" | "30d" | "90d";
|
|
3229
|
-
/** 活动计数(与 session-rdb `./usage` 的回报结构镜像):轮次 / 步骤 / 用户输入 / 工具调用。 */
|
|
3230
3219
|
interface UsageActivityTotals {
|
|
3231
3220
|
turns: number;
|
|
3232
3221
|
steps: number;
|
|
3233
3222
|
userInputs: number;
|
|
3234
3223
|
toolCalls: number;
|
|
3235
3224
|
}
|
|
3236
|
-
/** 一段用量合计:token 用量 + 活动计数。 */
|
|
3237
3225
|
interface UsageTotals extends UsageActivityTotals {
|
|
3238
3226
|
inputTokens: number;
|
|
3239
3227
|
outputTokens: number;
|
|
@@ -3241,7 +3229,6 @@ interface UsageTotals extends UsageActivityTotals {
|
|
|
3241
3229
|
reasoningTokens: number;
|
|
3242
3230
|
totalTokens: number;
|
|
3243
3231
|
}
|
|
3244
|
-
/** 一天 × 一个模型 × 是否子代理 的用量桶:活动计数没有模型归属,只有 token 用量。 */
|
|
3245
3232
|
interface UsageBucket {
|
|
3246
3233
|
day: string;
|
|
3247
3234
|
provider: string | null;
|
|
@@ -3253,37 +3240,30 @@ interface UsageBucket {
|
|
|
3253
3240
|
reasoningTokens: number;
|
|
3254
3241
|
totalTokens: number;
|
|
3255
3242
|
}
|
|
3256
|
-
/** 一条会话的用量行。 */
|
|
3257
3243
|
interface UsageSessionRow extends UsageTotals {
|
|
3258
3244
|
sessionId: string;
|
|
3259
3245
|
title: string | null;
|
|
3260
3246
|
subagent: boolean;
|
|
3261
3247
|
archived: boolean;
|
|
3262
3248
|
}
|
|
3263
|
-
/** 一次统计请求的回报:总览 + subagent 拆分 + 桶 + 会话行。 */
|
|
3264
3249
|
interface SessionUsageReport {
|
|
3265
3250
|
totals: UsageTotals;
|
|
3266
3251
|
subagent: UsageTotals;
|
|
3267
|
-
/** 只被人类会话引用的部分。 */
|
|
3268
3252
|
human: UsageTotals;
|
|
3269
3253
|
buckets: UsageBucket[];
|
|
3270
3254
|
sessions: UsageSessionRow[];
|
|
3271
3255
|
}
|
|
3272
|
-
/** 页面之外的服务面:归档状态与列表刷新都归它们的既有 owner。 */
|
|
3273
3256
|
interface ConversationManagerPorts {
|
|
3274
3257
|
archiveSession(sessionId: SessionId): Promise<void>;
|
|
3275
3258
|
unarchiveSession(sessionId: SessionId): Promise<void>;
|
|
3276
3259
|
refresh(): Promise<void>;
|
|
3277
3260
|
}
|
|
3278
|
-
/** GC 一次执行的回报。 */
|
|
3279
3261
|
interface ConversationManagerGcResult {
|
|
3280
3262
|
orphanSessions: number;
|
|
3281
3263
|
orphanEvents: number;
|
|
3282
3264
|
stoppedAgents: number;
|
|
3283
3265
|
}
|
|
3284
|
-
/** 页面从注入面拿到的动作(属性语法:页面解构后直接调用,不绑 this)。 */
|
|
3285
3266
|
interface ConversationManagerFace {
|
|
3286
|
-
/** 管理面自己的列表:完整语料(含归档),标题与最后活动时间随行给出;搜索与分页都在后端。 */
|
|
3287
3267
|
listRows: (query?: SessionRowsQuery) => Promise<SessionRowsPage>;
|
|
3288
3268
|
archive: (sessionId: SessionId) => Promise<void>;
|
|
3289
3269
|
unarchive: (sessionId: SessionId) => Promise<void>;
|
|
@@ -3293,12 +3273,10 @@ interface ConversationManagerFace {
|
|
|
3293
3273
|
collectGarbage: () => Promise<ConversationManagerGcResult>;
|
|
3294
3274
|
loadUsage: (range: UsageRangeKey) => Promise<SessionUsageReport>;
|
|
3295
3275
|
}
|
|
3296
|
-
/** 带 host 错误码的请求失败:页面据此选本地化文案。 */
|
|
3297
3276
|
declare class ConversationManagerRequestError extends Error {
|
|
3298
3277
|
readonly code: string | undefined;
|
|
3299
3278
|
constructor(message: string, code: string | undefined);
|
|
3300
3279
|
}
|
|
3301
|
-
/** 页面的动作:host 交互收在这里,页面只见数据与回调。 */
|
|
3302
3280
|
declare class ConversationManagerController {
|
|
3303
3281
|
private readonly ports;
|
|
3304
3282
|
readonly face: ConversationManagerFace;
|
|
@@ -3308,19 +3286,13 @@ declare class ConversationManagerController {
|
|
|
3308
3286
|
private exportZip;
|
|
3309
3287
|
private importZip;
|
|
3310
3288
|
private collectGarbage;
|
|
3311
|
-
/**
|
|
3312
|
-
* 用量统计:host 侧聚合,前端各维度本地折叠。
|
|
3313
|
-
* @param range - 时间范围语义键(`all` 不限、`day`/`week` 自然日/周、其余最近 N 天)。
|
|
3314
|
-
*/
|
|
3315
3289
|
private loadUsage;
|
|
3316
3290
|
}
|
|
3317
3291
|
//#endregion
|
|
3318
3292
|
//#region src/client/ConversationManagerIcon.d.ts
|
|
3319
|
-
/** nav 图标只画字形:尺寸由 nav 行给,选中态由 nav 行的按钮承担。 */
|
|
3320
3293
|
type ConversationManagerIconProps = PropsRuntime<"sidebar.panellist">;
|
|
3321
3294
|
//#endregion
|
|
3322
3295
|
//#region src/client/ConversationManagerPage.d.ts
|
|
3323
|
-
/** 页面 props:main 座位的运行时份额 + 本包字典 + 注入的动作。 */
|
|
3324
3296
|
type ConversationManagerPageProps = PropsRuntime<"main"> & PropsLocale<"conversationManager"> & InjectFace<ConversationManagerFace>;
|
|
3325
3297
|
//#endregion
|
|
3326
3298
|
//#region src/client/index.d.ts
|
|
@@ -3329,13 +3301,9 @@ declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
|
3329
3301
|
conversationManager: ConversationManagerKey;
|
|
3330
3302
|
}
|
|
3331
3303
|
}
|
|
3332
|
-
/** 本包字典的 namespace。 */
|
|
3333
3304
|
declare const NS = "conversationManager";
|
|
3334
|
-
/** nav 行与主面板共用的 id。 */
|
|
3335
3305
|
declare const PANEL_ID: MainPanelId;
|
|
3336
|
-
/** nav 行的位置:紧邻官方 Plugins 行(order 0)。 */
|
|
3337
3306
|
declare const PANEL_ORDER = 1;
|
|
3338
|
-
/** 页面用到的服务。 */
|
|
3339
3307
|
declare const inject: string[];
|
|
3340
3308
|
declare function apply(ctx: Context): void;
|
|
3341
3309
|
//#endregion
|
package/dist/client.d.mts
CHANGED
|
@@ -3,8 +3,6 @@ import { Context, Service } from "@deepseek-ai/cordis";
|
|
|
3
3
|
import { MainPanelId } from "@deepseek-ai/dsh-client-ui-layout/client";
|
|
4
4
|
import { InjectFace, PropsLocale, PropsRuntime } from "@deepseek-ai/dsh-client-ui-slots";
|
|
5
5
|
//#region src/client/locales.d.ts
|
|
6
|
-
/** 「对话管理」页面的文案字典。 */
|
|
7
|
-
/** 简体中文是 key 真源。 */
|
|
8
6
|
declare const zh: {
|
|
9
7
|
panel: string;
|
|
10
8
|
title: string;
|
|
@@ -79,7 +77,6 @@ declare const zh: {
|
|
|
79
77
|
"time.months": string;
|
|
80
78
|
"time.years": string;
|
|
81
79
|
};
|
|
82
|
-
/** 「对话管理」页面字典的 key 集合。 */
|
|
83
80
|
type ConversationManagerKey = keyof typeof zh;
|
|
84
81
|
//#endregion
|
|
85
82
|
//#region ../../../vendor/deepseek-harness/packages/core/scope/lib/types/index.d.ts
|
|
@@ -3196,7 +3193,6 @@ declare class SessionStore extends Service {
|
|
|
3196
3193
|
}
|
|
3197
3194
|
//#endregion
|
|
3198
3195
|
//#region src/client/controller.d.ts
|
|
3199
|
-
/** 一行会话:标题、origin、最后活动时间、归档标记与工作区归属都由 host 给出。 */
|
|
3200
3196
|
interface SessionRowRecord {
|
|
3201
3197
|
sessionId: string;
|
|
3202
3198
|
title: string | null;
|
|
@@ -3207,7 +3203,6 @@ interface SessionRowRecord {
|
|
|
3207
3203
|
archived: boolean;
|
|
3208
3204
|
workspace: string | null;
|
|
3209
3205
|
}
|
|
3210
|
-
/** 列表请求:搜索、子代理过滤与分页都在后端做(前端分页等于每次拉全量)。 */
|
|
3211
3206
|
interface SessionRowsQuery {
|
|
3212
3207
|
query?: string;
|
|
3213
3208
|
includeSubagents?: boolean;
|
|
@@ -3216,24 +3211,17 @@ interface SessionRowsQuery {
|
|
|
3216
3211
|
}
|
|
3217
3212
|
interface SessionRowsPage {
|
|
3218
3213
|
items: SessionRowRecord[];
|
|
3219
|
-
/** 过滤后的总数(分页前),页面据此算页数。 */
|
|
3220
3214
|
total: number;
|
|
3221
3215
|
page: number;
|
|
3222
3216
|
pageSize: number;
|
|
3223
3217
|
}
|
|
3224
|
-
/**
|
|
3225
|
-
* 时间范围的语义键(与 session-rdb `./usage` 的 `UsageRangeKey` 镜像):`day` / `week` 是本地自然日 /
|
|
3226
|
-
* 自然周,`7d` / `30d` / `90d` 是最近 N 个自然日(含今天)——边界由 host 按本地时区算,客户端只传语义。
|
|
3227
|
-
*/
|
|
3228
3218
|
type UsageRangeKey = "all" | "day" | "week" | "7d" | "30d" | "90d";
|
|
3229
|
-
/** 活动计数(与 session-rdb `./usage` 的回报结构镜像):轮次 / 步骤 / 用户输入 / 工具调用。 */
|
|
3230
3219
|
interface UsageActivityTotals {
|
|
3231
3220
|
turns: number;
|
|
3232
3221
|
steps: number;
|
|
3233
3222
|
userInputs: number;
|
|
3234
3223
|
toolCalls: number;
|
|
3235
3224
|
}
|
|
3236
|
-
/** 一段用量合计:token 用量 + 活动计数。 */
|
|
3237
3225
|
interface UsageTotals extends UsageActivityTotals {
|
|
3238
3226
|
inputTokens: number;
|
|
3239
3227
|
outputTokens: number;
|
|
@@ -3241,7 +3229,6 @@ interface UsageTotals extends UsageActivityTotals {
|
|
|
3241
3229
|
reasoningTokens: number;
|
|
3242
3230
|
totalTokens: number;
|
|
3243
3231
|
}
|
|
3244
|
-
/** 一天 × 一个模型 × 是否子代理 的用量桶:活动计数没有模型归属,只有 token 用量。 */
|
|
3245
3232
|
interface UsageBucket {
|
|
3246
3233
|
day: string;
|
|
3247
3234
|
provider: string | null;
|
|
@@ -3253,37 +3240,30 @@ interface UsageBucket {
|
|
|
3253
3240
|
reasoningTokens: number;
|
|
3254
3241
|
totalTokens: number;
|
|
3255
3242
|
}
|
|
3256
|
-
/** 一条会话的用量行。 */
|
|
3257
3243
|
interface UsageSessionRow extends UsageTotals {
|
|
3258
3244
|
sessionId: string;
|
|
3259
3245
|
title: string | null;
|
|
3260
3246
|
subagent: boolean;
|
|
3261
3247
|
archived: boolean;
|
|
3262
3248
|
}
|
|
3263
|
-
/** 一次统计请求的回报:总览 + subagent 拆分 + 桶 + 会话行。 */
|
|
3264
3249
|
interface SessionUsageReport {
|
|
3265
3250
|
totals: UsageTotals;
|
|
3266
3251
|
subagent: UsageTotals;
|
|
3267
|
-
/** 只被人类会话引用的部分。 */
|
|
3268
3252
|
human: UsageTotals;
|
|
3269
3253
|
buckets: UsageBucket[];
|
|
3270
3254
|
sessions: UsageSessionRow[];
|
|
3271
3255
|
}
|
|
3272
|
-
/** 页面之外的服务面:归档状态与列表刷新都归它们的既有 owner。 */
|
|
3273
3256
|
interface ConversationManagerPorts {
|
|
3274
3257
|
archiveSession(sessionId: SessionId): Promise<void>;
|
|
3275
3258
|
unarchiveSession(sessionId: SessionId): Promise<void>;
|
|
3276
3259
|
refresh(): Promise<void>;
|
|
3277
3260
|
}
|
|
3278
|
-
/** GC 一次执行的回报。 */
|
|
3279
3261
|
interface ConversationManagerGcResult {
|
|
3280
3262
|
orphanSessions: number;
|
|
3281
3263
|
orphanEvents: number;
|
|
3282
3264
|
stoppedAgents: number;
|
|
3283
3265
|
}
|
|
3284
|
-
/** 页面从注入面拿到的动作(属性语法:页面解构后直接调用,不绑 this)。 */
|
|
3285
3266
|
interface ConversationManagerFace {
|
|
3286
|
-
/** 管理面自己的列表:完整语料(含归档),标题与最后活动时间随行给出;搜索与分页都在后端。 */
|
|
3287
3267
|
listRows: (query?: SessionRowsQuery) => Promise<SessionRowsPage>;
|
|
3288
3268
|
archive: (sessionId: SessionId) => Promise<void>;
|
|
3289
3269
|
unarchive: (sessionId: SessionId) => Promise<void>;
|
|
@@ -3293,12 +3273,10 @@ interface ConversationManagerFace {
|
|
|
3293
3273
|
collectGarbage: () => Promise<ConversationManagerGcResult>;
|
|
3294
3274
|
loadUsage: (range: UsageRangeKey) => Promise<SessionUsageReport>;
|
|
3295
3275
|
}
|
|
3296
|
-
/** 带 host 错误码的请求失败:页面据此选本地化文案。 */
|
|
3297
3276
|
declare class ConversationManagerRequestError extends Error {
|
|
3298
3277
|
readonly code: string | undefined;
|
|
3299
3278
|
constructor(message: string, code: string | undefined);
|
|
3300
3279
|
}
|
|
3301
|
-
/** 页面的动作:host 交互收在这里,页面只见数据与回调。 */
|
|
3302
3280
|
declare class ConversationManagerController {
|
|
3303
3281
|
private readonly ports;
|
|
3304
3282
|
readonly face: ConversationManagerFace;
|
|
@@ -3308,19 +3286,13 @@ declare class ConversationManagerController {
|
|
|
3308
3286
|
private exportZip;
|
|
3309
3287
|
private importZip;
|
|
3310
3288
|
private collectGarbage;
|
|
3311
|
-
/**
|
|
3312
|
-
* 用量统计:host 侧聚合,前端各维度本地折叠。
|
|
3313
|
-
* @param range - 时间范围语义键(`all` 不限、`day`/`week` 自然日/周、其余最近 N 天)。
|
|
3314
|
-
*/
|
|
3315
3289
|
private loadUsage;
|
|
3316
3290
|
}
|
|
3317
3291
|
//#endregion
|
|
3318
3292
|
//#region src/client/ConversationManagerIcon.d.ts
|
|
3319
|
-
/** nav 图标只画字形:尺寸由 nav 行给,选中态由 nav 行的按钮承担。 */
|
|
3320
3293
|
type ConversationManagerIconProps = PropsRuntime<"sidebar.panellist">;
|
|
3321
3294
|
//#endregion
|
|
3322
3295
|
//#region src/client/ConversationManagerPage.d.ts
|
|
3323
|
-
/** 页面 props:main 座位的运行时份额 + 本包字典 + 注入的动作。 */
|
|
3324
3296
|
type ConversationManagerPageProps = PropsRuntime<"main"> & PropsLocale<"conversationManager"> & InjectFace<ConversationManagerFace>;
|
|
3325
3297
|
//#endregion
|
|
3326
3298
|
//#region src/client/index.d.ts
|
|
@@ -3329,13 +3301,9 @@ declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
|
3329
3301
|
conversationManager: ConversationManagerKey;
|
|
3330
3302
|
}
|
|
3331
3303
|
}
|
|
3332
|
-
/** 本包字典的 namespace。 */
|
|
3333
3304
|
declare const NS = "conversationManager";
|
|
3334
|
-
/** nav 行与主面板共用的 id。 */
|
|
3335
3305
|
declare const PANEL_ID: MainPanelId;
|
|
3336
|
-
/** nav 行的位置:紧邻官方 Plugins 行(order 0)。 */
|
|
3337
3306
|
declare const PANEL_ORDER = 1;
|
|
3338
|
-
/** 页面用到的服务。 */
|
|
3339
3307
|
declare const inject: string[];
|
|
3340
3308
|
declare function apply(ctx: Context): void;
|
|
3341
3309
|
//#endregion
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "Conversation Manager",
|
|
4
|
-
"description": "Full-page conversation management panel (browser half):
|
|
4
|
+
"description": "Full-page conversation management panel (browser half): two tab levels over sessions and usage — search, paging, archive / unarchive / export / delete, import as a new session and orphan-data GC in the session view, token usage across all conversations in the usage view."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morlay/ui-conversation-manager",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "「对话管理」全局面板页(浏览器半):会话与统计两层 Tabs——会话视图给出全量会话(含已归档)的搜索、分页、归档 / 取消归档 / 导出 / 删除、导入为新会话与孤儿数据 GC,统计视图给出全部对话的 token 用量。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"archived-sessions",
|
|
7
7
|
"conversation-manager",
|
|
@@ -40,17 +40,17 @@
|
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
43
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.
|
|
44
|
-
"@deepseek-ai/dsh-client-locale": "^0.
|
|
45
|
-
"@deepseek-ai/dsh-client-ui-layout": "^0.
|
|
46
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.
|
|
47
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.
|
|
48
|
-
"@deepseek-ai/dsh-client-ui-session": "^0.
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "^0.
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-workspace": "^0.
|
|
52
|
-
"@deepseek-ai/dsh-session": "^0.
|
|
53
|
-
"@morlay/dsh-client-ui-primitives": "0.0.
|
|
43
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.2.0-rc.1",
|
|
44
|
+
"@deepseek-ai/dsh-client-locale": "^0.2.0-rc.1",
|
|
45
|
+
"@deepseek-ai/dsh-client-ui-layout": "^0.2.0-rc.1",
|
|
46
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.2.0-rc.1",
|
|
47
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.2.0-rc.1",
|
|
48
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.2.0-rc.1",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "^0.2.0-rc.1",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.1",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-workspace": "^0.2.0-rc.1",
|
|
52
|
+
"@deepseek-ai/dsh-session": "^0.2.0-rc.1",
|
|
53
|
+
"@morlay/dsh-client-ui-primitives": "0.0.5"
|
|
54
54
|
},
|
|
55
55
|
"dsh": {
|
|
56
56
|
"client": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IconArchiveOutlineRegular } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
2
2
|
import type { PropsRuntime } from "@deepseek-ai/dsh-client-ui-slots";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// nav 图标只画字形:尺寸由 nav 行给,选中态由 nav 行的按钮承担。
|
|
5
5
|
export type ConversationManagerIconProps = PropsRuntime<"sidebar.panellist">;
|
|
6
6
|
|
|
7
7
|
export function ConversationManagerIcon({ size }: ConversationManagerIconProps) {
|
|
@@ -5,10 +5,9 @@ const spin = styling.keyframes({
|
|
|
5
5
|
to: { transform: "rotate(360deg)" },
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* 压到 min-content(官方 Input 的 32px 高会被压成一行文字高)。 */
|
|
8
|
+
// 「对话管理」页面的样式表(官方 --dsw-* 变量 + 我们的 css-in-js 层):页面根是纵向 flex 且**自己不滚**,
|
|
9
|
+
// 页头(标题 / tab 条 / 导入 / 清理)与搜索行固定在顶部,只有 `scroll` 那层随内容滚;除滚动层外的项都
|
|
10
|
+
// `flex: none`,否则内容变长时会被压到 min-content(官方 Input 的 32px 高会被压成一行文字高)。
|
|
12
11
|
export const styles = {
|
|
13
12
|
page: {
|
|
14
13
|
display: "flex",
|
|
@@ -26,41 +26,41 @@ import {
|
|
|
26
26
|
import { formatCount, formatPercent, formatTokens } from "./format.ts";
|
|
27
27
|
import { styles } from "./ConversationManagerPage.styles.ts";
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
// 一页的行数(会话列表)。
|
|
30
30
|
const PAGE_SIZE = 20;
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// 统计视图按会话列出时的行数上限。
|
|
33
33
|
const USAGE_SESSION_ROWS = 20;
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
// 页面 props:main 座位的运行时份额 + 本包字典 + 注入的动作。
|
|
36
36
|
export type ConversationManagerPageProps = PropsRuntime<"main"> &
|
|
37
37
|
PropsLocale<"conversationManager"> &
|
|
38
38
|
InjectFace<ConversationManagerFace>;
|
|
39
39
|
|
|
40
40
|
type Translate = ConversationManagerPageProps["t"];
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
// GC 的三段状态:确认 → 运行(阻塞界面)→ 收尾。
|
|
43
43
|
type GcPhase = "idle" | "confirm" | "running";
|
|
44
44
|
|
|
45
45
|
interface ConversationRow {
|
|
46
46
|
id: SessionId;
|
|
47
47
|
title: string;
|
|
48
|
-
|
|
48
|
+
// 所属工作区标题;不在任何工作区里的会话用未分组文案。
|
|
49
49
|
workspace: string;
|
|
50
|
-
|
|
50
|
+
// 只有已归档的行允许取消归档与删除(host 侧同样守卫)。
|
|
51
51
|
archived: boolean;
|
|
52
|
-
|
|
52
|
+
// 子代理派生会话:默认不显示(既不可删也不可取消归档)。
|
|
53
53
|
subagent: boolean;
|
|
54
54
|
updatedAt: number;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
// 行上显示的紧凑相对时间。
|
|
58
58
|
function timeLabel(updatedAt: number, now: number, t: Translate): string {
|
|
59
59
|
const { unit, n } = relativeTime(updatedAt, now);
|
|
60
60
|
return unit === "now" ? t("time.now") : t(`time.${unit}`, { n });
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
// host 错误码 → 可读文案;没有码时保留原文。
|
|
64
64
|
function failureText(error: unknown, t: Translate): string {
|
|
65
65
|
const code = error instanceof ConversationManagerRequestError ? error.code : undefined;
|
|
66
66
|
if (code === "SESSION_NOT_ARCHIVED") return t("failure.notArchived");
|
|
@@ -538,18 +538,18 @@ export function ConversationManagerPage({
|
|
|
538
538
|
);
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
-
|
|
541
|
+
// 一层视图:会话列表 / 用量统计。
|
|
542
542
|
type PageView = "sessions" | "usage";
|
|
543
543
|
|
|
544
|
-
|
|
544
|
+
// 统计视图的二层维度。
|
|
545
545
|
type UsageTab = "overview" | "models" | "sessions";
|
|
546
546
|
|
|
547
|
-
|
|
547
|
+
// 时间范围选项:默认本日,其后是本周(周一起算)与最近 N 天,「全部」放在最后。
|
|
548
548
|
const USAGE_RANGES: readonly UsageRange[] = ["day", "week", "7d", "30d", "90d", "all"];
|
|
549
549
|
|
|
550
550
|
type UsageRange = UsageRangeKey;
|
|
551
551
|
|
|
552
|
-
|
|
552
|
+
// 范围按钮的文案。
|
|
553
553
|
function rangeLabel(range: UsageRange, t: Translate): string {
|
|
554
554
|
switch (range) {
|
|
555
555
|
case "all":
|
|
@@ -567,27 +567,25 @@ function rangeLabel(range: UsageRange, t: Translate): string {
|
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
|
|
570
|
+
// 一个用量单项:标签在上、值在下;单项之间横向排布。
|
|
571
571
|
interface UsageMetric {
|
|
572
572
|
key: string;
|
|
573
573
|
label: string;
|
|
574
|
-
|
|
574
|
+
// 原始值:token 数、计数,或百分点(`percent` 项)。
|
|
575
575
|
value: number;
|
|
576
576
|
kind: "tokens" | "count" | "percent";
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
-
|
|
579
|
+
// 缓存命中率(百分点):缓存输入占总输入(含缓存)的比例。
|
|
580
580
|
function cacheHitPercent(totals: UsageTotals): number {
|
|
581
581
|
const total = totals.inputTokens + totals.cacheReadTokens;
|
|
582
582
|
if (total <= 0) return 0;
|
|
583
583
|
return (totals.cacheReadTokens / total) * 100;
|
|
584
584
|
}
|
|
585
585
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
* 活动计数在按模型的行上不显示(事件没有模型归属,见 `withActivity`)。
|
|
590
|
-
*/
|
|
586
|
+
// 显示口径的单项:输入(含缓存输入)、缓存输入、缓存命中率、输出、推理,
|
|
587
|
+
// 以及活动计数(轮次 / 步骤 / 用户输入 / 工具调用)——没有合计项。
|
|
588
|
+
// 活动计数在按模型的行上不显示(事件没有模型归属,见 `withActivity`)。
|
|
591
589
|
function usageMetrics(
|
|
592
590
|
totals: UsageTotals,
|
|
593
591
|
t: Translate,
|
|
@@ -640,7 +638,7 @@ function usageMetrics(
|
|
|
640
638
|
];
|
|
641
639
|
}
|
|
642
640
|
|
|
643
|
-
|
|
641
|
+
// 折叠行的排序口径(不显示):输入(含缓存)+ 输出。
|
|
644
642
|
function sortWeight(totals: UsageTotals): number {
|
|
645
643
|
return totals.inputTokens + totals.cacheReadTokens + totals.outputTokens;
|
|
646
644
|
}
|
|
@@ -665,7 +663,7 @@ function emptyTotals(): UsageTotals {
|
|
|
665
663
|
};
|
|
666
664
|
}
|
|
667
665
|
|
|
668
|
-
|
|
666
|
+
// 把桶按一个键折叠成行并按用量降序(按天 / 按模型都用它)。
|
|
669
667
|
function foldBuckets(
|
|
670
668
|
buckets: readonly UsageBucket[],
|
|
671
669
|
keyOf: (bucket: UsageBucket) => string,
|
|
@@ -686,7 +684,7 @@ function foldBuckets(
|
|
|
686
684
|
);
|
|
687
685
|
}
|
|
688
686
|
|
|
689
|
-
|
|
687
|
+
// 一行用量:label 在上,下面是横向排布的单项。
|
|
690
688
|
function UsageRow({
|
|
691
689
|
rowKey,
|
|
692
690
|
label,
|
|
@@ -758,7 +756,7 @@ function UsageList({
|
|
|
758
756
|
);
|
|
759
757
|
}
|
|
760
758
|
|
|
761
|
-
|
|
759
|
+
// 总览:全部与「其中子代理」两行,与列表行同形。
|
|
762
760
|
function UsageOverview({ report, t }: { report: SessionUsageReport; t: Translate }): ReactNode {
|
|
763
761
|
return (
|
|
764
762
|
<ul {...styling.props(styles.usageList)}>
|
|
@@ -768,7 +766,7 @@ function UsageOverview({ report, t }: { report: SessionUsageReport; t: Translate
|
|
|
768
766
|
);
|
|
769
767
|
}
|
|
770
768
|
|
|
771
|
-
|
|
769
|
+
// 统计视图:时间范围过滤 + 二层维度切换(总览 / 按模型 / 按会话)。
|
|
772
770
|
function UsageView({
|
|
773
771
|
report,
|
|
774
772
|
loading,
|
package/src/client/controller.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import type { SessionId } from "@deepseek-ai/dsh-session";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
export const SESSION_DELETE_PATH = "/api/session
|
|
5
|
-
export const SESSION_IMPORT_PATH = "/api/session
|
|
6
|
-
export const SESSION_EXPORT_PATH = "/api/session
|
|
7
|
-
export const SESSION_GC_PATH = "/api/session
|
|
8
|
-
export const SESSION_USAGE_PATH = "/api/session
|
|
3
|
+
// host 侧的会话管理面路由:路径的 home 是 `@morlay/session-rdb` 的 `src/routes.ts`,改前缀要两边同步。
|
|
4
|
+
export const SESSION_DELETE_PATH = "/api/morlay/v1/session/delete";
|
|
5
|
+
export const SESSION_IMPORT_PATH = "/api/morlay/v1/session/import";
|
|
6
|
+
export const SESSION_EXPORT_PATH = "/api/morlay/v1/session/export";
|
|
7
|
+
export const SESSION_GC_PATH = "/api/morlay/v1/session/gc";
|
|
8
|
+
export const SESSION_USAGE_PATH = "/api/morlay/v1/session/usage";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
export const SESSION_ROWS_PATH = "/api/session.rows";
|
|
10
|
+
// 管理面的会话行(**完整语料,含归档**):我们自己的路由,与官方 `session/list` 分开——那条按部署策略
|
|
11
|
+
// 默认排除归档(给上游 UI 用),归档集的管理动作需要完整集合。
|
|
12
|
+
export const SESSION_ROWS_PATH = "/api/morlay/v1/session/rows";
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
// 一行会话:标题、origin、最后活动时间、归档标记与工作区归属都由 host 给出。
|
|
17
15
|
export interface SessionRowRecord {
|
|
18
16
|
sessionId: string;
|
|
19
17
|
title: string | null;
|
|
@@ -25,7 +23,7 @@ export interface SessionRowRecord {
|
|
|
25
23
|
workspace: string | null;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
// 列表请求:搜索、子代理过滤与分页都在后端做(前端分页等于每次拉全量)。
|
|
29
27
|
export interface SessionRowsQuery {
|
|
30
28
|
query?: string;
|
|
31
29
|
includeSubagents?: boolean;
|
|
@@ -35,19 +33,17 @@ export interface SessionRowsQuery {
|
|
|
35
33
|
|
|
36
34
|
export interface SessionRowsPage {
|
|
37
35
|
items: SessionRowRecord[];
|
|
38
|
-
|
|
36
|
+
// 过滤后的总数(分页前),页面据此算页数。
|
|
39
37
|
total: number;
|
|
40
38
|
page: number;
|
|
41
39
|
pageSize: number;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* 自然周,`7d` / `30d` / `90d` 是最近 N 个自然日(含今天)——边界由 host 按本地时区算,客户端只传语义。
|
|
47
|
-
*/
|
|
42
|
+
// 时间范围的语义键(与 session-rdb `./usage` 的 `UsageRangeKey` 镜像):`day` / `week` 是本地自然日 /
|
|
43
|
+
// 自然周,`7d` / `30d` / `90d` 是最近 N 个自然日(含今天)——边界由 host 按本地时区算,客户端只传语义。
|
|
48
44
|
export type UsageRangeKey = "all" | "day" | "week" | "7d" | "30d" | "90d";
|
|
49
45
|
|
|
50
|
-
|
|
46
|
+
// 活动计数(与 session-rdb `./usage` 的回报结构镜像):轮次 / 步骤 / 用户输入 / 工具调用。
|
|
51
47
|
export interface UsageActivityTotals {
|
|
52
48
|
turns: number;
|
|
53
49
|
steps: number;
|
|
@@ -55,7 +51,7 @@ export interface UsageActivityTotals {
|
|
|
55
51
|
toolCalls: number;
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
// 一段用量合计:token 用量 + 活动计数。
|
|
59
55
|
export interface UsageTotals extends UsageActivityTotals {
|
|
60
56
|
inputTokens: number;
|
|
61
57
|
outputTokens: number;
|
|
@@ -64,7 +60,7 @@ export interface UsageTotals extends UsageActivityTotals {
|
|
|
64
60
|
totalTokens: number;
|
|
65
61
|
}
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
// 一天 × 一个模型 × 是否子代理 的用量桶:活动计数没有模型归属,只有 token 用量。
|
|
68
64
|
export interface UsageBucket {
|
|
69
65
|
day: string;
|
|
70
66
|
provider: string | null;
|
|
@@ -77,7 +73,7 @@ export interface UsageBucket {
|
|
|
77
73
|
totalTokens: number;
|
|
78
74
|
}
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
// 一条会话的用量行。
|
|
81
77
|
export interface UsageSessionRow extends UsageTotals {
|
|
82
78
|
sessionId: string;
|
|
83
79
|
title: string | null;
|
|
@@ -85,33 +81,33 @@ export interface UsageSessionRow extends UsageTotals {
|
|
|
85
81
|
archived: boolean;
|
|
86
82
|
}
|
|
87
83
|
|
|
88
|
-
|
|
84
|
+
// 一次统计请求的回报:总览 + subagent 拆分 + 桶 + 会话行。
|
|
89
85
|
export interface SessionUsageReport {
|
|
90
86
|
totals: UsageTotals;
|
|
91
87
|
subagent: UsageTotals;
|
|
92
|
-
|
|
88
|
+
// 只被人类会话引用的部分。
|
|
93
89
|
human: UsageTotals;
|
|
94
90
|
buckets: UsageBucket[];
|
|
95
91
|
sessions: UsageSessionRow[];
|
|
96
92
|
}
|
|
97
93
|
|
|
98
|
-
|
|
94
|
+
// 页面之外的服务面:归档状态与列表刷新都归它们的既有 owner。
|
|
99
95
|
export interface ConversationManagerPorts {
|
|
100
96
|
archiveSession(sessionId: SessionId): Promise<void>;
|
|
101
97
|
unarchiveSession(sessionId: SessionId): Promise<void>;
|
|
102
98
|
refresh(): Promise<void>;
|
|
103
99
|
}
|
|
104
100
|
|
|
105
|
-
|
|
101
|
+
// GC 一次执行的回报。
|
|
106
102
|
export interface ConversationManagerGcResult {
|
|
107
103
|
orphanSessions: number;
|
|
108
104
|
orphanEvents: number;
|
|
109
105
|
stoppedAgents: number;
|
|
110
106
|
}
|
|
111
107
|
|
|
112
|
-
|
|
108
|
+
// 页面从注入面拿到的动作(属性语法:页面解构后直接调用,不绑 this)。
|
|
113
109
|
export interface ConversationManagerFace {
|
|
114
|
-
|
|
110
|
+
// 管理面自己的列表:完整语料(含归档),标题与最后活动时间随行给出;搜索与分页都在后端。
|
|
115
111
|
listRows: (query?: SessionRowsQuery) => Promise<SessionRowsPage>;
|
|
116
112
|
archive: (sessionId: SessionId) => Promise<void>;
|
|
117
113
|
unarchive: (sessionId: SessionId) => Promise<void>;
|
|
@@ -122,7 +118,7 @@ export interface ConversationManagerFace {
|
|
|
122
118
|
loadUsage: (range: UsageRangeKey) => Promise<SessionUsageReport>;
|
|
123
119
|
}
|
|
124
120
|
|
|
125
|
-
|
|
121
|
+
// 带 host 错误码的请求失败:页面据此选本地化文案。
|
|
126
122
|
export class ConversationManagerRequestError extends Error {
|
|
127
123
|
constructor(
|
|
128
124
|
message: string,
|
|
@@ -167,7 +163,7 @@ async function zipBase64(file: File): Promise<string> {
|
|
|
167
163
|
return comma < 0 ? dataUrl : dataUrl.slice(comma + 1);
|
|
168
164
|
}
|
|
169
165
|
|
|
170
|
-
|
|
166
|
+
// 页面的动作:host 交互收在这里,页面只见数据与回调。
|
|
171
167
|
export class ConversationManagerController {
|
|
172
168
|
readonly face: ConversationManagerFace;
|
|
173
169
|
|
|
@@ -242,10 +238,7 @@ export class ConversationManagerController {
|
|
|
242
238
|
};
|
|
243
239
|
}
|
|
244
240
|
|
|
245
|
-
|
|
246
|
-
* 用量统计:host 侧聚合,前端各维度本地折叠。
|
|
247
|
-
* @param range - 时间范围语义键(`all` 不限、`day`/`week` 自然日/周、其余最近 N 天)。
|
|
248
|
-
*/
|
|
241
|
+
// 用量统计:host 侧聚合,前端各维度本地折叠;`range` 是时间范围语义键(`all` / `day` / `week` / 最近 N 天)。
|
|
249
242
|
private async loadUsage(range: UsageRangeKey): Promise<SessionUsageReport> {
|
|
250
243
|
const value = await postJson(SESSION_USAGE_PATH, { range });
|
|
251
244
|
const report = value as unknown as Partial<SessionUsageReport>;
|
|
@@ -260,13 +253,13 @@ export class ConversationManagerController {
|
|
|
260
253
|
}
|
|
261
254
|
}
|
|
262
255
|
|
|
263
|
-
|
|
256
|
+
// 导出文件名优先取 host 给的 Content-Disposition。
|
|
264
257
|
function filenameOf(disposition: string | null, sessionId: string): string {
|
|
265
258
|
const matched = disposition === null ? null : /filename="([^"]+)"/u.exec(disposition);
|
|
266
259
|
return matched?.[1] ?? `${sessionId}.zip`;
|
|
267
260
|
}
|
|
268
261
|
|
|
269
|
-
|
|
262
|
+
// 浏览器下载:blob URL + 一次性 anchor;URL 在下一轮事件循环回收。
|
|
270
263
|
function downloadBlob(blob: Blob, filename: string): void {
|
|
271
264
|
const url = URL.createObjectURL(blob);
|
|
272
265
|
const anchor = document.createElement("a");
|
package/src/client/format.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// token 数字的紧凑显示:表格里以总量为主,明细用 K/M/B 压缩。
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// 一位小数;三位数以上不留小数。
|
|
4
4
|
function trim(value: number): string {
|
|
5
5
|
return value >= 100 ? String(Math.round(value)) : value.toFixed(1).replace(/\.0$/u, "");
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
/** @param value - token 数(非负整数)。 @returns 紧凑文本,例如 `1.2K`、`44.2B`。 */
|
|
9
8
|
export function formatTokens(value: number): string {
|
|
10
9
|
if (!Number.isFinite(value) || value <= 0) return "0";
|
|
11
10
|
if (value < 1_000) return String(Math.round(value));
|
|
@@ -14,13 +13,11 @@ export function formatTokens(value: number): string {
|
|
|
14
13
|
return `${trim(value / 1_000_000_000)}B`;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
/** @param value - 计数(轮次 / 步骤 / 用户输入 / 工具调用)。 @returns 千分位原文,例如 `1,024`。 */
|
|
18
16
|
export function formatCount(value: number): string {
|
|
19
17
|
if (!Number.isFinite(value) || value <= 0) return "0";
|
|
20
18
|
return Math.round(value).toLocaleString("en-US");
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
/** @param value - 百分点(0~100)。 @returns 紧凑百分比,例如 `98.4%`、`100%`。 */
|
|
24
21
|
export function formatPercent(value: number): string {
|
|
25
22
|
if (!Number.isFinite(value) || value <= 0) return "0%";
|
|
26
23
|
const rounded = value >= 100 ? Math.round(value) : Math.round(value * 10) / 10;
|
package/src/client/index.ts
CHANGED
|
@@ -26,16 +26,16 @@ declare module "@deepseek-ai/dsh-client-ui-slots" {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
// 本包字典的 namespace。
|
|
30
30
|
export const NS = "conversationManager";
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// nav 行与主面板共用的 id。
|
|
33
33
|
export const PANEL_ID = "conversations" as MainPanelId;
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
// nav 行的位置:紧邻官方 Plugins 行(order 0)。
|
|
36
36
|
export const PANEL_ORDER = 1;
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
// 页面用到的服务。
|
|
39
39
|
export const inject = ["slots", "locale", "uiWorkspace", "sessions"];
|
|
40
40
|
|
|
41
41
|
export function apply(ctx: ClientContext): void {
|
package/src/client/locales.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// 「对话管理」页面的文案字典。
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// 简体中文是 key 真源。
|
|
4
4
|
export const zh = {
|
|
5
5
|
panel: "对话管理",
|
|
6
6
|
title: "对话管理",
|
|
@@ -77,10 +77,10 @@ export const zh = {
|
|
|
77
77
|
"time.years": "{n}年",
|
|
78
78
|
} satisfies Record<string, string>;
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
// 「对话管理」页面字典的 key 集合。
|
|
81
81
|
export type ConversationManagerKey = keyof typeof zh;
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
// 英文对照表。
|
|
84
84
|
export const en = {
|
|
85
85
|
panel: "Conversations",
|
|
86
86
|
title: "Conversations",
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
// 「对话管理」页面的 host 半:没有 host 面(页面只读已有服务与路由)。
|
|
2
2
|
export function apply(): void {}
|