Browse Source

chore: cleanup

adamelmore 1 month ago
parent
commit
09f45320b7

+ 10 - 8
packages/app/src/context/global-sdk.tsx

@@ -24,6 +24,7 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
     type Queued = { directory: string; payload: Event }
 
     let queue: Array<Queued | undefined> = []
+    let buffer: Array<Queued | undefined> = []
     const coalesced = new Map<string, number>()
     let timer: ReturnType<typeof setTimeout> | undefined
     let last = 0
@@ -41,10 +42,13 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
       if (timer) clearTimeout(timer)
       timer = undefined
 
+      if (queue.length === 0) return
+
       const events = queue
-      queue = []
+      queue = buffer
+      buffer = events
+      queue.length = 0
       coalesced.clear()
-      if (events.length === 0) return
 
       last = Date.now()
       batch(() => {
@@ -53,6 +57,8 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
           emitter.emit(event.directory, event.payload)
         }
       })
+
+      buffer.length = 0
     }
 
     const schedule = () => {
@@ -61,10 +67,6 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
       timer = setTimeout(flush, Math.max(0, 16 - elapsed))
     }
 
-    const stop = () => {
-      flush()
-    }
-
     void (async () => {
       const events = await eventSdk.global.event()
       let yielded = Date.now()
@@ -87,12 +89,12 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
         await new Promise<void>((resolve) => setTimeout(resolve, 0))
       }
     })()
