summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages
diff options
context:
space:
mode:
authorDaniel Polito <[email protected]>2026-01-16 13:51:02 -0300
committerGitHub <[email protected]>2026-01-16 10:51:02 -0600
commit88fd6a294b3ad88d50cb8e1853589ee4e68dc74e (patch)
tree1b187dab4b86528430498edc3ec3d3d4a243ea5f /packages/app/src/pages
parentea8ef37d50408203d2ef7ebbdb82bbd15bbf8461 (diff)
downloadopencode-88fd6a294b3ad88d50cb8e1853589ee4e68dc74e.tar.gz
opencode-88fd6a294b3ad88d50cb8e1853589ee4e68dc74e.zip
feat(desktop): Terminal Splits (#8767)
Diffstat (limited to 'packages/app/src/pages')
-rw-r--r--packages/app/src/pages/session.tsx46
1 files changed, 36 insertions, 10 deletions
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index ca5e73a9b..585156afa 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -26,6 +26,7 @@ import { useSync } from "@/context/sync"
import { useTerminal, type LocalPTY } from "@/context/terminal"
import { useLayout } from "@/context/layout"
import { Terminal } from "@/components/terminal"
+import { TerminalSplit } from "@/components/terminal-split"
import { checksum, base64Encode, base64Decode } from "@opencode-ai/util/encode"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { DialogSelectFile } from "@/components/dialog-select-file"
@@ -170,6 +171,7 @@ export default function Page() {
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
const tabs = createMemo(() => layout.tabs(sessionKey()))
const view = createMemo(() => layout.view(sessionKey()))
+ const activeTerminal = createMemo(() => terminal.active())
if (import.meta.env.DEV) {
createEffect(
@@ -380,7 +382,7 @@ export default function Page() {
createEffect(() => {
if (!view().terminal.opened()) return
if (!terminal.ready()) return
- if (terminal.all().length !== 0) return
+ if (terminal.tabs().length !== 0) return
terminal.new()
})
@@ -460,6 +462,30 @@ export default function Page() {
onSelect: () => terminal.new(),
},
{
+ id: "terminal.split.vertical",
+ title: "Split terminal right",
+ description: "Split the current terminal vertically",
+ category: "Terminal",
+ keybind: "mod+d",
+ disabled: !terminal.active(),
+ onSelect: () => {
+ const active = terminal.active()
+ if (active) terminal.split(active, "vertical")
+ },
+ },
+ {
+ id: "terminal.split.horizontal",
+ title: "Split terminal down",
+ description: "Split the current terminal horizontally",
+ category: "Terminal",
+ keybind: "mod+shift+d",
+ disabled: !terminal.active(),
+ onSelect: () => {
+ const active = terminal.active()
+ if (active) terminal.split(active, "horizontal")
+ },
+ },
+ {
id: "steps.toggle",
title: "Toggle steps",
description: "Show or hide steps for the current message",
@@ -707,7 +733,7 @@ export default function Page() {
const handleTerminalDragOver = (event: DragEvent) => {
const { draggable, droppable } = event
if (draggable && droppable) {
- const terminals = terminal.all()
+ const terminals = terminal.tabs()
const fromIndex = terminals.findIndex((t: LocalPTY) => t.id === draggable.id.toString())
const toIndex = terminals.findIndex((t: LocalPTY) => t.id === droppable.id.toString())
if (fromIndex !== -1 && toIndex !== -1 && fromIndex !== toIndex) {
@@ -1009,7 +1035,7 @@ export default function Page() {
createEffect(() => {
if (!terminal.ready()) return
- handoff.terminals = terminal.all().map((t) => t.title)
+ handoff.terminals = terminal.tabs().map((t) => t.title)
})
createEffect(() => {
@@ -1666,10 +1692,10 @@ export default function Page() {
>
<DragDropSensors />
<ConstrainDragYAxis />
- <Tabs variant="alt" value={terminal.active()} onChange={terminal.open}>
+ <Tabs variant="alt" value={activeTerminal()} onChange={terminal.open}>
<Tabs.List class="h-10">
- <SortableProvider ids={terminal.all().map((t: LocalPTY) => t.id)}>
- <For each={terminal.all()}>{(pty) => <SortableTerminalTab terminal={pty} />}</For>
+ <SortableProvider ids={terminal.tabs().map((t: LocalPTY) => t.id)}>
+ <For each={terminal.tabs()}>{(pty) => <SortableTerminalTab terminal={pty} />}</For>
</SortableProvider>
<div class="h-full flex items-center justify-center">
<TooltipKeybind
@@ -1681,10 +1707,10 @@ export default function Page() {
</TooltipKeybind>
</div>
</Tabs.List>
- <For each={terminal.all()}>
+ <For each={terminal.tabs()}>
{(pty) => (
- <Tabs.Content value={pty.id}>
- <Terminal pty={pty} onCleanup={terminal.update} onConnectError={() => terminal.clone(pty.id)} />
+ <Tabs.Content value={pty.id} class="h-[calc(100%-2.5rem)]">
+ <TerminalSplit tabId={pty.id} />
</Tabs.Content>
)}
</For>
@@ -1692,7 +1718,7 @@ export default function Page() {
<DragOverlay>
<Show when={store.activeTerminalDraggable}>
{(draggedId) => {
- const pty = createMemo(() => terminal.all().find((t: LocalPTY) => t.id === draggedId()))
+ const pty = createMemo(() => terminal.tabs().find((t: LocalPTY) => t.id === draggedId()))
return (
<Show when={pty()}>
{(t) => (