fix(app): working scrollback search + stop prompt duplication on focus
Search fixes: - TerminalView sets allowProposedApi (the search addon's match decorations use registerMarker/registerDecoration); without it findNext threw before firing results, so the counter was stuck at 0/0. - The search bar now renders inside the panel it targets (in the header) instead of a global top-right overlay, so it's obvious which panel is searched. - Search is anchored to the panel it was opened on (searchSurfaceId) and no longer follows focus — opening it in one panel and switching away no longer shows it open elsewhere. Prompt duplication: - The focus border was 1px when unfocused, 2px when focused; with border-box that resized the content on every focus switch, firing ResizeObserver -> fit -> PTY SIGWINCH and making zsh/powerlevel10k reprint its prompt. The border is now a constant 2px, color-only on focus. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+10
-6
@@ -3,7 +3,6 @@ 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";
|
||||
@@ -35,9 +34,10 @@ export function App() {
|
||||
const [health, setHealth] = useState<DaemonHealth | null>(null);
|
||||
const [connected, setConnected] = useState(false);
|
||||
const [focusedId, setFocusedId] = useState<string | null>(null);
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const [searchSurfaceId, setSearchSurfaceId] = useState<string | null>(null);
|
||||
const [searchNonce, setSearchNonce] = useState(0);
|
||||
const activeRef = useRef<string | null>(null);
|
||||
const effectiveFocusRef = useRef<string | null>(null);
|
||||
const wsRef = useRef<WorkspaceView[]>([]);
|
||||
activeRef.current = activeId;
|
||||
wsRef.current = workspaces;
|
||||
@@ -107,7 +107,11 @@ export function App() {
|
||||
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); }
|
||||
if (activeRef.current && effectiveFocusRef.current) {
|
||||
e.preventDefault();
|
||||
setSearchSurfaceId(effectiveFocusRef.current); // anchor to the focused panel
|
||||
setSearchNonce((n) => n + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
@@ -121,6 +125,7 @@ export function App() {
|
||||
const active = workspaces.find((w) => w.id === activeId) ?? null;
|
||||
const leaves = active ? leafIds(active.layout) : [];
|
||||
const effectiveFocus = focusedId && leaves.includes(focusedId) ? focusedId : leaves[0] ?? null;
|
||||
effectiveFocusRef.current = effectiveFocus;
|
||||
|
||||
function selectWorkspace(id: string) {
|
||||
setActiveId(id);
|
||||
@@ -135,13 +140,12 @@ export function App() {
|
||||
{sidebarOpen && <Sidebar groups={groups} workspaces={workspaces} activeId={activeId} onSelect={selectWorkspace} onNew={() => setWizard(true)} health={health} connected={connected} />}
|
||||
<div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
|
||||
{active && (
|
||||
<CenterToolbar selected="" onSelect={(p) => { if (active) void applyPreset(active.id, p, []); }} onOpenSearch={() => setSearchOpen(true)} />
|
||||
<CenterToolbar selected="" onSelect={(p) => { if (active) void applyPreset(active.id, p, []); }} onOpenSearch={() => { if (effectiveFocus) { setSearchSurfaceId(effectiveFocus); setSearchNonce((n) => n + 1); } }} />
|
||||
)}
|
||||
<div style={{ flex: 1, minHeight: 0, position: "relative" }}>
|
||||
{active
|
||||
? <LayoutEngine workspaceId={active.id} layout={active.layout} running={running} states={states} surfaces={active.surfaces} focusedId={effectiveFocus} onFocus={setFocusedId} zoomed={active.zoomed} />
|
||||
? <LayoutEngine workspaceId={active.id} layout={active.layout} running={running} states={states} surfaces={active.surfaces} focusedId={effectiveFocus} onFocus={setFocusedId} zoomed={active.zoomed} searchSurfaceId={searchSurfaceId} searchNonce={searchNonce} onCloseSearch={() => setSearchSurfaceId(null)} />
|
||||
: <div style={{ color: COLORS.textMuted, padding: 24 }}>No workspace — create one to begin.</div>}
|
||||
{searchOpen && active && <SearchBar surfaceId={effectiveFocus} reopenNonce={searchNonce} onClose={() => setSearchOpen(false)} />}
|
||||
</div>
|
||||
</div>
|
||||
{eventsOpen && (
|
||||
|
||||
Reference in New Issue
Block a user