summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-11-05 16:32:05 -0600
committerAdam <[email protected]>2025-11-05 16:32:08 -0600
commite006e3355cff3de25e023edcee0b59985e7db66b (patch)
tree97d54c28fe10801fadfdb28adce18d1c68c4c0ea
parentd7e31f76c480be81985590dfabcca353be8442bc (diff)
downloadopencode-e006e3355cff3de25e023edcee0b59985e7db66b.tar.gz
opencode-e006e3355cff3de25e023edcee0b59985e7db66b.zip
feat(desktop): incrementally load sessions in side nav
-rw-r--r--packages/desktop/src/context/sync.tsx22
-rw-r--r--packages/desktop/src/pages/layout.tsx95
2 files changed, 70 insertions, 47 deletions
diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx
index c60206b0b..1e960397b 100644
--- a/packages/desktop/src/context/sync.tsx
+++ b/packages/desktop/src/context/sync.tsx
@@ -16,6 +16,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
config: Config
path: Path
session: Session[]
+ limit: number
+ more: boolean
message: {
[sessionID: string]: Message[]
}
@@ -32,6 +34,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
agent: [],
provider: [],
session: [],
+ limit: 10,
+ more: false,
message: {},
part: {},
node: [],
@@ -106,12 +110,14 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
path: () => sdk.client.path.get().then((x) => setStore("path", x.data!)),
agent: () => sdk.client.app.agents().then((x) => setStore("agent", x.data ?? [])),
session: () =>
- sdk.client.session.list().then((x) =>
- setStore(
- "session",
- (x.data ?? []).slice().sort((a, b) => a.id.localeCompare(b.id)),
- ),
- ),
+ sdk.client.session.list().then((x) => {
+ const sessions = (x.data ?? [])
+ .slice()
+ .sort((a, b) => a.id.localeCompare(b.id))
+ .slice(0, store.limit)
+ setStore("session", sessions)
+ setStore("more", sessions.length === store.limit)
+ }),
config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)),
changes: () => sdk.client.file.status().then((x) => setStore("changes", x.data!)),
node: () => sdk.client.file.list({ query: { path: "/" } }).then((x) => setStore("node", x.data!)),
@@ -184,6 +190,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
}),
)
},
+ fetch: async (count = 10) => {
+ setStore("limit", (x) => x + count)
+ await load.session()
+ },
},
load,
absolute,
diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx
index 22f0a50de..1d9669604 100644
--- a/packages/desktop/src/pages/layout.tsx
+++ b/packages/desktop/src/pages/layout.tsx
@@ -1,5 +1,5 @@
import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui"
-import { createMemo, ParentProps, Show } from "solid-js"
+import { createMemo, For, ParentProps, Show } from "solid-js"
import { getFilename } from "@/utils"
import { DateTime } from "luxon"
import { useSync } from "@/context/sync"
@@ -9,61 +9,74 @@ import { A, useParams } from "@solidjs/router"
export default function Layout(props: ParentProps) {
const params = useParams()
const sync = useSync()
+
return (
<div class="relative h-screen flex flex-col">
<header class="hidden h-12 shrink-0 bg-background-strong border-b border-border-weak-base"></header>
<div class="h-[calc(100vh-0rem)] flex">
<div class="w-70 shrink-0 bg-background-weak border-r border-border-weak-base flex flex-col items-start">
- <div class="h-10 flex items-center self-stretch px-5 border-b border-border-weak-base">
+ <div class="h-10 shrink-0 flex items-center self-stretch px-5 border-b border-border-weak-base">
<span class="text-14-regular overflow-hidden text-ellipsis">{getFilename(sync.data.path.directory)}</span>
</div>
- <div class="flex flex-col items-start gap-4 self-stretch flex-1 py-4 px-3">
+ <div class="flex flex-col items-start gap-4 self-stretch flex-1 py-4 px-3 overflow-hidden">
<A href="/session" class="w-full">
<Button class="w-full" size="large" icon="edit-small-2">
New Session
</Button>
</A>
- <VList data={sync.data.session} class="no-scrollbar">
- {(session) => {
- const updated = createMemo(() => DateTime.fromMillis(session.time.updated))
- return (
- <A
- data-active={session.id === params.id}
- href={`/session/${session.id}`}
- class="group/session focus:outline-none"
- >
- <Tooltip placement="right" value={session.title}>
- <div
- class="w-full mb-1.5 px-3 py-1 rounded-md
+ <div class="w-full h-full overflow-y-auto no-scrollbar flex flex-col flex-1">
+ <nav class="w-full">
+ <For each={sync.data.session}>
+ {(session) => {
+ const updated = createMemo(() => DateTime.fromMillis(session.time.updated))
+ return (
+ <A
+ data-active={session.id === params.id}
+ href={`/session/${session.id}`}
+ class="group/session focus:outline-none"
+ >
+ <Tooltip placement="right" value={session.title}>
+ <div
+ class="w-full mb-1.5 px-3 py-1 rounded-md
group-data-[active=true]/session:bg-surface-raised-base-hover
group-hover/session:bg-surface-raised-base-hover
group-focus/session:bg-surface-raised-base-hover"
- >
- <div class="flex items-center self-stretch gap-6 justify-between">
- <span class="text-14-regular text-text-strong overflow-hidden text-ellipsis truncate">
- {session.title}
- </span>
- <span class="text-12-regular text-text-weak text-right whitespace-nowrap">
- {Math.abs(updated().diffNow().as("seconds")) < 60
- ? "Now"
- : updated()
- .toRelative({ style: "short", unit: ["days", "hours", "minutes"] })
- ?.replace(" ago", "")
- ?.replace(/ days?/, "d")
- ?.replace(" min.", "m")
- ?.replace(" hr.", "h")}
- </span>
- </div>
- <div class="flex justify-between items-center self-stretch">
- <span class="text-12-regular text-text-weak">{`${session.summary?.files || "No"} file${session.summary?.files !== 1 ? "s" : ""} changed`}</span>
- <Show when={session.summary}>{(summary) => <DiffChanges changes={summary()} />}</Show>
- </div>
- </div>
- </Tooltip>
- </A>
- )
- }}
- </VList>
+ >
+ <div class="flex items-center self-stretch gap-6 justify-between">
+ <span class="text-14-regular text-text-strong overflow-hidden text-ellipsis truncate">
+ {session.title}
+ </span>
+ <span class="text-12-regular text-text-weak text-right whitespace-nowrap">
+ {Math.abs(updated().diffNow().as("seconds")) < 60
+ ? "Now"
+ : updated()
+ .toRelative({ style: "short", unit: ["days", "hours", "minutes"] })
+ ?.replace(" ago", "")
+ ?.replace(/ days?/, "d")
+ ?.replace(" min.", "m")
+ ?.replace(" hr.", "h")}
+ </span>
+ </div>
+ <div class="flex justify-between items-center self-stretch">
+ <span class="text-12-regular text-text-weak">{`${session.summary?.files || "No"} file${session.summary?.files !== 1 ? "s" : ""} changed`}</span>
+ <Show when={session.summary}>{(summary) => <DiffChanges changes={summary()} />}</Show>
+ </div>
+ </div>
+ </Tooltip>
+ </A>
+ )
+ }}
+ </For>
+ </nav>
+ <Show when={sync.data.more}>
+ <button
+ class="shrink-0 self-start p-3 text-12-medium text-text-weak hover:text-text-strong"
+ onClick={() => sync.session.fetch()}
+ >
+ Show more
+ </button>
+ </Show>
+ </div>
</div>
</div>
<main class="size-full overflow-x-hidden">{props.children}</main>