@morlay/ui-conversation-manager 0.1.10 → 0.1.11-alpha.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.
@@ -1,4 +1,4 @@
1
- // 「对话管理」页面:已归档会话的搜索、取消归档、导出、删除,以及导入为新会话与孤儿数据 GC。
1
+ // 「对话管理」页面:已归档会话的搜索、取消归档、导出、删除,以及导入为新会话与整理数据(孤儿回收 + 统计重算)。
2
2
  // 数据面:会话行读**我们自己的**列表路由(注入面 listRows,含归档;搜索 / 子代理过滤 / 分页都在后端),
3
3
  // 动作面只读注入面。
4
4
  // 形态:页面框架与上游一级页面同款(960px 居中列 + 标题行 + 过滤 chip 行 + 搜索行,见
@@ -45,8 +45,8 @@ export type ConversationManagerPageProps = PropsRuntime<"main"> &
45
45
 
46
46
  type Translate = ConversationManagerPageProps["t"];
47
47
 
48
- // GC 的三段状态:确认 → 运行(阻塞界面)→ 收尾。
49
- type GcPhase = "idle" | "confirm" | "running";
48
+ // 整理数据的三段状态:确认 → 运行(阻塞界面)→ 收尾。
49
+ type OrganizePhase = "idle" | "confirm" | "running";
50
50
 
