From f736751ab275f52701d40ca0488b75081ed851ec Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:19:51 -0600 Subject: [PATCH 1/9] wip(desktop): progress --- packages/desktop/src/context/global-sync.tsx | 31 ++++++++++++++++---- packages/desktop/src/context/sync.tsx | 9 +++++- packages/desktop/src/pages/home.tsx | 15 +++++++--- packages/desktop/src/pages/session.tsx | 22 ++++++++------ 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/packages/desktop/src/context/global-sync.tsx b/packages/desktop/src/context/global-sync.tsx index 4ac86b9fd..9a113578e 100644 --- a/packages/desktop/src/context/global-sync.tsx +++ b/packages/desktop/src/context/global-sync.tsx @@ -22,7 +22,7 @@ type State = { ready: boolean provider: Provider[] agent: Agent[] - project: Project + project: string config: Config path: Path session: Session[] @@ -60,11 +60,10 @@ export const { use: useGlobalSync, provider: GlobalSyncProvider } = createSimple }) const children: Record>> = {} - function child(directory: string) { if (!children[directory]) { setGlobalStore("children", directory, { - project: { id: "", worktree: "", time: { created: 0, initialized: 0, updated: 0 } }, + project: "", config: {}, path: { state: "", config: "", worktree: "", directory: "" }, ready: false, @@ -88,9 +87,29 @@ export const { use: useGlobalSync, provider: GlobalSyncProvider } = createSimple const sdk = useGlobalSDK() sdk.event.listen((e) => { const directory = e.name - const [store, setStore] = child(directory) - const event = e.details + + if (directory === "global") { + switch (event.type) { + case "project.updated": { + const result = Binary.search(globalStore.projects, event.properties.id, (s) => s.id) + if (result.found) { + setGlobalStore("projects", result.index, reconcile(event.properties)) + break + } + setGlobalStore( + "projects", + produce((draft) => { + draft.splice(result.index, 0, event.properties) + }), + ) + break + } + } + return + } + + const [store, setStore] = child(directory) switch (event.type) { case "session.updated": { const result = Binary.search(store.session, event.properties.info.id, (s) => s.id) @@ -166,7 +185,7 @@ export const { use: useGlobalSync, provider: GlobalSyncProvider } = createSimple "projects", x .data!.filter((x) => !x.worktree.includes("opencode-test") && x.vcs) - .sort((a, b) => b.time.created - a.time.created), + .sort((a, b) => a.id.localeCompare(b.id)), ), ), ]).then(() => setGlobalStore("ready", true)) diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index d8d7c7b95..a9de0f02d 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -13,7 +13,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ const [store, setStore] = globalSync.child(sdk.directory) const load = { - project: () => sdk.client.project.current().then((x) => setStore("project", x.data!)), + project: () => sdk.client.project.current().then((x) => setStore("project", x.data!.id)), provider: () => sdk.client.config.providers().then((x) => setStore("provider", x.data!.providers)), path: () => sdk.client.path.get().then((x) => setStore("path", x.data!)), agent: () => sdk.client.app.agents().then((x) => setStore("agent", x.data ?? [])), @@ -41,6 +41,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ get ready() { return store.ready }, + get project() { + console.log("project", store.project) + console.log("projects", globalSync.data.projects) + const match = Binary.search(globalSync.data.projects, store.project, (p) => p.id) + if (match.found) return globalSync.data.projects[match.index] + return undefined + }, session: { get(sessionID: string) { const match = Binary.search(store.session, sessionID, (s) => s.id) diff --git a/packages/desktop/src/pages/home.tsx b/packages/desktop/src/pages/home.tsx index 517926613..a4491f813 100644 --- a/packages/desktop/src/pages/home.tsx +++ b/packages/desktop/src/pages/home.tsx @@ -7,6 +7,7 @@ import { useNavigate } from "@solidjs/router" import { base64Encode } from "@opencode-ai/util/encode" import { Icon } from "@opencode-ai/ui/icon" import { usePlatform } from "@/context/platform" +import { DateTime } from "luxon" export default function Home() { const sync = useGlobalSync() @@ -47,8 +48,12 @@ export default function Home() { -
    - +
      + (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created)) + .slice(0, 5)} + > {(project) => ( )} -
+ diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 68a2418f4..08e5f4f01 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -401,15 +401,19 @@ export default function Page() { {getFilename(sync.data.path.directory)} -
- -
- Last modified  - - {DateTime.fromMillis(sync.data.project.time.created).toRelative()} - -
-
+ + {(project) => ( +
+ +
+ Last modified  + + {DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()} + +
+
+ )} +
From b9fa7d91630fdaddf1744ba73b6dfcc8821e0b28 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:49:00 -0600 Subject: [PATCH 2/9] wip(desktop): progress --- packages/desktop/src/context/layout.tsx | 33 +++++++++----- packages/desktop/src/pages/layout.tsx | 43 +++++++++++-------- packages/enterprise/src/core/share.ts | 2 +- .../enterprise/src/routes/share/[shareID].tsx | 2 +- packages/ui/src/components/message-nav.tsx | 2 +- packages/ui/src/components/message-part.tsx | 2 +- .../ui/src/components/message-progress.tsx | 2 +- .../src/components/session-message-rail.tsx | 2 +- packages/ui/src/components/session-review.tsx | 2 +- packages/ui/src/components/session-turn.tsx | 2 +- packages/ui/src/context/data.tsx | 2 +- packages/util/src/sanitize.ts | 2 +- 12 files changed, 57 insertions(+), 39 deletions(-) diff --git a/packages/desktop/src/context/layout.tsx b/packages/desktop/src/context/layout.tsx index 58d947af4..b7d1fabb5 100644 --- a/packages/desktop/src/context/layout.tsx +++ b/packages/desktop/src/context/layout.tsx @@ -12,7 +12,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( const globalSync = useGlobalSync() const [store, setStore] = makePersisted( createStore({ - projects: [] as { directory: string; expanded: boolean }[], + projects: [] as { worktree: string; expanded: boolean }[], sidebar: { opened: false, width: 280, @@ -26,7 +26,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }, }), { - name: "default-layout.v4", + name: "default-layout.v6", }, ) @@ -43,32 +43,43 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( onMount(() => { Promise.all( - store.projects.map(({ directory }) => { - return loadProjectSessions(directory) + store.projects.map(({ worktree }) => { + return loadProjectSessions(worktree) }), ) }) + function enrich(project: { worktree: string; expanded: boolean }) { + const metadata = globalSync.data.projects.find((x) => x.worktree === project.worktree) + if (!metadata) return [] + return [ + { + ...project, + ...metadata, + }, + ] + } + return { projects: { - list: createMemo(() => store.projects), + list: createMemo(() => store.projects.flatMap(enrich)), open(directory: string) { - if (store.projects.find((x) => x.directory === directory)) return + if (store.projects.find((x) => x.worktree === directory)) return loadProjectSessions(directory) - setStore("projects", (x) => [...x, { directory, expanded: true }]) + setStore("projects", (x) => [...x, { worktree: directory, expanded: true }]) }, close(directory: string) { - setStore("projects", (x) => x.filter((x) => x.directory !== directory)) + setStore("projects", (x) => x.filter((x) => x.worktree !== directory)) }, expand(directory: string) { - setStore("projects", (x) => x.map((x) => (x.directory === directory ? { ...x, expanded: true } : x))) + setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: true } : x))) }, collapse(directory: string) { - setStore("projects", (x) => x.map((x) => (x.directory === directory ? { ...x, expanded: false } : x))) + setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: false } : x))) }, move(directory: string, toIndex: number) { setStore("projects", (projects) => { - const fromIndex = projects.findIndex((x) => x.directory === directory) + const fromIndex = projects.findIndex((x) => x.worktree === directory) if (fromIndex === -1 || fromIndex === toIndex) return projects const result = [...projects] const [item] = result.splice(fromIndex, 1) diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 4d3f6a268..c2755b9dc 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -16,7 +16,7 @@ import { DiffChanges } from "@opencode-ai/ui/diff-changes" import { getFilename } from "@opencode-ai/util/path" import { Select } from "@opencode-ai/ui/select" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" -import { Session } from "@opencode-ai/sdk/v2/client" +import { Session, Project } from "@opencode-ai/sdk/v2/client" import { usePlatform } from "@/context/platform" import { createStore } from "solid-js/store" import { @@ -106,8 +106,8 @@ export default function Layout(props: ParentProps) { const { draggable, droppable } = event if (draggable && droppable) { const projects = layout.projects.list() - const fromIndex = projects.findIndex((p) => p.directory === draggable.id.toString()) - const toIndex = projects.findIndex((p) => p.directory === droppable.id.toString()) + const fromIndex = projects.findIndex((p) => p.worktree === draggable.id.toString()) + const toIndex = projects.findIndex((p) => p.worktree === droppable.id.toString()) if (fromIndex !== toIndex && toIndex !== -1) { layout.projects.move(draggable.id.toString(), toIndex) } @@ -176,11 +176,11 @@ export default function Layout(props: ParentProps) { ) } - const SortableProject = (props: { project: { directory: string; expanded: boolean } }): JSX.Element => { - const sortable = createSortable(props.project.directory) - const [projectStore] = globalSync.child(props.project.directory) - const slug = createMemo(() => base64Encode(props.project.directory)) - const name = createMemo(() => getFilename(props.project.directory)) + const SortableProject = (props: { project: Project & { expanded: boolean } }): JSX.Element => { + const sortable = createSortable(props.project.worktree) + const [projectStore] = globalSync.child(props.project.worktree) + const slug = createMemo(() => base64Encode(props.project.worktree)) + const name = createMemo(() => getFilename(props.project.worktree)) return ( // @ts-ignore
@@ -194,11 +194,18 @@ export default function Layout(props: ParentProps) { >
- + + + {(url) => } + + + + + - closeProject(props.project.directory)}> + closeProject(props.project.worktree)}> Close Project @@ -274,8 +281,8 @@ export default function Layout(props: ParentProps) { - - + + @@ -315,7 +322,7 @@ export default function Layout(props: ParentProps) {