From 30f0d3b3941f5c01826e9903cc4ebcb791704dec Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 9 Feb 2026 07:22:40 -0600 Subject: [PATCH] fix(app): update tab file contents on change --- packages/app/src/context/file.tsx | 4 +++ packages/app/src/context/file/watcher.test.ts | 31 +++++++++++++++++++ packages/app/src/context/file/watcher.ts | 3 +- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/app/src/context/file.tsx b/packages/app/src/context/file.tsx index 996ea2aaf..88b70cd41 100644 --- a/packages/app/src/context/file.tsx +++ b/packages/app/src/context/file.tsx @@ -7,6 +7,7 @@ import { getFilename } from "@opencode-ai/util/path" import { useSDK } from "./sdk" import { useSync } from "./sync" import { useLanguage } from "@/context/language" +import { useLayout } from "@/context/layout" import { createPathHelpers } from "./file/path" import { approxBytes, @@ -50,9 +51,11 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({ useSync() const params = useParams() const language = useLanguage() + const layout = useLayout() const scope = createMemo(() => sdk.directory) const path = createPathHelpers(scope) + const tabs = layout.tabs(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const inflight = new Map>() const [store, setStore] = createStore<{ @@ -183,6 +186,7 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({ invalidateFromWatcher(e.details, { normalize: path.normalize, hasFile: (file) => Boolean(store.file[file]), + isOpen: (file) => tabs.all().some((tab) => path.pathFromTab(tab) === file), loadFile: (file) => { void load(file, { force: true }) }, diff --git a/packages/app/src/context/file/watcher.test.ts b/packages/app/src/context/file/watcher.test.ts index 653e0aa75..9536b5253 100644 --- a/packages/app/src/context/file/watcher.test.ts +++ b/packages/app/src/context/file/watcher.test.ts @@ -27,6 +27,37 @@ describe("file watcher invalidation", () => { expect(refresh).toEqual(["src"]) }) + test("reloads files that are open in tabs", () => { + const loads: string[] = [] + + invalidateFromWatcher( + { + type: "file.watcher.updated", + properties: { + file: "src/open.ts", + event: "change", + }, + }, + { + normalize: (input) => input, + hasFile: () => false, + isOpen: (path) => path === "src/open.ts", + loadFile: (path) => loads.push(path), + node: () => ({ + path: "src/open.ts", + type: "file", + name: "open.ts", + absolute: "/repo/src/open.ts", + ignored: false, + }), + isDirLoaded: () => false, + refreshDir: () => {}, + }, + ) + + expect(loads).toEqual(["src/open.ts"]) + }) + test("refreshes only changed loaded directory nodes", () => { const refresh: string[] = [] diff --git a/packages/app/src/context/file/watcher.ts b/packages/app/src/context/file/watcher.ts index a3a98eae4..fbf719927 100644 --- a/packages/app/src/context/file/watcher.ts +++ b/packages/app/src/context/file/watcher.ts @@ -8,6 +8,7 @@ type WatcherEvent = { type WatcherOps = { normalize: (input: string) => string hasFile: (path: string) => boolean + isOpen?: (path: string) => boolean loadFile: (path: string) => void node: (path: string) => FileNode | undefined isDirLoaded: (path: string) => boolean @@ -27,7 +28,7 @@ export function invalidateFromWatcher(event: WatcherEvent, ops: WatcherOps) { if (!path) return if (path.startsWith(".git/")) return - if (ops.hasFile(path)) { + if (ops.hasFile(path) || ops.isOpen?.(path)) { ops.loadFile(path) }