51
51
  interface ConversationRow {
52
52
  id: SessionId;
@@ -146,7 +146,7 @@ export function ConversationManagerPage({
146
146
  remove,
147
147
  exportZip,
148
148
  importZip,
149
- collectGarbage,
149
+ organizeData,
150
150
  loadUsage,
151
151
  }: ConversationManagerPageProps): ReactNode {
152
152
  // 数据面是**我们自己的**列表路由(完整语料,含归档):官方 `session/list` 按部署策略排除归档,
@@ -165,7 +165,7 @@ export function ConversationManagerPage({
165
165
  const [usageLoading, setUsageLoading] = useState(false);
166
166
  const [usageError, setUsageError] = useState<string | null>(null);
167
167
  const [confirming, setConfirming] = useState<ConversationRow | null>(null);
168
- const [gcPhase, setGcPhase] = useState<GcPhase>("idle");
168
+ const [organizePhase, setOrganizePhase] = useState<OrganizePhase>("idle");
169
169
  const [importing, setImporting] = useState(false);
170
170
  const [notice, setNotice] = useState<string | null>(null);
171
171
  const [failure, setFailure] = useState<string | null>(null);
@@ -236,18 +236,24 @@ export function ConversationManagerPage({
236
236
  );
237
237
  };
238
238
 
239
- const startGc = (): void => {
240
- setGcPhase("running");
239
+ const startOrganize = (): void => {
240
+ setOrganizePhase("running");
241
241
  setFailure(null);
242
242
  setNotice(null);
243
- void collectGarbage().then(
243
+ void organizeData().then(
244
244
  (result) => {
245
- setGcPhase("idle");
246
- setNotice(t("gc.done", { sessions: result.orphanSessions, events: result.orphanEvents }));
245
+ setOrganizePhase("idle");
246
+ setNotice(
247
+ t("organize.done", {
248
+ sessions: result.orphanSessions,
249
+ events: result.orphanEvents,
250
+ rebuilt: result.rebuiltSessions,
251
+ }),
252
+ );
247
253
  void reloadRows();
248
254
  },
249
255
  (error: unknown) => {
250
- setGcPhase("idle");
256
+ setOrganizePhase("idle");
251
257
  setFailure(failureText(error, t));
252
258
  },
253
259
  );
@@ -324,15 +330,15 @@ export function ConversationManagerPage({
324
330
  variant="outline"
325
331
  size="sm"
326
332
  data-action="gc"
327
- disabled={gcPhase !== "idle"}
328
- aria-label={t("gc.button")}
333
+ disabled={organizePhase !== "idle"}
334
+ aria-label={t("organize.button")}
329
335
  onClick={() => {
330
336
  setFailure(null);
331
337
  setNotice(null);
332
- setGcPhase("confirm");
338
+ setOrganizePhase("confirm");
333
339
  }}
334
340
  >
335
- {t("gc.button")}
341
+ {t("organize.button")}
336
342
  </Button>
337
343
  </div>
338
344
  </div>
@@ -619,35 +625,35 @@ export function ConversationManagerPage({
619
625
  }
620
626
  />
621
627
  <Modal
622
- open={gcPhase === "confirm"}
628
+ open={organizePhase === "confirm"}
623
629
  onClose={() => {
624
- setGcPhase("idle");
630
+ setOrganizePhase("idle");
625
631
  }}
626
- title={t("gc.title")}
632
+ title={t("organize.title")}
627
633
  closeLabel={t("close")}
628
- description={t("gc.description")}
634
+ description={t("organize.description")}
629
635
  footer={
630
636
  <>
631
637
  <Button
632
638
  variant="ghost"
633
639
  onClick={() => {
634
- setGcPhase("idle");
640
+ setOrganizePhase("idle");
635
641
  }}
636
642
  >
637
- {t("gc.cancel")}
643
+ {t("organize.cancel")}
638
644
  </Button>
639
- <Button variant="primary" onClick={startGc}>
640
- {t("gc.confirm")}
645
+ <Button variant="primary" onClick={startOrganize}>
646
+ {t("organize.confirm")}
641
647
  </Button>
642
648
  </>
643
649
  }
644
650
  />
645
651
  {/* 运行期不给关闭手段:没有关闭按钮,mask 与 Escape 都只走空 onClose。 */}
646
- <Modal open={gcPhase === "running"} onClose={() => {}} headless title={t("gc.title")}>
652
+ <Modal open={organizePhase === "running"} onClose={() => {}} headless title={t("organize.title")}>
647
653
  <Stack align="center" gap={14} pad="blocking">
648
- <Spinner label={t("gc.running")} />
654
+ <Spinner label={t("organize.running")} />
649
655
  <Text as="p" size="md">
650
- {t("gc.running")}
656
+ {t("organize.running")}
651
657
  </Text>
652
658
  </Stack>
653
659
  </Modal>
@@ -4,7 +4,7 @@ import type { SessionId } from "@deepseek-ai/dsh-session";
4
4
  export const SESSION_DELETE_PATH = "/api/morlay/v1/session/delete";
5
5
  export const SESSION_IMPORT_PATH = "/api/morlay/v1/session/import";
6
6
  export const SESSION_EXPORT_PATH = "/api/morlay/v1/session/export";
7
- export const SESSION_GC_PATH = "/api/morlay/v1/session/gc";
7
+ export const SESSION_ORGANIZE_PATH = "/api/morlay/v1/session/organize";
8
8
  export const SESSION_USAGE_PATH = "/api/morlay/v1/session/usage";
9
9
 
10
10
  // 管理面的会话行(**完整语料,含归档**):我们自己的路由,与官方 `session/list` 分开——那条按部署策略
@@ -98,10 +98,12 @@ export interface ConversationManagerPorts {
98
98
  refresh(): Promise<void>;
99
99
  }
100
100
 
101
- // GC 一次执行的回报。
102
- export interface ConversationManagerGcResult {
101
+ // 一次「整理数据」的回报。
102
+ export interface ConversationManagerOrganizeResult {
103
103
  orphanSessions: number;
104
104
  orphanEvents: number;
105
+ // 统计表全量重算涉及的会话数。
106
+ rebuiltSessions: number;
105
107
  stoppedAgents: number;
106
108
  }
107
109
 
@@ -114,7 +116,7 @@ export interface ConversationManagerFace {
114
116
  remove: (sessionId: SessionId) => Promise<void>;
115
117
  exportZip: (sessionId: SessionId) => Promise<void>;
116
118
  importZip: (file: File) => Promise<SessionId>;
117
- collectGarbage: () => Promise<ConversationManagerGcResult>;
119
+ organizeData: () => Promise<ConversationManagerOrganizeResult>;
118
120
  loadUsage: (range: UsageRangeKey) => Promise<SessionUsageReport>;
119
121
  }
120
122
 
@@ -175,7 +177,7 @@ export class ConversationManagerController {
175
177
  remove: (sessionId) => this.remove(sessionId),
176
178
  exportZip: (sessionId) => this.exportZip(sessionId),
177
179
  importZip: (file) => this.importZip(file),
178
- collectGarbage: () => this.collectGarbage(),
180
+ organizeData: () => this.organizeData(),
179
181
  loadUsage: (range) => this.loadUsage(range),
180
182
  };
181
183
  }
@@ -228,12 +230,13 @@ export class ConversationManagerController {
228
230
  return value["sessionId"] as SessionId;
229
231
  }
230
232
 
231
- private async collectGarbage(): Promise<ConversationManagerGcResult> {
232
- const value = await postJson(SESSION_GC_PATH, {});
233
+ private async organizeData(): Promise<ConversationManagerOrganizeResult> {
234
+ const value = await postJson(SESSION_ORGANIZE_PATH, {});
233
235
  await this.ports.refresh();
234
236
  return {
235
237
  orphanSessions: typeof value["orphanSessions"] === "number" ? value["orphanSessions"] : 0,
236
238
  orphanEvents: typeof value["orphanEvents"] === "number" ? value["orphanEvents"] : 0,
239
+ rebuiltSessions: typeof value["rebuiltSessions"] === "number" ? value["rebuiltSessions"] : 0,
237
240
  stoppedAgents: typeof value["stoppedAgents"] === "number" ? value["stoppedAgents"] : 0,
238
241
  };
239
242
  }
@@ -54,14 +54,15 @@ export const zh = {
54
54
  "page.previous": "上一页",
55
55
  "page.next": "下一页",
56
56
  "page.label": "第 {page} / {total} 页",
57
- "gc.button": "清理孤儿数据",
58
- "gc.title": "清理孤儿数据",
59
- "gc.description":
60
- "会先停止所有运行中的 Agent,期间界面不可操作;随后回收孤儿 subagent 会话与孤儿数据,并执行 VACUUM。",
61
- "gc.confirm": "开始清理",
62
- "gc.cancel": "取消",
63
- "gc.running": "正在清理,请稍候…",
64
- "gc.done": "已回收 {sessions} 条孤儿会话、{events} 行孤儿数据。",
57
+ "organize.button": "整理数据",
58
+ "organize.title": "整理数据",
59
+ "organize.description":
60
+ "会先停止所有运行中的 Agent,期间界面不可操作;随后回收孤儿 subagent 会话与孤儿数据,重算全部会话的统计数据(token 用量与活动计数),最后执行 VACUUM。",
61
+ "organize.confirm": "开始整理",
62
+ "organize.cancel": "取消",
63
+ "organize.running": "正在整理,请稍候…",
64
+ "organize.done":
65
+ "已回收 {sessions} 条孤儿会话、{events} 行孤儿数据,并重算 {rebuilt} 个会话的统计数据。",
65
66
  import: "导入对话",
66
67
  importing: "导入中…",
67
68
  imported: "已导入为新会话。",
@@ -139,14 +140,15 @@ export const en = {
139
140
  "page.previous": "Previous",
140
141
  "page.next": "Next",
141
142
  "page.label": "Page {page} / {total}",
142
- "gc.button": "Clean orphan data",
143
- "gc.title": "Clean orphan data",
144
- "gc.description":
145
- "Stops every running Agent first — the UI is blocked meanwhile — then reclaims orphan subagent sessions and orphan rows, and runs VACUUM.",
146
- "gc.confirm": "Start",
147
- "gc.cancel": "Cancel",
148
- "gc.running": "Cleaning, please wait…",
149
- "gc.done": "Reclaimed {sessions} orphan sessions and {events} orphan rows.",
143
+ "organize.button": "Organize data",
144
+ "organize.title": "Organize data",
145
+ "organize.description":
146
+ "Stops every running Agent first — the UI is blocked meanwhile — then reclaims orphan subagent sessions and orphan rows, recomputes usage and activity statistics for all sessions, and runs VACUUM.",
147
+ "organize.confirm": "Start",
148
+ "organize.cancel": "Cancel",
149
+ "organize.running": "Organizing, please wait…",
150
+ "organize.done":
151
+ "Reclaimed {sessions} orphan sessions and {events} orphan rows; recomputed statistics for {rebuilt} sessions.",
150
152
  import: "Import conversation",
151
153
  importing: "Importing…",
152
154
  imported: "Imported as a new session.",