From ff4414bb152acfddb5c0eb073c38bedc1df4ae14 Mon Sep 17 00:00:00 2001
From: Adam <2363879+adamdotdevin@users.noreply.github.com>
Date: Thu, 12 Feb 2026 09:49:14 -0600
Subject: chore: refactor packages/app files (#13236)

Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Frank <frank@anoma.ly>
---
 packages/app/src/components/terminal.tsx | 192 +++++++++++++++++--------------
 1 file changed, 104 insertions(+), 88 deletions(-)

(limited to 'packages/app/src/components/terminal.tsx')

diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx
index 09c04db40..f6bb0b48a 100644
--- a/packages/app/src/components/terminal.tsx
+++ b/packages/app/src/components/terminal.tsx
@@ -56,6 +56,91 @@ const DEFAULT_TERMINAL_COLORS: Record<"light" | "dark", TerminalColors> = {
   },
 }
 
+const debugTerminal = (...values: unknown[]) => {
+  if (!import.meta.env.DEV) return
+  console.debug("[terminal]", ...values)
+}
+
+const useTerminalUiBindings = (input: {
+  container: HTMLDivElement
+  term: Term
+  cleanups: VoidFunction[]
+  handlePointerDown: () => void
+  handleLinkClick: (event: MouseEvent) => void
+}) => {
+  const handleCopy = (event: ClipboardEvent) => {
+    const selection = input.term.getSelection()
+    if (!selection) return
+
+    const clipboard = event.clipboardData
+    if (!clipboard) return
+
+    event.preventDefault()
+    clipboard.setData("text/plain", selection)
+  }
+
+  const handlePaste = (event: ClipboardEvent) => {
+    const clipboard = event.clipboardData
+    const text = clipboard?.getData("text/plain") ?? clipboard?.getData("text") ?? ""
+    if (!text) return
+
+    event.preventDefault()
+    event.stopPropagation()
+    input.term.paste(text)
+  }
+
+  const handleTextareaFocus = () => {
+    input.term.options.cursorBlink = true
+  }
+  const handleTextareaBlur = () => {
+    input.term.options.cursorBlink = false
+  }
+
+  input.container.addEventListener("copy", handleCopy, true)
+  input.cleanups.push(() => input.container.removeEventListener("copy", handleCopy, true))
+
+  input.container.addEventListener("paste", handlePaste, true)
+  input.cleanups.push(() => input.container.removeEventListener("paste", handlePaste, true))
+
+  input.container.addEventListener("pointerdown", input.handlePointerDown)
+  input.cleanups.push(() => input.container.removeEventListener("pointerdown", input.handlePointerDown))
+
+  input.container.addEventListener("click", input.handleLinkClick, { capture: true })
+  input.cleanups.push(() => input.container.removeEventListener("click", input.handleLinkClick, { capture: true }))
+
+  input.term.textarea?.addEventListener("focus", handleTextareaFocus)
+  input.term.textarea?.addEventListener("blur", handleTextareaBlur)
+  input.cleanups.push(() => input.term.textarea?.removeEventListener("focus", handleTextareaFocus))
+  input.cleanups.push(() => input.term.textarea?.removeEventListener("blur", handleTextareaBlur))
+}
+
+const persistTerminal = (input: {
+  term: Term | undefined
+  addon: SerializeAddon | undefined
+  cursor: number
+  pty: LocalPTY
+  onCleanup?: (pty: LocalPTY) => void
+}) => {
+  if (!input.addon || !input.onCleanup || !input.term) return
+  const buffer = (() => {
+    try {
+      return input.addon.serialize()
+    } catch {
+      debugTerminal("failed to serialize terminal buffer")
+      return ""
+    }
+  })()
+
+  input.onCleanup({
+    ...input.pty,
+    buffer,
+    cursor: input.cursor,
+    rows: input.term.rows,
+    cols: input.term.cols,
+    scrollY: input.term.getViewportY(),
+  })
+}
+
 export const Terminal = (props: TerminalProps) => {
   const platform = usePlatform()
   const sdk = useSDK()
@@ -70,8 +155,6 @@ export const Terminal = (props: TerminalProps) => {
   let serializeAddon: SerializeAddon
   let fitAddon: FitAddon
   let handleResize: () => void
-  let handleTextareaFocus: () => void
-  let handleTextareaBlur: () => void
   let disposed = false
   const cleanups: VoidFunction[] = []
   const start =
@@ -84,12 +167,23 @@ export const Terminal = (props: TerminalProps) => {
     for (const fn of fns) {
       try {
         fn()
-      } catch {
-        // ignore
+      } catch (err) {
+        debugTerminal("cleanup failed", err)
       }
     }
   }
 
+  const pushSize = (cols: number, rows: number) => {
+    return sdk.client.pty
+      .update({
+        ptyID: local.pty.id,
+        size: { cols, rows },
+      })
+      .catch((err) => {
+        debugTerminal("failed to sync terminal size", err)
+      })
+  }
+
   const getTerminalColors = (): TerminalColors => {
     const mode = theme.mode() === "dark" ? "dark" : "light"
     const fallback = DEFAULT_TERMINAL_COLORS[mode]
@@ -219,27 +313,6 @@ export const Terminal = (props: TerminalProps) => {
       ghostty = g
       term = t
 
-      const handleCopy = (event: ClipboardEvent) => {
-        const selection = t.getSelection()
-        if (!selection) return
-
-        const clipboard = event.clipboardData
-        if (!clipboard) return
-
-        event.preventDefault()
-        clipboard.setData("text/plain", selection)
-      }
-
-      const handlePaste = (event: ClipboardEvent) => {
-        const clipboard = event.clipboardData
-        const text = clipboard?.getData("text/plain") ?? clipboard?.getData("text") ?? ""
-        if (!text) return
-
-        event.preventDefault()
-        event.stopPropagation()
-        t.paste(text)
-      }
-
       t.attachCustomKeyEventHandler((event) => {
         const key = event.key.toLowerCase()
 
@@ -255,12 +328,6 @@ export const Terminal = (props: TerminalProps) => {
         return matchKeybind(keybinds, event)
       })
 
-      container.addEventListener("copy", handleCopy, true)
-      cleanups.push(() => container.removeEventListener("copy", handleCopy, true))
-
-      container.addEventListener("paste", handlePaste, true)
-      cleanups.push(() => container.removeEventListener("paste", handlePaste, true))
-
       const fit = new mod.FitAddon()
       const serializer = new SerializeAddon()
       cleanups.push(() => disposeIfDisposable(fit))
@@ -270,24 +337,7 @@ export const Terminal = (props: TerminalProps) => {
       serializeAddon = serializer
 
       t.open(container)
-
-      container.addEventListener("pointerdown", handlePointerDown)
-      cleanups.push(() => container.removeEventListener("pointerdown", handlePointerDown))
-
-      container.addEventListener("click", handleLinkClick, { capture: true })
-      cleanups.push(() => container.removeEventListener("click", handleLinkClick, { capture: true }))
-
-      handleTextareaFocus = () => {
-        t.options.cursorBlink = true
-      }
-      handleTextareaBlur = () => {
-        t.options.cursorBlink = false
-      }
-
-      t.textarea?.addEventListener("focus", handleTextareaFocus)
-      t.textarea?.addEventListener("blur", handleTextareaBlur)
-      cleanups.push(() => t.textarea?.removeEventListener("focus", handleTextareaFocus))
-      cleanups.push(() => t.textarea?.removeEventListener("blur", handleTextareaBlur))
+      useTerminalUiBindings({ container, term: t, cleanups, handlePointerDown, handleLinkClick })
 
       focusTerminal()
 
@@ -316,15 +366,7 @@ export const Terminal = (props: TerminalProps) => {
 
       const onResize = t.onResize(async (size) => {
         if (socket.readyState === WebSocket.OPEN) {
-          await sdk.client.pty
-            .update({
-              ptyID: local.pty.id,
-              size: {
-                cols: size.cols,
-                rows: size.rows,
-              },
-            })
-            .catch(() => {})
+          await pushSize(size.cols, size.rows)
         }
       })
       cleanups.push(() => disposeIfDisposable(onResize))
@@ -346,15 +388,7 @@ export const Terminal = (props: TerminalProps) => {
 
       const handleOpen = () => {
         local.onConnect?.()
-        sdk.client.pty
-          .update({
-            ptyID: local.pty.id,
-            size: {
-              cols: t.cols,
-              rows: t.rows,
-            },
-          })
-          .catch(() => {})
+        void pushSize(t.cols, t.rows)
       }
       socket.addEventListener("open", handleOpen)
       cleanups.push(() => socket.removeEventListener("open", handleOpen))
@@ -374,8 +408,8 @@ export const Terminal = (props: TerminalProps) => {
             if (typeof next === "number" && Number.isSafeInteger(next) && next >= 0) {
               cursor = next
             }
-          } catch {
-            // ignore
+          } catch (err) {
+            debugTerminal("invalid websocket control frame", err)
           }
           return
         }
@@ -425,25 +459,7 @@ export const Terminal = (props: TerminalProps) => {
 
   onCleanup(() => {
     disposed = true
-    const t = term
-    if (serializeAddon && props.onCleanup && t) {
-      const buffer = (() => {
-        try {
-          return serializeAddon.serialize()
-        } catch {
-          return ""
-        }
-      })()
-      props.onCleanup({
-        ...local.pty,
-        buffer,
-        cursor,
-        rows: t.rows,
-        cols: t.cols,
-        scrollY: t.getViewportY(),
-      })
-    }
-
+    persistTerminal({ term, addon: serializeAddon, cursor, pty: local.pty, onCleanup: props.onCleanup })
     cleanup()
   })
 
-- 
cgit v1.2.3

