diff --git a/app/src/App.tsx b/app/src/App.tsx index 9c2ece2..bbf58cc 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -3,6 +3,7 @@ import { LayoutEngine } from "./LayoutEngine"; import { Sidebar } from "./Sidebar"; import { TopBar } from "./TopBar"; import { CenterToolbar } from "./CenterToolbar"; +import { SearchBar } from "./SearchBar"; import { Wizard } from "./Wizard"; import { EventCenter } from "./EventCenter"; import { maybeNotify } from "./notify"; @@ -24,6 +25,8 @@ export function App() { const [health, setHealth] = useState(null); const [connected, setConnected] = useState(false); const [focusedId, setFocusedId] = useState(null); + const [searchOpen, setSearchOpen] = useState(false); + const [searchNonce, setSearchNonce] = useState(0); const activeRef = useRef(null); const wsRef = useRef([]); activeRef.current = activeId; @@ -91,6 +94,16 @@ export function App() { return () => { void unlisten.then((f) => f()); void reconnect.then((f) => f()); }; }, [refresh, seedEvents, loadHealth]); + useEffect(() => { + const onKey = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "f") { + if (activeRef.current) { e.preventDefault(); setSearchOpen(true); setSearchNonce((n) => n + 1); } + } + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, []); + const unread = useMemo(() => events.filter((e) => !e.read).length, [events]); const active = workspaces.find((w) => w.id === activeId) ?? null; const leaves = active ? leafIds(active.layout) : []; @@ -109,12 +122,13 @@ export function App() { setWizard(true)} health={health} connected={connected} />
{active && ( - { if (active) void applyPreset(active.id, p, []); }} /> + { if (active) void applyPreset(active.id, p, []); }} onOpenSearch={() => setSearchOpen(true)} /> )} -
+
{active ? :
No workspace — create one to begin.
} + {searchOpen && active && setSearchOpen(false)} />}
{eventsOpen && ( diff --git a/app/src/CenterToolbar.tsx b/app/src/CenterToolbar.tsx index 699e5ca..f674cc6 100644 --- a/app/src/CenterToolbar.tsx +++ b/app/src/CenterToolbar.tsx @@ -3,13 +3,14 @@ import { COLORS, FONT } from "./theme"; import { PresetPicker } from "./PresetPicker"; /** Top-of-grid toolbar: layout presets on the left, scrollback search on the right (search is a mock). */ -export function CenterToolbar({ selected, onSelect }: { selected: string; onSelect: (id: string) => void }) { +export function CenterToolbar({ selected, onSelect, onOpenSearch }: { selected: string; onSelect: (id: string) => void; onOpenSearch: () => void }) { return (
void; +}) { + const [term, setTerm] = useState(""); + const [count, setCount] = useState({ index: -1, total: 0 }); + const inputRef = useRef(null); + + useEffect(() => { + inputRef.current?.focus(); + inputRef.current?.select(); + }, [reopenNonce]); + + useEffect(() => { + inputRef.current?.focus(); + if (!surfaceId) return; + const addon = getSearch(surfaceId); + if (!addon) return; + const sub = addon.onDidChangeResults((r) => + setCount({ index: r.resultIndex, total: r.resultCount }) + ); + return () => { + sub.dispose(); + addon.clearDecorations(); + }; + }, [surfaceId]); + + function run(forward: boolean) { + if (!surfaceId) return; + const addon = getSearch(surfaceId); + if (!addon || !term) { + addon?.clearDecorations(); + setCount({ index: -1, total: 0 }); + return; + } + if (forward) addon.findNext(term, SEARCH_OPTS); + else addon.findPrevious(term, SEARCH_OPTS); + } + + return ( +
+ setTerm(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + run(!e.shiftKey); + } else if (e.key === "Escape") { + e.preventDefault(); + onClose(); + } + }} + placeholder="Search scrollback" + style={{ + width: 200, + background: "transparent", + border: "none", + outline: "none", + color: COLORS.textPrimary, + fontFamily: FONT.ui, + fontSize: 13, + }} + /> + + {count.total > 0 ? `${count.index + 1}/${count.total}` : "0/0"} + + run(false)} + /> + run(true)} + /> + +
+ ); +} diff --git a/app/src/theme.ts b/app/src/theme.ts index 700b335..e1421ce 100644 --- a/app/src/theme.ts +++ b/app/src/theme.ts @@ -18,6 +18,7 @@ export const COLORS = { stDone: "#3FB950", stError: "#F4544E", stIdle: "#5A6573", + searchMatch: "#5A4A1F", } as const; export const FONT = {