diff options
| author | Dax <[email protected]> | 2025-09-15 03:28:08 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-09-15 03:28:08 -0400 |
| commit | 725104572e2b6d64dcfc145d4748124186427c7b (patch) | |
| tree | daf5b26437fd267bc41848e0578ed13d1b43bb52 /packages/app/src/components/file-tree.tsx | |
| parent | 4954edf8aeb5b8b395fc4f4e91b7fe36cfab212d (diff) | |
| download | opencode-725104572e2b6d64dcfc145d4748124186427c7b.tar.gz opencode-725104572e2b6d64dcfc145d4748124186427c7b.zip | |
feat: add desktop/web app package (#2606)
Co-authored-by: adamdotdevin <[email protected]>
Co-authored-by: Adam <[email protected]>
Co-authored-by: GitHub Action <[email protected]>
Diffstat (limited to 'packages/app/src/components/file-tree.tsx')
| -rw-r--r-- | packages/app/src/components/file-tree.tsx | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx new file mode 100644 index 000000000..a3c4f42df --- /dev/null +++ b/packages/app/src/components/file-tree.tsx @@ -0,0 +1,85 @@ +import { useLocal } from "@/context" +import type { LocalFile } from "@/context/local" +import { Collapsible, FileIcon, Tooltip } from "@/ui" +import { For, Match, Switch, Show, type ComponentProps, type ParentProps } from "solid-js" +import { Dynamic } from "solid-js/web" + +export default function FileTree(props: { + path: string + class?: string + nodeClass?: string + level?: number + onFileClick?: (file: LocalFile) => void +}) { + const local = useLocal() + const level = props.level ?? 0 + + const Node = (p: ParentProps & ComponentProps<"div"> & { node: LocalFile; as?: "div" | "button" }) => ( + <Dynamic + component={p.as ?? "div"} + classList={{ + "p-0.5 w-full flex items-center gap-x-2 hover:bg-background-panel cursor-pointer": true, + "bg-background-element": local.file.active()?.path === p.node.path, + [props.nodeClass ?? ""]: !!props.nodeClass, + }} + style={`padding-left: ${level * 10}px`} + {...p} + > + {p.children} + <span + classList={{ + "text-xs whitespace-nowrap truncate": true, + "text-text-muted/40": p.node.ignored, + "text-text-muted/80": !p.node.ignored, + "!text-text": local.file.active()?.path === p.node.path, + "!text-primary": local.file.changed(p.node.path), + }} + > + {p.node.name} + </span> + <Show when={local.file.changed(p.node.path)}> + <span class="ml-auto mr-1 w-1.5 h-1.5 rounded-full bg-primary/50 shrink-0" /> + </Show> + </Dynamic> + ) + + return ( + <div class={`flex flex-col ${props.class}`}> + <For each={local.file.children(props.path)}> + {(node) => ( + <Tooltip forceMount={false} openDelay={2000} value={node.path} placement="right"> + <Switch> + <Match when={node.type === "directory"}> + <Collapsible + forceMount={false} + open={local.file.node(node.path)?.expanded} + onOpenChange={(open) => (open ? local.file.expand(node.path) : local.file.collapse(node.path))} + > + <Collapsible.Trigger> + <Node node={node}> + <Collapsible.Arrow size={16} class="text-text-muted/60 ml-1" /> + <FileIcon + node={node} + expanded={local.file.node(node.path).expanded} + class="text-text-muted/60 -ml-1" + /> + </Node> + </Collapsible.Trigger> + <Collapsible.Content> + <FileTree path={node.path} level={level + 1} onFileClick={props.onFileClick} /> + </Collapsible.Content> + </Collapsible> + </Match> + <Match when={node.type === "file"}> + <Node node={node} as="button" onClick={() => props.onFileClick?.(node)}> + <div class="w-4 shrink-0" /> + <FileIcon node={node} class="text-primary" /> + </Node> + </Match> + </Switch> + </Tooltip> + )} + </For> + </div> + ) +} |
