diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx index 3439d366cee..791b33b4a9e 100644 --- a/packages/app/src/components/file-tree.tsx +++ b/packages/app/src/components/file-tree.tsx @@ -1,111 +1,121 @@ -import { useLocal, type LocalFile } from "@/context/local" +import { useFile } from "@/context/file" import { Collapsible } from "@opencode-ai/ui/collapsible" import { FileIcon } from "@opencode-ai/ui/file-icon" import { Tooltip } from "@opencode-ai/ui/tooltip" -import { For, Match, Switch, type ComponentProps, type ParentProps } from "solid-js" +import { createEffect, For, Match, splitProps, Switch, type ComponentProps, type ParentProps } from "solid-js" import { Dynamic } from "solid-js/web" +import type { FileNode } from "@opencode-ai/sdk/v2" export default function FileTree(props: { path: string class?: string nodeClass?: string level?: number - onFileClick?: (file: LocalFile) => void + onFileClick?: (file: FileNode) => void }) { - const local = useLocal() + const file = useFile() const level = props.level ?? 0 - const Node = (p: ParentProps & ComponentProps<"div"> & { node: LocalFile; as?: "div" | "button" }) => ( - { - const evt = e as globalThis.DragEvent - evt.dataTransfer!.effectAllowed = "copy" - evt.dataTransfer!.setData("text/plain", `file:${p.node.path}`) + createEffect(() => { + void file.tree.list(props.path) + }) - // Create custom drag image without margins - const dragImage = document.createElement("div") - dragImage.className = - "flex items-center gap-x-2 px-2 py-1 bg-background-element rounded-md border border-border-1" - dragImage.style.position = "absolute" - dragImage.style.top = "-1000px" + const Node = ( + p: ParentProps & + ComponentProps<"div"> & + ComponentProps<"button"> & { + node: FileNode + as?: "div" | "button" + }, + ) => { + const [local, rest] = splitProps(p, ["node", "as", "children", "class", "classList"]) + return ( + { + e.dataTransfer?.setData("text/plain", `file:${local.node.path}`) + e.dataTransfer?.setData("text/uri-list", `file://${local.node.path}`) + if (e.dataTransfer) e.dataTransfer.effectAllowed = "copy" - // Copy only the icon and text content without padding - const icon = e.currentTarget.querySelector("svg") - const text = e.currentTarget.querySelector("span") - if (icon && text) { - dragImage.innerHTML = icon.outerHTML + text.outerHTML - } + const dragImage = document.createElement("div") + dragImage.className = + "flex items-center gap-x-2 px-2 py-1 bg-surface-raised-base rounded-md border border-border-base text-12-regular text-text-strong" + dragImage.style.position = "absolute" + dragImage.style.top = "-1000px" - document.body.appendChild(dragImage) - evt.dataTransfer!.setDragImage(dragImage, 0, 12) - setTimeout(() => document.body.removeChild(dragImage), 0) - }} - {...p} - > - {p.children} - document.body.removeChild(dragImage), 0) }} + {...rest} > - {p.node.name} - - {/* */} - {/* */} - {/* */} - - ) + {local.children} + + {local.node.name} + + + ) + } return ( -
- - {(node) => ( - - - - (open ? local.file.expand(node.path) : local.file.collapse(node.path))} - > - - - - - - - - - - - - - props.onFileClick?.(node)}> -
- - - - - - )} +
+ + {(node) => { + const expanded = () => file.tree.state(node.path)?.expanded ?? false + return ( + + + + (open ? file.tree.expand(node.path) : file.tree.collapse(node.path))} + > + + + + + + + + + + + + + props.onFileClick?.(node)}> +
+ + + + + + ) + }}
) diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index e70e0790cbc..0020dc8032d 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -165,6 +165,25 @@ export function SessionHeader() {
+