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 ----- packages/desktop/src/context/index.ts | 1 - packages/desktop/src/context/local.tsx | 50 ++--- packages/desktop/src/context/theme.tsx | 92 -------- packages/desktop/src/index.css | 169 +-------------- packages/desktop/src/index.tsx | 53 ++--- packages/desktop/src/pages/index.tsx | 110 +++++----- packages/desktop/src/pages/layout.tsx | 5 - packages/desktop/src/ui/button.tsx | 36 ---- packages/desktop/src/ui/collapsible.tsx | 2 +- packages/desktop/src/ui/icon.tsx | 155 -------------- packages/desktop/src/ui/index.ts | 5 - packages/desktop/src/ui/link.tsx | 13 -- packages/desktop/src/ui/logo.tsx | 125 ----------- packages/desktop/src/ui/tabs.tsx | 71 ------ packages/desktop/src/ui/tooltip.tsx | 58 ----- 24 files changed, 229 insertions(+), 1341 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 delete mode 100644 packages/desktop/src/context/theme.tsx delete mode 100644 packages/desktop/src/pages/layout.tsx delete mode 100644 packages/desktop/src/ui/button.tsx delete mode 100644 packages/desktop/src/ui/icon.tsx delete mode 100644 packages/desktop/src/ui/link.tsx delete mode 100644 packages/desktop/src/ui/logo.tsx delete mode 100644 packages/desktop/src/ui/tabs.tsx delete mode 100644 packages/desktop/src/ui/tooltip.tsx (limited to 'packages/desktop/src') 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) => } + + + + - - {(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 ( - - ) -} diff --git a/packages/desktop/src/context/index.ts b/packages/desktop/src/context/index.ts index bc4bf3b1d..6ca3bbf97 100644 --- a/packages/desktop/src/context/index.ts +++ b/packages/desktop/src/context/index.ts @@ -4,4 +4,3 @@ export { MarkedProvider, useMarked } from "./marked" export { SDKProvider, useSDK } from "./sdk" export { ShikiProvider, useShiki } from "./shiki" export { SyncProvider, useSync } from "./sync" -export { ThemeProvider, useTheme } from "./theme" diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx index e9fe8ee5c..c60e4520e 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/desktop/src/context/local.tsx @@ -131,31 +131,31 @@ function init() { const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path))) const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b))) - createEffect((prev: FileStatus[]) => { - const removed = prev.filter((p) => !sync.data.changes.find((c) => c.path === p.path)) - for (const p of removed) { - setStore( - "node", - p.path, - produce((draft) => { - draft.status = undefined - draft.view = "raw" - }), - ) - load(p.path) - } - for (const p of sync.data.changes) { - if (store.node[p.path] === undefined) { - fetch(p.path).then(() => { - if (store.node[p.path] === undefined) return - setStore("node", p.path, "status", p) - }) - } else { - setStore("node", p.path, "status", p) - } - } - return sync.data.changes - }, sync.data.changes) + // createEffect((prev: FileStatus[]) => { + // const removed = prev.filter((p) => !sync.data.changes.find((c) => c.path === p.path)) + // for (const p of removed) { + // setStore( + // "node", + // p.path, + // produce((draft) => { + // draft.status = undefined + // draft.view = "raw" + // }), + // ) + // load(p.path) + // } + // for (const p of sync.data.changes) { + // if (store.node[p.path] === undefined) { + // fetch(p.path).then(() => { + // if (store.node[p.path] === undefined) return + // setStore("node", p.path, "status", p) + // }) + // } else { + // setStore("node", p.path, "status", p) + // } + // } + // return sync.data.changes + // }, sync.data.changes) const changed = (path: string) => { const node = store.node[path] diff --git a/packages/desktop/src/context/theme.tsx b/packages/desktop/src/context/theme.tsx deleted file mode 100644 index 0b344ea97..000000000 --- a/packages/desktop/src/context/theme.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { - createContext, - useContext, - createSignal, - createEffect, - onMount, - type ParentComponent, - onCleanup, -} from "solid-js" - -export interface ThemeContextValue { - theme: string | undefined - isDark: boolean - setTheme: (themeName: string) => void - setDarkMode: (isDark: boolean) => void -} - -const ThemeContext = createContext() - -export const useTheme = () => { - const context = useContext(ThemeContext) - if (!context) { - throw new Error("useTheme must be used within a ThemeProvider") - } - return context -} - -interface ThemeProviderProps { - defaultTheme?: string - defaultDarkMode?: boolean -} - -const themes = ["opencode", "tokyonight", "ayu", "nord", "catppuccin"] - -export const ThemeProvider: ParentComponent = (props) => { - const [theme, setThemeSignal] = createSignal() - const [isDark, setIsDark] = createSignal(props.defaultDarkMode ?? false) - - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === "t" && event.ctrlKey) { - event.preventDefault() - const current = theme() - if (!current) return - const index = themes.indexOf(current) - const next = themes[(index + 1) % themes.length] - setTheme(next) - } - } - - onMount(() => { - window.addEventListener("keydown", handleKeyDown) - }) - - onCleanup(() => { - window.removeEventListener("keydown", handleKeyDown) - }) - - onMount(() => { - const savedTheme = localStorage.getItem("theme") ?? "opencode" - const savedDarkMode = localStorage.getItem("darkMode") ?? "true" - setIsDark(savedDarkMode === "true") - setTheme(savedTheme) - }) - - createEffect(() => { - const currentTheme = theme() - const darkMode = isDark() - if (currentTheme) { - document.documentElement.setAttribute("data-theme", currentTheme) - document.documentElement.setAttribute("data-dark", darkMode.toString()) - } - }) - - const setTheme = async (theme: string) => { - setThemeSignal(theme) - localStorage.setItem("theme", theme) - } - - const setDarkMode = (dark: boolean) => { - setIsDark(dark) - localStorage.setItem("darkMode", dark.toString()) - } - - const contextValue: ThemeContextValue = { - theme: theme(), - isDark: isDark(), - setTheme, - setDarkMode, - } - - return {props.children} -} diff --git a/packages/desktop/src/index.css b/packages/desktop/src/index.css index cac2e91a6..b4717a4e8 100644 --- a/packages/desktop/src/index.css +++ b/packages/desktop/src/index.css @@ -1,168 +1 @@ -@import "tailwindcss"; - -:root { - interpolate-size: allow-keywords; -} - -@layer components { - [data-popper-positioner] { - pointer-events: none; - } - - body { - line-height: 1; - } - - ::selection { - background-color: color-mix(in srgb, var(--color-primary) 33%, transparent); - /* background-color: var(--color-primary); */ - /* color: var(--color-background); */ - } - - ::-webkit-scrollbar-track { - background: var(--theme-background-panel); - } - - ::-webkit-scrollbar-thumb { - background-color: var(--theme-border-subtle); - border-radius: 6px; - } - - * { - scrollbar-color: var(--theme-border-subtle) var(--theme-background-panel); - } - - .prose h1 { - color: var(--color-text); - font-size: var(--text-sm); - line-height: var(--text-sm--line-height); - margin-bottom: calc(var(--spacing) * 3); - } - .prose h2 { - color: var(--color-text); - font-size: var(--text-sm); - line-height: var(--text-sm--line-height); - margin-bottom: calc(var(--spacing) * 3); - } - .prose h3 { - color: var(--color-text); - font-size: var(--text-xs); - line-height: var(--text-xs--line-height); - margin-bottom: calc(var(--spacing) * 2); - } - .prose h4 { - color: var(--color-text); - font-size: var(--text-xs); - line-height: var(--text-xs--line-height); - margin-bottom: calc(var(--spacing) * 2); - } - .prose h5 { - color: var(--color-text); - font-size: var(--text-xs); - line-height: var(--text-xs--line-height); - margin-bottom: calc(var(--spacing) * 2); - } - .prose h6 { - color: var(--color-text); - font-size: var(--text-xs); - line-height: var(--text-xs--line-height); - margin-bottom: calc(var(--spacing) * 2); - } - .prose p { - font-size: var(--text-xs); - line-height: var(--text-xs--line-height); - margin-bottom: calc(var(--spacing) * 2); - } - .prose strong { - color: var(--color-text); - } - .prose ul, - ol { - list-style-type: disc; - list-style-position: inside; - margin-bottom: calc(var(--spacing) * 2); - } - .prose pre { - background-color: var(--color-background-panel); - padding: calc(var(--spacing) * 2); - border-radius: var(--radius-md); - border: 1px solid var(--color-border-subtle); - overflow-x: auto; - white-space: pre; - margin-bottom: calc(var(--spacing) * 2); - @apply no-scrollbar; - } - .prose code { - font-family: var(--font-mono); - font-size: var(--text-xs); - line-height: var(--text-xs--line-height); - } - .prose blockquote { - margin-bottom: calc(var(--spacing) * 2); - } -} - -@utility no-scrollbar { - &::-webkit-scrollbar { - display: none; - } - /* Hide scrollbar for IE, Edge and Firefox */ - & { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ - } -} - -@theme { - --color-*: initial; - --color-primary: var(--theme-primary); - --color-secondary: var(--theme-secondary); - --color-accent: var(--theme-accent); - --color-error: var(--theme-error); - --color-warning: var(--theme-warning); - --color-success: var(--theme-success); - --color-info: var(--theme-info); - --color-text: var(--theme-text); - --color-text-muted: var(--theme-text-muted); - --color-background: var(--theme-background); - --color-background-panel: var(--theme-background-panel); - --color-background-element: var(--theme-background-element); - --color-border: var(--theme-border); - --color-border-active: var(--theme-border-active); - --color-border-subtle: var(--theme-border-subtle); - --color-diff-added: var(--theme-diff-added); - --color-diff-removed: var(--theme-diff-removed); - --color-diff-context: var(--theme-diff-context); - --color-diff-hunk-header: var(--theme-diff-hunk-header); - --color-diff-highlight-added: var(--theme-diff-highlight-added); - --color-diff-highlight-removed: var(--theme-diff-highlight-removed); - --color-diff-added-bg: var(--theme-diff-added-bg); - --color-diff-removed-bg: var(--theme-diff-removed-bg); - --color-diff-context-bg: var(--theme-diff-context-bg); - --color-diff-line-number: var(--theme-diff-line-number); - --color-diff-added-line-number-bg: var(--theme-diff-added-line-number-bg); - --color-diff-removed-line-number-bg: var(--theme-diff-removed-line-number-bg); - --color-markdown-text: var(--theme-markdown-text); - --color-markdown-heading: var(--theme-markdown-heading); - --color-markdown-link: var(--theme-markdown-link); - --color-markdown-link-text: var(--theme-markdown-link-text); - --color-markdown-code: var(--theme-markdown-code); - --color-markdown-block-quote: var(--theme-markdown-block-quote); - --color-markdown-emph: var(--theme-markdown-emph); - --color-markdown-strong: var(--theme-markdown-strong); - --color-markdown-horizontal-rule: var(--theme-markdown-horizontal-rule); - --color-markdown-list-item: var(--theme-markdown-list-item); - --color-markdown-list-enumeration: var(--theme-markdown-list-enumeration); - --color-markdown-image: var(--theme-markdown-image); - --color-markdown-image-text: var(--theme-markdown-image-text); - --color-markdown-code-block: var(--theme-markdown-code-block); - --color-syntax-comment: var(--theme-syntax-comment); - --color-syntax-keyword: var(--theme-syntax-keyword); - --color-syntax-function: var(--theme-syntax-function); - --color-syntax-variable: var(--theme-syntax-variable); - --color-syntax-string: var(--theme-syntax-string); - --color-syntax-number: var(--theme-syntax-number); - --color-syntax-type: var(--theme-syntax-type); - --color-syntax-operator: var(--theme-syntax-operator); - --color-syntax-punctuation: var(--theme-syntax-punctuation); -} +@import "@opencode-ai/css/tailwind.css"; diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 66ed93d3a..5fa840f0d 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -1,21 +1,13 @@ /* @refresh reload */ +import "@/index.css" import { render } from "solid-js/web" import { Router, Route } from "@solidjs/router" -import "@/index.css" -import Layout from "@/pages/layout" +import { MetaProvider } from "@solidjs/meta" +import { EventProvider, SDKProvider, SyncProvider, LocalProvider, ShikiProvider, MarkedProvider } from "@/context" +import { Fonts } from "@opencode-ai/ui" import Home from "@/pages" -import { - EventProvider, - SDKProvider, - SyncProvider, - LocalProvider, - ThemeProvider, - ShikiProvider, - MarkedProvider, -} from "@/context" const root = document.getElementById("root") - if (import.meta.env.DEV && !(root instanceof HTMLElement)) { throw new Error( "Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?", @@ -24,25 +16,24 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) { render( () => ( -
- - - - - - - - - - - - - - - - - -
+ + + + + + + + + + + + + + + + + + ), root!, ) diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 2e26eb0c2..1174f1e16 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -1,5 +1,5 @@ -import { FileIcon, Icon, IconButton, Tooltip } from "@/ui" -import * as KobalteTabs from "@kobalte/core/tabs" +import { Icon, Tooltip } from "@opencode-ai/ui" +import { FileIcon, IconButton } from "@/ui" import FileTree from "@/components/file-tree" import EditorPane from "@/components/editor-pane" import { For, Match, onCleanup, onMount, Show, Switch } from "solid-js" @@ -11,9 +11,7 @@ import SessionTimeline from "@/components/session-timeline" import PromptForm, { type PromptContentPart, type PromptSubmitValue } from "@/components/prompt-form" import { createStore } from "solid-js/store" import { getDirectory, getFilename } from "@/utils" -import { Select } from "@/components/select" -import { Tabs } from "@/ui/tabs" -import { Code } from "@/components/code" +import { PromptInput } from "@/components/prompt-input" export default function Page() { const local = useLocal() @@ -52,7 +50,7 @@ export default function Page() { const focused = document.activeElement === inputRef if (focused) { if (event.key === "Escape") { - inputRef?.blur() + // inputRef?.blur() } return } @@ -79,7 +77,7 @@ export default function Page() { } if (event.key.length === 1 && event.key !== "Unidentified") { - inputRef?.focus() + // inputRef?.focus() } } @@ -106,6 +104,8 @@ export default function Page() { } } + const handlePromptSubmit2 = () => {} + const handlePromptSubmit = async (prompt: PromptSubmitValue) => { const existingSession = local.session.active() let session = existingSession @@ -231,51 +231,54 @@ export default function Page() {
-
- - {(activeSession) => } - -
- - } - > -
    - - {(path) => ( -
  • - -
  • - )} -
    -
- -
- -
- setStore("modelSelectOpen", true)} - onInputRefChange={(element: HTMLTextAreaElement | undefined) => { - inputRef = element ?? undefined - }} - /> +
+
+ + {(activeSession) => } + +
+
+ +
+
+ + {/* setStore("modelSelectOpen", true)} */} + {/* onInputRefChange={(element: HTMLTextAreaElement | undefined) => { */} + {/* inputRef = element ?? undefined */} + {/* }} */} + {/* /> */} +
+ + } + > +
    + + {(path) => ( +
  • + +
  • + )} +
    +
+ +
@@ -343,8 +346,7 @@ export default function Page() {
)} onClose={() => setStore("fileSelectOpen", false)} - onSelect={(x) => (x ? local.context.openFile(x) : undefined)} - // onSelect={(x) => (x ? local.file.open(x, { pinned: true }) : undefined)} + onSelect={(x) => (x ? local.file.open(x, { pinned: true }) : undefined)} /> diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx deleted file mode 100644 index d2e33a2ca..000000000 --- a/packages/desktop/src/pages/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { type ParentProps } from "solid-js" - -export default function Layout(props: ParentProps) { - return
{props.children}
-} diff --git a/packages/desktop/src/ui/button.tsx b/packages/desktop/src/ui/button.tsx deleted file mode 100644 index 21d89fbee..000000000 --- a/packages/desktop/src/ui/button.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Button as Kobalte } from "@kobalte/core/button" -import { type ComponentProps, splitProps } from "solid-js" - -export interface ButtonProps { - variant?: "primary" | "secondary" | "ghost" - size?: "sm" | "md" | "lg" -} - -export function Button(props: ComponentProps<"button"> & ButtonProps) { - const [split, rest] = splitProps(props, ["variant", "size", "class", "classList"]) - return ( - - {props.children} - - ) -} diff --git a/packages/desktop/src/ui/collapsible.tsx b/packages/desktop/src/ui/collapsible.tsx index e0202f617..d17b3e623 100644 --- a/packages/desktop/src/ui/collapsible.tsx +++ b/packages/desktop/src/ui/collapsible.tsx @@ -1,7 +1,7 @@ import { Collapsible as KobalteCollapsible } from "@kobalte/core/collapsible" import { splitProps } from "solid-js" import type { ComponentProps, ParentProps } from "solid-js" -import { Icon, type IconProps } from "./icon" +import { Icon, type IconProps } from "@opencode-ai/ui" export interface CollapsibleProps extends ComponentProps {} export interface CollapsibleTriggerProps extends ComponentProps {} diff --git a/packages/desktop/src/ui/icon.tsx b/packages/desktop/src/ui/icon.tsx deleted file mode 100644 index 35c77c078..000000000 --- a/packages/desktop/src/ui/icon.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { splitProps, type ComponentProps } from "solid-js" - -export interface IconProps extends ComponentProps<"svg"> { - name: keyof typeof icons - size?: number -} - -// prettier-ignore -const icons = { - close: '', - menu: ' ', - "chevron-right": '', - "chevron-left": '', - "chevron-down": '', - "chevron-up": '', - "chevron-down-square": '', - "chevron-up-square": '', - "chevron-right-square": '', - "chevron-left-square": '', - settings: '', - globe: '', - github: '', - hammer: '', - "avatar-square": '', - slash: '', - robot: '', - cloud: '', - "file-text": '', - file: '', - "file-checkmark": '', - "file-code": '', - "file-important": '', - "file-minus": '', - "file-plus": '', - files: '', - "file-zip": '', - jpg: '', - pdf: '', - png: '', - gif: '', - archive: '', - sun: '', - moon: '', - monitor: '', - command: '', - link: '', - share: '', - branch: '', - logout: '', - login: '', - keys: '', - key: '', - info: '', - warning: '', - checkmark: '', - "checkmark-square": '', - plus: '', - minus: '', - undo: '', - merge: '', - redo: '', - refresh: '', - rotate: '', - "arrow-left": '', - "arrow-down": '', - "arrow-right": '', - "arrow-up": '', - enter: '', - trash: '', - package: '', - box: '', - lock: '', - unlocked: '', - activity: '', - asterisk: '', - bell: '', - "bell-off": '', - bolt: '', - bookmark: '', - brain: '', - browser: '', - "browser-cursor": '', - bug: '', - "carat-down": '', - "carat-left": '', - "carat-right": '', - "carat-up": '', - cards: '', - chart: '', - "check-circle": '', - checklist: '', - "checklist-cards": '', - lab: '', - circle: '', - "circle-dotted": '', - clipboard: '', - clock: '', - "close-circle": '', - terminal: '', - code: '', - components: '', - copy: '', - cpu: '', - dashboard: '', - transfer: '', - devices: '', - diamond: '', - dice: '', - discord: '', - dots: '', - expand: '', - droplet: '', - "chevron-double-down": '', - "chevron-double-left": '', - "chevron-double-right": '', - "chevron-double-up": '', - "speech-bubble": '', - message: '', - annotation: '', - square: '', - "pull-request": '', - pencil: '', - sparkles: '', - photo: '', - columns: '', - "open-pane": '', - "close-pane": '', - "file-search": '', - "folder-search": '', - search: '', - "web-search": '', - loading: '', - mic: '', - } as const - -export function Icon(props: IconProps) { - const [local, others] = splitProps(props, ["name", "size", "class", "classList"]) - const size = local.size ?? 24 - return ( - - ) -} diff --git a/packages/desktop/src/ui/index.ts b/packages/desktop/src/ui/index.ts index d3f24a192..a6ade6ff3 100644 --- a/packages/desktop/src/ui/index.ts +++ b/packages/desktop/src/ui/index.ts @@ -1,4 +1,3 @@ -export { Button, type ButtonProps } from "./button" export { Collapsible, type CollapsibleProps, @@ -6,8 +5,4 @@ export { type CollapsibleContentProps, } from "./collapsible" export { FileIcon, type FileIconProps } from "./file-icon" -export { Icon, type IconProps } from "./icon" export { IconButton, type IconButtonProps } from "./icon-button" -export { Link, type LinkProps } from "./link" -export { Logo, type LogoProps } from "./logo" -export { Tooltip, type TooltipProps } from "./tooltip" diff --git a/packages/desktop/src/ui/link.tsx b/packages/desktop/src/ui/link.tsx deleted file mode 100644 index 461206d58..000000000 --- a/packages/desktop/src/ui/link.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { A } from "@solidjs/router" -import { splitProps } from "solid-js" -import type { ComponentProps } from "solid-js" - -export interface LinkProps extends ComponentProps { - variant?: "primary" | "secondary" | "ghost" - size?: "sm" | "md" | "lg" -} - -export function Link(props: LinkProps) { - const [, others] = splitProps(props, ["variant", "size", "class"]) - return -} diff --git a/packages/desktop/src/ui/logo.tsx b/packages/desktop/src/ui/logo.tsx deleted file mode 100644 index 0bbaba835..000000000 --- a/packages/desktop/src/ui/logo.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import type { ComponentProps } from "solid-js" - -export interface LogoProps extends ComponentProps<"svg"> { - variant?: "mark" | "full" | "ornate" - size?: number -} - -export function Logo(props: LogoProps) { - const { variant = "mark", size = 64, ...others } = props - - if (variant === "mark") { - return ( - - - - - ) - } - - if (variant === "full") { - return ( - - - - - - - - - - - - - ) - } - - return ( - - - - - - - - - - - - - - - - - - - ) -} diff --git a/packages/desktop/src/ui/tabs.tsx b/packages/desktop/src/ui/tabs.tsx deleted file mode 100644 index d82d3327a..000000000 --- a/packages/desktop/src/ui/tabs.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { Tabs as KobalteTabs } from "@kobalte/core/tabs" -import { splitProps } from "solid-js" -import type { ComponentProps, ParentProps } from "solid-js" - -export interface TabsProps extends ComponentProps {} -export interface TabsListProps extends ComponentProps {} -export interface TabsTriggerProps extends ComponentProps {} -export interface TabsContentProps extends ComponentProps {} - -function TabsRoot(props: TabsProps) { - return -} - -function TabsList(props: TabsListProps) { - const [local, others] = splitProps(props, ["class"]) - return ( - - ) -} - -function TabsTrigger(props: ParentProps) { - const [local, others] = splitProps(props, ["class", "children"]) - return ( - - {local.children} - - ) -} - -function TabsContent(props: ParentProps) { - const [local, others] = splitProps(props, ["class", "children"]) - return ( - - {local.children} - - ) -} - -export const Tabs = Object.assign(TabsRoot, { - List: TabsList, - Trigger: TabsTrigger, - Content: TabsContent, -}) diff --git a/packages/desktop/src/ui/tooltip.tsx b/packages/desktop/src/ui/tooltip.tsx deleted file mode 100644 index 8bf1dc429..000000000 --- a/packages/desktop/src/ui/tooltip.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Tooltip as KobalteTooltip } from "@kobalte/core/tooltip" -import { children, createEffect, createSignal, splitProps } from "solid-js" -import type { ComponentProps } from "solid-js" - -export interface TooltipProps extends ComponentProps { - value: string | (() => string) - class?: string -} - -export function Tooltip(props: TooltipProps) { - const [open, setOpen] = createSignal(false) - const [local, others] = splitProps(props, ["class", "children"]) - - const c = children(() => local.children) - - createEffect(() => { - const childElements = c() - if (childElements instanceof HTMLElement) { - childElements.addEventListener("focus", () => setOpen(true)) - childElements.addEventListener("blur", () => setOpen(false)) - } else if (Array.isArray(childElements)) { - for (const child of childElements) { - if (child instanceof HTMLElement) { - child.addEventListener("focus", () => setOpen(true)) - child.addEventListener("blur", () => setOpen(false)) - } - } - } - }) - - return ( - - - {c()} - - - - {typeof others.value === "function" ? others.value() : others.value} - - - - - ) -} -- cgit v1.2.3