claude-code-best/src/context/promptOverlayContext.tsx
claude-code-best fcbc882232 chore: 清理 src 下 113 项未使用导入和死代码
删除未使用的文件(BuiltinStatusLine.tsx、4 个重复的 .ts stub)、
移除约 55 个文件中未使用的 React 导入、
清理约 50 处未使用的导入/变量/参数。
净减少 ~296 行代码,precheck 4077 测试全部通过。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 20:05:15 +08:00

84 lines
2.9 KiB
TypeScript

/**
* Portal for content that floats above the prompt so it escapes
* FullscreenLayout's bottom-slot `overflowY:hidden` clip.
*
* The clip is load-bearing (CC-668: tall pastes squash the ScrollBox
* without it), but floating overlays use `position:absolute
* bottom="100%"` to float above the prompt — and Ink's clip stack
* intersects ALL descendants, so they were clipped to ~1 row.
*
* Two channels:
* - `useSetPromptOverlay` — slash-command suggestion data (structured,
* written by PromptInputFooter)
* - `useSetPromptOverlayDialog` — arbitrary dialog node (e.g.
* AutoModeOptInDialog, written by PromptInput)
*
* FullscreenLayout reads both and renders them outside the clipped slot.
*
* Split into data/setter context pairs so writers never re-render on
* their own writes — the setter contexts are stable.
*/
import { createContext, type ReactNode, useContext, useEffect, useState } from 'react';
import type { SuggestionItem } from '../components/PromptInput/PromptInputFooterSuggestions.js';
export type PromptOverlayData = {
suggestions: SuggestionItem[];
selectedSuggestion: number;
maxColumnWidth?: number;
};
type Setter<T> = (d: T | null) => void;
const DataContext = createContext<PromptOverlayData | null>(null);
const SetContext = createContext<Setter<PromptOverlayData> | null>(null);
const DialogContext = createContext<ReactNode>(null);
const SetDialogContext = createContext<Setter<ReactNode> | null>(null);
export function PromptOverlayProvider({ children }: { children: ReactNode }): ReactNode {
const [data, setData] = useState<PromptOverlayData | null>(null);
const [dialog, setDialog] = useState<ReactNode>(null);
return (
<SetContext.Provider value={setData}>
<SetDialogContext.Provider value={setDialog}>
<DataContext.Provider value={data}>
<DialogContext.Provider value={dialog}>{children}</DialogContext.Provider>
</DataContext.Provider>
</SetDialogContext.Provider>
</SetContext.Provider>
);
}
export function usePromptOverlay(): PromptOverlayData | null {
return useContext(DataContext);
}
export function usePromptOverlayDialog(): ReactNode {
return useContext(DialogContext);
}
/**
* Register suggestion data for the floating overlay. Clears on unmount.
* No-op outside the provider (non-fullscreen renders inline instead).
*/
export function useSetPromptOverlay(data: PromptOverlayData | null): void {
const set = useContext(SetContext);
useEffect(() => {
if (!set) return;
set(data);
return () => set(null);
}, [set, data]);
}
/**
* Register a dialog node to float above the prompt. Clears on unmount.
* No-op outside the provider (non-fullscreen renders inline instead).
*/
export function useSetPromptOverlayDialog(node: ReactNode): void {
const set = useContext(SetDialogContext);
useEffect(() => {
if (!set) return;
set(node);
return () => set(null);
}, [set, node]);
}