Bläddra i källkod

fix(app): search every known project in the open project dialog (#40202)

Joshua 1 vecka sedan
förälder
incheckning
3016830e25

+ 60 - 0
packages/app/e2e/regression/project-picker-recent-search.spec.ts

@@ -0,0 +1,60 @@
+import { expect, test } from "@playwright/test"
+import type { Page } from "@playwright/test"
+import { fixture, pageMessages } from "../smoke/session-timeline.fixture"
+import { mockOpenCodeServer } from "../utils/mock-server"
+import { expectAppVisible } from "../utils/waits"
+
+const NAMES = ["alpha-service", "bravo-web", "charlie-api", "delta-tools", "echo-infra", "foxtrot-docs"]
+const worktrees = NAMES.map((name) => `/opencode-demo/${name}`)
+
+// The sixth project sits outside the five-item recent cap, so it is only reachable if the
+// dialog hands every recent project to the list filter instead of a pre-truncated slice.
+const OUTSIDE_CAP = "foxtrot-docs"
+
+// Dialog rows carry data-directory-path; the sidebar project list does not, so this
+// scopes assertions to the picker instead of matching the sidebar entry of the same name.
+const rows = (page: Page) => page.locator("[data-directory-path]")
+const row = (page: Page, name: string) => page.locator(`[data-directory-path*="${name}"]`)
+
+async function openProjectDialog(page: Page) {
+  await mockOpenCodeServer(page, {
+    sessions: fixture.sessions,
+    provider: fixture.provider,
+    directory: fixture.directory,
+    project: fixture.project,
+    pageMessages,
+    fileList: () => [],
+    findFiles: () => [],
+  })
+  await page.addInitScript((dirs) => {
+    localStorage.setItem(
+      "opencode.global.dat:server",
+      JSON.stringify({
+        projects: { local: dirs.map((worktree: string) => ({ worktree, expanded: false })) },
+        lastProject: {},
+      }),
+    )
+  }, worktrees)
+  await page.goto("/")
+  const add = page.getByRole("button", { name: "Add project" }).first()
+  await expectAppVisible(add)
+  await add.click()
+  await expect(rows(page)).toHaveCount(5)
+  return page.getByRole("textbox").last()
+}
+
+test("searches every recent project, not just the five most recent", async ({ page }) => {
+  const search = await openProjectDialog(page)
+  await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
+
+  await search.fill("foxtrot")
+
+  await expect(row(page, OUTSIDE_CAP)).toHaveCount(1)
+})
+
+test("still caps the idle recent list at five projects", async ({ page }) => {
+  await openProjectDialog(page)
+
+  await expect(row(page, NAMES[4])).toHaveCount(1)
+  await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
+})

+ 6 - 2
packages/app/src/components/dialog-select-directory.tsx

@@ -18,6 +18,8 @@ interface DialogSelectDirectoryProps {
   server: ServerConnection.Any
   server: ServerConnection.Any
 }
 }
 
 
+const RECENT_PROJECT_LIMIT = 5
+
 type Row = {
 type Row = {
   absolute: string
   absolute: string
   search: string
   search: string
@@ -102,7 +104,6 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
     return projects
     return projects
       .map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
       .map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
       .sort((a, b) => b.at - a.at || a.index - b.index)
       .sort((a, b) => b.at - a.at || a.index - b.index)
-      .slice(0, 5)
       .map(({ project }) => {
       .map(({ project }) => {
         const row = toRow(project.worktree, home(), "recent")
         const row = toRow(project.worktree, home(), "recent")
         const name = project.name || getFilename(project.worktree)
         const name = project.name || getFilename(project.worktree)
@@ -116,7 +117,10 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
   const items = async (value: string) => {
   const items = async (value: string) => {
     const results = await directories(value)
     const results = await directories(value)
     const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
     const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
-    return uniqueRows([...recentProjects(), ...directoryRows])
+    // Cap the idle list only. Once a query narrows the results, every project stays searchable.
+    const recent = recentProjects()
+    const visible = value ? recent : recent.slice(0, RECENT_PROJECT_LIMIT)
+    return uniqueRows([...visible, ...directoryRows])
   }
   }
 
 
   function resolve(absolute: string) {
   function resolve(absolute: string) {