summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-11-21 17:41:55 -0500
committerDax Raad <[email protected]>2025-11-21 17:42:02 -0500
commitd8b60875c4f317cd0fa3b64edcc16af617592b3e (patch)
tree95f9efef529ef111fb3b1cfb333cbf74fa2f17b8
parent48949a6e9d71d9fa94661cd6406f892936aa4895 (diff)
downloadopencode-d8b60875c4f317cd0fa3b64edcc16af617592b3e.tar.gz
opencode-d8b60875c4f317cd0fa3b64edcc16af617592b3e.zip
tui: batch SDK events to reduce render churn and improve responsiveness
Intelligently queue events and flush them in 16ms batches. Events processed within 16ms of the last flush are batched together to reduce the number of store updates and re-renders, preventing jank when receiving rapid consecutive events. Events after a 16ms gap are processed immediately to avoid adding latency.
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/sdk.tsx38
1 files changed, 36 insertions, 2 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx
index 41f69f0d9..fa3b1c633 100644
--- a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx
@@ -1,7 +1,7 @@
import { createOpencodeClient, type Event } from "@opencode-ai/sdk"
import { createSimpleContext } from "./helper"
import { createGlobalEmitter } from "@solid-primitives/event-bus"
-import { onCleanup } from "solid-js"
+import { batch, onCleanup } from "solid-js"
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
name: "SDK",
@@ -17,8 +17,42 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
}>()
sdk.event.subscribe().then(async (events) => {
+ let queue: Event[] = []
+ let timer: Timer | undefined
+ let last = 0
+
+ const flush = () => {
+ if (queue.length === 0) return
+ const events = queue
+ queue = []
+ timer = undefined
+ last = Date.now()
+ // Batch all event emissions so all store updates result in a single render
+ batch(() => {
+ for (const event of events) {
+ emitter.emit(event.type, event)
+ }
+ })
+ }
+
for await (const event of events.stream) {
- emitter.emit(event.type, event)
+ queue.push(event)
+ const elapsed = Date.now() - last
+
+ if (timer) continue
+ // If we just flushed recently (within 16ms), batch this with future events
+ // Otherwise, process immediately to avoid latency
+ if (elapsed < 16) {
+ timer = setTimeout(flush, 16)
+ continue
+ }
+ flush()
+ }
+
+ // Flush any remaining events
+ if (timer) clearTimeout(timer)
+ if (queue.length > 0) {
+ flush()
}
})