Skip to content

Commit 7222fc0

Browse files
committed
fix(app): terminal resize
1 parent 17bdb5d commit 7222fc0

3 files changed

Lines changed: 84 additions & 22 deletions

File tree

packages/app/src/components/terminal.tsx

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export const Terminal = (props: TerminalProps) => {
9191
}
9292

9393
const getTerminalColors = (): TerminalColors => {
94-
const mode = theme.mode()
94+
const mode = theme.mode() === "dark" ? "dark" : "light"
9595
const fallback = DEFAULT_TERMINAL_COLORS[mode]
9696
const currentTheme = theme.themes()[theme.themeId()]
9797
if (!currentTheme) return fallback
@@ -186,9 +186,23 @@ export const Terminal = (props: TerminalProps) => {
186186
}
187187
ws = socket
188188

189+
const restore = typeof local.pty.buffer === "string" ? local.pty.buffer : ""
190+
const restoreSize =
191+
restore &&
192+
typeof local.pty.cols === "number" &&
193+
Number.isSafeInteger(local.pty.cols) &&
194+
local.pty.cols > 0 &&
195+
typeof local.pty.rows === "number" &&
196+
Number.isSafeInteger(local.pty.rows) &&
197+
local.pty.rows > 0
198+
? { cols: local.pty.cols, rows: local.pty.rows }
199+
: undefined
200+
189201
const t = new mod.Terminal({
190202
cursorBlink: true,
191203
cursorStyle: "bar",
204+
cols: restoreSize?.cols,
205+
rows: restoreSize?.rows,
192206
fontSize: 14,
193207
fontFamily: monoFontFamily(settings.appearance.font()),
194208
allowTransparency: false,
@@ -277,19 +291,29 @@ export const Terminal = (props: TerminalProps) => {
277291

278292
focusTerminal()
279293

280-
fit.fit()
294+
const startResize = () => {
295+
fit.observeResize()
296+
handleResize = () => fit.fit()
297+
window.addEventListener("resize", handleResize)
298+
cleanups.push(() => window.removeEventListener("resize", handleResize))
299+
}
281300

282-
if (local.pty.buffer) {
283-
t.write(local.pty.buffer, () => {
284-
if (local.pty.scrollY) t.scrollToLine(local.pty.scrollY)
301+
if (restore && restoreSize) {
302+
t.write(restore, () => {
303+
fit.fit()
304+
if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY)
305+
startResize()
285306
})
307+
} else {
308+
fit.fit()
309+
if (restore) {
310+
t.write(restore, () => {
311+
if (typeof local.pty.scrollY === "number") t.scrollToLine(local.pty.scrollY)
312+
})
313+
}
314+
startResize()
286315
}
287316

288-
fit.observeResize()
289-
handleResize = () => fit.fit()
290-
window.addEventListener("resize", handleResize)
291-
cleanups.push(() => window.removeEventListener("resize", handleResize))
292-
293317
const onResize = t.onResize(async (size) => {
294318
if (socket.readyState === WebSocket.OPEN) {
295319
await sdk.client.pty

packages/app/src/context/terminal.tsx

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createSimpleContext } from "@opencode-ai/ui/context"
33
import { batch, createEffect, createMemo, createRoot, onCleanup } from "solid-js"
44
import { useParams } from "@solidjs/router"
55
import { useSDK } from "./sdk"
6-
import { Persist, persisted } from "@/utils/persist"
6+
import { Persist, persisted, removePersisted } from "@/utils/persist"
77

88
export type LocalPTY = {
99
id: string
@@ -35,6 +35,28 @@ type TerminalCacheEntry = {
3535
dispose: VoidFunction
3636
}
3737

38+
const caches = new Set<Map<string, TerminalCacheEntry>>()
39+
40+
export function clearWorkspaceTerminals(dir: string, sessionIDs?: string[]) {
41+
const key = getWorkspaceTerminalCacheKey(dir)
42+
for (const cache of caches) {
43+
const entry = cache.get(key)
44+
entry?.value.clear()
45+
}
46+
47+
removePersisted(Persist.workspace(dir, "terminal"))
48+
49+
const legacy = new Set(getLegacyTerminalStorageKeys(dir))
50+
for (const id of sessionIDs ?? []) {
51+
for (const key of getLegacyTerminalStorageKeys(dir, id)) {
52+
legacy.add(key)
53+
}
54+
}
55+
for (const key of legacy) {
56+
removePersisted({ key })
57+
}
58+
}
59+
3860
function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: string, legacySessionID?: string) {
3961
const legacy = getLegacyTerminalStorageKeys(dir, legacySessionID)
4062

@@ -56,7 +78,7 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
5678
}),
5779
)
5880

59-
const unsub = sdk.event.on("pty.exited", (event) => {
81+
const unsub = sdk.event.on("pty.exited", (event: { properties: { id: string } }) => {
6082
const id = event.properties.id
6183
if (!store.all.some((x) => x.id === id)) return
6284
batch(() => {
@@ -96,6 +118,12 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
96118
ready,
97119
all: createMemo(() => Object.values(store.all)),
98120
active: createMemo(() => store.active),
121+
clear() {
122+
batch(() => {
123+
setStore("active", undefined)
124+
setStore("all", [])
125+
})
126+
},
99127
new() {
100128
const existingTitleNumbers = new Set(
101129
store.all.flatMap((pty) => {
@@ -114,7 +142,7 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
114142

115143
sdk.client.pty
116144
.create({ title: `Terminal ${nextNumber}` })
117-
.then((pty) => {
145+
.then((pty: { data?: { id?: string; title?: string } }) => {
118146
const id = pty.data?.id
119147
if (!id) return
120148
const newTerminal = {
@@ -128,8 +156,8 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
128156
})
129157
setStore("active", id)
130158
})
131-
.catch((e) => {
132-
console.error("Failed to create terminal", e)
159+
.catch((error: unknown) => {
160+
console.error("Failed to create terminal", error)
133161
})
134162
},
135163
update(pty: Partial<LocalPTY> & { id: string }) {
@@ -143,8 +171,8 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
143171
title: pty.title,
144172
size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined,
145173
})
146-
.catch((e) => {
147-
console.error("Failed to update terminal", e)
174+
.catch((error: unknown) => {
175+
console.error("Failed to update terminal", error)
148176
})
149177
},
150178
async clone(id: string) {
@@ -155,8 +183,8 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
155183
.create({
156184
title: pty.title,
157185
})
158-
.catch((e) => {
159-
console.error("Failed to clone terminal", e)
186+
.catch((error: unknown) => {
187+
console.error("Failed to clone terminal", error)
160188
return undefined
161189
})
162190
if (!clone?.data) return
@@ -200,8 +228,8 @@ function createWorkspaceTerminalSession(sdk: ReturnType<typeof useSDK>, dir: str
200228
setStore("all", filtered)
201229
})
202230

203-
await sdk.client.pty.remove({ ptyID: id }).catch((e) => {
204-
console.error("Failed to close terminal", e)
231+
await sdk.client.pty.remove({ ptyID: id }).catch((error: unknown) => {
232+
console.error("Failed to close terminal", error)
205233
})
206234
},
207235
move(id: string, to: number) {
@@ -225,6 +253,9 @@ export const { use: useTerminal, provider: TerminalProvider } = createSimpleCont
225253
const params = useParams()
226254
const cache = new Map<string, TerminalCacheEntry>()
227255

256+
caches.add(cache)
257+
onCleanup(() => caches.delete(cache))
258+
228259
const disposeAll = () => {
229260
for (const entry of cache.values()) {
230261
entry.dispose()

packages/app/src/pages/layout.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import type { DragEvent } from "@thisbeyond/solid-dnd"
3434
import { useProviders } from "@/hooks/use-providers"
3535
import { showToast, Toast, toaster } from "@opencode-ai/ui/toast"
3636
import { useGlobalSDK } from "@/context/global-sdk"
37+
import { clearWorkspaceTerminals } from "@/context/terminal"
3738
import { useNotification } from "@/context/notification"
3839
import { usePermission } from "@/context/permission"
3940
import { Binary } from "@opencode-ai/util/binary"
@@ -1221,11 +1222,17 @@ export default function Layout(props: ParentProps) {
12211222
})
12221223
const dismiss = () => toaster.dismiss(progress)
12231224

1224-
const sessions = await globalSDK.client.session
1225+
const sessions: Session[] = await globalSDK.client.session
12251226
.list({ directory })
12261227
.then((x) => x.data ?? [])
12271228
.catch(() => [])
12281229

1230+
clearWorkspaceTerminals(
1231+
directory,
1232+
sessions.map((s) => s.id),
1233+
)
1234+
await globalSDK.client.instance.dispose({ directory }).catch(() => undefined)
1235+
12291236
const result = await globalSDK.client.worktree
12301237
.reset({ directory: root, worktreeResetInput: { directory } })
12311238
.then((x) => x.data)

0 commit comments

Comments
 (0)