import { For, Show, createEffect, createMemo, on } from "solid-js" import { createStore } from "solid-js/store" import { createMediaQuery } from "@solid-primitives/media" import { useParams } from "@solidjs/router" import { Tabs } from "@opencode-ai/ui/tabs" import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { IconButton } from "@opencode-ai/ui/icon-button" import { TooltipKeybind } from "@opencode-ai/ui/tooltip" import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter } from "@thisbeyond/solid-dnd" import type { DragEvent } from "@thisbeyond/solid-dnd" import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd" import { SortableTerminalTab } from "@/components/session" import { Terminal } from "@/components/terminal" import { useCommand } from "@/context/command" import { useLanguage } from "@/context/language" import { useLayout } from "@/context/layout" import { useTerminal, type LocalPTY } from "@/context/terminal" import { terminalTabLabel } from "@/pages/session/terminal-label" import { focusTerminalById } from "@/pages/session/helpers" import { getTerminalHandoff, setTerminalHandoff } from "@/pages/session/handoff" export function TerminalPanel() { const params = useParams() const layout = useLayout() const terminal = useTerminal() const language = useLanguage() const command = useCommand() const isDesktop = createMediaQuery("(min-width: 768px)") const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const view = createMemo(() => layout.view(sessionKey)) const opened = createMemo(() => view().terminal.opened()) const open = createMemo(() => isDesktop() && opened()) const height = createMemo(() => layout.terminal.height()) const close = () => view().terminal.close() const [store, setStore] = createStore({ autoCreated: false, activeDraggable: undefined as string | undefined, }) createEffect(() => { if (!opened()) { setStore("autoCreated", false) return } if (!terminal.ready() || terminal.all().length !== 0 || store.autoCreated) return terminal.new() setStore("autoCreated", true) }) createEffect( on( () => terminal.all().length, (count, prevCount) => { if (prevCount !== undefined && prevCount > 0 && count === 0) { if (opened()) view().terminal.toggle() } }, ), ) createEffect( on( () => terminal.active(), (activeId) => { if (!activeId || !open()) return if (document.activeElement instanceof HTMLElement) { document.activeElement.blur() } setTimeout(() => focusTerminalById(activeId), 0) }, ), ) createEffect(() => { const dir = params.dir if (!dir) return if (!terminal.ready()) return language.locale() setTerminalHandoff( dir, terminal.all().map((pty) => terminalTabLabel({ title: pty.title, titleNumber: pty.titleNumber, t: language.t as (key: string, vars?: Record) => string, }), ), ) }) const handoff = createMemo(() => { const dir = params.dir if (!dir) return [] return getTerminalHandoff(dir) ?? [] }) const all = createMemo(() => terminal.all()) const ids = createMemo(() => all().map((pty) => pty.id)) const byId = createMemo(() => new Map(all().map((pty) => [pty.id, pty]))) const handleTerminalDragStart = (event: unknown) => { const id = getDraggableId(event) if (!id) return setStore("activeDraggable", id) } const handleTerminalDragOver = (event: DragEvent) => { const { draggable, droppable } = event if (!draggable || !droppable) return const terminals = terminal.all() 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) { terminal.move(draggable.id.toString(), toIndex) } } const handleTerminalDragEnd = () => { setStore("activeDraggable", undefined) const activeId = terminal.active() if (!activeId) return setTimeout(() => { focusTerminalById(activeId) }, 0) } return (
{(title) => (
{title}
)}
{language.t("common.loading")} {language.t("common.loading.ellipsis")}
{language.t("terminal.loading")}
} >
terminal.open(id)} class="!h-auto !flex-none" > {(pty) => }
{(id) => ( {(pty) => (
terminal.clone(id)} />
)}
)}
{(draggedId) => ( {(t) => (
{terminalTabLabel({ title: t().title, titleNumber: t().titleNumber, t: language.t as (key: string, vars?: Record) => string, })}
)}
)}
) }