From 52a502c38baeb138f38c08ce8a489d6b0323e868 Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Wed, 10 Jun 2026 12:35:41 +0700 Subject: [PATCH] =?UTF-8?q?feat(app):=20load=20xterm=20search=20addon=20+?= =?UTF-8?q?=20surface=E2=86=92addon=20registry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/package-lock.json | 7 +++++++ app/package.json | 1 + app/src/TerminalView.tsx | 9 ++++++++- app/src/searchRegistry.ts | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 app/src/searchRegistry.ts diff --git a/app/package-lock.json b/app/package-lock.json index 3ed1d59..f6be5ad 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -12,6 +12,7 @@ "@fontsource/inter": "^5.2.8", "@tauri-apps/api": "^2", "@tauri-apps/plugin-notification": "^2", + "@xterm/addon-search": "^0.16.0", "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", "lucide-react": "^1.17.0", @@ -1462,6 +1463,12 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@xterm/addon-search": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0.tgz", + "integrity": "sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA==", + "license": "MIT" + }, "node_modules/@xterm/addon-webgl": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.18.0.tgz", diff --git a/app/package.json b/app/package.json index 62e4672..e751bc6 100644 --- a/app/package.json +++ b/app/package.json @@ -13,6 +13,7 @@ "@fontsource/inter": "^5.2.8", "@tauri-apps/api": "^2", "@tauri-apps/plugin-notification": "^2", + "@xterm/addon-search": "^0.16.0", "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", "lucide-react": "^1.17.0", diff --git a/app/src/TerminalView.tsx b/app/src/TerminalView.tsx index fd070de..52e0116 100644 --- a/app/src/TerminalView.tsx +++ b/app/src/TerminalView.tsx @@ -1,7 +1,9 @@ import { useEffect, useRef } from "react"; import { Terminal } from "@xterm/xterm"; import { WebglAddon } from "@xterm/addon-webgl"; +import { SearchAddon } from "@xterm/addon-search"; import { attachSurface, detachSurface, sendInput, resizeSurface } from "./socketBridge"; +import { registerSearch, unregisterSearch } from "./searchRegistry"; const decoder = new TextDecoder(); const encoder = new TextEncoder(); @@ -11,7 +13,7 @@ export function TerminalView({ surfaceId }: { surfaceId: string }) { useEffect(() => { if (!ref.current) return; - const term = new Terminal({ fontFamily: "'JetBrains Mono Variable', 'JetBrains Mono', monospace", fontSize: 13, convertEol: false }); + const term = new Terminal({ fontFamily: "'JetBrains Mono Variable', 'JetBrains Mono', monospace", fontSize: 13, convertEol: false, scrollback: 10000 }); try { term.loadAddon(new WebglAddon()); } catch { @@ -19,6 +21,10 @@ export function TerminalView({ surfaceId }: { surfaceId: string }) { } term.open(ref.current); + const search = new SearchAddon(); + term.loadAddon(search); + registerSearch(surfaceId, search); + // Input → daemon. const inputDisposable = term.onData((data) => { void sendInput(surfaceId, encoder.encode(data)); @@ -42,6 +48,7 @@ export function TerminalView({ surfaceId }: { surfaceId: string }) { disposed = true; inputDisposable.dispose(); void detachSurface(surfaceId); + unregisterSearch(surfaceId); term.dispose(); }; }, [surfaceId]); diff --git a/app/src/searchRegistry.ts b/app/src/searchRegistry.ts new file mode 100644 index 0000000..4152278 --- /dev/null +++ b/app/src/searchRegistry.ts @@ -0,0 +1,17 @@ +import type { SearchAddon } from "@xterm/addon-search"; + +/** Maps a surfaceId to its terminal's SearchAddon so the search bar can reach + * the focused panel without prop-drilling through the layout tree. */ +const registry = new Map(); + +export function registerSearch(surfaceId: string, addon: SearchAddon): void { + registry.set(surfaceId, addon); +} + +export function unregisterSearch(surfaceId: string): void { + registry.delete(surfaceId); +} + +export function getSearch(surfaceId: string): SearchAddon | undefined { + return registry.get(surfaceId); +}