From 47d9e017657c4d265dea53bd86d727097a7ba282 Mon Sep 17 00:00:00 2001
From: Adam <2363879+adamdotdevin@users.noreply.github.com>
Date: Thu, 16 Oct 2025 14:53:44 -0500
Subject: wip: css/ui and desktop work
---
packages/desktop/src/components/editor-pane.tsx | 237 ++++++++++-----------
packages/desktop/src/components/file-tree.tsx | 3 +-
packages/desktop/src/components/prompt-form.tsx | 4 +-
.../desktop/src/components/resizeable-pane.tsx | 217 -------------------
packages/desktop/src/components/select-dialog.tsx | 3 +-
packages/desktop/src/components/select.tsx | 108 ----------
packages/desktop/src/components/session-list.tsx | 2 +-
.../desktop/src/components/session-timeline.tsx | 3 +-
packages/desktop/src/components/sidebar-nav.tsx | 48 -----
9 files changed, 124 insertions(+), 501 deletions(-)
delete mode 100644 packages/desktop/src/components/resizeable-pane.tsx
delete mode 100644 packages/desktop/src/components/select.tsx
delete mode 100644 packages/desktop/src/components/sidebar-nav.tsx
(limited to 'packages/desktop/src/components')
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 (
-
-
-
-
-
-
-
- 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")}
- >
-
-
-
-
- )
- })()}
-
-
+
+
+
+
+
+
+ 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")}
+ >
+
+
+
+
+ )
+ })()}
+
-
- {(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 (
-
-
-
- )
- })()}
-
-
-
+
+
+ {(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 (
+
+
+
+ )
+ })()}
+
+
)
}
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
(undefined)
-
-export interface ResizeableLayoutProps {
- id: string
- defaults: Record
- 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>({})
- const [dragging, setDragging] = createSignal()
- 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 (
-
- {
- 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}
-
-
- )
-}
-
-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 (
-
-
- {props.children}
-
- context.startDrag(props.id, next(), event)}
- >
-
-
-
-
-
- )
-}
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 {
- 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(props: SelectProps & 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 (
-
- 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) => (
-
- {props.section.rawValue.category}
-
- )}
- itemComponent={(itemProps) => (
-
-
- {props.label ? props.label(itemProps.item.rawValue) : (itemProps.item.rawValue as string)}
-
-
-
-
-
- )}
- onChange={(v) => {
- props.onSelect?.(v ?? undefined)
- }}
- >
-
- 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
- }}
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
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 (
-
-
-
-
-
-
- )
-}
--
cgit v1.2.3