diff options
| author | Adam <[email protected]> | 2025-10-16 14:53:44 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-10-16 14:53:44 -0500 |
| commit | 47d9e017657c4d265dea53bd86d727097a7ba282 (patch) | |
| tree | e278fb3983f13f6fa474228cf5031c3b4680f566 /packages/desktop/src/components | |
| parent | fc18fc8a08e703a54553e714344e638673b2d313 (diff) | |
| download | opencode-47d9e017657c4d265dea53bd86d727097a7ba282.tar.gz opencode-47d9e017657c4d265dea53bd86d727097a7ba282.zip | |
wip: css/ui and desktop work
Diffstat (limited to 'packages/desktop/src/components')
| -rw-r--r-- | packages/desktop/src/components/editor-pane.tsx | 237 | ||||
| -rw-r--r-- | packages/desktop/src/components/file-tree.tsx | 3 | ||||
| -rw-r--r-- | packages/desktop/src/components/prompt-form.tsx | 4 | ||||
| -rw-r--r-- | packages/desktop/src/components/resizeable-pane.tsx | 217 | ||||
| -rw-r--r-- | packages/desktop/src/components/select-dialog.tsx | 3 | ||||
| -rw-r--r-- | packages/desktop/src/components/select.tsx | 108 | ||||
| -rw-r--r-- | packages/desktop/src/components/session-list.tsx | 2 | ||||
| -rw-r--r-- | packages/desktop/src/components/session-timeline.tsx | 3 | ||||
| -rw-r--r-- | packages/desktop/src/components/sidebar-nav.tsx | 48 |
9 files changed, 124 insertions, 501 deletions
diff --git a/packages/desktop/src/components/editor-pane.tsx b/packages/desktop/src/components/editor-pane.tsx index 2f4c87e25..2741a6208 100644 --- a/packages/desktop/src/components/editor-pane.tsx +++ b/packages/desktop/src/components/editor-pane.tsx @@ -1,6 +1,7 @@ import { For, Match, Show, Switch, createSignal, splitProps } from "solid-js" -import { Tabs } from "@/ui/tabs" -import { FileIcon, Icon, IconButton, Logo, Tooltip } from "@/ui" +import { Tabs, Tooltip } from "@opencode-ai/ui" +import { Icon } from "@opencode-ai/ui" +import { FileIcon, IconButton } from "@/ui" import { DragDropProvider, DragDropSensors, @@ -64,127 +65,119 @@ export default function EditorPane(props: EditorPaneProps): JSX.Element { } return ( - <div class="relative flex h-full flex-col"> - <DragDropProvider - onDragStart={handleDragStart} - onDragEnd={handleDragEnd} - onDragOver={handleDragOver} - collisionDetector={closestCenter} - > - <DragDropSensors /> - <ConstrainDragYAxis /> - <Tabs - class="relative grow w-full flex flex-col h-full" - value={local.file.active()?.path} - onChange={handleTabChange} - > - <div class="sticky top-0 shrink-0 flex"> - <Tabs.List class="grow"> - <SortableProvider ids={local.file.opened().map((file) => file.path)}> - <For each={local.file.opened()}> - {(file) => ( - <SortableTab file={file} onTabClick={localProps.onFileClick} onTabClose={handleTabClose} /> - )} - </For> - </SortableProvider> - </Tabs.List> - <div class="shrink-0 h-full flex items-center gap-1 px-2 border-b border-border-subtle/40"> - <Show when={local.file.active() && local.file.active()!.content?.diff}> - {(() => { - const activeFile = local.file.active()! - const view = local.file.view(activeFile.path) - return ( - <div class="flex items-center gap-1"> - <Show when={view !== "raw"}> - <div class="mr-1 flex items-center gap-1"> - <Tooltip value="Previous change" placement="bottom"> - <IconButton size="xs" variant="ghost" onClick={() => navigateChange(-1)}> - <Icon name="arrow-up" size={14} /> - </IconButton> - </Tooltip> - <Tooltip value="Next change" placement="bottom"> - <IconButton size="xs" variant="ghost" onClick={() => navigateChange(1)}> - <Icon name="arrow-down" size={14} /> - </IconButton> - </Tooltip> - </div> - </Show> - <Tooltip value="Raw" placement="bottom"> - <IconButton - size="xs" - variant="ghost" - classList={{ - "text-text": view === "raw", - "text-text-muted/70": view !== "raw", - "bg-background-element": view === "raw", - }} - onClick={() => local.file.setView(activeFile.path, "raw")} - > - <Icon name="file-text" size={14} /> - </IconButton> - </Tooltip> - <Tooltip value="Unified diff" placement="bottom"> - <IconButton - size="xs" - variant="ghost" - classList={{ - "text-text": view === "diff-unified", - "text-text-muted/70": view !== "diff-unified", - "bg-background-element": view === "diff-unified", - }} - onClick={() => local.file.setView(activeFile.path, "diff-unified")} - > - <Icon name="checklist" size={14} /> - </IconButton> - </Tooltip> - <Tooltip value="Split diff" placement="bottom"> - <IconButton - size="xs" - variant="ghost" - classList={{ - "text-text": view === "diff-split", - "text-text-muted/70": view !== "diff-split", - "bg-background-element": view === "diff-split", - }} - onClick={() => local.file.setView(activeFile.path, "diff-split")} - > - <Icon name="columns" size={14} /> - </IconButton> - </Tooltip> - </div> - ) - })()} - </Show> - </div> + <DragDropProvider + onDragStart={handleDragStart} + onDragEnd={handleDragEnd} + onDragOver={handleDragOver} + collisionDetector={closestCenter} + > + <DragDropSensors /> + <ConstrainDragYAxis /> + <Tabs value={local.file.active()?.path} onChange={handleTabChange}> + <div class="sticky top-0 shrink-0 flex"> + <Tabs.List> + <SortableProvider ids={local.file.opened().map((file) => file.path)}> + <For each={local.file.opened()}> + {(file) => <SortableTab file={file} onTabClick={localProps.onFileClick} onTabClose={handleTabClose} />} + </For> + </SortableProvider> + </Tabs.List> + <div class="hidden shrink-0 h-full _flex items-center gap-1 px-2 border-b border-border-subtle/40"> + <Show when={local.file.active() && local.file.active()!.content?.diff}> + {(() => { + const activeFile = local.file.active()! + const view = local.file.view(activeFile.path) + return ( + <div class="flex items-center gap-1"> + <Show when={view !== "raw"}> + <div class="mr-1 flex items-center gap-1"> + <Tooltip value="Previous change" placement="bottom"> + <IconButton size="xs" variant="ghost" onClick={() => navigateChange(-1)}> + <Icon name="arrow-up" size={14} /> + </IconButton> + </Tooltip> + <Tooltip value="Next change" placement="bottom"> + <IconButton size="xs" variant="ghost" onClick={() => navigateChange(1)}> + <Icon name="arrow-down" size={14} /> + </IconButton> + </Tooltip> + </div> + </Show> + <Tooltip value="Raw" placement="bottom"> + <IconButton + size="xs" + variant="ghost" + classList={{ + "text-text": view === "raw", + "text-text-muted/70": view !== "raw", + "bg-background-element": view === "raw", + }} + onClick={() => local.file.setView(activeFile.path, "raw")} + > + <Icon name="file-text" size={14} /> + </IconButton> + </Tooltip> + <Tooltip value="Unified diff" placement="bottom"> + <IconButton + size="xs" + variant="ghost" + classList={{ + "text-text": view === "diff-unified", + "text-text-muted/70": view !== "diff-unified", + "bg-background-element": view === "diff-unified", + }} + onClick={() => local.file.setView(activeFile.path, "diff-unified")} + > + <Icon name="checklist" size={14} /> + </IconButton> + </Tooltip> + <Tooltip value="Split diff" placement="bottom"> + <IconButton + size="xs" + variant="ghost" + classList={{ + "text-text": view === "diff-split", + "text-text-muted/70": view !== "diff-split", + "bg-background-element": view === "diff-split", + }} + onClick={() => local.file.setView(activeFile.path, "diff-split")} + > + <Icon name="columns" size={14} /> + </IconButton> + </Tooltip> + </div> + ) + })()} + </Show> </div> - <For each={local.file.opened()}> - {(file) => ( - <Tabs.Content value={file.path} class="grow h-full pt-1 select-text"> - {(() => { - const view = local.file.view(file.path) - const showRaw = view === "raw" || !file.content?.diff - const code = showRaw ? (file.content?.content ?? "") : (file.content?.diff ?? "") - return <Code path={file.path} code={code} class="[&_code]:pb-60" /> - })()} - </Tabs.Content> - )} - </For> - </Tabs> - <DragOverlay> - {(() => { - const id = activeItem() - if (!id) return null - const draggedFile = local.file.node(id) - if (!draggedFile) return null - return ( - <div class="relative px-3 h-8 flex items-center text-sm font-medium text-text whitespace-nowrap shrink-0 bg-background-panel border-x border-border-subtle/40 border-b border-b-transparent"> - <TabVisual file={draggedFile} /> - </div> - ) - })()} - </DragOverlay> - </DragDropProvider> - </div> + </div> + <For each={local.file.opened()}> + {(file) => ( + <Tabs.Content value={file.path} class="select-text"> + {(() => { + const view = local.file.view(file.path) + const showRaw = view === "raw" || !file.content?.diff + const code = showRaw ? (file.content?.content ?? "") : (file.content?.diff ?? "") + return <Code path={file.path} code={code} class="[&_code]:pb-60" /> + })()} + </Tabs.Content> + )} + </For> + </Tabs> + <DragOverlay> + {(() => { + const id = activeItem() + if (!id) return null + const draggedFile = local.file.node(id) + if (!draggedFile) return null + return ( + <div class="relative px-3 h-8 flex items-center text-sm font-medium text-text whitespace-nowrap shrink-0 bg-background-panel border-x border-border-subtle/40 border-b border-b-transparent"> + <TabVisual file={draggedFile} /> + </div> + ) + })()} + </DragOverlay> + </DragDropProvider> ) } diff --git a/packages/desktop/src/components/file-tree.tsx b/packages/desktop/src/components/file-tree.tsx index d31255ced..348e25ad7 100644 --- a/packages/desktop/src/components/file-tree.tsx +++ b/packages/desktop/src/components/file-tree.tsx @@ -1,6 +1,7 @@ import { useLocal } from "@/context" import type { LocalFile } from "@/context/local" -import { Collapsible, FileIcon, Tooltip } from "@/ui" +import { Tooltip } from "@opencode-ai/ui" +import { Collapsible, FileIcon } from "@/ui" import { For, Match, Switch, Show, type ComponentProps, type ParentProps } from "solid-js" import { Dynamic } from "solid-js/web" diff --git a/packages/desktop/src/components/prompt-form.tsx b/packages/desktop/src/components/prompt-form.tsx index 5e6441596..06fbfbb03 100644 --- a/packages/desktop/src/components/prompt-form.tsx +++ b/packages/desktop/src/components/prompt-form.tsx @@ -1,8 +1,8 @@ import { For, Show, createMemo, onCleanup, type JSX } from "solid-js" import { createStore } from "solid-js/store" import { Popover } from "@kobalte/core/popover" -import { Button, FileIcon, Icon, IconButton, Tooltip } from "@/ui" -import { Select } from "@/components/select" +import { Tooltip, Button, Icon, Select } from "@opencode-ai/ui" +import { FileIcon, IconButton } from "@/ui" import { useLocal } from "@/context" import type { FileContext, LocalFile } from "@/context/local" import { getDirectory, getFilename } from "@/utils" diff --git a/packages/desktop/src/components/resizeable-pane.tsx b/packages/desktop/src/components/resizeable-pane.tsx deleted file mode 100644 index 49ccc4e70..000000000 --- a/packages/desktop/src/components/resizeable-pane.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { batch, createContext, createMemo, createSignal, onCleanup, Show, useContext } from "solid-js" -import type { ComponentProps, JSX } from "solid-js" -import { createStore } from "solid-js/store" -import { useLocal } from "@/context" - -type PaneDefault = number | { size: number; visible?: boolean } - -type LayoutContextValue = { - id: string - register: (pane: string, options: { min?: number | string; max?: number | string }) => void - size: (pane: string) => number - visible: (pane: string) => boolean - percent: (pane: string) => number - next: (pane: string) => string | undefined - startDrag: (left: string, right: string | undefined, event: MouseEvent) => void - dragging: () => string | undefined -} - -const LayoutContext = createContext<LayoutContextValue | undefined>(undefined) - -export interface ResizeableLayoutProps { - id: string - defaults: Record<string, PaneDefault> - class?: ComponentProps<"div">["class"] - classList?: ComponentProps<"div">["classList"] - children: JSX.Element -} - -export interface ResizeablePaneProps { - id: string - minSize?: number | string - maxSize?: number | string - class?: ComponentProps<"div">["class"] - classList?: ComponentProps<"div">["classList"] - children: JSX.Element -} - -export function ResizeableLayout(props: ResizeableLayoutProps) { - const local = useLocal() - const [meta, setMeta] = createStore<Record<string, { min: number; max: number; minPx?: number; maxPx?: number }>>({}) - const [dragging, setDragging] = createSignal<string>() - let container: HTMLDivElement | undefined - - local.layout.ensure(props.id, props.defaults) - - const order = createMemo(() => local.layout.order(props.id)) - const visibleOrder = createMemo(() => order().filter((pane) => local.layout.visible(props.id, pane))) - const totalVisible = createMemo(() => { - const panes = visibleOrder() - if (!panes.length) return 0 - return panes.reduce((total, pane) => total + local.layout.size(props.id, pane), 0) - }) - - const percent = (pane: string) => { - const panes = visibleOrder() - if (!panes.length) return 0 - const total = totalVisible() - if (!total) return 100 / panes.length - return (local.layout.size(props.id, pane) / total) * 100 - } - - const nextPane = (pane: string) => { - const panes = visibleOrder() - const index = panes.indexOf(pane) - if (index === -1) return undefined - return panes[index + 1] - } - - const minMax = (pane: string) => meta[pane] ?? { min: 5, max: 95 } - - const pxToPercent = (px: number, total: number) => (px / total) * 100 - - const boundsForPair = (left: string, right: string, total: number) => { - const leftMeta = minMax(left) - const rightMeta = minMax(right) - const containerWidth = container?.getBoundingClientRect().width ?? 0 - - let minLeft = leftMeta.min - let maxLeft = leftMeta.max - let minRight = rightMeta.min - let maxRight = rightMeta.max - - if (containerWidth && leftMeta.minPx !== undefined) minLeft = pxToPercent(leftMeta.minPx, containerWidth) - if (containerWidth && leftMeta.maxPx !== undefined) maxLeft = pxToPercent(leftMeta.maxPx, containerWidth) - if (containerWidth && rightMeta.minPx !== undefined) minRight = pxToPercent(rightMeta.minPx, containerWidth) - if (containerWidth && rightMeta.maxPx !== undefined) maxRight = pxToPercent(rightMeta.maxPx, containerWidth) - - const finalMinLeft = Math.max(minLeft, total - maxRight) - const finalMaxLeft = Math.min(maxLeft, total - minRight) - return { - min: Math.min(finalMinLeft, finalMaxLeft), - max: Math.max(finalMinLeft, finalMaxLeft), - } - } - - const setPair = (left: string, right: string, leftSize: number, rightSize: number) => { - batch(() => { - local.layout.setSize(props.id, left, leftSize) - local.layout.setSize(props.id, right, rightSize) - }) - } - - const startDrag = (left: string, right: string | undefined, event: MouseEvent) => { - if (!right) return - if (!container) return - const rect = container.getBoundingClientRect() - if (!rect.width) return - event.preventDefault() - const startX = event.clientX - const startLeft = local.layout.size(props.id, left) - const startRight = local.layout.size(props.id, right) - const total = startLeft + startRight - const bounds = boundsForPair(left, right, total) - const move = (moveEvent: MouseEvent) => { - const delta = ((moveEvent.clientX - startX) / rect.width) * 100 - const nextLeft = Math.max(bounds.min, Math.min(bounds.max, startLeft + delta)) - const nextRight = total - nextLeft - setPair(left, right, nextLeft, nextRight) - } - const stop = () => { - setDragging() - document.removeEventListener("mousemove", move) - document.removeEventListener("mouseup", stop) - } - setDragging(left) - document.addEventListener("mousemove", move) - document.addEventListener("mouseup", stop) - onCleanup(() => stop()) - } - - const register = (pane: string, options: { min?: number | string; max?: number | string }) => { - let min = 5 - let max = 95 - let minPx: number | undefined - let maxPx: number | undefined - - if (typeof options.min === "string" && options.min.endsWith("px")) { - minPx = parseInt(options.min) - min = 0 - } else if (typeof options.min === "number") { - min = options.min - } - - if (typeof options.max === "string" && options.max.endsWith("px")) { - maxPx = parseInt(options.max) - max = 100 - } else if (typeof options.max === "number") { - max = options.max - } - - setMeta(pane, () => ({ min, max, minPx, maxPx })) - const fallback = props.defaults[pane] - local.layout.ensurePane(props.id, pane, fallback ?? { size: min, visible: true }) - } - - const contextValue: LayoutContextValue = { - id: props.id, - register, - size: (pane) => local.layout.size(props.id, pane), - visible: (pane) => local.layout.visible(props.id, pane), - percent, - next: nextPane, - startDrag, - dragging, - } - - return ( - <LayoutContext.Provider value={contextValue}> - <div - ref={(node) => { - container = node ?? undefined - }} - class={props.class ? `relative flex h-full w-full ${props.class}` : "relative flex h-full w-full"} - classList={props.classList} - > - {props.children} - </div> - </LayoutContext.Provider> - ) -} - -export function ResizeablePane(props: ResizeablePaneProps) { - const context = useContext(LayoutContext)! - context.register(props.id, { min: props.minSize, max: props.maxSize }) - const visible = () => context.visible(props.id) - const width = () => context.percent(props.id) - const next = () => context.next(props.id) - const dragging = () => context.dragging() === props.id - - return ( - <Show when={visible()}> - <div - class={props.class ? `relative flex h-full flex-col ${props.class}` : "relative flex h-full flex-col"} - classList={props.classList} - style={{ - width: `${width()}%`, - flex: `0 0 ${width()}%`, - }} - > - {props.children} - <Show when={next()}> - <div - class="absolute top-0 -right-1 h-full w-1.5 cursor-col-resize z-50 group" - onMouseDown={(event) => context.startDrag(props.id, next(), event)} - > - <div - classList={{ - "w-0.5 h-full bg-transparent transition-colors group-hover:bg-border-active": true, - "bg-border-active!": dragging(), - }} - /> - </div> - </Show> - </div> - </Show> - ) -} diff --git a/packages/desktop/src/components/select-dialog.tsx b/packages/desktop/src/components/select-dialog.tsx index 315fe14e5..bf9aa0dbd 100644 --- a/packages/desktop/src/components/select-dialog.tsx +++ b/packages/desktop/src/components/select-dialog.tsx @@ -1,6 +1,7 @@ import { createEffect, Show, For, createMemo, type JSX, createResource } from "solid-js" import { Dialog } from "@kobalte/core/dialog" -import { Icon, IconButton } from "@/ui" +import { Icon } from "@opencode-ai/ui" +import { IconButton } from "@/ui" import { createStore } from "solid-js/store" import { entries, flatMap, groupBy, map, pipe } from "remeda" import { createList } from "solid-list" diff --git a/packages/desktop/src/components/select.tsx b/packages/desktop/src/components/select.tsx deleted file mode 100644 index 6ab3f4012..000000000 --- a/packages/desktop/src/components/select.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { Select as KobalteSelect } from "@kobalte/core/select" -import { createMemo } from "solid-js" -import type { ComponentProps } from "solid-js" -import { Icon } from "@/ui/icon" -import { pipe, groupBy, entries, map } from "remeda" -import { Button, type ButtonProps } from "@/ui" - -export interface SelectProps<T> { - placeholder?: string - options: T[] - current?: T - value?: (x: T) => string - label?: (x: T) => string - groupBy?: (x: T) => string - onSelect?: (value: T | undefined) => void - class?: ComponentProps<"div">["class"] - classList?: ComponentProps<"div">["classList"] -} - -export function Select<T>(props: SelectProps<T> & ButtonProps) { - const grouped = createMemo(() => { - const result = pipe( - props.options, - groupBy((x) => (props.groupBy ? props.groupBy(x) : "")), - // mapValues((x) => x.sort((a, b) => a.title.localeCompare(b.title))), - entries(), - map(([k, v]) => ({ category: k, options: v })), - ) - return result - }) - - return ( - <KobalteSelect<T, { category: string; options: T[] }> - value={props.current} - options={grouped()} - optionValue={(x) => (props.value ? props.value(x) : (x as string))} - optionTextValue={(x) => (props.label ? props.label(x) : (x as string))} - optionGroupChildren="options" - placeholder={props.placeholder} - sectionComponent={(props) => ( - <KobalteSelect.Section class="text-xs uppercase text-text-muted/60 font-light mt-3 first:mt-0 ml-2"> - {props.section.rawValue.category} - </KobalteSelect.Section> - )} - itemComponent={(itemProps) => ( - <KobalteSelect.Item - classList={{ - "relative flex cursor-pointer select-none items-center": true, - "rounded-sm px-2 py-0.5 text-xs outline-none text-text": true, - "transition-colors data-[disabled]:pointer-events-none": true, - "data-[highlighted]:bg-background-element data-[disabled]:opacity-50": true, - [props.class ?? ""]: !!props.class, - }} - {...itemProps} - > - <KobalteSelect.ItemLabel> - {props.label ? props.label(itemProps.item.rawValue) : (itemProps.item.rawValue as string)} - </KobalteSelect.ItemLabel> - <KobalteSelect.ItemIndicator class="ml-auto"> - <Icon name="checkmark" size={16} /> - </KobalteSelect.ItemIndicator> - </KobalteSelect.Item> - )} - onChange={(v) => { - props.onSelect?.(v ?? undefined) - }} - > - <KobalteSelect.Trigger - as={Button} - size={props.size || "sm"} - variant={props.variant || "secondary"} - classList={{ - ...(props.classList ?? {}), - [props.class ?? ""]: !!props.class, - }} - > - <KobalteSelect.Value<T> class="truncate"> - {(state) => { - const selected = state.selectedOption() ?? props.current - if (!selected) return props.placeholder || "" - if (props.label) return props.label(selected) - return selected as string - }} - </KobalteSelect.Value> - <KobalteSelect.Icon - classList={{ - "group size-fit shrink-0 text-text-muted transition-transform duration-100": true, - }} - > - <Icon name="chevron-up" size={16} class="-my-2 group-data-[expanded]:rotate-180" /> - <Icon name="chevron-down" size={16} class="-my-2 group-data-[expanded]:rotate-180" /> - </KobalteSelect.Icon> - </KobalteSelect.Trigger> - <KobalteSelect.Portal> - <KobalteSelect.Content - classList={{ - "min-w-32 overflow-hidden rounded-md border border-border-subtle/40": true, - "bg-background-panel p-1 shadow-md z-50": true, - "data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95": true, - "data-[expanded]:animate-in data-[expanded]:fade-in-0 data-[expanded]:zoom-in-95": true, - }} - > - <KobalteSelect.Listbox class="overflow-y-auto max-h-48 whitespace-nowrap overflow-x-hidden" /> - </KobalteSelect.Content> - </KobalteSelect.Portal> - </KobalteSelect> - ) -} diff --git a/packages/desktop/src/components/session-list.tsx b/packages/desktop/src/components/session-list.tsx index a339fc6b4..f26d39af8 100644 --- a/packages/desktop/src/components/session-list.tsx +++ b/packages/desktop/src/components/session-list.tsx @@ -1,5 +1,5 @@ import { useSync, useLocal } from "@/context" -import { Tooltip } from "@/ui" +import { Tooltip } from "@opencode-ai/ui" import { DateTime } from "luxon" import { VList } from "virtua/solid" diff --git a/packages/desktop/src/components/session-timeline.tsx b/packages/desktop/src/components/session-timeline.tsx index 07d93031e..d4adf2e4a 100644 --- a/packages/desktop/src/components/session-timeline.tsx +++ b/packages/desktop/src/components/session-timeline.tsx @@ -1,5 +1,6 @@ import { useLocal, useSync } from "@/context" -import { Collapsible, Icon } from "@/ui" +import { Icon } from "@opencode-ai/ui" +import { Collapsible } from "@/ui" import type { Part, ToolPart } from "@opencode-ai/sdk" import { DateTime } from "luxon" import { diff --git a/packages/desktop/src/components/sidebar-nav.tsx b/packages/desktop/src/components/sidebar-nav.tsx deleted file mode 100644 index 24750bdba..000000000 --- a/packages/desktop/src/components/sidebar-nav.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { For } from "solid-js" -import { Icon, Link, Logo, Tooltip } from "@/ui" -import { useLocation } from "@solidjs/router" - -const navigation = [ - { name: "Sessions", href: "/sessions", icon: "dashboard" as const }, - { name: "Commands", href: "/commands", icon: "slash" as const }, - { name: "Agents", href: "/agents", icon: "bolt" as const }, - { name: "Providers", href: "/providers", icon: "cloud" as const }, - { name: "Tools (MCP)", href: "/tools", icon: "hammer" as const }, - { name: "LSP", href: "/lsp", icon: "code" as const }, - { name: "Settings", href: "/settings", icon: "settings" as const }, -] - -export default function SidebarNav() { - const location = useLocation() - return ( - <div class="hidden md:fixed md:inset-y-0 md:left-0 md:z-50 md:block md:w-16 md:overflow-y-auto md:bg-background-panel md:pb-4"> - <div class="flex h-16 shrink-0 items-center justify-center"> - <Logo variant="mark" size={28} /> - </div> - <nav class="mt-5"> - <ul role="list" class="flex flex-col items-center space-y-1"> - <For each={navigation}> - {(item) => ( - <li> - <Tooltip placement="right" value={item.name}> - <Link - href={item.href} - classList={{ - "bg-background-element text-text": location.pathname.startsWith(item.href), - "text-text-muted hover:bg-background-element hover:text-text": location.pathname !== item.href, - "flex gap-x-3 rounded-md p-3 text-sm font-semibold": true, - "focus-visible:outline-1 focus-visible:-outline-offset-1 focus-visible:outline-border-active": true, - }} - > - <Icon name={item.icon} size={20} /> - <span class="sr-only">{item.name}</span> - </Link> - </Tooltip> - </li> - )} - </For> - </ul> - </nav> - </div> - ) -} |
