From eb37a7ebc73ce4ba51657dde547248c1b0879272 Mon Sep 17 00:00:00 2001 From: usrnk1 <7547651+usrnk1@users.noreply.github.com> Date: Thu, 10 Sep 2026 00:40:16 +0200 Subject: [PATCH] feat(desktop): polish branch search and session spacing (#48150) Co-authored-by: Hona <10430890+Hona@users.noreply.github.com> --- .../new-session-workspace-branch.spec.ts | 22 +++++++++++++++++++ .../regression/review-toggle-position.spec.ts | 9 -------- .../src/new-session/workspace/selector.tsx | 18 +++++++-------- .../app/src/session/header/session-header.tsx | 5 ++--- packages/app/src/session/screen.tsx | 1 + .../src/session/timeline/message-timeline.tsx | 3 ++- .../src/components/message-part.css | 2 +- 7 files changed, 37 insertions(+), 23 deletions(-) diff --git a/packages/app/e2e/regression/new-session-workspace-branch.spec.ts b/packages/app/e2e/regression/new-session-workspace-branch.spec.ts index 4ccbc1609f0..e2e05a88901 100644 --- a/packages/app/e2e/regression/new-session-workspace-branch.spec.ts +++ b/packages/app/e2e/regression/new-session-workspace-branch.spec.ts @@ -22,6 +22,10 @@ test("selects a base branch for a new workspace", async ({ page }) => { pageMessages: () => ({ items: [] }), vcsBranches: ["feature/api", "main", "origin/release"], }) + await page.route("**/api/vcs/branches?*", (route) => { + if (new URL(route.request().url()).searchParams.get("search") !== "feature") return route.fallback() + return route.fulfill({ json: { location: { directory }, data: ["feature/api"] } }) + }) await page.addInitScript( ({ directory, draftID, server }) => { localStorage.setItem( @@ -44,10 +48,28 @@ test("selects a base branch for a new workspace", async ({ page }) => { await page.getByRole("button", { name: "Local", exact: true }).click() await page.getByRole("menuitem", { name: "New worktree", exact: true }).click() await page.getByRole("button", { name: "from main", exact: true }).click() + const search = page.getByRole("textbox", { name: "Search branches", exact: true }) + await expect(search).toBeFocused() + await page.keyboard.type("feature") + await expect(search).toHaveValue("feature") + await expect(page.getByRole("menuitemradio")).toHaveText(["feature/api"]) + await expect(search).toBeFocused() await page.getByRole("menuitemradio", { name: "feature/api", exact: true }).click() const selected = page.getByRole("button", { name: "from feature/api", exact: true }) await expect(selected).toBeVisible() await selected.click() + await expect(search).toBeFocused() + await expect(search).toHaveValue("") await expect(page.getByRole("menuitemradio", { name: "feature/api", exact: true })).toBeChecked() + await page.keyboard.press("Escape") + await expect(selected).toBeFocused() + await page.keyboard.press("Enter") + await expect(search).toBeFocused() + await page.keyboard.type("feature") + await expect(search).toHaveValue("feature") + await expect(page.getByRole("menuitemradio")).toHaveText(["feature/api"]) + await page.getByRole("button", { name: "Clear", exact: true }).click() + await expect(search).toHaveValue("") + await expect(page.getByRole("menuitemradio")).toHaveText(["feature/api", "main", "origin/release"]) }) diff --git a/packages/app/e2e/regression/review-toggle-position.spec.ts b/packages/app/e2e/regression/review-toggle-position.spec.ts index c50129aa404..f48b4dc0d1b 100644 --- a/packages/app/e2e/regression/review-toggle-position.spec.ts +++ b/packages/app/e2e/regression/review-toggle-position.spec.ts @@ -238,16 +238,9 @@ async function expectHeaderClearOfToggle(page: Page, toggle: Locator, progress: }) const chatBounds = chat.getBoundingClientRect() const panelBounds = document.querySelector("#review-panel")!.getBoundingClientRect() - const summaryBounds = document - .querySelector('[data-session-title] button[aria-label="Session details"]')! - .getBoundingClientRect() return { row: row.getBoundingClientRect().width, panelWidth: panelBounds.width, - timelineControlInset: - getComputedStyle(row).direction === "rtl" - ? summaryBounds.left - chatBounds.left - : chatBounds.right - summaryBounds.right, gap: getComputedStyle(row).direction === "rtl" ? chatBounds.left - panelBounds.right @@ -257,8 +250,6 @@ async function expectHeaderClearOfToggle(page: Page, toggle: Locator, progress: } }, progress) expect(geometry.gap).toBeCloseTo(8, 1) - // Reserve the fixed toggle's 28px width, the 8px control gap, and the 12px header inset. - expect(geometry.timelineControlInset).toBeCloseTo(48, 1) if (geometry.panelWidth > 0) expect(Math.abs(geometry.row - geometry.panels)).toBeLessThanOrEqual(1) if (progress === 0.25) { expect(geometry.contentOpacity).toBeGreaterThan(0) diff --git a/packages/app/src/new-session/workspace/selector.tsx b/packages/app/src/new-session/workspace/selector.tsx index b25d4e7162b..14430b4a5fa 100644 --- a/packages/app/src/new-session/workspace/selector.tsx +++ b/packages/app/src/new-session/workspace/selector.tsx @@ -242,14 +242,7 @@ export function PromptWorkspaceSelector(props: { class="ms-1 min-w-0 max-w-[220px]" contentClass="max-w-[calc(100vw-32px)] break-all" > - { - onOpenChange(open) - if (open) requestAnimationFrame(() => branchSearchInput?.focus()) - }} - > + @@ -258,7 +251,14 @@ export function PromptWorkspaceSelector(props: { - + { + event.preventDefault() + // Kobalte defers its list autofocus until after the focus scope opens. + setTimeout(() => requestAnimationFrame(() => branchSearchInput?.focus({ preventScroll: true }))) + }} + >
- {/* Keep the fixed toggle's slot mounted throughout panel motion. */} - +
diff --git a/packages/app/src/session/screen.tsx b/packages/app/src/session/screen.tsx index f08c420db5c..76d9a5ba7a2 100644 --- a/packages/app/src/session/screen.tsx +++ b/packages/app/src/session/screen.tsx @@ -276,6 +276,7 @@ export function SessionScreen(props: { session: SessionModel }) { onSelectionInteraction={timeline.view.selectionInteraction} pinned={timeline.view.pinned()} centered={screen.centered()} + reserveReviewToggle={!sideVisible()} setContentRef={timeline.view.setContentRef} diffs={review.details.diffs} onReview={review.open} diff --git a/packages/app/src/session/timeline/message-timeline.tsx b/packages/app/src/session/timeline/message-timeline.tsx index 463c5bfd168..135894d2a1c 100644 --- a/packages/app/src/session/timeline/message-timeline.tsx +++ b/packages/app/src/session/timeline/message-timeline.tsx @@ -363,6 +363,7 @@ type MessageTimelineProps = { onSelectionInteraction: (event: MouseEvent) => void pinned: boolean centered: boolean + reserveReviewToggle: boolean setContentRef: (el: HTMLDivElement) => void diffs: Accessor<{ additions: number; deletions: number }[] | undefined> onReview: () => void @@ -845,7 +846,7 @@ function MessageTimelineView( )} - +
)}
diff --git a/packages/session-ui/src/components/message-part.css b/packages/session-ui/src/components/message-part.css index 5f2246d3eae..ed694fc2432 100644 --- a/packages/session-ui/src/components/message-part.css +++ b/packages/session-ui/src/components/message-part.css @@ -1308,7 +1308,7 @@ flex-grow: 1; display: flex; align-items: center; - gap: 20px; + gap: 12px; min-width: 0; }