返回 DeepSeek-Reasonix
useHistoryCommands.ts
根目录 / desktop / frontend / src / app-runtime / useHistoryCommands.ts
1 import type { Dispatch, SetStateAction } from "react";
2 import { app } from "../lib/bridge";
3 import { useCommittedCommand } from "../lib/useCommittedCommand";
4 import { sessionsForScope, type HistoryViewState } from "./historyViewProjection";
5 import { useOverlayStore } from "../store/overlays";
6 import type { SessionMeta } from "../lib/types";
7
8 export type HistoryCommandsInput = {
9 running: boolean;
10 setHistView: Dispatch<SetStateAction<HistoryViewState | null>>;
11 ports: {
12 listSessions(): Promise<SessionMeta[]>;
13 deleteSession(path: string): Promise<void>;
14 renameSession(path: string, title: string): Promise<void>;
15 openPage(page: { kind: "trash" }): void;
16 };
17 };
18
19 /**
20 * Owns the trash/history commands: opening the trash page, closing and
21 * refreshing the history view, deleting a history session (local filtering
22 * after the backend succeeds) and renaming one (topic or session path by
23 * availability). Deletes/renames are gated on a stopped runtime.
24 */
25 export function useHistoryCommands(input: HistoryCommandsInput) {
26 const { running, setHistView, ports } = input;
27 const setTransientOverlayDismissSignal = useOverlayStore((state) => state.setTransientOverlayDismissSignal);
28
29 const closeTransientOverlays = useCommittedCommand(() => {
30 setTransientOverlayDismissSignal((signal) => signal + 1);
31 });
32
33 const openTrash = useCommittedCommand(async () => {
34 closeTransientOverlays();
35 setHistView(null);
36 ports.openPage({ kind: "trash" });
37 });
38 const closeHistory = useCommittedCommand(() => {
39 closeTransientOverlays();
40 setHistView(null);
41 });
42 const refreshHistoryView = useCommittedCommand(async () => {
43 const sessions = await ports.listSessions().catch(() => null);
44 if (!sessions) return;
45 setHistView((cur) =>
46 cur === null || cur.kind !== "history"
47 ? cur
48 : cur.source === "scope"
49 ? { ...cur, sessions: sessionsForScope(sessions, cur.filter) }
50 : { ...cur, sessions },
51 );
52 });
53
54 const onDeleteSession = useCommittedCommand(async (path: string) => {
55 if (running) return;
56 try {
57 await ports.deleteSession(path);
58 } catch {
59 await refreshHistoryView();
60 return;
61 }
62 // Local state removal: filter the deleted session out of the current
63 // history view instead of re-fetching the full list from the backend.
64 setHistView((cur) =>
65 cur === null || cur.kind !== "history"
66 ? cur
67 : { ...cur, sessions: cur.sessions.filter((s) => s.path !== path) },
68 );
69 });
70 const onRenameHistorySession = useCommittedCommand(async (session: SessionMeta, title: string) => {
71 if (running) return;
72 await app.RenameSessionTarget({
73 ref: session.sessionId ? { hostId: session.hostId || "local", sessionId: session.sessionId } : undefined,
74 source: session.source,
75 sessionPath: session.path,
76 }, title);
77 const sessions = await ports.listSessions();
78 setHistView((cur) =>
79 cur === null
80 ? null
81 : cur.kind === "history"
82 ? { ...cur, sessions: cur.source === "scope" ? sessionsForScope(sessions, cur.filter) : sessions }
83 : cur,
84 );
85 });
86
87 return { openTrash, closeHistory, refreshHistoryView, onDeleteSession, onRenameHistorySession };
88 }
89
89 lines TYPESCRIPT