Forráskód Böngészése

feat(app): sidebar reveal animation, hover peek overlay, and weaker dividers (#16374)

Co-authored-by: Adam <[email protected]>
David Hill 1 hónapja
szülő
commit
b0bc3d87f5

+ 48 - 45
packages/app/e2e/actions.ts

@@ -5,10 +5,10 @@ import path from "node:path"
 import { execSync } from "node:child_process"
 import { modKey, serverUrl } from "./utils"
 import {
-  sessionItemSelector,
   dropdownMenuTriggerSelector,
   dropdownMenuContentSelector,
   projectMenuTriggerSelector,
+  projectCloseMenuSelector,
   projectWorkspacesToggleSelector,
   titlebarRightSelector,
   popoverBodySelector,
@@ -61,9 +61,9 @@ export async function closeDialog(page: Page, dialog: Locator) {
 }
 
 export async function isSidebarClosed(page: Page) {
-  const main = page.locator("main")
-  const classes = (await main.getAttribute("class")) ?? ""
-  return classes.includes("xl:border-l")
+  const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
+  await expect(button).toBeVisible()
+  return (await button.getAttribute("aria-expanded")) !== "true"
 }
 
 export async function toggleSidebar(page: Page) {
@@ -75,48 +75,34 @@ export async function openSidebar(page: Page) {
   if (!(await isSidebarClosed(page))) return
 
   const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
-  const visible = await button
-    .isVisible()
-    .then((x) => x)
-    .catch(() => false)
-
-  if (visible) await button.click()
-  if (!visible) await toggleSidebar(page)
+  await button.click()
 
-  const main = page.locator("main")
-  const opened = await expect(main)
-    .not.toHaveClass(/xl:border-l/, { timeout: 1500 })
+  const opened = await expect(button)
+    .toHaveAttribute("aria-expanded", "true", { timeout: 1500 })
     .then(() => true)
     .catch(() => false)
 
   if (opened) return
 
   await toggleSidebar(page)
-  await expect(main).not.toHaveClass(/xl:border-l/)
+  await expect(button).toHaveAttribute("aria-expanded", "true")
 }
 
 export async function closeSidebar(page: Page) {
   if (await isSidebarClosed(page)) return
 
   const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
-  const visible = await button
-    .isVisible()
-    .then((x) => x)
-    .catch(() => false)
-
-  if (visible) await button.click()
-  if (!visible) await toggleSidebar(page)
+  await button.click()
 
-  const main = page.locator("main")
-  const closed = await expect(main)
-    .toHaveClass(/xl:border-l/, { timeout: 1500 })
+  const closed = await expect(button)
+    .toHaveAttribute("aria-expanded", "false", { timeout: 1500 })
     .then(() => true)
     .catch(() => false)
 
   if (closed) return
 
   await toggleSidebar(page)
-  await expect(main).toHaveClass(/xl:border-l/)
+  await expect(button).toHaveAttribute("aria-expanded", "false")
 }
 
 export async function openSettings(page: Page) {
@@ -220,7 +206,7 @@ export function sessionIDFromUrl(url: string) {
 }
 
 export async function hoverSessionItem(page: Page, sessionID: string) {
-  const sessionEl = page.locator(sessionItemSelector(sessionID)).first()
+  const sessionEl = page.locator(`[data-session-id="${sessionID}"]`).last()
   await expect(sessionEl).toBeVisible()
   await sessionEl.hover()
   return sessionEl
@@ -570,32 +556,42 @@ export async function openProjectMenu(page: Page, projectSlug: string) {
   const trigger = page.locator(projectMenuTriggerSelector(projectSlug)).first()
   await expect(trigger).toHaveCount(1)
 
+  const menu = page
+    .locator(dropdownMenuContentSelector)
+    .filter({ has: page.locator(projectCloseMenuSelector(projectSlug)) })
+    .first()
+  const close = menu.locator(projectCloseMenuSelector(projectSlug)).first()
+
+  const clicked = await trigger
+    .click({ timeout: 1500 })
+    .then(() => true)
+    .catch(() => false)
+
+  if (clicked) {
+    const opened = await menu
+      .waitFor({ state: "visible", timeout: 1500 })
+      .then(() => true)
+      .catch(() => false)
+    if (opened) {
+      await expect(close).toBeVisible()
+      return menu
+    }
+  }
+
   await trigger.focus()
   await page.keyboard.press("Enter")
 
-  const menu = page.locator(dropdownMenuContentSelector).first()
   const opened = await menu
     .waitFor({ state: "visible", timeout: 1500 })
     .then(() => true)
     .catch(() => false)
 
   if (opened) {
-    const viewport = page.viewportSize()
-    const x = viewport ? Math.max(viewport.width - 5, 0) : 1200
-    const y = viewport ? Math.max(viewport.height - 5, 0) : 800
-    await page.mouse.move(x, y)
+    await expect(close).toBeVisible()
     return menu
   }
 
-  await trigger.click({ force: true })
-
-  await expect(menu).toBeVisible()
-
-  const viewport = page.viewportSize()
-  const x = viewport ? Math.max(viewport.width - 5, 0) : 1200
-  const y = viewport ? Math.max(viewport.height - 5, 0) : 800
-  await page.mouse.move(x, y)
-  return menu
+  throw new Error(`Failed to open project menu: ${projectSlug}`)
 }
 
 export async function setWorkspacesEnabled(page: Page, projectSlug: string, enabled: boolean) {
@@ -608,11 +604,18 @@ export async function setWorkspacesEnabled(page: Page, projectSlug: string, enab
 
   if (current === enabled) return
 
-  await openProjectMenu(page, projectSlug)
+  const flip = async (timeout?: number) => {
+    const menu = await openProjectMenu(page, projectSlug)
+    const toggle = menu.locator(projectWorkspacesToggleSelector(projectSlug)).first()
+    await expect(toggle).toBeVisible()
+    return toggle.click({ force: true, timeout })
+  }
+
+  const flipped = await flip(1500)
+    .then(() => true)
+    .catch(() => false)
 
-  const toggle = page.locator(projectWorkspacesToggleSelector(projectSlug)).first()
-  await expect(toggle).toBeVisible()
-  await toggle.click({ force: true })
+  if (!flipped) await flip()
 
   const expected = enabled ? "New workspace" : "New session"
   await expect(page.getByRole("button", { name: expected }).first()).toBeVisible()

+ 0 - 4
packages/app/e2e/app/titlebar-history.spec.ts

@@ -16,7 +16,6 @@ test("titlebar back/forward navigates between sessions", async ({ page, slug, sd
 
       const link = page.locator(`[data-session-id="${two.id}"] a`).first()
       await expect(link).toBeVisible()
-      await link.scrollIntoViewIfNeeded()
       await link.click()
 
       await expect(page).toHaveURL(new RegExp(`/${slug}/session/${two.id}(?:\\?|#|$)`))
@@ -56,7 +55,6 @@ test("titlebar forward is cleared after branching history from sidebar", async (
 
         const second = page.locator(`[data-session-id="${b.id}"] a`).first()
         await expect(second).toBeVisible()
-        await second.scrollIntoViewIfNeeded()
         await second.click()
 
         await expect(page).toHaveURL(new RegExp(`/${slug}/session/${b.id}(?:\\?|#|$)`))
@@ -76,7 +74,6 @@ test("titlebar forward is cleared after branching history from sidebar", async (
 
         const third = page.locator(`[data-session-id="${c.id}"] a`).first()
         await expect(third).toBeVisible()
-        await third.scrollIntoViewIfNeeded()
         await third.click()
 
         await expect(page).toHaveURL(new RegExp(`/${slug}/session/${c.id}(?:\\?|#|$)`))
@@ -102,7 +99,6 @@ test("keyboard shortcuts navigate titlebar history", async ({ page, slug, sdk, g
 
       const link = page.locator(`[data-session-id="${two.id}"] a`).first()
       await expect(link).toBeVisible()
-      await link.scrollIntoViewIfNeeded()
       await link.click()
 
       await expect(page).toHaveURL(new RegExp(`/${slug}/session/${two.id}(?:\\?|#|$)`))

+ 4 - 14
packages/app/e2e/projects/project-edit.spec.ts

@@ -1,25 +1,15 @@
 import { test, expect } from "../fixtures"
-import { openSidebar } from "../actions"
+import { clickMenuItem, openProjectMenu, openSidebar } from "../actions"
 
 test("dialog edit project updates name and startup script", async ({ page, withProject }) => {
   await page.setViewportSize({ width: 1400, height: 800 })
 
-  await withProject(async () => {
+  await withProject(async ({ slug }) => {
     await openSidebar(page)
 
     const open = async () => {
-      const header = page.locator(".group\\/project").first()
-      await header.hover()
-      const trigger = header.getByRole("button", { name: "More options" }).first()
-      await expect(trigger).toBeVisible()
-      await trigger.click({ force: true })
-
-      const menu = page.locator('[data-component="dropdown-menu-content"]').first()
-      await expect(menu).toBeVisible()
-
-      const editItem = menu.getByRole("menuitem", { name: "Edit" }).first()
-      await expect(editItem).toBeVisible()
-      await editItem.click({ force: true })
+      const menu = await openProjectMenu(page, slug)
+      await clickMenuItem(menu, /^Edit$/i, { force: true })
 
       const dialog = page.getByRole("dialog")
       await expect(dialog).toBeVisible()

+ 37 - 9
packages/app/e2e/projects/projects-switch.spec.ts

@@ -1,13 +1,7 @@
 import { base64Decode } from "@opencode-ai/util/encode"
+import type { Page } from "@playwright/test"
 import { test, expect } from "../fixtures"
-import {
-  defocus,
-  createTestProject,
-  cleanupTestProject,
-  openSidebar,
-  setWorkspacesEnabled,
-  sessionIDFromUrl,
-} from "../actions"
+import { defocus, createTestProject, cleanupTestProject, openSidebar, sessionIDFromUrl } from "../actions"
 import { projectSwitchSelector, promptSelector, workspaceItemSelector, workspaceNewSessionSelector } from "../selectors"
 import { createSdk, dirSlug, sessionPath } from "../utils"
 
@@ -15,6 +9,37 @@ function slugFromUrl(url: string) {
   return /\/([^/]+)\/session(?:\/|$)/.exec(url)?.[1] ?? ""
 }
 
+async function workspaces(page: Page, directory: string, enabled: boolean) {
+  await page.evaluate(
+    ({ directory, enabled }: { directory: string; enabled: boolean }) => {
+      const key = "opencode.global.dat:layout"
+      const raw = localStorage.getItem(key)
+      const data = raw ? JSON.parse(raw) : {}
+      const sidebar = data.sidebar && typeof data.sidebar === "object" ? data.sidebar : {}
+      const current =
+        sidebar.workspaces && typeof sidebar.workspaces === "object" && !Array.isArray(sidebar.workspaces)
+          ? sidebar.workspaces
+          : {}
+      const next = { ...current }
+
+      if (enabled) next[directory] = true
+      if (!enabled) delete next[directory]
+
+      localStorage.setItem(
+        key,
+        JSON.stringify({
+          ...data,
+          sidebar: {
+            ...sidebar,
+            workspaces: next,
+          },
+        }),
+      )
+    },
+    { directory, enabled },
+  )
+}
+
 test("can switch between projects from sidebar", async ({ page, withProject }) => {
   await page.setViewportSize({ width: 1400, height: 800 })
 
@@ -60,8 +85,11 @@ test("switching back to a project opens the latest workspace session", async ({
       async ({ directory, slug }) => {
         rootDir = directory
         await defocus(page)
+        await workspaces(page, directory, true)
+        await page.reload()
+        await expect(page.locator(promptSelector)).toBeVisible()
         await openSidebar(page)
-        await setWorkspacesEnabled(page, slug, true)
+        await expect(page.getByRole("button", { name: "New workspace" }).first()).toBeVisible()
 
         await page.getByRole("button", { name: "New workspace" }).first().click()
 

+ 0 - 3
packages/app/e2e/projects/workspaces.spec.ts

@@ -336,9 +336,6 @@ test("can reorder workspaces by drag and drop", async ({ page, withProject }) =>
       const src = page.locator(workspaceItemSelector(from)).first()
       const dst = page.locator(workspaceItemSelector(to)).first()
 
-      await src.scrollIntoViewIfNeeded()
-      await dst.scrollIntoViewIfNeeded()
-
       const a = await src.boundingBox()
       const b = await dst.boundingBox()
       if (!a || !b) throw new Error("Failed to resolve workspace drag bounds")

+ 6 - 9
packages/app/e2e/settings/settings-keybinds.spec.ts

@@ -32,22 +32,19 @@ test("changing sidebar toggle keybind works", async ({ page, gotoSession }) => {
 
   await closeDialog(page, dialog)
 
-  const main = page.locator("main")
-  const initialClasses = (await main.getAttribute("class")) ?? ""
-  const initiallyClosed = initialClasses.includes("xl:border-l")
+  const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
+  const initiallyClosed = (await button.getAttribute("aria-expanded")) !== "true"
 
   await page.keyboard.press(`${modKey}+Shift+H`)
-  await page.waitForTimeout(100)
+  await expect(button).toHaveAttribute("aria-expanded", initiallyClosed ? "true" : "false")
 
-  const afterToggleClasses = (await main.getAttribute("class")) ?? ""
-  const afterToggleClosed = afterToggleClasses.includes("xl:border-l")
+  const afterToggleClosed = (await button.getAttribute("aria-expanded")) !== "true"
   expect(afterToggleClosed).toBe(!initiallyClosed)
 
   await page.keyboard.press(`${modKey}+Shift+H`)
-  await page.waitForTimeout(100)
+  await expect(button).toHaveAttribute("aria-expanded", initiallyClosed ? "false" : "true")
 
-  const finalClasses = (await main.getAttribute("class")) ?? ""
-  const finalClosed = finalClasses.includes("xl:border-l")
+  const finalClosed = (await button.getAttribute("aria-expanded")) !== "true"
   expect(finalClosed).toBe(initiallyClosed)
 })
 

+ 7 - 4
packages/app/e2e/sidebar/sidebar-popover-actions.spec.ts

@@ -1,6 +1,6 @@
 import { test, expect } from "../fixtures"
 import { closeSidebar, hoverSessionItem } from "../actions"
-import { projectSwitchSelector, sessionItemSelector } from "../selectors"
+import { projectSwitchSelector } from "../selectors"
 
 test("collapsed sidebar popover stays open when archiving a session", async ({ page, slug, sdk, gotoSession }) => {
   const stamp = Date.now()
@@ -15,12 +15,15 @@ test("collapsed sidebar popover stays open when archiving a session", async ({ p
     await gotoSession(one.id)
     await closeSidebar(page)
 
+    const oneItem = page.locator(`[data-session-id="${one.id}"]`).last()
+    const twoItem = page.locator(`[data-session-id="${two.id}"]`).last()
+
     const project = page.locator(projectSwitchSelector(slug)).first()
     await expect(project).toBeVisible()
     await project.hover()
 
-    await expect(page.locator(sessionItemSelector(one.id)).first()).toBeVisible()
-    await expect(page.locator(sessionItemSelector(two.id)).first()).toBeVisible()
+    await expect(oneItem).toBeVisible()
+    await expect(twoItem).toBeVisible()
 
     const item = await hoverSessionItem(page, one.id)
     await item
@@ -28,7 +31,7 @@ test("collapsed sidebar popover stays open when archiving a session", async ({ p
       .first()
       .click()
 
-    await expect(page.locator(sessionItemSelector(two.id)).first()).toBeVisible()
+    await expect(twoItem).toBeVisible()
   } finally {
     await sdk.session.delete({ sessionID: one.id }).catch(() => undefined)
     await sdk.session.delete({ sessionID: two.id }).catch(() => undefined)

+ 0 - 1
packages/app/e2e/sidebar/sidebar-session-links.spec.ts

@@ -18,7 +18,6 @@ test("sidebar session links navigate to the selected session", async ({ page, sl
 
     const target = page.locator(`[data-session-id="${two.id}"] a`).first()
     await expect(target).toBeVisible()
-    await target.scrollIntoViewIfNeeded()
     await target.click()
 
     await expect(page).toHaveURL(new RegExp(`/${slug}/session/${two.id}(?:\\?|#|$)`))

+ 8 - 5
packages/app/e2e/sidebar/sidebar.spec.ts

@@ -5,12 +5,14 @@ test("sidebar can be collapsed and expanded", async ({ page, gotoSession }) => {
   await gotoSession()
 
   await openSidebar(page)
+  const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
+  await expect(button).toHaveAttribute("aria-expanded", "true")
 
   await toggleSidebar(page)
-  await expect(page.locator("main")).toHaveClass(/xl:border-l/)
+  await expect(button).toHaveAttribute("aria-expanded", "false")
 
   await toggleSidebar(page)
-  await expect(page.locator("main")).not.toHaveClass(/xl:border-l/)
+  await expect(button).toHaveAttribute("aria-expanded", "true")
 })
 
 test("sidebar collapsed state persists across navigation and reload", async ({ page, sdk, gotoSession }) => {
@@ -19,14 +21,15 @@ test("sidebar collapsed state persists across navigation and reload", async ({ p
       await gotoSession(session1.id)
 
       await openSidebar(page)
+      const button = page.getByRole("button", { name: /toggle sidebar/i }).first()
       await toggleSidebar(page)
-      await expect(page.locator("main")).toHaveClass(/xl:border-l/)
+      await expect(button).toHaveAttribute("aria-expanded", "false")
 
       await gotoSession(session2.id)
-      await expect(page.locator("main")).toHaveClass(/xl:border-l/)
+      await expect(button).toHaveAttribute("aria-expanded", "false")
 
       await page.reload()
-      await expect(page.locator("main")).toHaveClass(/xl:border-l/)
+      await expect(button).toHaveAttribute("aria-expanded", "false")
 
       const opened = await page.evaluate(
         () => JSON.parse(localStorage.getItem("opencode.global.dat:layout") ?? "{}").sidebar?.opened,

+ 162 - 38
packages/app/src/pages/layout.tsx

@@ -155,6 +155,8 @@ export default function Layout(props: ParentProps) {
   const isBusy = (directory: string) => !!state.busyWorkspaces[workspaceKey(directory)]
   const navLeave = { current: undefined as number | undefined }
   const [sortNow, setSortNow] = createSignal(Date.now())
+  const [sizing, setSizing] = createSignal(false)
+  let sizet: number | undefined
   let sortNowInterval: ReturnType<typeof setInterval> | undefined
   const sortNowTimeout = setTimeout(
     () => {
@@ -167,7 +169,7 @@ export default function Layout(props: ParentProps) {
   const aim = createAim({
     enabled: () => !layout.sidebar.opened(),
     active: () => state.hoverProject,
-    el: () => state.nav,
+    el: () => state.nav?.querySelector<HTMLElement>("[data-component='sidebar-rail']") ?? state.nav,
     onActivate: (directory) => {
       globalSync.child(directory)
       setState("hoverProject", directory)
@@ -179,9 +181,23 @@ export default function Layout(props: ParentProps) {
     if (navLeave.current !== undefined) clearTimeout(navLeave.current)
     clearTimeout(sortNowTimeout)
     if (sortNowInterval) clearInterval(sortNowInterval)
+    if (sizet !== undefined) clearTimeout(sizet)
+    if (peekt !== undefined) clearTimeout(peekt)
     aim.reset()
   })
 
+  onMount(() => {
+    const stop = () => setSizing(false)
+    window.addEventListener("pointerup", stop)
+    window.addEventListener("pointercancel", stop)
+    window.addEventListener("blur", stop)
+    onCleanup(() => {
+      window.removeEventListener("pointerup", stop)
+      window.removeEventListener("pointercancel", stop)
+      window.removeEventListener("blur", stop)
+    })
+  })
+
   const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined)
   const sidebarExpanded = createMemo(() => layout.sidebar.opened() || sidebarHovering())
   const setHoverProject = (value: string | undefined) => {
@@ -192,12 +208,54 @@ export default function Layout(props: ParentProps) {
   const clearHoverProjectSoon = () => queueMicrotask(() => setHoverProject(undefined))
   const setHoverSession = (id: string | undefined) => setState("hoverSession", id)
 
+  const disarm = () => {
+    if (navLeave.current === undefined) return
+    clearTimeout(navLeave.current)
+    navLeave.current = undefined
+  }
+
+  const arm = () => {
+    if (layout.sidebar.opened()) return
+    if (state.hoverProject === undefined) return
+    disarm()
+    navLeave.current = window.setTimeout(() => {
+      navLeave.current = undefined
+      setHoverProject(undefined)
+      setState("hoverSession", undefined)
+    }, 300)
+  }
+
+  const [peek, setPeek] = createSignal<LocalProject | undefined>(undefined)
+  const [peeked, setPeeked] = createSignal(false)
+  let peekt: number | undefined
+
   const hoverProjectData = createMemo(() => {
     const id = state.hoverProject
     if (!id) return
     return layout.projects.list().find((project) => project.worktree === id)
   })
 
+  createEffect(() => {
+    const p = hoverProjectData()
+    if (p) {
+      if (peekt !== undefined) {
+        clearTimeout(peekt)
+        peekt = undefined
+      }
+      setPeek(p)
+      setPeeked(true)
+      return
+    }
+
+    setPeeked(false)
+    if (peek() === undefined) return
+    if (peekt !== undefined) clearTimeout(peekt)
+    peekt = window.setTimeout(() => {
+      peekt = undefined
+      setPeek(undefined)
+    }, 180)
+  })
+
   createEffect(() => {
     if (!layout.sidebar.opened()) return
     setHoverProject(undefined)
@@ -1123,6 +1181,12 @@ export default function Layout(props: ParentProps) {
     }
     const openSession = async (target: { directory: string; id: string }) => {
       if (!canOpen(target.directory)) return false
+      const [data] = globalSync.child(target.directory, { bootstrap: false })
+      if (data.session.some((item) => item.id === target.id)) {
+        setStore("lastProjectSession", root, { directory: target.directory, id: target.id, at: Date.now() })
+        navigateWithSidebarReset(`/${base64Encode(target.directory)}/session/${target.id}`)
+        return true
+      }
       const resolved = await globalSDK.client.session
         .get({ sessionID: target.id })
         .then((x) => x.data)
@@ -1813,7 +1877,8 @@ export default function Layout(props: ParentProps) {
     setHoverSession,
   }
 
-  const SidebarPanel = (panelProps: { project: LocalProject | undefined; mobile?: boolean }) => {
+  const SidebarPanel = (panelProps: { project: LocalProject | undefined; mobile?: boolean; merged?: boolean }) => {
+    const merged = createMemo(() => panelProps.mobile || (panelProps.merged ?? layout.sidebar.opened()))
     const projectName = createMemo(() => {
       const project = panelProps.project
       if (!project) return ""
@@ -1839,10 +1904,17 @@ export default function Layout(props: ParentProps) {
     return (
       <div
         classList={{
-          "flex flex-col min-h-0 bg-background-stronger border border-b-0 border-border-weak-base rounded-tl-[12px]": true,
+          "flex flex-col min-h-0 min-w-0 rounded-tl-[12px] px-2": true,
+          "border border-b-0 border-border-weak-base": !merged(),
+          "border-l border-t border-border-weaker-base": merged(),
+          "bg-background-base": merged(),
+          "bg-background-stronger": !merged(),
           "flex-1 min-w-0": panelProps.mobile,
+          "max-w-full overflow-hidden": panelProps.mobile,
+        }}
+        style={{
+          width: panelProps.mobile ? undefined : `${Math.max(Math.max(layout.sidebar.width(), 244) - 64, 0)}px`,
         }}
-        style={{ width: panelProps.mobile ? undefined : `${Math.max(layout.sidebar.width() - 64, 0)}px` }}
       >
         <Show when={panelProps.project}>
           {(p) => (
@@ -2041,33 +2113,27 @@ export default function Layout(props: ParentProps) {
   return (
     <div class="relative bg-background-base flex-1 min-h-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
       <Titlebar />
-      <div class="flex-1 min-h-0 flex">
+      <div class="flex-1 min-h-0 relative overflow-x-hidden">
         <nav
           aria-label={language.t("sidebar.nav.projectsAndSessions")}
           data-component="sidebar-nav-desktop"
           classList={{
             "hidden xl:block": true,
-            "relative shrink-0": true,
+            "absolute inset-y-0 left-0": true,
+            "z-10": true,
           }}
-          style={{ width: layout.sidebar.opened() ? `${Math.max(layout.sidebar.width(), 244)}px` : "64px" }}
+          style={{ width: `${Math.max(layout.sidebar.width(), 244)}px` }}
           ref={(el) => {
             setState("nav", el)
           }}
           onMouseEnter={() => {
-            if (navLeave.current === undefined) return
-            clearTimeout(navLeave.current)
-            navLeave.current = undefined
+            disarm()
           }}
           onMouseLeave={() => {
             aim.reset()
             if (!sidebarHovering()) return
 
-            if (navLeave.current !== undefined) clearTimeout(navLeave.current)
-            navLeave.current = window.setTimeout(() => {
-              navLeave.current = undefined
-              setHoverProject(undefined)
-              setState("hoverSession", undefined)
-            }, 300)
+            arm()
           }}
         >
           <div class="@container w-full h-full contain-strict">
@@ -2094,28 +2160,36 @@ export default function Layout(props: ParentProps) {
               onOpenHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
               renderPanel={() => (
                 <Show when={currentProject()} keyed>
-                  {(project) => <SidebarPanel project={project} />}
+                  {(project) => <SidebarPanel project={project} merged />}
                 </Show>
               )}
             />
           </div>
-          <Show when={!layout.sidebar.opened() ? hoverProjectData()?.worktree : undefined} keyed>
-            <div class="absolute inset-y-0 left-16 z-50 flex" onMouseEnter={aim.reset}>
-              <SidebarPanel project={hoverProjectData()} />
-            </div>
-          </Show>
           <Show when={layout.sidebar.opened()}>
-            <ResizeHandle
-              direction="horizontal"
-              size={layout.sidebar.width()}
-              min={244}
-              max={typeof window === "undefined" ? 1000 : window.innerWidth * 0.3 + 64}
-              collapseThreshold={244}
-              onResize={layout.sidebar.resize}
-              onCollapse={layout.sidebar.close}
-            />
+            <div onPointerDown={() => setSizing(true)}>
+              <ResizeHandle
+                direction="horizontal"
+                size={layout.sidebar.width()}
+                min={244}
+                max={typeof window === "undefined" ? 1000 : window.innerWidth * 0.3 + 64}
+                collapseThreshold={244}
+                onResize={(w) => {
+                  setSizing(true)
+                  if (sizet !== undefined) clearTimeout(sizet)
+                  sizet = window.setTimeout(() => setSizing(false), 120)
+                  layout.sidebar.resize(w)
+                }}
+                onCollapse={layout.sidebar.close}
+              />
+            </div>
           </Show>
         </nav>
+
+        <div
+          class="hidden xl:block pointer-events-none absolute top-0 right-0 z-0 border-t border-border-weaker-base"
+          style={{ left: "calc(4rem + 12px)" }}
+        />
+
         <div class="xl:hidden">
           <div
             classList={{
@@ -2131,7 +2205,7 @@ export default function Layout(props: ParentProps) {
             aria-label={language.t("sidebar.nav.projectsAndSessions")}
             data-component="sidebar-nav-mobile"
             classList={{
-              "@container fixed top-10 bottom-0 left-0 z-50 w-72 bg-background-base transition-transform duration-200 ease-out": true,
+              "@container fixed top-10 bottom-0 left-0 z-50 w-full max-w-[400px] overflow-hidden border-r border-border-weaker-base bg-background-base transition-transform duration-200 ease-out": true,
               "translate-x-0": layout.mobileSidebar.opened(),
               "-translate-x-full": !layout.mobileSidebar.opened(),
             }}
@@ -2164,16 +2238,66 @@ export default function Layout(props: ParentProps) {
           </nav>
         </div>
 
-        <main
+        <div
           classList={{
-            "size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base": true,
-            "xl:border-l xl:rounded-tl-[12px]": !layout.sidebar.opened(),
+            "absolute inset-0": true,
+            "xl:inset-y-0 xl:right-0 xl:left-[var(--main-left)]": true,
+            "z-20": true,
+            "transition-[left] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[left] motion-reduce:transition-none":
+              !sizing(),
+          }}
+          style={{
+            "--main-left": layout.sidebar.opened() ? `${Math.max(layout.sidebar.width(), 244)}px` : "4rem",
           }}
         >
-          <Show when={!autoselecting()} fallback={<div class="size-full" />}>
-            {props.children}
+          <main
+            classList={{
+              "size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base xl:border-l xl:rounded-tl-[12px]": true,
+            }}
+          >
+            <Show when={!autoselecting()} fallback={<div class="size-full" />}>
+              {props.children}
+            </Show>
+          </main>
+        </div>
+
+        <div
+          classList={{
+            "hidden xl:flex absolute inset-y-0 left-16 z-30": true,
+            "opacity-100 translate-x-0 pointer-events-auto": peeked() && !layout.sidebar.opened(),
+            "opacity-0 -translate-x-2 pointer-events-none": !peeked() || layout.sidebar.opened(),
+            "transition-[opacity,transform] motion-reduce:transition-none": true,
+            "duration-180 ease-out": peeked() && !layout.sidebar.opened(),
+            "duration-120 ease-in": !peeked() || layout.sidebar.opened(),
+          }}
+          onMouseMove={disarm}
+          onMouseEnter={() => {
+            disarm()
+            aim.reset()
+          }}
+          onPointerDown={disarm}
+          onMouseLeave={() => {
+            arm()
+          }}
+        >
+          <Show when={peek()} keyed>
+            {(project) => <SidebarPanel project={project} merged={false} />}
           </Show>
-        </main>
+        </div>
+
+        <div
+          classList={{
+            "hidden xl:block pointer-events-none absolute inset-y-0 right-0 z-25 overflow-hidden": true,
+            "opacity-100 translate-x-0": peeked() && !layout.sidebar.opened(),
+            "opacity-0 -translate-x-2": !peeked() || layout.sidebar.opened(),
+            "transition-[opacity,transform] motion-reduce:transition-none": true,
+            "duration-180 ease-out": peeked() && !layout.sidebar.opened(),
+            "duration-120 ease-in": !peeked() || layout.sidebar.opened(),
+          }}
+          style={{ left: `calc(4rem + ${Math.max(Math.max(layout.sidebar.width(), 244) - 64, 0)}px)` }}
+        >
+          <div class="h-full w-px" style={{ "box-shadow": "var(--shadow-sidebar-overlay)" }} />
+        </div>
       </div>
       <Toast.Region />
     </div>

+ 23 - 3
packages/app/src/pages/layout/sidebar-shell.tsx

@@ -1,4 +1,4 @@
-import { createMemo, For, Show, type Accessor, type JSX } from "solid-js"
+import { createEffect, createMemo, For, Show, type Accessor, type JSX } from "solid-js"
 import {
   DragDropProvider,
   DragDropSensors,
@@ -35,10 +35,22 @@ export const SidebarContent = (props: {
 }): JSX.Element => {
   const expanded = createMemo(() => sidebarExpanded(props.mobile, props.opened()))
   const placement = () => (props.mobile ? "bottom" : "right")
+  let panel: HTMLDivElement | undefined
+
+  createEffect(() => {
+    const el = panel
+    if (!el) return
+    if (expanded()) {
+      el.removeAttribute("inert")
+      return
+    }
+    el.setAttribute("inert", "")
+  })
 
   return (
-    <div class="flex h-full w-full overflow-hidden">
+    <div class="flex h-full w-full min-w-0 overflow-hidden">
       <div
+        data-component="sidebar-rail"
         class="w-16 shrink-0 bg-background-base flex flex-col items-center overflow-hidden"
         onMouseMove={props.aimMove}
       >
@@ -100,7 +112,15 @@ export const SidebarContent = (props: {
         </div>
       </div>
 
-      <Show when={expanded()}>{props.renderPanel()}</Show>
+      <div
+        ref={(el) => {
+          panel = el
+        }}
+        classList={{ "flex h-full min-h-0 min-w-0 overflow-hidden": true, "pointer-events-none": !expanded() }}
+        aria-hidden={!expanded()}
+      >
+        {props.renderPanel()}
+      </div>
     </div>
   )
 }

+ 2 - 2
packages/app/src/pages/layout/sidebar-workspace.tsx

@@ -249,7 +249,7 @@ const WorkspaceSessionList = (props: {
   loadMore: () => Promise<void>
   language: ReturnType<typeof useLanguage>
 }): JSX.Element => (
-  <nav class="flex flex-col gap-1 px-2">
+  <nav class="flex flex-col gap-1 px-3">
     <Show when={props.showNew()}>
       <NewSessionItem
         slug={props.slug()}
@@ -490,7 +490,7 @@ export const LocalWorkspace = (props: {
       ref={(el) => props.ctx.setScrollContainerRef(el, props.mobile)}
       class="size-full flex flex-col py-2 overflow-y-auto no-scrollbar [overflow-anchor:none]"
     >
-      <nav class="flex flex-col gap-1 px-2">
+      <nav class="flex flex-col gap-1 px-3">
         <Show when={loading()}>
           <SessionSkeleton />
         </Show>

+ 2 - 2
packages/app/src/pages/session/session-side-panel.tsx

@@ -208,7 +208,7 @@ export function SessionSidePanel(props: {
       <aside
         id="review-panel"
         aria-label={language.t("session.panel.reviewAndFiles")}
-        class="relative min-w-0 h-full border-l border-border-weak-base flex"
+        class="relative min-w-0 h-full border-l border-border-weaker-base flex"
         classList={{
           "flex-1": reviewOpen(),
           "shrink-0": !reviewOpen(),
@@ -346,7 +346,7 @@ export function SessionSidePanel(props: {
           <div id="file-tree-panel" class="relative shrink-0 h-full" style={{ width: `${layout.fileTree.width()}px` }}>
             <div
               class="h-full flex flex-col overflow-hidden group/filetree"
-              classList={{ "border-l border-border-weak-base": reviewOpen() }}
+              classList={{ "border-l border-border-weaker-base": reviewOpen() }}
             >
               <Tabs
                 variant="pill"

+ 2 - 2
packages/app/src/pages/session/terminal-panel.tsx

@@ -154,7 +154,7 @@ export function TerminalPanel() {
           when={terminal.ready()}
           fallback={
             <div class="flex flex-col h-full pointer-events-none">
-              <div class="h-10 flex items-center gap-2 px-2 border-b border-border-weak-base bg-background-stronger overflow-hidden">
+              <div class="h-10 flex items-center gap-2 px-2 border-b border-border-weaker-base bg-background-stronger overflow-hidden">
                 <For each={handoff()}>
                   {(title) => (
                     <div class="px-2 py-1 rounded-md bg-surface-base text-14-regular text-text-weak truncate max-w-40">
@@ -187,7 +187,7 @@ export function TerminalPanel() {
                 onChange={(id) => terminal.open(id)}
                 class="!h-auto !flex-none"
               >
-                <Tabs.List class="h-10">
+                <Tabs.List class="h-10 border-b border-border-weaker-base">
                   <SortableProvider ids={ids()}>
                     <For each={ids()}>
                       {(id) => (

+ 1 - 6
packages/ui/script/colors.txt

@@ -114,6 +114,7 @@
 --border-weak-selected: var(--cobalt-light-alpha-5);
 --border-weak-disabled: var(--smoke-light-alpha-6);
 --border-weak-focus: var(--smoke-light-alpha-7);
+--border-weaker-base: var(--smoke-light-alpha-3);
 --border-interactive-base: var(--cobalt-light-7);
 --border-interactive-hover: var(--cobalt-light-8);
 --border-interactive-active: var(--cobalt-light-9);
@@ -224,11 +225,5 @@
 --markdown-image-text: #318795;
 --markdown-code-block: #1A1A1A;
 --border-color: #FFFFFF;
---border-weaker-base: var(--smoke-light-alpha-3);
---border-weaker-hover: var(--smoke-light-alpha-4);
---border-weaker-active: var(--smoke-light-alpha-6);
---border-weaker-selected: var(--cobalt-light-alpha-4);
---border-weaker-disabled: var(--smoke-light-alpha-2);
---border-weaker-focus: var(--smoke-light-alpha-6);
 --button-ghost-hover: var(--smoke-light-alpha-2);
 --button-ghost-hover2: var(--smoke-light-alpha-3);

+ 1 - 1
packages/ui/src/components/tabs.css

@@ -146,7 +146,7 @@
       --tabs-review-fade: 16px;
       gap: var(--tabs-review-gap);
       background-color: var(--background-stronger);
-      border-bottom: 1px solid var(--border-weak-base);
+      border-bottom: 1px solid var(--border-weaker-base);
 
       &::after {
         display: none;

+ 3 - 12
packages/ui/src/styles/tailwind/colors.css

@@ -1,4 +1,4 @@
-/* Generated by script/colors.ts */
+/* Generated by script/tailwind.ts */
 /* Do not edit this file manually */
 
 @theme {
@@ -77,10 +77,6 @@
   --color-text-weaker: var(--text-weaker);
   --color-text-strong: var(--text-strong);
   --color-text-interactive-base: var(--text-interactive-base);
-  --color-text-invert-base: var(--text-invert-base);
-  --color-text-invert-weak: var(--text-invert-weak);
-  --color-text-invert-weaker: var(--text-invert-weaker);
-  --color-text-invert-strong: var(--text-invert-strong);
   --color-text-on-brand-base: var(--text-on-brand-base);
   --color-text-on-interactive-base: var(--text-on-interactive-base);
   --color-text-on-interactive-weak: var(--text-on-interactive-weak);
@@ -123,6 +119,7 @@
   --color-border-weak-selected: var(--border-weak-selected);
   --color-border-weak-disabled: var(--border-weak-disabled);
   --color-border-weak-focus: var(--border-weak-focus);
+  --color-border-weaker-base: var(--border-weaker-base);
   --color-border-interactive-base: var(--border-interactive-base);
   --color-border-interactive-hover: var(--border-interactive-hover);
   --color-border-interactive-active: var(--border-interactive-active);
@@ -233,12 +230,6 @@
   --color-markdown-image-text: var(--markdown-image-text);
   --color-markdown-code-block: var(--markdown-code-block);
   --color-border-color: var(--border-color);
-  --color-border-weaker-base: var(--border-weaker-base);
-  --color-border-weaker-hover: var(--border-weaker-hover);
-  --color-border-weaker-active: var(--border-weaker-active);
-  --color-border-weaker-selected: var(--border-weaker-selected);
-  --color-border-weaker-disabled: var(--border-weaker-disabled);
-  --color-border-weaker-focus: var(--border-weaker-focus);
   --color-button-ghost-hover: var(--button-ghost-hover);
   --color-button-ghost-hover2: var(--button-ghost-hover2);
-}
+}

+ 6 - 12
packages/ui/src/styles/theme.css

@@ -85,6 +85,10 @@
     0 0 0 1px var(--border-weak-base, rgba(0, 0, 0, 0.07)), 0 36px 80px 0 rgba(0, 0, 0, 0.03),
     0 13.141px 29.201px 0 rgba(0, 0, 0, 0.04), 0 6.38px 14.177px 0 rgba(0, 0, 0, 0.05),
     0 3.127px 6.95px 0 rgba(0, 0, 0, 0.06), 0 1.237px 2.748px 0 rgba(0, 0, 0, 0.09);
+  --shadow-sidebar-overlay:
+    0 100px 80px 0 rgba(0, 0, 0, 0.29), 0 41.778px 33.422px 0 rgba(0, 0, 0, 0.21),
+    0 22.336px 17.869px 0 rgba(0, 0, 0, 0.17), 0 12.522px 10.017px 0 rgba(0, 0, 0, 0.14),
+    0 6.65px 5.32px 0 rgba(0, 0, 0, 0.12), 0 2.767px 2.214px 0 rgba(0, 0, 0, 0.08);
 
   color-scheme: light;
   --text-mix-blend-mode: multiply;
@@ -212,6 +216,7 @@
   --border-weak-selected: var(--cobalt-light-alpha-5);
   --border-weak-disabled: var(--smoke-light-alpha-6);
   --border-weak-focus: var(--smoke-light-alpha-7);
+  --border-weaker-base: var(--smoke-light-alpha-3);
   --border-interactive-base: var(--cobalt-light-7);
   --border-interactive-hover: var(--cobalt-light-8);
   --border-interactive-active: var(--cobalt-light-9);
@@ -323,12 +328,6 @@
   --markdown-image-text: #318795;
   --markdown-code-block: #1a1a1a;
   --border-color: #ffffff;
-  --border-weaker-base: var(--smoke-light-alpha-3);
-  --border-weaker-hover: var(--smoke-light-alpha-4);
-  --border-weaker-active: var(--smoke-light-alpha-6);
-  --border-weaker-selected: var(--cobalt-light-alpha-4);
-  --border-weaker-disabled: var(--smoke-light-alpha-2);
-  --border-weaker-focus: var(--smoke-light-alpha-6);
   --button-ghost-hover: var(--smoke-light-alpha-2);
   --button-ghost-hover2: var(--smoke-light-alpha-3);
   --avatar-background-pink: #feeef8;
@@ -582,12 +581,7 @@
     --markdown-image-text: #56b6c2;
     --markdown-code-block: #eeeeee;
     --border-color: #ffffff;
-    --border-weaker-base: var(--smoke-dark-alpha-3);
-    --border-weaker-hover: var(--smoke-dark-alpha-4);
-    --border-weaker-active: var(--smoke-dark-alpha-6);
-    --border-weaker-selected: var(--cobalt-dark-alpha-3);
-    --border-weaker-disabled: var(--smoke-dark-alpha-2);
-    --border-weaker-focus: var(--smoke-dark-alpha-6);
+    --border-weaker-base: var(--smoke-dark-alpha-2);
     --button-ghost-hover: var(--smoke-dark-alpha-2);
     --button-ghost-hover2: var(--smoke-dark-alpha-3);
     --avatar-background-pink: #501b3f;

+ 0 - 5
packages/ui/src/theme/resolve.ts

@@ -152,11 +152,6 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
   tokens["border-weak-disabled"] = neutralAlpha[5]
   tokens["border-weak-focus"] = neutralAlpha[isDark ? 7 : 6]
   tokens["border-weaker-base"] = neutralAlpha[2]
-  tokens["border-weaker-hover"] = neutralAlpha[3]
-  tokens["border-weaker-active"] = neutralAlpha[5]
-  tokens["border-weaker-selected"] = withAlpha(interactive[3], isDark ? 0.3 : 0.4) as ColorValue
-  tokens["border-weaker-disabled"] = neutralAlpha[1]
-  tokens["border-weaker-focus"] = neutralAlpha[5]
 
   tokens["border-interactive-base"] = interactive[6]
   tokens["border-interactive-hover"] = interactive[7]

+ 0 - 10
packages/ui/src/theme/themes/oc-1.json

@@ -247,11 +247,6 @@
       "markdown-code-block": "#1a1a1a",
       "border-color": "#ffffff",
       "border-weaker-base": "var(--smoke-light-alpha-3)",
-      "border-weaker-hover": "var(--smoke-light-alpha-4)",
-      "border-weaker-active": "var(--smoke-light-alpha-6)",
-      "border-weaker-selected": "var(--cobalt-light-alpha-4)",
-      "border-weaker-disabled": "var(--smoke-light-alpha-2)",
-      "border-weaker-focus": "var(--smoke-light-alpha-6)",
       "button-ghost-hover": "var(--smoke-light-alpha-2)",
       "button-ghost-hover2": "var(--smoke-light-alpha-3)",
       "avatar-background-pink": "#feeef8",
@@ -513,11 +508,6 @@
       "markdown-code-block": "#eeeeee",
       "border-color": "#ffffff",
       "border-weaker-base": "var(--smoke-dark-alpha-3)",
-      "border-weaker-hover": "var(--smoke-dark-alpha-4)",
-      "border-weaker-active": "var(--smoke-dark-alpha-6)",
-      "border-weaker-selected": "var(--cobalt-dark-alpha-3)",
-      "border-weaker-disabled": "var(--smoke-dark-alpha-2)",
-      "border-weaker-focus": "var(--smoke-dark-alpha-6)",
       "button-ghost-hover": "var(--smoke-dark-alpha-2)",
       "button-ghost-hover2": "var(--smoke-dark-alpha-3)",
       "avatar-background-pink": "#501b3f",

+ 170 - 180
packages/ui/src/theme/themes/oc-2.json

@@ -4,7 +4,7 @@
   "id": "oc-2",
   "light": {
     "seeds": {
-      "neutral": "#8e8b8b",
+      "neutral": "#8f8f8f",
       "primary": "#dcde8d",
       "success": "#12c905",
       "warning": "#ffdc17",
@@ -15,32 +15,32 @@
       "diffDelete": "#fc533a"
     },
     "overrides": {
-      "background-base": "#f8f7f7",
-      "background-weak": "var(--gray-light-3)",
-      "background-strong": "var(--gray-light-1)",
+      "background-base": "#f8f8f8",
+      "background-weak": "#f3f3f3",
+      "background-strong": "#fcfcfc",
       "background-stronger": "#fcfcfc",
-      "surface-base": "var(--gray-light-alpha-2)",
-      "base": "var(--gray-light-alpha-2)",
-      "surface-base-hover": "#0500000f",
-      "surface-base-active": "var(--gray-light-alpha-3)",
+      "surface-base": "#00000008",
+      "base": "#00000008",
+      "surface-base-hover": "#0000000f",
+      "surface-base-active": "#0000000d",
       "surface-base-interactive-active": "var(--cobalt-light-alpha-3)",
-      "base2": "var(--gray-light-alpha-2)",
-      "base3": "var(--gray-light-alpha-2)",
-      "surface-inset-base": "var(--gray-light-alpha-2)",
-      "surface-inset-base-hover": "var(--gray-light-alpha-3)",
-      "surface-inset-strong": "#1f000017",
-      "surface-inset-strong-hover": "#1f000017",
-      "surface-raised-base": "var(--gray-light-alpha-2)",
-      "surface-float-base": "var(--gray-dark-1)",
-      "surface-float-base-hover": "var(--gray-dark-2)",
-      "surface-raised-base-hover": "var(--gray-light-alpha-3)",
-      "surface-raised-base-active": "var(--gray-light-alpha-5)",
-      "surface-raised-strong": "var(--gray-light-1)",
+      "base2": "#00000008",
+      "base3": "#00000008",
+      "surface-inset-base": "#00000008",
+      "surface-inset-base-hover": "#0000000d",
+      "surface-inset-strong": "#00000017",
+      "surface-inset-strong-hover": "#00000017",
+      "surface-raised-base": "#00000008",
+      "surface-float-base": "#161616",
+      "surface-float-base-hover": "#1c1c1c",
+      "surface-raised-base-hover": "#0000000d",
+      "surface-raised-base-active": "#00000017",
+      "surface-raised-strong": "#fcfcfc",
       "surface-raised-strong-hover": "var(--white)",
       "surface-raised-stronger": "var(--white)",
       "surface-raised-stronger-hover": "var(--white)",
-      "surface-weak": "var(--gray-light-alpha-3)",
-      "surface-weaker": "var(--gray-light-alpha-4)",
+      "surface-weak": "#0000000d",
+      "surface-weaker": "#00000012",
       "surface-strong": "#ffffff",
       "surface-raised-stronger-non-alpha": "var(--white)",
       "surface-brand-base": "var(--yuzu-light-9)",
@@ -62,7 +62,7 @@
       "surface-info-weak": "var(--lilac-light-2)",
       "surface-info-strong": "var(--lilac-light-9)",
       "surface-diff-unchanged-base": "#ffffff00",
-      "surface-diff-skip-base": "var(--gray-light-2)",
+      "surface-diff-skip-base": "#f8f8f8",
       "surface-diff-hidden-base": "var(--blue-light-3)",
       "surface-diff-hidden-weak": "var(--blue-light-2)",
       "surface-diff-hidden-weaker": "var(--blue-light-1)",
@@ -78,69 +78,69 @@
       "surface-diff-delete-weaker": "var(--ember-light-1)",
       "surface-diff-delete-strong": "var(--ember-light-6)",
       "surface-diff-delete-stronger": "var(--ember-light-9)",
-      "input-base": "var(--gray-light-1)",
-      "input-hover": "var(--gray-light-2)",
+      "input-base": "#fcfcfc",
+      "input-hover": "#f8f8f8",
       "input-active": "var(--cobalt-light-1)",
       "input-selected": "var(--cobalt-light-4)",
       "input-focus": "var(--cobalt-light-1)",
-      "input-disabled": "var(--gray-light-4)",
-      "text-base": "var(--gray-light-11)",
-      "text-weak": "var(--gray-light-9)",
-      "text-weaker": "var(--gray-light-8)",
-      "text-strong": "var(--gray-light-12)",
-      "text-invert-base": "var(--gray-dark-alpha-11)",
-      "text-invert-weak": "var(--gray-dark-alpha-9)",
-      "text-invert-weaker": "var(--gray-dark-alpha-8)",
-      "text-invert-strong": "var(--gray-dark-alpha-12)",
+      "input-disabled": "#ededed",
+      "text-base": "#6f6f6f",
+      "text-weak": "#8f8f8f",
+      "text-weaker": "#c7c7c7",
+      "text-strong": "#171717",
+      "text-invert-base": "#ffffff96",
+      "text-invert-weak": "#ffffff63",
+      "text-invert-weaker": "#ffffff40",
+      "text-invert-strong": "#ffffffeb",
       "text-interactive-base": "var(--cobalt-light-9)",
-      "text-on-brand-base": "var(--gray-light-alpha-11)",
-      "text-on-interactive-base": "var(--gray-light-1)",
-      "text-on-interactive-weak": "var(--gray-dark-alpha-11)",
+      "text-on-brand-base": "#0000008f",
+      "text-on-interactive-base": "#fcfcfc",
+      "text-on-interactive-weak": "#ffffff96",
       "text-on-success-base": "var(--apple-light-10)",
       "text-on-critical-base": "var(--ember-light-10)",
       "text-on-critical-weak": "var(--ember-light-8)",
       "text-on-critical-strong": "var(--ember-light-12)",
-      "text-on-warning-base": "var(--gray-dark-alpha-11)",
-      "text-on-info-base": "var(--gray-dark-alpha-11)",
+      "text-on-warning-base": "#ffffff96",
+      "text-on-info-base": "#ffffff96",
       "text-diff-add-base": "var(--mint-light-11)",
       "text-diff-delete-base": "var(--ember-light-10)",
       "text-diff-delete-strong": "var(--ember-light-12)",
       "text-diff-add-strong": "var(--mint-light-12)",
-      "text-on-info-weak": "var(--gray-dark-alpha-9)",
-      "text-on-info-strong": "var(--gray-dark-alpha-12)",
-      "text-on-warning-weak": "var(--gray-dark-alpha-9)",
-      "text-on-warning-strong": "var(--gray-dark-alpha-12)",
+      "text-on-info-weak": "#ffffff63",
+      "text-on-info-strong": "#ffffffeb",
+      "text-on-warning-weak": "#ffffff63",
+      "text-on-warning-strong": "#ffffffeb",
       "text-on-success-weak": "var(--apple-light-6)",
       "text-on-success-strong": "var(--apple-light-12)",
-      "text-on-brand-weak": "var(--gray-light-alpha-9)",
-      "text-on-brand-weaker": "var(--gray-light-alpha-8)",
-      "text-on-brand-strong": "var(--gray-light-alpha-12)",
-      "button-primary-base": "var(--gray-light-12)",
-      "button-secondary-base": "var(--gray-light-1)",
+      "text-on-brand-weak": "#00000070",
+      "text-on-brand-weaker": "#00000038",
+      "text-on-brand-strong": "#000000e8",
+      "button-primary-base": "#171717",
+      "button-secondary-base": "#fcfcfc",
       "button-secondary-hover": "FFFFFF0A",
-      "border-base": "var(--gray-light-alpha-7)",
-      "border-hover": "var(--gray-light-alpha-8)",
-      "border-active": "var(--gray-light-alpha-9)",
+      "border-base": "#00000024",
+      "border-hover": "#00000038",
+      "border-active": "#00000070",
       "border-selected": "var(--cobalt-light-alpha-9)",
-      "border-disabled": "var(--gray-light-alpha-8)",
-      "border-focus": "var(--gray-light-alpha-9)",
-      "border-weak-base": "var(--gray-light-alpha-5)",
-      "border-strong-base": "var(--gray-light-alpha-7)",
-      "border-strong-hover": "var(--gray-light-alpha-8)",
-      "border-strong-active": "var(--gray-light-alpha-7)",
+      "border-disabled": "#00000038",
+      "border-focus": "#00000070",
+      "border-weak-base": "#e5e5e5",
+      "border-strong-base": "#00000024",
+      "border-strong-hover": "#00000038",
+      "border-strong-active": "#00000024",
       "border-strong-selected": "var(--cobalt-light-alpha-6)",
-      "border-strong-disabled": "var(--gray-light-alpha-6)",
-      "border-strong-focus": "var(--gray-light-alpha-7)",
-      "border-weak-hover": "var(--gray-light-alpha-6)",
-      "border-weak-active": "var(--gray-light-alpha-7)",
+      "border-strong-disabled": "#0000001c",
+      "border-strong-focus": "#00000024",
+      "border-weak-hover": "#0000001c",
+      "border-weak-active": "#00000024",
       "border-weak-selected": "var(--cobalt-light-alpha-5)",
-      "border-weak-disabled": "var(--gray-light-alpha-6)",
-      "border-weak-focus": "var(--gray-light-alpha-7)",
+      "border-weak-disabled": "#0000001c",
+      "border-weak-focus": "#00000024",
       "border-interactive-base": "var(--cobalt-light-7)",
       "border-interactive-hover": "var(--cobalt-light-8)",
       "border-interactive-active": "var(--cobalt-light-9)",
       "border-interactive-selected": "var(--cobalt-light-9)",
-      "border-interactive-disabled": "var(--gray-light-8)",
+      "border-interactive-disabled": "#c7c7c7",
       "border-interactive-focus": "var(--cobalt-light-9)",
       "border-success-base": "var(--apple-light-6)",
       "border-success-hover": "var(--apple-light-7)",
@@ -154,26 +154,26 @@
       "border-info-base": "var(--lilac-light-6)",
       "border-info-hover": "var(--lilac-light-7)",
       "border-info-selected": "var(--lilac-light-9)",
-      "icon-base": "var(--gray-light-9)",
-      "icon-hover": "var(--gray-light-11)",
-      "icon-active": "var(--gray-light-12)",
-      "icon-selected": "var(--gray-light-12)",
-      "icon-disabled": "var(--gray-light-8)",
-      "icon-focus": "var(--gray-light-12)",
+      "icon-base": "#8f8f8f",
+      "icon-hover": "#6f6f6f",
+      "icon-active": "#171717",
+      "icon-selected": "#171717",
+      "icon-disabled": "#c7c7c7",
+      "icon-focus": "#171717",
       "icon-invert-base": "#ffffff",
-      "icon-weak-base": "var(--gray-light-7)",
-      "icon-weak-hover": "var(--gray-light-8)",
-      "icon-weak-active": "var(--gray-light-9)",
-      "icon-weak-selected": "var(--gray-light-10)",
-      "icon-weak-disabled": "var(--gray-light-6)",
-      "icon-weak-focus": "var(--gray-light-9)",
-      "icon-strong-base": "var(--gray-light-12)",
-      "icon-strong-hover": "#151313",
+      "icon-weak-base": "#dbdbdb",
+      "icon-weak-hover": "#c7c7c7",
+      "icon-weak-active": "#8f8f8f",
+      "icon-weak-selected": "#858585",
+      "icon-weak-disabled": "#e2e2e2",
+      "icon-weak-focus": "#8f8f8f",
+      "icon-strong-base": "#171717",
+      "icon-strong-hover": "#151515",
       "icon-strong-active": "#020202",
       "icon-strong-selected": "#020202",
-      "icon-strong-disabled": "var(--gray-light-6)",
+      "icon-strong-disabled": "#e2e2e2",
       "icon-strong-focus": "#020202",
-      "icon-brand-base": "var(--gray-light-12)",
+      "icon-brand-base": "#171717",
       "icon-interactive-base": "var(--cobalt-light-9)",
       "icon-success-base": "var(--apple-light-7)",
       "icon-success-hover": "var(--apple-light-8)",
@@ -187,10 +187,10 @@
       "icon-info-base": "var(--lilac-light-7)",
       "icon-info-hover": "var(--lilac-light-8)",
       "icon-info-active": "var(--lilac-light-11)",
-      "icon-on-brand-base": "var(--gray-light-alpha-11)",
-      "icon-on-brand-hover": "var(--gray-light-alpha-12)",
-      "icon-on-brand-selected": "var(--gray-light-alpha-12)",
-      "icon-on-interactive-base": "var(--gray-light-1)",
+      "icon-on-brand-base": "#0000008f",
+      "icon-on-brand-hover": "#000000e8",
+      "icon-on-brand-selected": "#000000e8",
+      "icon-on-interactive-base": "#fcfcfc",
       "icon-agent-plan-base": "var(--purple-light-9)",
       "icon-agent-docs-base": "var(--amber-light-9)",
       "icon-agent-ask-base": "var(--cyan-light-9)",
@@ -246,14 +246,9 @@
       "markdown-image-text": "#318795",
       "markdown-code-block": "#1a1a1a",
       "border-color": "#ffffff",
-      "border-weaker-base": "var(--gray-light-alpha-3)",
-      "border-weaker-hover": "var(--gray-light-alpha-4)",
-      "border-weaker-active": "var(--gray-light-alpha-6)",
-      "border-weaker-selected": "var(--cobalt-light-alpha-4)",
-      "border-weaker-disabled": "var(--gray-light-alpha-2)",
-      "border-weaker-focus": "var(--gray-light-alpha-6)",
-      "button-ghost-hover": "var(--gray-light-alpha-2)",
-      "button-ghost-hover2": "var(--gray-light-alpha-3)",
+      "border-weaker-base": "#efefef",
+      "button-ghost-hover": "#00000008",
+      "button-ghost-hover2": "#0000000d",
       "avatar-background-pink": "#feeef8",
       "avatar-background-mint": "#e1fbf4",
       "avatar-background-orange": "#fff1e7",
@@ -270,7 +265,7 @@
   },
   "dark": {
     "seeds": {
-      "neutral": "#716c6b",
+      "neutral": "#707070",
       "primary": "#fab283",
       "success": "#12c905",
       "warning": "#fcd53a",
@@ -281,33 +276,33 @@
       "diffDelete": "#fc533a"
     },
     "overrides": {
-      "base": "var(--gray-dark-alpha-2)",
-      "base2": "var(--gray-dark-alpha-2)",
-      "base3": "var(--gray-dark-alpha-2)",
+      "base": "#ffffff08",
+      "base2": "#ffffff08",
+      "base3": "#ffffff08",
       "background-base": "#101010",
       "background-weak": "#1E1E1E",
       "background-strong": "#121212",
       "background-stronger": "#151515",
-      "surface-base": "var(--gray-dark-alpha-2)",
+      "surface-base": "#ffffff08",
       "surface-base-hover": "#FFFFFF0A",
-      "surface-base-active": "var(--gray-dark-alpha-3)",
+      "surface-base-active": "#ffffff0f",
       "surface-base-interactive-active": "var(--cobalt-dark-alpha-2)",
-      "surface-inset-base": "#0e0b0b7f",
-      "surface-inset-base-hover": "#0e0b0b7f",
-      "surface-inset-strong": "#060505cc",
-      "surface-inset-strong-hover": "#060505cc",
-      "surface-raised-base": "var(--gray-dark-alpha-3)",
-      "surface-float-base": "var(--gray-dark-1)",
-      "surface-float-base-hover": "var(--gray-dark-2)",
-      "surface-raised-base-hover": "var(--gray-dark-alpha-4)",
-      "surface-raised-base-active": "var(--gray-dark-alpha-5)",
-      "surface-raised-strong": "var(--gray-dark-alpha-4)",
-      "surface-raised-strong-hover": "var(--gray-dark-alpha-6)",
-      "surface-raised-stronger": "var(--gray-dark-alpha-6)",
-      "surface-raised-stronger-hover": "var(--gray-dark-alpha-7)",
-      "surface-weak": "var(--gray-dark-alpha-4)",
-      "surface-weaker": "var(--gray-dark-alpha-5)",
-      "surface-strong": "var(--gray-dark-alpha-7)",
+      "surface-inset-base": "#0000007f",
+      "surface-inset-base-hover": "#0000007f",
+      "surface-inset-strong": "#000000cc",
+      "surface-inset-strong-hover": "#000000cc",
+      "surface-raised-base": "#ffffff0f",
+      "surface-float-base": "#161616",
+      "surface-float-base-hover": "#1c1c1c",
+      "surface-raised-base-hover": "#ffffff14",
+      "surface-raised-base-active": "#ffffff1a",
+      "surface-raised-strong": "#ffffff14",
+      "surface-raised-strong-hover": "#ffffff21",
+      "surface-raised-stronger": "#ffffff21",
+      "surface-raised-stronger-hover": "#ffffff2b",
+      "surface-weak": "#ffffff14",
+      "surface-weaker": "#ffffff1a",
+      "surface-strong": "#ffffff2b",
       "surface-raised-stronger-non-alpha": "#1B1B1B",
       "surface-brand-base": "var(--yuzu-light-9)",
       "surface-brand-hover": "var(--yuzu-light-10)",
@@ -327,8 +322,8 @@
       "surface-info-base": "var(--lilac-light-3)",
       "surface-info-weak": "var(--lilac-light-2)",
       "surface-info-strong": "var(--lilac-light-9)",
-      "surface-diff-unchanged-base": "var(--gray-dark-1)",
-      "surface-diff-skip-base": "var(--gray-dark-alpha-1)",
+      "surface-diff-unchanged-base": "#161616",
+      "surface-diff-skip-base": "#00000000",
       "surface-diff-hidden-base": "var(--blue-dark-2)",
       "surface-diff-hidden-weak": "var(--blue-dark-1)",
       "surface-diff-hidden-weaker": "var(--blue-dark-3)",
@@ -344,64 +339,64 @@
       "surface-diff-delete-weaker": "var(--ember-dark-3)",
       "surface-diff-delete-strong": "var(--ember-dark-5)",
       "surface-diff-delete-stronger": "var(--ember-dark-11)",
-      "input-base": "var(--gray-dark-2)",
-      "input-hover": "var(--gray-dark-2)",
+      "input-base": "#1c1c1c",
+      "input-hover": "#1c1c1c",
       "input-active": "var(--cobalt-dark-1)",
       "input-selected": "var(--cobalt-dark-2)",
       "input-focus": "var(--cobalt-dark-1)",
-      "input-disabled": "var(--gray-dark-4)",
-      "text-base": "var(--gray-dark-alpha-11)",
-      "text-weak": "var(--gray-dark-alpha-9)",
-      "text-weaker": "var(--gray-dark-alpha-8)",
-      "text-strong": "var(--gray-dark-alpha-12)",
-      "text-invert-base": "var(--gray-dark-alpha-11)",
-      "text-invert-weak": "var(--gray-dark-alpha-9)",
-      "text-invert-weaker": "var(--gray-dark-alpha-8)",
-      "text-invert-strong": "var(--gray-dark-alpha-12)",
+      "input-disabled": "#282828",
+      "text-base": "#ffffff96",
+      "text-weak": "#ffffff63",
+      "text-weaker": "#ffffff40",
+      "text-strong": "#ffffffeb",
+      "text-invert-base": "#ffffff96",
+      "text-invert-weak": "#ffffff63",
+      "text-invert-weaker": "#ffffff40",
+      "text-invert-strong": "#ffffffeb",
       "text-interactive-base": "var(--cobalt-dark-11)",
-      "text-on-brand-base": "var(--gray-dark-alpha-11)",
-      "text-on-interactive-base": "var(--gray-dark-12)",
-      "text-on-interactive-weak": "var(--gray-dark-alpha-11)",
+      "text-on-brand-base": "#ffffff96",
+      "text-on-interactive-base": "#ededed",
+      "text-on-interactive-weak": "#ffffff96",
       "text-on-success-base": "var(--apple-dark-9)",
       "text-on-critical-base": "var(--ember-dark-9)",
       "text-on-critical-weak": "var(--ember-dark-8)",
       "text-on-critical-strong": "var(--ember-dark-12)",
-      "text-on-warning-base": "var(--gray-dark-alpha-11)",
-      "text-on-info-base": "var(--gray-dark-alpha-11)",
+      "text-on-warning-base": "#ffffff96",
+      "text-on-info-base": "#ffffff96",
       "text-diff-add-base": "var(--mint-dark-11)",
       "text-diff-delete-base": "var(--ember-dark-9)",
       "text-diff-delete-strong": "var(--ember-dark-12)",
       "text-diff-add-strong": "var(--mint-dark-8)",
-      "text-on-info-weak": "var(--gray-dark-alpha-9)",
-      "text-on-info-strong": "var(--gray-dark-alpha-12)",
-      "text-on-warning-weak": "var(--gray-dark-alpha-9)",
-      "text-on-warning-strong": "var(--gray-dark-alpha-12)",
+      "text-on-info-weak": "#ffffff63",
+      "text-on-info-strong": "#ffffffeb",
+      "text-on-warning-weak": "#ffffff63",
+      "text-on-warning-strong": "#ffffffeb",
       "text-on-success-weak": "var(--apple-dark-8)",
       "text-on-success-strong": "var(--apple-dark-12)",
-      "text-on-brand-weak": "var(--gray-dark-alpha-9)",
-      "text-on-brand-weaker": "var(--gray-dark-alpha-8)",
-      "text-on-brand-strong": "var(--gray-dark-alpha-12)",
-      "button-primary-base": "var(--gray-dark-12)",
-      "button-secondary-base": "var(--gray-dark-2)",
+      "text-on-brand-weak": "#ffffff63",
+      "text-on-brand-weaker": "#ffffff40",
+      "text-on-brand-strong": "#ffffffeb",
+      "button-primary-base": "#ededed",
+      "button-secondary-base": "#1c1c1c",
       "button-secondary-hover": "#FFFFFF0A",
-      "border-base": "var(--gray-dark-alpha-7)",
-      "border-hover": "var(--gray-dark-alpha-8)",
-      "border-active": "var(--gray-dark-alpha-9)",
+      "border-base": "#ffffff2b",
+      "border-hover": "#ffffff40",
+      "border-active": "#ffffff63",
       "border-selected": "var(--cobalt-dark-alpha-11)",
-      "border-disabled": "var(--gray-dark-alpha-8)",
-      "border-focus": "var(--gray-dark-alpha-9)",
-      "border-weak-base": "var(--gray-dark-alpha-5)",
-      "border-weak-hover": "var(--gray-dark-alpha-7)",
-      "border-weak-active": "var(--gray-dark-alpha-8)",
+      "border-disabled": "#ffffff40",
+      "border-focus": "#ffffff63",
+      "border-weak-base": "#282828",
+      "border-weak-hover": "#ffffff2b",
+      "border-weak-active": "#ffffff40",
       "border-weak-selected": "var(--cobalt-dark-alpha-6)",
-      "border-weak-disabled": "var(--gray-dark-alpha-6)",
-      "border-weak-focus": "var(--gray-dark-alpha-8)",
-      "border-strong-base": "var(--gray-dark-alpha-8)",
+      "border-weak-disabled": "#ffffff21",
+      "border-weak-focus": "#ffffff40",
+      "border-strong-base": "#ffffff40",
       "border-interactive-base": "var(--cobalt-light-7)",
       "border-interactive-hover": "var(--cobalt-light-8)",
       "border-interactive-active": "var(--cobalt-light-9)",
       "border-interactive-selected": "var(--cobalt-light-9)",
-      "border-interactive-disabled": "var(--gray-light-8)",
+      "border-interactive-disabled": "#c7c7c7",
       "border-interactive-focus": "var(--cobalt-light-9)",
       "border-success-base": "var(--apple-light-6)",
       "border-success-hover": "var(--apple-light-7)",
@@ -415,24 +410,24 @@
       "border-info-base": "var(--lilac-light-6)",
       "border-info-hover": "var(--lilac-light-7)",
       "border-info-selected": "var(--lilac-light-9)",
-      "icon-base": "var(--gray-dark-10)",
-      "icon-hover": "var(--gray-dark-11)",
-      "icon-active": "var(--gray-dark-12)",
-      "icon-selected": "var(--gray-dark-12)",
-      "icon-disabled": "var(--gray-dark-8)",
-      "icon-focus": "var(--gray-dark-12)",
-      "icon-invert-base": "var(--gray-dark-1)",
-      "icon-weak-base": "var(--gray-dark-6)",
-      "icon-weak-hover": "var(--gray-light-7)",
-      "icon-weak-active": "var(--gray-light-8)",
-      "icon-weak-selected": "var(--gray-light-9)",
-      "icon-weak-disabled": "var(--gray-light-4)",
-      "icon-weak-focus": "var(--gray-light-9)",
-      "icon-strong-base": "var(--gray-dark-12)",
+      "icon-base": "#7e7e7e",
+      "icon-hover": "#a0a0a0",
+      "icon-active": "#ededed",
+      "icon-selected": "#ededed",
+      "icon-disabled": "#505050",
+      "icon-focus": "#ededed",
+      "icon-invert-base": "#161616",
+      "icon-weak-base": "#343434",
+      "icon-weak-hover": "#dbdbdb",
+      "icon-weak-active": "#c7c7c7",
+      "icon-weak-selected": "#8f8f8f",
+      "icon-weak-disabled": "#ededed",
+      "icon-weak-focus": "#8f8f8f",
+      "icon-strong-base": "#ededed",
       "icon-strong-hover": "#F3F3F3",
       "icon-strong-active": "#EBEBEB",
       "icon-strong-selected": "#FCFCFC",
-      "icon-strong-disabled": "var(--gray-dark-7)",
+      "icon-strong-disabled": "#3e3e3e",
       "icon-strong-focus": "#FCFCFC",
       "icon-brand-base": "var(--white)",
       "icon-interactive-base": "var(--cobalt-dark-11)",
@@ -448,10 +443,10 @@
       "icon-info-base": "var(--lilac-dark-7)",
       "icon-info-hover": "var(--lilac-dark-8)",
       "icon-info-active": "var(--lilac-dark-11)",
-      "icon-on-brand-base": "var(--gray-light-alpha-11)",
-      "icon-on-brand-hover": "var(--gray-light-alpha-12)",
-      "icon-on-brand-selected": "var(--gray-light-alpha-12)",
-      "icon-on-interactive-base": "var(--gray-dark-12)",
+      "icon-on-brand-base": "#0000008f",
+      "icon-on-brand-hover": "#000000e8",
+      "icon-on-brand-selected": "#000000e8",
+      "icon-on-interactive-base": "#ededed",
       "icon-agent-plan-base": "var(--purple-dark-9)",
       "icon-agent-docs-base": "var(--amber-dark-9)",
       "icon-agent-ask-base": "var(--cyan-dark-9)",
@@ -507,14 +502,9 @@
       "markdown-image-text": "#56b6c2",
       "markdown-code-block": "#eeeeee",
       "border-color": "#ffffff",
-      "border-weaker-base": "var(--gray-dark-alpha-3)",
-      "border-weaker-hover": "var(--gray-dark-alpha-4)",
-      "border-weaker-active": "var(--gray-dark-alpha-6)",
-      "border-weaker-selected": "var(--cobalt-dark-alpha-3)",
-      "border-weaker-disabled": "var(--gray-dark-alpha-2)",
-      "border-weaker-focus": "var(--gray-dark-alpha-6)",
-      "button-ghost-hover": "var(--gray-dark-alpha-2)",
-      "button-ghost-hover2": "var(--gray-dark-alpha-3)",
+      "border-weaker-base": "#1e1e1e",
+      "button-ghost-hover": "#ffffff08",
+      "button-ghost-hover2": "#ffffff0f",
       "avatar-background-pink": "#501b3f",
       "avatar-background-mint": "#033a34",
       "avatar-background-orange": "#5f2a06",