返回 DeepSeek-Reasonix
SessionStatusBanners.tsx
根目录 / desktop / frontend / src / app-shell / SessionStatusBanners.tsx
1 import { lazy, Suspense } from "react";
2 import type { Translator } from "../lib/i18n";
3 import { RemoteReclaimBanner } from "../components/RemoteReclaimBanner";
4 import { UpdateBanner } from "../components/UpdateBanner";
5 import type { HistoricalSessionBannerProps } from "../components/SessionTakeoverDialog";
6
7 const SessionRuntimeOverlays = lazy(() => import("../components/SessionTakeoverDialog").then((module) => ({ default: module.SessionRuntimeOverlays })));
8
9 export type SessionStatusBannersProps = {
10 t: Translator;
11 takenOver: boolean;
12 reclaimTabId: string;
13 reclaimBusyTabId: string | null;
14 onReclaim: (tabId: string) => void;
15 leaseBlocked: { tabId: string; message: string } | null;
16 startupError: string | undefined;
17 takeoverDialogTabId: string | null;
18 onOpenTakeover: (tabId: string) => void;
19 onCloseTakeover: () => void;
20 configWarnings: readonly string[];
21 onOpenConfigFile: () => void;
22 onReloadConfigFile: () => void;
23 onDismissConfigWarnings: () => void;
24 providerSetupNeeded: boolean;
25 needsOnboarding: boolean | null;
26 onConfigureProvider: () => void;
27 updateChecksEnabled: boolean;
28 onShowReleaseNotes: (latest: string) => void;
29 historical?: HistoricalSessionBannerProps;
30 };
31
32 /** Presentation-only banner stack between the topic bar and the main pane. */
33 export function SessionStatusBanners(props: SessionStatusBannersProps) {
34 const { t } = props;
35 return (
36 <>
37 {props.takenOver ? (
38 <RemoteReclaimBanner
39 tabId={props.reclaimTabId}
40 busyTabId={props.reclaimBusyTabId}
41 onReclaim={props.onReclaim}
42 />
43 ) : null}
44 {props.leaseBlocked ? (
45 <div className="banner banner--error">
46 <span className="banner__msg">{t("topbar.startupError", { msg: props.leaseBlocked.message })}</span>
47 <span className="banner__spacer" />
48 <button type="button" className="btn btn--small" onClick={() => props.onOpenTakeover(props.leaseBlocked!.tabId)}>
49 {t("takeover.bannerButton")}
50 </button>
51 </div>
52 ) : props.startupError ? (
53 <div className="banner banner--error">
54 <span className="banner__msg">{t("topbar.startupError", { msg: props.startupError })}</span>
55 </div>
56 ) : null}
57 {props.configWarnings.length > 0 && (
58 <div className="banner banner--warning banner--actionable">
59 <span className="banner__msg" title={props.configWarnings.join("\n")}>
60 {t("config.loadWarning", { msg: props.configWarnings[0] })}
61 </span>
62 <span className="banner__spacer" />
63 <button type="button" className="btn btn--small" onClick={props.onOpenConfigFile}>
64 {t("config.openConfig")}
65 </button>
66 <button type="button" className="btn btn--small" onClick={props.onReloadConfigFile}>
67 {t("config.reloadConfig")}
68 </button>
69 <span className="banner__hint">{t("config.doctorHint")}</span>
70 <button type="button" className="btn btn--small" onClick={props.onDismissConfigWarnings}>
71 {t("updater.dismiss")}
72 </button>
73 </div>
74 )}
75 {props.providerSetupNeeded && (
76 <div className="banner banner--warning banner--actionable">
77 <span className="banner__msg">{t("onboarding.inlinePrompt")}</span>
78 <span className="banner__spacer" />
79 <button type="button" className="btn btn--small" onClick={props.onConfigureProvider}>
80 {t("onboarding.configureProvider")}
81 </button>
82 </div>
83 )}
84 <UpdateBanner
85 enabled={props.updateChecksEnabled}
86 onShowReleaseNotes={props.onShowReleaseNotes}
87 />
88 {props.takeoverDialogTabId || props.historical ? <Suspense fallback={null}>
89 <SessionRuntimeOverlays takeoverTabId={props.takeoverDialogTabId} onCloseTakeover={props.onCloseTakeover} historical={props.historical} />
90 </Suspense> : null}
91 </>
92 );
93 }
94
94 lines Plain Text