Kaynağa Gözat

Revert "fix(app): more startup efficiency (#18985)"

This reverts commit 98b3340ceeb6928d0d57898d02665d763ef1ea9c.
Adam 3 hafta önce
ebeveyn
işleme
cbe1337f24

+ 0 - 1
packages/app/src/components/prompt-input.tsx

@@ -572,7 +572,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
       const open = recent()
       const seen = new Set(open)
       const pinned: AtOption[] = open.map((path) => ({ type: "file", path, display: path, recent: true }))
-      if (!query.trim()) return [...agents, ...pinned]
       const paths = await files.searchFilesAndDirectories(query)
       const fileOptions: AtOption[] = paths
         .filter((path) => !seen.has(path))

+ 121 - 189
packages/app/src/context/global-sync/bootstrap.ts

@@ -31,47 +31,6 @@ type GlobalStore = {
   reload: undefined | "pending" | "complete"
 }
 
-function waitForPaint() {
-  return new Promise<void>((resolve) => {
-    let done = false
-    const finish = () => {
-      if (done) return
-      done = true
-      resolve()
-    }
-    const timer = setTimeout(finish, 50)
-    if (typeof requestAnimationFrame !== "function") return
-    requestAnimationFrame(() => {
-      clearTimeout(timer)
-      finish()
-    })
-  })
-}
-
-function errors(list: PromiseSettledResult<unknown>[]) {
-  return list.filter((item): item is PromiseRejectedResult => item.status === "rejected").map((item) => item.reason)
-}
-
-function runAll(list: Array<() => Promise<unknown>>) {
-  return Promise.allSettled(list.map((item) => item()))
-}
-
-function showErrors(input: {
-  errors: unknown[]
-  title: string
-  translate: (key: string, vars?: Record<string, string | number>) => string
-  formatMoreCount: (count: number) => string
-}) {
-  if (input.errors.length === 0) return
-  const message = formatServerError(input.errors[0], input.translate)
-  const more = input.errors.length > 1 ? input.formatMoreCount(input.errors.length - 1) : ""
-  showToast({
-    variant: "error",
-    title: input.title,
-    description: message + more,
-  })
-}
-
 export async function bootstrapGlobal(input: {
   globalSDK: OpencodeClient
   requestFailedTitle: string
@@ -79,54 +38,45 @@ export async function bootstrapGlobal(input: {
   formatMoreCount: (count: number) => string
   setGlobalStore: SetStoreFunction<GlobalStore>
 }) {
-  const fast = [
-    () =>
-      retry(() =>
-        input.globalSDK.path.get().then((x) => {
-          input.setGlobalStore("path", x.data!)
-        }),
-      ),
-    () =>
-      retry(() =>
-        input.globalSDK.global.config.get().then((x) => {
-          input.setGlobalStore("config", x.data!)
-        }),
-      ),
-    () =>
-      retry(() =>
-        input.globalSDK.provider.list().then((x) => {
-          input.setGlobalStore("provider", normalizeProviderList(x.data!))
-        }),
-      ),
+  const tasks = [
+    retry(() =>
+      input.globalSDK.path.get().then((x) => {
+        input.setGlobalStore("path", x.data!)
+      }),
+    ),
+    retry(() =>
+      input.globalSDK.global.config.get().then((x) => {
+        input.setGlobalStore("config", x.data!)
+      }),
+    ),
+    retry(() =>
+      input.globalSDK.project.list().then((x) => {
+        const projects = (x.data ?? [])
+          .filter((p) => !!p?.id)
+          .filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
+          .slice()
+          .sort((a, b) => cmp(a.id, b.id))
+        input.setGlobalStore("project", projects)
+      }),
+    ),
+    retry(() =>
+      input.globalSDK.provider.list().then((x) => {
+        input.setGlobalStore("provider", normalizeProviderList(x.data!))
+      }),
+    ),
   ]
 
-  const slow = [
-    () =>
-      retry(() =>
-        input.globalSDK.project.list().then((x) => {
-          const projects = (x.data ?? [])
-            .filter((p) => !!p?.id)
-            .filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
-            .slice()
-            .sort((a, b) => cmp(a.id, b.id))
-          input.setGlobalStore("project", projects)
-        }),
-      ),
-  ]
-
-  showErrors({
-    errors: errors(await runAll(fast)),
-    title: input.requestFailedTitle,
-    translate: input.translate,
-    formatMoreCount: input.formatMoreCount,
-  })
-  await waitForPaint()
-  showErrors({
-    errors: errors(await runAll(slow)),
-    title: input.requestFailedTitle,
-    translate: input.translate,
-    formatMoreCount: input.formatMoreCount,
-  })
+  const results = await Promise.allSettled(tasks)
+  const errors = results.filter((r): r is PromiseRejectedResult => r.status === "rejected").map((r) => r.reason)
+  if (errors.length) {
+    const message = formatServerError(errors[0], input.translate)
+    const more = errors.length > 1 ? input.formatMoreCount(errors.length - 1) : ""
+    showToast({
+      variant: "error",
+      title: input.requestFailedTitle,
+      description: message + more,
+    })
+  }
   input.setGlobalStore("ready", true)
 }
 
@@ -169,113 +119,95 @@ export async function bootstrapDirectory(input: {
   }
   if (loading) input.setStore("status", "partial")
 
-  const fast = [
-    () =>
-      seededProject
-        ? Promise.resolve()
-        : retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id)),
-    () => retry(() => input.sdk.app.agents().then((x) => input.setStore("agent", x.data ?? []))),
-    () => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))),
-    () =>
-      retry(() =>
-        input.sdk.path.get().then((x) => {
-          input.setStore("path", x.data!)
-          const next = projectID(x.data?.directory ?? input.directory, input.global.project)
-          if (next) input.setStore("project", next)
-        }),
-      ),
-    () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))),
-    () =>
-      retry(() =>
-        input.sdk.vcs.get().then((x) => {
-          const next = x.data ?? input.store.vcs
-          input.setStore("vcs", next)
-          if (next?.branch) input.vcsCache.setStore("value", next)
-        }),
-      ),
-    () => retry(() => input.sdk.command.list().then((x) => input.setStore("command", x.data ?? []))),
-    () =>
-      retry(() =>
-        input.sdk.permission.list().then((x) => {
-          const grouped = groupBySession(
-            (x.data ?? []).filter((perm): perm is PermissionRequest => !!perm?.id && !!perm.sessionID),
-          )
-          batch(() => {
-            for (const sessionID of Object.keys(input.store.permission)) {
-              if (grouped[sessionID]) continue
-              input.setStore("permission", sessionID, [])
-            }
-            for (const [sessionID, permissions] of Object.entries(grouped)) {
-              input.setStore(
-                "permission",
-                sessionID,
-                reconcile(
-                  permissions.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id)),
-                  { key: "id" },
-                ),
-              )
-            }
-          })
-        }),
-      ),
-    () =>
-      retry(() =>
-        input.sdk.question.list().then((x) => {
-          const grouped = groupBySession((x.data ?? []).filter((q): q is QuestionRequest => !!q?.id && !!q.sessionID))
-          batch(() => {
-            for (const sessionID of Object.keys(input.store.question)) {
-              if (grouped[sessionID]) continue
-              input.setStore("question", sessionID, [])
-            }
-            for (const [sessionID, questions] of Object.entries(grouped)) {
-              input.setStore(
-                "question",
-                sessionID,
-                reconcile(
-                  questions.filter((q) => !!q?.id).sort((a, b) => cmp(a.id, b.id)),
-                  { key: "id" },
-                ),
-              )
-            }
-          })
-        }),
-      ),
-  ]
-
-  const slow = [
-    () =>
-      retry(() =>
-        input.sdk.provider.list().then((x) => {
-          input.setStore("provider", normalizeProviderList(x.data!))
-        }),
-      ),
-    () => Promise.resolve(input.loadSessions(input.directory)),
-    () => retry(() => input.sdk.mcp.status().then((x) => input.setStore("mcp", x.data!))),
-    () => retry(() => input.sdk.lsp.status().then((x) => input.setStore("lsp", x.data!))),
-  ]
-
-  const errs = errors(await runAll(fast))
-  if (errs.length > 0) {
-    console.error("Failed to bootstrap instance", errs[0])
-    const project = getFilename(input.directory)
-    showToast({
-      variant: "error",
-      title: input.translate("toast.project.reloadFailed.title", { project }),
-      description: formatServerError(errs[0], input.translate),
-    })
-  }
-
-  await waitForPaint()
-  const slowErrs = errors(await runAll(slow))
-  if (slowErrs.length > 0) {
-    console.error("Failed to finish bootstrap instance", slowErrs[0])
+  const results = await Promise.allSettled([
+    seededProject
+      ? Promise.resolve()
+      : retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id)),
+    retry(() =>
+      input.sdk.provider.list().then((x) => {
+        input.setStore("provider", normalizeProviderList(x.data!))
+      }),
+    ),
+    retry(() => input.sdk.app.agents().then((x) => input.setStore("agent", x.data ?? []))),
+    retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))),
+    retry(() =>
+      input.sdk.path.get().then((x) => {
+        input.setStore("path", x.data!)
+        const next = projectID(x.data?.directory ?? input.directory, input.global.project)
+        if (next) input.setStore("project", next)
+      }),
+    ),
+    retry(() => input.sdk.command.list().then((x) => input.setStore("command", x.data ?? []))),
+    retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))),
+    input.loadSessions(input.directory),
+    retry(() => input.sdk.mcp.status().then((x) => input.setStore("mcp", x.data!))),
+    retry(() => input.sdk.lsp.status().then((x) => input.setStore("lsp", x.data!))),
+    retry(() =>
+      input.sdk.vcs.get().then((x) => {
+        const next = x.data ?? input.store.vcs
+        input.setStore("vcs", next)
+        if (next?.branch) input.vcsCache.setStore("value", next)
+      }),
+    ),
+    retry(() =>
+      input.sdk.permission.list().then((x) => {
+        const grouped = groupBySession(
+          (x.data ?? []).filter((perm): perm is PermissionRequest => !!perm?.id && !!perm.sessionID),
+        )
+        batch(() => {
+          for (const sessionID of Object.keys(input.store.permission)) {
+            if (grouped[sessionID]) continue
+            input.setStore("permission", sessionID, [])
+          }
+          for (const [sessionID, permissions] of Object.entries(grouped)) {
+            input.setStore(
+              "permission",
+              sessionID,
+              reconcile(
+                permissions.filter((p) => !!p?.id).sort((a, b) => cmp(a.id, b.id)),
+                { key: "id" },
+              ),
+            )
+          }
+        })
+      }),
+    ),
+    retry(() =>
+      input.sdk.question.list().then((x) => {
+        const grouped = groupBySession((x.data ?? []).filter((q): q is QuestionRequest => !!q?.id && !!q.sessionID))
+        batch(() => {
+          for (const sessionID of Object.keys(input.store.question)) {
+            if (grouped[sessionID]) continue
+            input.setStore("question", sessionID, [])
+          }
+          for (const [sessionID, questions] of Object.entries(grouped)) {
+            input.setStore(
+              "question",
+              sessionID,
+              reconcile(
+                questions.filter((q) => !!q?.id).sort((a, b) => cmp(a.id, b.id)),
+                { key: "id" },
+              ),
+            )
+          }
+        })
+      }),
+    ),
+  ])
+
+  const errors = results
+    .filter((item): item is PromiseRejectedResult => item.status === "rejected")
+    .map((item) => item.reason)
+  if (errors.length > 0) {
+    console.error("Failed to bootstrap instance", errors[0])
     const project = getFilename(input.directory)
     showToast({
       variant: "error",
       title: input.translate("toast.project.reloadFailed.title", { project }),
-      description: formatServerError(slowErrs[0], input.translate),
+      description: formatServerError(errors[0], input.translate),
     })
+    return
   }
 