-      .finally(stop)
+      .finally(flush)
       .catch(() => undefined)
 
     onCleanup(() => {
       abort.abort()
-      stop()
+      flush()
     })
 
     const sdk = createOpencodeClient({

+ 48 - 54
packages/app/src/context/global-sync.tsx

@@ -119,6 +119,16 @@ type ChildOptions = {
   bootstrap?: boolean
 }
 
+function normalizeProviderList(input: ProviderListResponse): ProviderListResponse {
+  return {
+    ...input,
+    all: input.all.map((provider) => ({
+      ...provider,
+      models: Object.fromEntries(Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated")),
+    })),
+  }
+}
+
 function createGlobalSync() {
   const globalSDK = useGlobalSDK()
   const platform = usePlatform()
@@ -129,6 +139,21 @@ function createGlobalSync() {
   const metaCache = new Map<string, MetaCache>()
   const iconCache = new Map<string, IconCache>()
 
+  const sdkCache = new Map<string, ReturnType<typeof createOpencodeClient>>()
+  const sdkFor = (directory: string) => {
+    const cached = sdkCache.get(directory)
+    if (cached) return cached
+
+    const sdk = createOpencodeClient({
+      baseUrl: globalSDK.url,
+      fetch: platform.fetch,
+      directory,
+      throwOnError: true,
+    })
+    sdkCache.set(directory, sdk)
+    return sdk
+  }
+
   const [projectCache, setProjectCache, , projectCacheReady] = persisted(
     Persist.global("globalSync.project", ["globalSync.project.v1"]),
     createStore({ value: [] as Project[] }),
@@ -183,7 +208,7 @@ function createGlobalSync() {
     setProjectCache("value", projects.map(sanitizeProject))
   })
 
-  createEffect(async () => {
+  createEffect(() => {
     if (globalStore.reload !== "complete") return
     if (bootstrapQueue.length) {
       for (const directory of bootstrapQueue) {
@@ -203,14 +228,16 @@ function createGlobalSync() {
   function ensureChild(directory: string) {
     if (!directory) console.error("No directory provided")
     if (!children[directory]) {
-      const cache = runWithOwner(owner, () =>
+      const vcs = runWithOwner(owner, () =>
         persisted(
           Persist.workspace(directory, "vcs", ["vcs.v1"]),
           createStore({ value: undefined as VcsInfo | undefined }),
         ),
       )
-      if (!cache) throw new Error("Failed to create persisted cache")
-      vcsCache.set(directory, { store: cache[0], setStore: cache[1], ready: cache[3] })
+      if (!vcs) throw new Error("Failed to create persisted cache")
+      const vcsStore = vcs[0]
+      const vcsReady = vcs[3]
+      vcsCache.set(directory, { store: vcsStore, setStore: vcs[1], ready: vcsReady })
 
       const meta = runWithOwner(owner, () =>
         persisted(
@@ -250,7 +277,7 @@ function createGlobalSync() {
           question: {},
           mcp: {},
           lsp: [],
-          vcs: cache[0].value,
+          vcs: vcsStore.value,
           limit: 5,
           message: {},
           part: {},
@@ -258,6 +285,13 @@ function createGlobalSync() {
 
         children[directory] = child
 
+        createEffect(() => {
+          if (!vcsReady()) return
+          const cached = vcsStore.value
+          if (!cached?.branch) return
+          child[1]("vcs", (value) => value ?? cached)
+        })
+
         createEffect(() => {
           child[1]("projectMeta", meta[0].value)
         })
@@ -297,7 +331,6 @@ function createGlobalSync() {
         const nonArchived = (x.data ?? [])
           .filter((s) => !!s?.id)
           .filter((s) => !s.time?.archived)
-          .slice()
           .sort((a, b) => a.id.localeCompare(b.id))
 
         // Read the current limit at resolve-time so callers that bump the limit while
@@ -348,38 +381,18 @@ function createGlobalSync() {
       if (!cache) return
       const meta = metaCache.get(directory)
       if (!meta) return
-      const sdk = createOpencodeClient({
-        baseUrl: globalSDK.url,
-        fetch: platform.fetch,
-        directory,
-        throwOnError: true,
-      })
+      const sdk = sdkFor(directory)
 
       setStore("status", "loading")
 
-      createEffect(() => {
-        if (!cache.ready()) return
-        const cached = cache.store.value
-        if (!cached?.branch) return
-        setStore("vcs", (value) => value ?? cached)
-      })
-
       // projectMeta is synced from persisted storage in ensureChild.
+      // vcs is seeded from persisted storage in ensureChild.
 
       const blockingRequests = {
         project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)),
         provider: () =>
           sdk.provider.list().then((x) => {
-            const data = x.data!
-            setStore("provider", {
-              ...data,
-              all: data.all.map((provider) => ({
-                ...provider,
-                models: Object.fromEntries(
-                  Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
-                ),
-              })),
-            })
+            setStore("provider", normalizeProviderList(x.data!))
           }),
         agent: () => sdk.app.agents().then((x) => setStore("agent", x.data ?? [])),
         config: () => sdk.config.get().then((x) => setStore("config", x.data!)),
@@ -432,10 +445,7 @@ function createGlobalSync() {
                 "permission",
                 sessionID,
                 reconcile(
-                  permissions
-                    .filter((p) => !!p?.id)
-                    .slice()
-                    .sort((a, b) => a.id.localeCompare(b.id)),
+                  permissions.filter((p) => !!p?.id).sort((a, b) => a.id.localeCompare(b.id)),
                   { key: "id" },
                 ),
               )
@@ -464,10 +474,7 @@ function createGlobalSync() {
                 "question",
                 sessionID,
                 reconcile(
-                  questions
-                    .filter((q) => !!q?.id)
-                    .slice()
-                    .sort((a, b) => a.id.localeCompare(b.id)),
+                  questions.filter((q) => !!q?.id).sort((a, b) => a.id.localeCompare(b.id)),
                   { key: "id" },
                 ),
               )
@@ -750,13 +757,9 @@ function createGlobalSync() {
         break
       }
       case "lsp.updated": {
-        const sdk = createOpencodeClient({
-          baseUrl: globalSDK.url,
-          fetch: platform.fetch,
-          directory,
-          throwOnError: true,
-        })
-        sdk.lsp.status().then((x) => setStore("lsp", x.data ?? []))
+        sdkFor(directory)
+          .lsp.status()
+          .then((x) => setStore("lsp", x.data ?? []))
         break
       }
     }
@@ -796,16 +799,7 @@ function createGlobalSync() {
       ),
       retry(() =>
         globalSDK.client.provider.list().then((x) => {
-          const data = x.data!
-          setGlobalStore("provider", {
-            ...data,
-            all: data.all.map((provider) => ({
-              ...provider,
-              models: Object.fromEntries(
-                Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
-              ),
-            })),
-          })
+          setGlobalStore("provider", normalizeProviderList(x.data!))
         }),
       ),
       retry(() =>

+ 30 - 10
packages/app/src/context/layout.tsx

@@ -209,6 +209,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
     })
 
     const [colors, setColors] = createStore<Record<string, AvatarColorKey>>({})
+    const colorRequested = new Map<string, AvatarColorKey>()
 
     function pickAvailableColor(used: Set<string>): AvatarColorKey {
       const available = AVATAR_COLOR_KEYS.filter((c) => !used.has(c))
@@ -324,13 +325,21 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
     createEffect(() => {
       const projects = enriched()
       if (projects.length === 0) return
+      if (!globalSync.ready) return
 
-      if (globalSync.ready) {
-        for (const project of projects) {
-          if (!project.id) continue
-          if (project.id === "global") continue
-          globalSync.project.icon(project.worktree, project.icon?.override)
-        }
+      for (const project of projects) {
+        if (!project.id) continue
+        if (project.id === "global") continue
+        globalSync.project.icon(project.worktree, project.icon?.override)
+      }
+    })
+
+    createEffect(() => {
+      const projects = enriched()
+      if (projects.length === 0) return
+
+      for (const project of projects) {
+        if (project.icon?.color) colorRequested.delete(project.worktree)
       }
 
       const used = new Set<string>()
@@ -341,18 +350,29 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
 
       for (const project of projects) {
         if (project.icon?.color) continue
-        const existing = colors[project.worktree]
+        const worktree = project.worktree
+        const existing = colors[worktree]
         const color = existing ?? pickAvailableColor(used)
         if (!existing) {
           used.add(color)
-          setColors(project.worktree, color)
+          setColors(worktree, color)
         }
         if (!project.id) continue
+
+        const requested = colorRequested.get(worktree)
+        if (requested === color) continue
+        colorRequested.set(worktree, color)
+
         if (project.id === "global") {
-          globalSync.project.meta(project.worktree, { icon: { color } })
+          globalSync.project.meta(worktree, { icon: { color } })
           continue
         }
-        void globalSdk.client.project.update({ projectID: project.id, directory: project.worktree, icon: { color } })
+
+        void globalSdk.client.project
+          .update({ projectID: project.id, directory: worktree, icon: { color } })
+          .catch(() => {
+            if (colorRequested.get(worktree) === color) colorRequested.delete(worktree)
+          })
       }
     })
 

+ 2 - 10
packages/app/src/context/sync.tsx

@@ -72,7 +72,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
           const next = items
             .map((x) => x.info)
             .filter((m) => !!m?.id)
-            .slice()
             .sort((a, b) => a.id.localeCompare(b.id))
 
           batch(() => {
@@ -83,10 +82,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
                 "part",
                 message.info.id,
                 reconcile(
-                  message.parts
-                    .filter((p) => !!p?.id)
-                    .slice()
-                    .sort((a, b) => a.id.localeCompare(b.id)),
+                  message.parts.filter((p) => !!p?.id).sort((a, b) => a.id.localeCompare(b.id)),
                   { key: "id" },
                 ),
               )
@@ -146,10 +142,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
                 const result = Binary.search(messages, input.messageID, (m) => m.id)
                 messages.splice(result.index, 0, message)
               }
-              draft.part[input.messageID] = input.parts
-                .filter((p) => !!p?.id)
-                .slice()
-                .sort((a, b) => a.id.localeCompare(b.id))
+              draft.part[input.messageID] = input.parts.filter((p) => !!p?.id).sort((a, b) => a.id.localeCompare(b.id))
             }),
           )
         },
@@ -291,7 +284,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
           await client.session.list().then((x) => {
             const sessions = (x.data ?? [])
               .filter((s) => !!s?.id)
-              .slice()
               .sort((a, b) => a.id.localeCompare(b.id))
               .slice(0, store.limit)
             setStore("session", reconcile(sessions, { key: "id" }))