diff options
| author | Adam <[email protected]> | 2025-12-09 15:39:44 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-12-09 15:39:44 -0600 |
| commit | 2dad56c9a214052f55dc067d74436aaa887e3b9d (patch) | |
| tree | 33401067e94dc3b6cf02418813da673353dbc1f4 | |
| parent | 41d78c1ecc8b9fa5f5c8d4862fe5c08e699b481a (diff) | |
| download | opencode-2dad56c9a214052f55dc067d74436aaa887e3b9d.tar.gz opencode-2dad56c9a214052f55dc067d74436aaa887e3b9d.zip | |
wip(desktop): progress
| -rw-r--r-- | packages/desktop/src/pages/session.tsx | 120 |
1 files changed, 89 insertions, 31 deletions
diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 08e5f4f01..890401723 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -28,7 +28,7 @@ import { import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" import type { JSX } from "solid-js" import { useSync } from "@/context/sync" -import { useSession } from "@/context/session" +import { useSession, type LocalPTY } from "@/context/session" import { useLayout } from "@/context/layout" import { getDirectory, getFilename } from "@opencode-ai/util/path" import { Terminal } from "@/components/terminal" @@ -43,6 +43,7 @@ export default function Page() { clickTimer: undefined as number | undefined, fileSelectOpen: false, activeDraggable: undefined as string | undefined, + activeTerminalDraggable: undefined as string | undefined, }) let inputRef!: HTMLDivElement @@ -178,6 +179,49 @@ export default function Page() { setStore("activeDraggable", undefined) } + const handleTerminalDragStart = (event: unknown) => { + const id = getDraggableId(event) + if (!id) return + setStore("activeTerminalDraggable", id) + } + + const handleTerminalDragOver = (event: DragEvent) => { + const { draggable, droppable } = event + if (draggable && droppable) { + const terminals = session.terminal.all() + const fromIndex = terminals.findIndex((t) => t.id === draggable.id.toString()) + const toIndex = terminals.findIndex((t) => t.id === droppable.id.toString()) + if (fromIndex !== -1 && toIndex !== -1 && fromIndex !== toIndex) { + session.terminal.move(draggable.id.toString(), toIndex) + } + } + } + + const handleTerminalDragEnd = () => { + setStore("activeTerminalDraggable", undefined) + } + + const SortableTerminalTab = (props: { terminal: LocalPTY }): JSX.Element => { + const sortable = createSortable(props.terminal.id) + return ( + // @ts-ignore + <div use:sortable classList={{ "h-full": true, "opacity-0": sortable.isActiveDraggable }}> + <div class="relative h-full"> + <Tabs.Trigger + value={props.terminal.id} + closeButton={ + session.terminal.all().length > 1 && ( + <IconButton icon="close" variant="ghost" onClick={() => session.terminal.close(props.terminal.id)} /> + ) + } + > + {props.terminal.title} + </Tabs.Trigger> + </div> + </div> + ) + } + const FileVisual = (props: { file: LocalFile; active?: boolean }): JSX.Element => { return ( <div class="flex items-center gap-x-1.5"> @@ -618,40 +662,54 @@ export default function Page() { onResize={layout.terminal.resize} onCollapse={layout.terminal.close} /> - <Tabs variant="alt" value={session.terminal.active()} onChange={session.terminal.open}> - <Tabs.List class="h-10"> + <DragDropProvider + onDragStart={handleTerminalDragStart} + onDragEnd={handleTerminalDragEnd} + onDragOver={handleTerminalDragOver} + collisionDetector={closestCenter} + > + <DragDropSensors /> + <ConstrainDragYAxis /> + <Tabs variant="alt" value={session.terminal.active()} onChange={session.terminal.open}> + <Tabs.List class="h-10"> + <SortableProvider ids={session.terminal.all().map((t) => t.id)}> + <For each={session.terminal.all()}>{(terminal) => <SortableTerminalTab terminal={terminal} />}</For> + </SortableProvider> + <div class="h-full flex items-center justify-center"> + <Tooltip value="Open file" class="flex items-center"> + <IconButton icon="plus-small" variant="ghost" iconSize="large" onClick={session.terminal.new} /> + </Tooltip> + </div> + </Tabs.List> <For each={session.terminal.all()}> {(terminal) => ( - <Tabs.Trigger - value={terminal.id} - closeButton={ - session.terminal.all().length > 1 && ( - <IconButton icon="close" variant="ghost" onClick={() => session.terminal.close(terminal.id)} /> - ) - } - > - {terminal.title} - </Tabs.Trigger> + <Tabs.Content value={terminal.id}> + <Terminal + pty={terminal} + onCleanup={session.terminal.update} + onConnectError={() => session.terminal.clone(terminal.id)} + /> + </Tabs.Content> )} </For> - <div class="h-full flex items-center justify-center"> - <Tooltip value="Open file" class="flex items-center"> - <IconButton icon="plus-small" variant="ghost" iconSize="large" onClick={session.terminal.new} /> - </Tooltip> - </div> - </Tabs.List> - <For each={session.terminal.all()}> - {(terminal) => ( - <Tabs.Content value={terminal.id}> - <Terminal - pty={terminal} - onCleanup={session.terminal.update} - onConnectError={() => session.terminal.clone(terminal.id)} - /> - </Tabs.Content> - )} - </For> - </Tabs> + </Tabs> + <DragOverlay> + <Show when={store.activeTerminalDraggable}> + {(draggedId) => { + const terminal = createMemo(() => session.terminal.all().find((t) => t.id === draggedId())) + return ( + <Show when={terminal()}> + {(t) => ( + <div class="relative p-1 h-10 flex items-center bg-background-stronger text-14-regular"> + {t().title} + </div> + )} + </Show> + ) + }} + </Show> + </DragOverlay> + </DragDropProvider> </div> </Show> </div> |
