返回 DeepSeek-Reasonix
useDockLauncherSpace.ts
根目录 / desktop / frontend / src / lib / useDockLauncherSpace.ts
1 // Measures the transcript surface the launcher card floats over and reports
2 // whether the card still fits. The card yields the space entirely below
3 // HIDE_BELOW_WIDTH so it never crowds the chat.
4 //
5 // The mode is mirrored onto <html> so siblings OUTSIDE the transcript surface
6 // (the footer/composer) reserve the same right inset via plain CSS — no
7 // App-level re-render on every resize tick.
8 import { useEffect, useState } from "react";
9 import type { RefObject } from "react";
10 import type { SpaceMode } from "./launcherCardState";
11
12 // Space priority for the docked launcher: the panel yields FIRST. The panel
13 // only appears when the surface is at least 1010px wide (history keeps ~770px
14 // of the 800px comfort target while the panel is docked); below that the
15 // panel hides and the history stretches/compresses to fill the whole window.
16 const HIDE_BELOW_WIDTH = 1010;
17
18 export function useDockLauncherSpace(
19 rootRef: RefObject<HTMLElement | null>,
20 onSpaceModeChange?: (mode: SpaceMode) => void,
21 ): SpaceMode {
22 const [spaceMode, setSpaceMode] = useState<SpaceMode>("full");
23
24 useEffect(() => {
25 const host = rootRef.current?.parentElement;
26 if (!host || typeof ResizeObserver === "undefined") return;
27 const observer = new ResizeObserver((entries) => {
28 const width = entries[0]?.contentRect.width ?? 0;
29 const next = width < HIDE_BELOW_WIDTH ? "hidden" : "full";
30 setSpaceMode(next);
31 onSpaceModeChange?.(next);
32 });
33 observer.observe(host);
34 return () => observer.disconnect();
35 }, [rootRef, onSpaceModeChange]);
36
37 useEffect(() => {
38 document.documentElement.dataset.dockLauncher = spaceMode;
39 return () => {
40 delete document.documentElement.dataset.dockLauncher;
41 };
42 }, [spaceMode]);
43
44 return spaceMode;
45 }
46
46 lines TYPESCRIPT