-  if (loading && errs.length === 0 && slowErrs.length === 0) input.setStore("status", "complete")
+  if (loading) input.setStore("status", "complete")
 }

+ 2 - 5
packages/app/src/context/settings.tsx

@@ -118,11 +118,8 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
 
     createEffect(() => {
       if (typeof document === "undefined") return
-      const id = store.appearance?.font ?? defaultSettings.appearance.font
-      if (id !== defaultSettings.appearance.font) {
-        void loadFont().then((x) => x.ensureMonoFont(id))
-      }
-      document.documentElement.style.setProperty("--font-family-mono", monoFontFamily(id))
+      void loadFont().then((x) => x.ensureMonoFont(store.appearance?.font))
+      document.documentElement.style.setProperty("--font-family-mono", monoFontFamily(store.appearance?.font))
     })
 
     return {

+ 3 - 4
packages/app/src/context/sync.tsx

@@ -180,8 +180,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
       return globalSync.child(directory)
     }
     const absolute = (path: string) => (current()[0].path.directory + "/" + path).replace("//", "/")
-    const initialMessagePageSize = 80
-    const historyMessagePageSize = 200
+    const messagePageSize = 200
     const inflight = new Map<string, Promise<void>>()
     const inflightDiff = new Map<string, Promise<void>>()
     const inflightTodo = new Map<string, Promise<void>>()
