mirror of
https://github.com/aljazceru/opencode.git
synced 2026-01-27 19:54:56 +01:00
feat(desktop): incrementally load sessions in side nav
This commit is contained in:
@@ -16,6 +16,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
config: Config
|
config: Config
|
||||||
path: Path
|
path: Path
|
||||||
session: Session[]
|
session: Session[]
|
||||||
|
limit: number
|
||||||
|
more: boolean
|
||||||
message: {
|
message: {
|
||||||
[sessionID: string]: Message[]
|
[sessionID: string]: Message[]
|
||||||
}
|
}
|
||||||
@@ -32,6 +34,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
agent: [],
|
agent: [],
|
||||||
provider: [],
|
provider: [],
|
||||||
session: [],
|
session: [],
|
||||||
|
limit: 10,
|
||||||
|
more: false,
|
||||||
message: {},
|
message: {},
|
||||||
part: {},
|
part: {},
|
||||||
node: [],
|
node: [],
|
||||||
@@ -106,12 +110,14 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
path: () => sdk.client.path.get().then((x) => setStore("path", x.data!)),
|
path: () => sdk.client.path.get().then((x) => setStore("path", x.data!)),
|
||||||
agent: () => sdk.client.app.agents().then((x) => setStore("agent", x.data ?? [])),
|
agent: () => sdk.client.app.agents().then((x) => setStore("agent", x.data ?? [])),
|
||||||
session: () =>
|
session: () =>
|
||||||
sdk.client.session.list().then((x) =>
|
sdk.client.session.list().then((x) => {
|
||||||
setStore(
|
const sessions = (x.data ?? [])
|
||||||
"session",
|
.slice()
|
||||||
(x.data ?? []).slice().sort((a, b) => a.id.localeCompare(b.id)),
|
.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!)),
|
config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)),
|
||||||
changes: () => sdk.client.file.status().then((x) => setStore("changes", 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!)),
|
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,
|
load,
|
||||||
absolute,
|
absolute,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui"
|
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 { getFilename } from "@/utils"
|
||||||
import { DateTime } from "luxon"
|
import { DateTime } from "luxon"
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
@@ -9,61 +9,74 @@ import { A, useParams } from "@solidjs/router"
|
|||||||
export default function Layout(props: ParentProps) {
|
export default function Layout(props: ParentProps) {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative h-screen flex flex-col">
|
<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>
|
<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="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="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>
|
<span class="text-14-regular overflow-hidden text-ellipsis">{getFilename(sync.data.path.directory)}</span>
|
||||||
</div>
|
</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">
|
<A href="/session" class="w-full">
|
||||||
<Button class="w-full" size="large" icon="edit-small-2">
|
<Button class="w-full" size="large" icon="edit-small-2">
|
||||||
New Session
|
New Session
|
||||||
</Button>
|
</Button>
|
||||||
</A>
|
</A>
|
||||||
<VList data={sync.data.session} class="no-scrollbar">
|
<div class="w-full h-full overflow-y-auto no-scrollbar flex flex-col flex-1">
|
||||||
{(session) => {
|
<nav class="w-full">
|
||||||
const updated = createMemo(() => DateTime.fromMillis(session.time.updated))
|
<For each={sync.data.session}>
|
||||||
return (
|
{(session) => {
|
||||||
<A
|
const updated = createMemo(() => DateTime.fromMillis(session.time.updated))
|
||||||
data-active={session.id === params.id}
|
return (
|
||||||
href={`/session/${session.id}`}
|
<A
|
||||||
class="group/session focus:outline-none"
|
data-active={session.id === params.id}
|
||||||
>
|
href={`/session/${session.id}`}
|
||||||
<Tooltip placement="right" value={session.title}>
|
class="group/session focus:outline-none"
|
||||||
<div
|
>
|
||||||
class="w-full mb-1.5 px-3 py-1 rounded-md
|
<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-data-[active=true]/session:bg-surface-raised-base-hover
|
||||||
group-hover/session:bg-surface-raised-base-hover
|
group-hover/session:bg-surface-raised-base-hover
|
||||||
group-focus/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">
|
<div class="flex items-center self-stretch gap-6 justify-between">
|
||||||
<span class="text-14-regular text-text-strong overflow-hidden text-ellipsis truncate">
|
<span class="text-14-regular text-text-strong overflow-hidden text-ellipsis truncate">
|
||||||
{session.title}
|
{session.title}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-12-regular text-text-weak text-right whitespace-nowrap">
|
<span class="text-12-regular text-text-weak text-right whitespace-nowrap">
|
||||||
{Math.abs(updated().diffNow().as("seconds")) < 60
|
{Math.abs(updated().diffNow().as("seconds")) < 60
|
||||||
? "Now"
|
? "Now"
|
||||||
: updated()
|
: updated()
|
||||||
.toRelative({ style: "short", unit: ["days", "hours", "minutes"] })
|
.toRelative({ style: "short", unit: ["days", "hours", "minutes"] })
|
||||||
?.replace(" ago", "")
|
?.replace(" ago", "")
|
||||||
?.replace(/ days?/, "d")
|
?.replace(/ days?/, "d")
|
||||||
?.replace(" min.", "m")
|
?.replace(" min.", "m")
|
||||||
?.replace(" hr.", "h")}
|
?.replace(" hr.", "h")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center self-stretch">
|
<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>
|
<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>
|
<Show when={session.summary}>{(summary) => <DiffChanges changes={summary()} />}</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</A>
|
</A>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</VList>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
<main class="size-full overflow-x-hidden">{props.children}</main>
|
<main class="size-full overflow-x-hidden">{props.children}</main>
|
||||||
|
|||||||
Reference in New Issue
Block a user