Przeglądaj źródła

feat(desktop): incrementally load sessions in side nav

Adam 3 miesięcy temu
rodzic
commit
e006e3355c

+ 16 - 6
packages/desktop/src/context/sync.tsx

@@ -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) =>
-          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!)),
       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,

+ 54 - 41
packages/desktop/src/pages/layout.tsx

@@ -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">
-              {(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-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">
-                          <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>
         </div>
         </div>
         <main class="size-full overflow-x-hidden">{props.children}</main>
         <main class="size-full overflow-x-hidden">{props.children}</main>