@@ -464,7 +463,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
             const cached = store.message[sessionID] !== undefined && meta.limit[key] !== undefined
             if (cached && hasSession && !opts?.force) return
 
-            const limit = meta.limit[key] ?? initialMessagePageSize
+            const limit = meta.limit[key] ?? messagePageSize
             const sessionReq =
               hasSession && !opts?.force
                 ? Promise.resolve()
@@ -561,7 +560,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
             const [, setStore] = globalSync.child(directory)
             touch(directory, setStore, sessionID)
             const key = keyFor(directory, sessionID)
-            const step = count ?? historyMessagePageSize
+            const step = count ?? messagePageSize
             if (meta.loading[key]) return
             if (meta.complete[key]) return
             const before = meta.cursor[key]

+ 0 - 8
packages/app/src/pages/home.tsx

@@ -113,14 +113,6 @@ export default function Home() {
             </ul>
           </div>
         </Match>
-        <Match when={!sync.ready}>
-          <div class="mt-30 mx-auto flex flex-col items-center gap-3">
-            <div class="text-12-regular text-text-weak">{language.t("common.loading")}</div>
-            <Button class="px-3" onClick={chooseProject}>
-              {language.t("command.project.open")}
-            </Button>
-          </div>
-        </Match>
         <Match when={true}>
           <div class="mt-30 mx-auto flex flex-col items-center gap-3">
             <Icon name="folder-add-left" size="large" />

+ 3 - 4
packages/app/src/pages/session.tsx

@@ -1184,6 +1184,8 @@ export default function Page() {
     on(
       () => sdk.directory,
       () => {
+        void file.tree.list("")
+
         const tab = activeFileTab()
         if (!tab) return
         const path = file.pathFromTab(tab)
@@ -1638,9 +1640,6 @@ export default function Page() {
     sessionID: () => params.id,
     messagesReady,
     visibleUserMessages,
-    historyMore,
-    historyLoading,
-    loadMore: (sessionID) => sync.session.history.loadMore(sessionID),
     turnStart: historyWindow.turnStart,
     currentMessageId: () => store.messageId,
     pendingMessage: () => ui.pendingMessage,
@@ -1712,7 +1711,7 @@ export default function Page() {
           <div class="flex-1 min-h-0 overflow-hidden">
             <Switch>
               <Match when={params.id}>
-                <Show when={messagesReady()}>
+                <Show when={lastUserMessage()}>
                   <MessageTimeline
                     mobileChanges={mobileChanges()}
                     mobileFallback={reviewContent({

+ 0 - 18
packages/app/src/pages/session/use-session-hash-scroll.ts

@@ -8,9 +8,6 @@ export const useSessionHashScroll = (input: {
   sessionID: () => string | undefined
   messagesReady: () => boolean
   visibleUserMessages: () => UserMessage[]
-  historyMore: () => boolean
-  historyLoading: () => boolean
-  loadMore: (sessionID: string) => Promise<void>
   turnStart: () => number
   currentMessageId: () => string | undefined
   pendingMessage: () => string | undefined
@@ -184,21 +181,6 @@ export const useSessionHashScroll = (input: {
     queue(() => scrollToMessage(msg, "auto"))
   })
 
-  createEffect(() => {
-    const sessionID = input.sessionID()
-    if (!sessionID || !input.messagesReady()) return
-
-    visibleUserMessages()
-
-    let targetId = input.pendingMessage()
-    if (!targetId && !clearing) targetId = messageIdFromHash(location.hash)
-    if (!targetId) return
-    if (messageById().has(targetId)) return
-    if (!input.historyMore() || input.historyLoading()) return
-
-    void input.loadMore(sessionID)
-  })
-
   onMount(() => {
     if (typeof window !== "undefined" && "scrollRestoration" in window.history) {
       window.history.scrollRestoration = "manual"

+ 0 - 12
packages/app/vite.js

@@ -1,10 +1,7 @@
-import { readFileSync } from "node:fs"
 import solidPlugin from "vite-plugin-solid"
 import tailwindcss from "@tailwindcss/vite"
 import { fileURLToPath } from "url"
 
-const theme = fileURLToPath(new URL("./public/oc-theme-preload.js", import.meta.url))
-
 /**
  * @type {import("vite").PluginOption}
  */
@@ -24,15 +21,6 @@ export default [
       }
     },
   },
-  {
-    name: "opencode-desktop:theme-preload",
-    transformIndexHtml(html) {
-      return html.replace(
-        '<script id="oc-theme-preload-script" src="/oc-theme-preload.js"></script>',
-        `<script id="oc-theme-preload-script">${readFileSync(theme, "utf8")}</script>`,
-      )
-    },
-  },
   tailwindcss(),
   solidPlugin(),
 ]