From 4069999b782cc00d4e707f5eca32082bdfad45bc Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:38:52 -0600 Subject: [PATCH] wip(desktop): new layout work --- packages/desktop/src/context/layout.tsx | 52 ++++++++++++ packages/desktop/src/context/local.tsx | 47 ----------- packages/desktop/src/context/sdk.tsx | 15 ++-- packages/desktop/src/context/session.tsx | 6 +- packages/desktop/src/context/sync.tsx | 2 + packages/desktop/src/index.tsx | 6 +- packages/desktop/src/pages/layout.tsx | 82 +++++++++++++++---- packages/desktop/src/pages/session-layout.tsx | 16 +++- packages/desktop/src/pages/session.tsx | 49 +++++++---- packages/desktop/src/ui/file-icon.tsx | 3 +- packages/sdk/js/src/client.ts | 12 ++- packages/ui/src/components/button.css | 42 +++++----- packages/ui/src/components/icon-button.css | 43 +++++----- packages/ui/src/components/icon.tsx | 4 + packages/ui/src/components/index.ts | 1 + packages/ui/src/components/logo.css | 4 + packages/ui/src/components/logo.tsx | 14 ++++ packages/ui/src/styles/index.css | 1 + 18 files changed, 258 insertions(+), 141 deletions(-) create mode 100644 packages/desktop/src/context/layout.tsx create mode 100644 packages/ui/src/components/logo.css create mode 100644 packages/ui/src/components/logo.tsx diff --git a/packages/desktop/src/context/layout.tsx b/packages/desktop/src/context/layout.tsx new file mode 100644 index 000000000..9e4af90aa --- /dev/null +++ b/packages/desktop/src/context/layout.tsx @@ -0,0 +1,52 @@ +import { createStore } from "solid-js/store" +import { createMemo } from "solid-js" +import { createSimpleContext } from "./helper" +import { makePersisted } from "@solid-primitives/storage" + +export const { use: useLayout, provider: LayoutProvider } = createSimpleContext({ + name: "Layout", + init: () => { + const [store, setStore] = makePersisted( + createStore({ + sidebar: { + opened: true, + width: 280, + }, + review: { + state: "pane" as "pane" | "tab", + }, + }), + { + name: "__default-layout", + }, + ) + + return { + sidebar: { + opened: createMemo(() => store.sidebar.opened), + open() { + setStore("sidebar", "opened", true) + }, + close() { + setStore("sidebar", "opened", false) + }, + toggle() { + setStore("sidebar", "opened", (x) => !x) + }, + width: createMemo(() => store.sidebar.width), + resize(width: number) { + setStore("sidebar", "width", width) + }, + }, + review: { + state: createMemo(() => store.review?.state ?? "closed"), + pane() { + setStore("review", "state", "pane") + }, + tab() { + setStore("review", "state", "tab") + }, + }, + } + }, +}) diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx index 1cef1c9f1..cef6c5555 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/desktop/src/context/local.tsx @@ -5,7 +5,6 @@ import type { FileContent, FileNode, Model, Provider, File as FileStatus } from import { createSimpleContext } from "./helper" import { useSDK } from "./sdk" import { useSync } from "./sync" -import { makePersisted } from "@solid-primitives/storage" export type LocalFile = FileNode & Partial<{ @@ -457,57 +456,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } })() - const layout = (() => { - const [store, setStore] = makePersisted( - createStore({ - sidebar: { - opened: true, - width: 240, - }, - review: { - state: "pane" as "pane" | "tab", - }, - }), - { - name: "_default-layout", - }, - ) - - return { - sidebar: { - opened: createMemo(() => store.sidebar.opened), - open() { - setStore("sidebar", "opened", true) - }, - close() { - setStore("sidebar", "opened", false) - }, - toggle() { - setStore("sidebar", "opened", (x) => !x) - }, - width: createMemo(() => store.sidebar.width), - resize(width: number) { - setStore("sidebar", "width", width) - }, - }, - review: { - state: createMemo(() => store.review?.state ?? "closed"), - pane() { - setStore("review", "state", "pane") - }, - tab() { - setStore("review", "state", "tab") - }, - }, - } - })() - const result = { model, agent, file, context, - layout, } return result }, diff --git a/packages/desktop/src/context/sdk.tsx b/packages/desktop/src/context/sdk.tsx index 8d0cace65..b7b753dbc 100644 --- a/packages/desktop/src/context/sdk.tsx +++ b/packages/desktop/src/context/sdk.tsx @@ -5,12 +5,15 @@ import { onCleanup } from "solid-js" export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ name: "SDK", - init: (props: { url: string }) => { + init: (props: { url: string; directory?: string }) => { const abort = new AbortController() - const sdk = createOpencodeClient({ - baseUrl: props.url, - signal: abort.signal, - }) + const sdk = createOpencodeClient( + { + baseUrl: props.url, + signal: abort.signal, + }, + { directory: props.directory }, + ) const emitter = createGlobalEmitter<{ [key in Event["type"]]: Extract @@ -27,6 +30,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ abort.abort() }) - return { client: sdk, event: emitter } + return { url: props.url, directory: props.directory, client: sdk, event: emitter } }, }) diff --git a/packages/desktop/src/context/session.tsx b/packages/desktop/src/context/session.tsx index b2e15a42c..a468f4673 100644 --- a/packages/desktop/src/context/session.tsx +++ b/packages/desktop/src/context/session.tsx @@ -3,7 +3,7 @@ import { createSimpleContext } from "./helper" import { batch, createEffect, createMemo } from "solid-js" import { useSync } from "./sync" import { makePersisted } from "@solid-primitives/storage" -import { TextSelection, useLocal } from "./local" +import { TextSelection } from "./local" import { pipe, sumBy } from "remeda" import { AssistantMessage } from "@opencode-ai/sdk" @@ -11,7 +11,6 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex name: "Session", init: (props: { sessionId?: string }) => { const sync = useSync() - const local = useLocal() const [store, setStore] = makePersisted( createStore<{ @@ -140,9 +139,6 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex setStore("tabs", "active", undefined) return } - if (tab.startsWith("file://")) { - await local.file.open(tab.replace("file://", "")) - } if (tab !== "review") { if (!store.tabs.opened.includes(tab)) { setStore("tabs", "opened", [...store.tabs.opened, tab]) diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index 3626cf54f..11b2c36b8 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -63,6 +63,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const sdk = useSDK() sdk.event.listen((e) => { + // fetch the child store + // make a set store function that always rights to the child store const event = e.details switch (event.type) { case "session.updated": { diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 63d96ae84..2499e5e85 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -6,10 +6,10 @@ import { MetaProvider } from "@solidjs/meta" import { Fonts, MarkedProvider } from "@opencode-ai/ui" import { SDKProvider } from "./context/sdk" import { SyncProvider } from "./context/sync" -import { LocalProvider } from "./context/local" import Layout from "@/pages/layout" import SessionLayout from "@/pages/session-layout" import Session from "@/pages/session" +import { LayoutProvider } from "./context/layout" const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1" const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096" @@ -32,7 +32,7 @@ render( - + @@ -41,7 +41,7 @@ render( - + diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index d88564007..6e0078a16 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -1,33 +1,85 @@ -import { Button, Tooltip, DiffChanges, IconButton } from "@opencode-ai/ui" -import { createMemo, For, ParentProps, Show } from "solid-js" +import { Button, Tooltip, DiffChanges, IconButton, Mark, Icon } from "@opencode-ai/ui" +import { createMemo, For, Match, ParentProps, Show, Switch } from "solid-js" import { DateTime } from "luxon" import { useSync } from "@/context/sync" import { A, useParams } from "@solidjs/router" -import { useLocal } from "@/context/local" +import { useLayout } from "@/context/layout" export default function Layout(props: ParentProps) { const params = useParams() const sync = useSync() - const local = useLocal() + const layout = useLayout() return (
- -
+
-
-
- - - -
+ +
+
+
+
+
+ + + + + + + + + +
- +
- +
- {(f) => } +
+ {(f) => }
) }} @@ -769,7 +776,13 @@ export default function Page() { items={local.file.searchFiles} key={(x) => x} onOpenChange={(open) => setStore("fileSelectOpen", open)} - onSelect={(x) => (x ? session.layout.openTab("file://" + x) : undefined)} + onSelect={(x) => { + if (x) { + local.file.open(x) + return session.layout.openTab("file://" + x) + } + return undefined + }} > {(i) => (
& { } export const FileIcon: Component = (props) => { - const [local, rest] = splitProps(props, ["node", "class", "expanded"]) + const [local, rest] = splitProps(props, ["node", "class", "classList", "expanded"]) const name = createMemo(() => chooseIconName(local.node.path, local.node.type, local.expanded || false)) return ( `, enter: ``, "layout-left": ``, + "layout-left-partial": ``, + "layout-left-full": ``, "layout-right": ``, + "layout-right-partial": ``, + "layout-right-full": ``, "speech-bubble": ``, "align-right": ``, expand: ``, diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index ebc897a1f..7dc7ee0f1 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -13,6 +13,7 @@ export * from "./input" export * from "./favicon" export * from "./fonts" export * from "./list" +export * from "./logo" export * from "./markdown" export * from "./message-part" export * from "./progress-circle" diff --git a/packages/ui/src/components/logo.css b/packages/ui/src/components/logo.css new file mode 100644 index 000000000..36e871859 --- /dev/null +++ b/packages/ui/src/components/logo.css @@ -0,0 +1,4 @@ +[data-component="logo-mark"] { + width: 16px; + height: 20px; +} diff --git a/packages/ui/src/components/logo.tsx b/packages/ui/src/components/logo.tsx new file mode 100644 index 000000000..d3588f301 --- /dev/null +++ b/packages/ui/src/components/logo.tsx @@ -0,0 +1,14 @@ +export const Mark = (props: { class?: string }) => { + return ( + + + + + ) +} diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index 60ce76ffe..b009b6daa 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -19,6 +19,7 @@ @import "../components/icon-button.css" layer(components); @import "../components/input.css" layer(components); @import "../components/list.css" layer(components); +@import "../components/logo.css" layer(components); @import "../components/markdown.css" layer(components); @import "../components/message-part.css" layer(components); @import "../components/progress-circle.css" layer(components);