import { For, Match, Show, Switch, createSignal, splitProps } from "solid-js" import { Tabs } from "@/ui/tabs" import { FileIcon, Icon, IconButton, Logo, Tooltip } from "@/ui" import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter, createSortable, useDragDropContext, } from "@thisbeyond/solid-dnd" import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" import type { LocalFile } from "@/context/local" import { Code } from "@/components/code" import PromptForm from "@/components/prompt-form" import { useLocal, useSDK, useSync } from "@/context" import { getFilename } from "@/utils" import type { JSX } from "solid-js" interface EditorPaneProps { layoutKey: string timelinePane: string onFileClick: (file: LocalFile) => void onOpenModelSelect: () => void onInputRefChange: (element: HTMLTextAreaElement | null) => void } export default function EditorPane(props: EditorPaneProps): JSX.Element { const [localProps] = splitProps(props, [ "layoutKey", "timelinePane", "onFileClick", "onOpenModelSelect", "onInputRefChange", ]) const local = useLocal() const sdk = useSDK() const sync = useSync() const [activeItem, setActiveItem] = createSignal(undefined) const navigateChange = (dir: 1 | -1) => { const active = local.file.active() if (!active) return const current = local.file.changeIndex(active.path) const next = current === undefined ? (dir === 1 ? 0 : -1) : current + dir local.file.setChangeIndex(active.path, next) } const handleTabChange = (path: string) => { local.file.open(path) } const handleTabClose = (file: LocalFile) => { local.file.close(file.path) } const handlePromptSubmit = async (prompt: string) => { const existingSession = local.layout.visible(localProps.layoutKey, localProps.timelinePane) ? local.session.active() : undefined let session = existingSession if (!session) { const created = await sdk.session.create() session = created.data ?? undefined } if (!session) return local.session.setActive(session.id) local.layout.show(localProps.layoutKey, localProps.timelinePane) await sdk.session.prompt({ path: { id: session.id }, body: { agent: local.agent.current()!.name, model: { modelID: local.model.current()!.id, providerID: local.model.current()!.provider.id, }, parts: [ { type: "text", text: prompt, }, ...(local.context.active() ? [ { type: "file" as const, mime: "text/plain", url: `file://${local.context.active()!.absolute}`, filename: local.context.active()!.name, source: { type: "file" as const, text: { value: "@" + local.context.active()!.name, start: 0, end: 0, }, path: local.context.active()!.absolute, }, }, ] : []), ...local.context.all().flatMap((file) => [ { type: "file" as const, mime: "text/plain", url: `file://${sync.absolute(file.path)}${file.selection ? `?start=${file.selection.startLine}&end=${file.selection.endLine}` : ""}`, filename: getFilename(file.path), source: { type: "file" as const, text: { value: "@" + getFilename(file.path), start: 0, end: 0, }, path: sync.absolute(file.path), }, }, ]), ], }, }) } const handleDragStart = (event: unknown) => { const id = getDraggableId(event) if (!id) return setActiveItem(id) } const handleDragOver = (event: DragEvent) => { const { draggable, droppable } = event if (draggable && droppable) { const currentFiles = local.file.opened().map((file) => file.path) const fromIndex = currentFiles.indexOf(draggable.id.toString()) const toIndex = currentFiles.indexOf(droppable.id.toString()) if (fromIndex !== toIndex) { local.file.move(draggable.id.toString(), toIndex) } } } const handleDragEnd = () => { setActiveItem(undefined) } return (
file.path)}> {(file) => ( )}
{(() => { const activeFile = local.file.active()! const view = local.file.view(activeFile.path) return (
navigateChange(-1)}> navigateChange(1)}>
local.file.setView(activeFile.path, "raw")} > local.file.setView(activeFile.path, "diff-unified")} > local.file.setView(activeFile.path, "diff-split")} >
) })()}
local.layout.toggle(localProps.layoutKey, localProps.timelinePane)} >
{(file) => ( {(() => { const view = local.file.view(file.path) const showRaw = view === "raw" || !file.content?.diff const code = showRaw ? (file.content?.content ?? "") : (file.content?.diff ?? "") return })()} )}
{(() => { const id = activeItem() if (!id) return null const draggedFile = local.file.node(id) if (!draggedFile) return null return (
) })()}
localProps.onInputRefChange(element ?? null)} />
) } function TabVisual(props: { file: LocalFile }): JSX.Element { return (
{props.file.name} M A D
) } function SortableTab(props: { file: LocalFile onTabClick: (file: LocalFile) => void onTabClose: (file: LocalFile) => void }): JSX.Element { const sortable = createSortable(props.file.path) return ( // @ts-ignore
props.onTabClick(props.file)}> props.onTabClose(props.file)} >
) } function ConstrainDragYAxis(): JSX.Element { const context = useDragDropContext() if (!context) return <> const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context const transformer: Transformer = { id: "constrain-y-axis", order: 100, callback: (transform) => ({ ...transform, y: 0 }), } onDragStart((event) => { const id = getDraggableId(event) if (!id) return addTransformer("draggables", id, transformer) }) onDragEnd((event) => { const id = getDraggableId(event) if (!id) return removeTransformer("draggables", id, transformer.id) }) return <> } const getDraggableId = (event: unknown): string | undefined => { if (typeof event !== "object" || event === null) return undefined if (!("draggable" in event)) return undefined const draggable = (event as { draggable?: { id?: unknown } }).draggable if (!draggable) return undefined return typeof draggable.id === "string" ? draggable.id : undefined }