diff options
Diffstat (limited to 'packages')
21 files changed, 482 insertions, 367 deletions
diff --git a/packages/app/e2e/actions.ts b/packages/app/e2e/actions.ts index 919a1add8..5d244ba02 100644 --- a/packages/app/e2e/actions.ts +++ b/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() diff --git a/packages/app/e2e/app/titlebar-history.spec.ts b/packages/app/e2e/app/titlebar-history.spec.ts index 9d6091176..a4592ff1d 100644 --- a/packages/app/e2e/app/titlebar-history.spec.ts +++ b/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}(?:\\?|#|$)`)) diff --git a/packages/app/e2e/projects/project-edit.spec.ts b/packages/app/e2e/projects/project-edit.spec.ts index 4a286fea7..7c20f29ec 100644 --- a/packages/app/e2e/projects/project-edit.spec.ts +++ b/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() diff --git a/packages/app/e2e/projects/projects-switch.spec.ts b/packages/app/e2e/projects/projects-switch.spec.ts index 81cca6988..2725100f4 100644 --- a/packages/app/e2e/projects/projects-switch.spec.ts +++ b/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() diff --git a/packages/app/e2e/projects/workspaces.spec.ts b/packages/app/e2e/projects/workspaces.spec.ts index 386739526..41c6bea8f 100644 --- a/packages/app/e2e/projects/workspaces.spec.ts +++ b/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") diff --git a/packages/app/e2e/settings/settings-keybinds.spec.ts b/packages/app/e2e/settings/settings-keybinds.spec.ts index 5e98bd158..e0d590b31 100644 --- a/packages/app/e2e/settings/settings-keybinds.spec.ts +++ b/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) }) diff --git a/packages/app/e2e/sidebar/sidebar-popover-actions.spec.ts b/packages/app/e2e/sidebar/sidebar-popover-actions.spec.ts index e37f94f3a..09701f3f9 100644 --- a/packages/app/e2e/sidebar/sidebar-popover-actions.spec.ts +++ b/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) diff --git a/packages/app/e2e/sidebar/sidebar-session-links.spec.ts b/packages/app/e2e/sidebar/sidebar-session-links.spec.ts index cda2278a9..052b7cb84 100644 --- a/packages/app/e2e/sidebar/sidebar-session-links.spec.ts +++ b/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}(?:\\?|#|$)`)) diff --git a/packages/app/e2e/sidebar/sidebar.spec.ts b/packages/app/e2e/sidebar/sidebar.spec.ts index 5c78c2220..c6bf3fa9a 100644 --- a/packages/app/e2e/sidebar/sidebar.spec.ts +++ b/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, diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index cf2c3b6c4..c9f6ae26f 100644 --- a/packages/app/src/pages/layout.tsx +++ b/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,6 +208,27 @@ 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 @@ -199,6 +236,27 @@ export default function Layout(props: ParentProps) { }) 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> diff --git a/packages/app/src/pages/layout/sidebar-shell.tsx b/packages/app/src/pages/layout/sidebar-shell.tsx index d813ef3e1..d3070e374 100644 --- a/packages/app/src/pages/layout/sidebar-shell.tsx +++ b/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> ) } diff --git a/packages/app/src/pages/layout/sidebar-workspace.tsx b/packages/app/src/pages/layout/sidebar-workspace.tsx index 43d99cf89..f2fd3af2a 100644 --- a/packages/app/src/pages/layout/sidebar-workspace.tsx +++ b/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> diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 66d4382c0..927461a2a 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/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" diff --git a/packages/app/src/pages/session/terminal-panel.tsx b/packages/app/src/pages/session/terminal-panel.tsx index cc4c17ee2..69c8aefcc 100644 --- a/packages/app/src/pages/session/terminal-panel.tsx +++ b/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) => ( diff --git a/packages/ui/script/colors.txt b/packages/ui/script/colors.txt index f84966853..2581b0e3e 100644 --- a/packages/ui/script/colors.txt +++ b/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); diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css index 1bf00b785..036533c10 100644 --- a/packages/ui/src/components/tabs.css +++ b/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; diff --git a/packages/ui/src/styles/tailwind/colors.css b/packages/ui/src/styles/tailwind/colors.css index 376cd35d3..adbe925f6 100644 --- a/packages/ui/src/styles/tailwind/colors.css +++ b/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); -} +}
\ No newline at end of file diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 832b43ec7..2637b4a28 100644 --- a/packages/ui/src/styles/theme.css +++ b/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; diff --git a/packages/ui/src/theme/resolve.ts b/packages/ui/src/theme/resolve.ts index f098e8028..e8fca4103 100644 --- a/packages/ui/src/theme/resolve.ts +++ b/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] diff --git a/packages/ui/src/theme/themes/oc-1.json b/packages/ui/src/theme/themes/oc-1.json index 03a67ee23..132825e3f 100644 --- a/packages/ui/src/theme/themes/oc-1.json +++ b/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", diff --git a/packages/ui/src/theme/themes/oc-2.json b/packages/ui/src/theme/themes/oc-2.json index 01ec1131a..73ca57da9 100644 --- a/packages/ui/src/theme/themes/oc-2.json +++ b/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", |
