diff --git a/packages/app/e2e/app/home.spec.ts b/packages/app/e2e/app/home.spec.ts index 6886f35c9..65de52756 100644 --- a/packages/app/e2e/app/home.spec.ts +++ b/packages/app/e2e/app/home.spec.ts @@ -8,7 +8,7 @@ test("@smoke root route renders seeded home entrypoints", async ({ page }) => { const home = page.locator('[data-component="session-new-home"]') await expect(home).toBeVisible() - await expect(page.getByRole("heading", { name: "Choose what to do" })).toBeVisible() + await expect(page.getByRole("heading", { name: "What do you want to do?" })).toBeVisible() await expect(home.locator(sessionComposerDockSelector)).toHaveCount(1) await openSidebar(page) await expect(page.getByRole("button", { name: "New session" })).toBeVisible() @@ -24,7 +24,7 @@ test("@smoke home renders the hero composer and starter cards", async ({ page, p const firstCard = home.getByRole("button", { name: /Process docs/i }) const workspaceChip = page.getByRole("button", { name: /Switch workspace|切换工作目录/i }) await expect(home).toBeVisible() - await expect(page.getByRole("heading", { name: "Choose what to do" })).toBeVisible() + await expect(page.getByRole("heading", { name: "What do you want to do?" })).toBeVisible() await expect(page.locator(sessionComposerDockSelector)).toHaveCount(1) await expect(composer).toHaveCount(1) await expect(composer).toHaveCSS("text-align", "left") diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 7181ed608..b794d6bef 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1127,7 +1127,7 @@ export const PromptInput: Component = (props) => { as="div" variant="ghost" size="normal" - class="h-[28px]! min-w-0 w-44 px-1.5 justify-start! text-13-regular! text-text-base group rounded-xl! transition-colors hover:bg-surface-base-hover" + class="h-[28px]! min-w-0 px-1.5 justify-start! text-13-regular! text-text-base group rounded-xl! transition-colors hover:bg-surface-base-hover" style={triggerStyle()} onClick={() => { void import("@/components/dialog-select-model-unpaid").then((x) => { @@ -1142,7 +1142,10 @@ export const PromptInput: Component = (props) => { style={{ "will-change": "opacity", transform: "translateZ(0)" }} /> - + {local.model.current()?.name ?? language.t("dialog.model.select.title")} @@ -1164,7 +1167,7 @@ export const PromptInput: Component = (props) => { size: "normal", style: triggerStyle(), class: - "h-[28px]! min-w-0 w-44 px-1.5 justify-start! text-13-regular! text-text-base group rounded-xl! transition-colors hover:bg-surface-base-hover", + "h-[28px]! min-w-0 px-1.5 justify-start! text-13-regular! text-text-base group rounded-xl! transition-colors hover:bg-surface-base-hover", "data-action": "prompt-model", }} onClose={restoreFocus} @@ -1176,7 +1179,10 @@ export const PromptInput: Component = (props) => { style={{ "will-change": "opacity", transform: "translateZ(0)" }} /> - + {local.model.current()?.name ?? language.t("dialog.model.select.title")} @@ -1208,7 +1214,7 @@ export const PromptInput: Component = (props) => { "aria-haspopup": "menu", style: triggerStyle(), class: - "h-[28px] px-2 max-w-[160px] inline-flex items-center gap-1.5 rounded-xl text-13-regular text-text-base transition-colors hover:bg-surface-base-hover", + "h-[28px] px-2 max-w-[160px] @max-[20rem]/composer:max-w-[80px] inline-flex items-center gap-1.5 rounded-xl text-13-regular text-text-base transition-[max-width,colors] duration-200 ease-out hover:bg-surface-base-hover", } as any } trigger={ @@ -1436,7 +1442,7 @@ export const PromptInput: Component = (props) => { = (props) => { }} /> -
+
- {label()} + {label()} } diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index e02e67b08..2bddd83db 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -1,7 +1,6 @@ import { createEffect, createMemo, Show, untrack } from "solid-js" import { createStore } from "solid-js/store" import { useLocation, useNavigate, useParams } from "@solidjs/router" -import { IconButton } from "@opencode-ai/ui/icon-button" import { Icon } from "@opencode-ai/ui/icon" import { Button } from "@opencode-ai/ui/button" import { TooltipKeybind } from "@opencode-ai/ui/tooltip" @@ -101,32 +100,10 @@ export function Titlebar() { >
-
- -
- - -
- -
{ expect(migrated.rightPanel.opened).toBe(false) expect(migrated.rightPanel.width).toBe(DEFAULT_RIGHT_PANEL_WIDTH) }) + + test("strips legacy mobileSidebar key while preserving sibling fields", () => { + const migrated = migrateStoredLayout({ + mobileSidebar: { opened: true }, + sidebar: { opened: true, width: 280 }, + rightPanel: { opened: false, width: 380 }, + sessionView: {}, + sessionTabs: {}, + }) as Record & { + sidebar: { opened: boolean; width: number } + rightPanel: { opened: boolean; width: number } + } + + expect("mobileSidebar" in migrated).toBe(false) + expect(migrated.sidebar).toEqual({ opened: true, width: 280 }) + expect(migrated.rightPanel).toEqual({ opened: false, width: 380 }) + }) }) diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index 77ad5b5df..7674e8aff 100644 --- a/packages/app/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -198,9 +198,6 @@ export function createDefaultLayoutState() { width: DEFAULT_RIGHT_PANEL_WIDTH, opened: false, }, - mobileSidebar: { - opened: false, - }, sessionTabs: {} as Record, sessionView: {} as Record, handoff: { @@ -293,19 +290,23 @@ export function migrateStoredLayout(value: unknown) { const sessionViewMigration = migrateSessionView(value.sessionView, migratedSessionTabs) const sessionStateChanged = sessionViewMigration.changed + const hasMobileSidebar = "mobileSidebar" in value + if ( migratedSidebar === sidebar && migratedReview === review && migratedFileTree === fileTree && migratedRightPanel === rightPanel && migratedSessionTabs === sessionTabs && - !sessionStateChanged + !sessionStateChanged && + !hasMobileSidebar ) { return value } + const { mobileSidebar: _mobileSidebar, ...rest } = value as Record return { - ...value, + ...rest, sidebar: migratedSidebar, review: migratedReview, fileTree: migratedFileTree, @@ -763,18 +764,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setStore("rightPanel", "width", clampRightPanelWidth(width)) }, }, - mobileSidebar: { - opened: createMemo(() => store.mobileSidebar?.opened ?? false), - show() { - setStore("mobileSidebar", "opened", true) - }, - hide() { - setStore("mobileSidebar", "opened", false) - }, - toggle() { - setStore("mobileSidebar", "opened", (x) => !x) - }, - }, pendingMessage: { set(sessionKey: string, messageID: string) { const at = Date.now() diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 3be1bee58..256c2bb7b 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -632,10 +632,7 @@ export const dict = { "session.revertDock.expand": "Expand rolled back messages", "session.revertDock.restore": "Restore message", - "session.new.title": "Choose what to do", - // Kept for parity/branding tests; UI usage removed in #300. - "session.new.subtitle": - "PawWork helps you process files, analyze information, write content, and tackle everyday tasks.", + "session.new.title": "What do you want to do?", "session.new.reassurance": "Files and conversations stay on your computer", "session.new.card.document.title": "Process docs", "session.new.card.document.description": "Edit, convert, and extract from Word, Excel, PowerPoint, and PDF files.", @@ -756,11 +753,10 @@ export const dict = { "common.unknown": "unknown", "common.time.justNow": "Just now", - "common.time.minutesAgo.short": "{{count}}m ago", - "common.time.hoursAgo.short": "{{count}}h ago", - "common.time.daysAgo.short": "{{count}}d ago", + "common.time.minutesAgo.short": "{{count}}m", + "common.time.hoursAgo.short": "{{count}}h", + "common.time.daysAgo.short": "{{count}}d", - "sidebar.menu.toggle": "Toggle menu", "sidebar.nav.projectsAndSessions": "Projects and sessions", "sidebar.settings": "Settings", "sidebar.workspaces.enable": "Enable workspaces", @@ -775,7 +771,6 @@ export const dict = { "sidebar.empty.description": "Open a project to get started", "sidebar.pawwork.search": "Search", "sidebar.pawwork.empty.description": "Open a project to start using the session-first sidebar.", - "sidebar.pawwork.empty.sessions": "No sessions yet. Start a new one to begin.", "sidebar.pawwork.pinned": "Pinned", "sidebar.pawwork.all": "All sessions", "sidebar.pawwork.pinSession": "Pin session", diff --git a/packages/app/src/i18n/parity.test.ts b/packages/app/src/i18n/parity.test.ts index 519b1d76c..6e1c81efd 100644 --- a/packages/app/src/i18n/parity.test.ts +++ b/packages/app/src/i18n/parity.test.ts @@ -7,7 +7,6 @@ const keys = [ "command.session.previous.unseen", "command.session.next.unseen", "session.new.title", - "session.new.subtitle", "session.new.card.document.title", "session.new.card.document.description", "session.new.card.analysis.title", diff --git a/packages/app/src/i18n/zh-branding.test.ts b/packages/app/src/i18n/zh-branding.test.ts index 6596e3fad..db109d892 100644 --- a/packages/app/src/i18n/zh-branding.test.ts +++ b/packages/app/src/i18n/zh-branding.test.ts @@ -4,7 +4,6 @@ import { dict as zh } from "./zh" describe("zh branding copy", () => { test("uses Chinese product naming on key user-facing surfaces", () => { expect(zh["dialog.model.unpaid.freeModels.title"]).toBe("爪印内置免费模型") - expect(zh["session.new.subtitle"]).toBe("爪印可以帮你处理文件、分析信息、撰写内容并完成各类任务。") expect(zh["sidebar.gettingStarted.line1"]).toBe("爪印内置免费模型,你可以立即开始使用。") expect(zh["app.name.desktop"]).toBe("爪印") expect(zh["toast.update.description"]).toBe("爪印有新版本 ({{version}}) 可安装。") @@ -14,7 +13,6 @@ describe("zh branding copy", () => { test("removes standalone PawWork from curated Chinese UI strings", () => { const curatedKeys = [ "dialog.model.unpaid.freeModels.title", - "session.new.subtitle", "sidebar.gettingStarted.line1", "app.name.desktop", "toast.update.description", diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index f853d60fb..53154f03f 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -590,8 +590,6 @@ export const dict = { "session.revertDock.expand": "展开已回滚消息", "session.revertDock.restore": "恢复消息", "session.new.title": "今天想做什么?", - // Kept for parity/branding tests; UI usage removed in #300. - "session.new.subtitle": "爪印可以帮你处理文件、分析信息、撰写内容并完成各类任务。", "session.new.reassurance": "文件和对话仅在本机处理", "session.new.card.document.title": "处理文档", "session.new.card.document.description": "编辑、转换并提取 Word、Excel、PowerPoint 和 PDF 文件内容。", @@ -674,7 +672,6 @@ export const dict = { "common.loadMore": "加载更多", "common.key.esc": "ESC", - "sidebar.menu.toggle": "切换菜单", "sidebar.nav.projectsAndSessions": "项目和会话", "sidebar.settings": "设置", "sidebar.workspaces.enable": "启用工作区", @@ -689,7 +686,6 @@ export const dict = { "sidebar.empty.description": "打开一个项目开始使用", "sidebar.pawwork.search": "搜索", "sidebar.pawwork.empty.description": "打开一个项目后,即可使用以会话为中心的侧边栏。", - "sidebar.pawwork.empty.sessions": "还没有会话,先新建一个开始。", "sidebar.pawwork.pinned": "已置顶", "sidebar.pawwork.all": "全部会话", "sidebar.pawwork.pinSession": "置顶会话", @@ -983,9 +979,9 @@ export const dict = { "error.server.invalidConfiguration": "配置无效", "common.moreCountSuffix": " (还有 {{count}} 个)", "common.time.justNow": "刚刚", - "common.time.minutesAgo.short": "{{count}}分钟前", - "common.time.hoursAgo.short": "{{count}}小时前", - "common.time.daysAgo.short": "{{count}}天前", + "common.time.minutesAgo.short": "{{count}}分钟", + "common.time.hoursAgo.short": "{{count}}小时", + "common.time.daysAgo.short": "{{count}}天", "settings.providers.connected.environmentDescription": "已通过环境变量连接", "settings.providers.custom.description": "通过基础 URL 添加与 OpenAI 兼容的提供商。", diff --git a/packages/app/src/index.css b/packages/app/src/index.css index 7baa100de..bad07064e 100644 --- a/packages/app/src/index.css +++ b/packages/app/src/index.css @@ -109,15 +109,20 @@ } /* Mac native unified-chrome pattern: titlebar bg matches whatever surface - sits below it on each side. Left of --sidebar-width = sidebar bg (seamless - vertical extension of the sidebar panel up through the titlebar). Right - = main bg. The only visual seam is the sidebar/main vertical divider, - which now runs continuously from window top to bottom. - The xl+ gradient form lives inside the existing 1280px media block below - to satisfy shell-frame-contract.test.ts CSS rule ordering. */ + sits below it. Left of --sidebar-width = sidebar bg (seamless vertical + extension of the sidebar panel up through the titlebar). Right = main + bg. Right-panel boundary is intentionally NOT drawn into the titlebar + (Mail / Finder / Notes do not split their toolbar) so it never overlaps + with breadcrumb or center-portal content when the panel is wide. */ [data-component="titlebar-shell"][data-platform="desktop"][data-os="macos"] { padding-inline: 8px; - background: var(--background-base); + background: linear-gradient( + to right, + var(--background-weak) 0, + var(--background-weak) var(--sidebar-width, 0px), + var(--background-base) var(--sidebar-width, 0px), + var(--background-base) 100% + ); } [data-component="titlebar-shell"][data-platform="desktop"][data-os="macos"] [data-shell-slot="left-portal"] { @@ -169,18 +174,5 @@ overflow: hidden; } - [data-component="titlebar-shell"][data-platform="desktop"][data-os="macos"] { - background: linear-gradient( - to right, - var(--background-weak) 0, - var(--background-weak) var(--sidebar-width, 0px), - var(--background-base) var(--sidebar-width, 0px), - var(--background-base) calc(100% - var(--right-panel-width, 0px)), - var(--right-panel-divider, transparent) calc(100% - var(--right-panel-width, 0px)), - var(--right-panel-divider, transparent) calc(100% - var(--right-panel-width, 0px) + 1px), - var(--background-base) calc(100% - var(--right-panel-width, 0px) + 1px), - var(--background-base) 100% - ); - } } } diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 03cf723c1..490430457 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -50,7 +50,6 @@ import { usePermission } from "@/context/permission" import { Binary } from "@opencode-ai/util/binary" import { retry } from "@opencode-ai/util/retry" import { playSoundById } from "@/utils/sound" -import { createAim } from "@/utils/aim" import { setNavigate } from "@/utils/notification-click" import { Worktree as WorktreeState } from "@/utils/worktree" import { setSessionHandoff } from "@/pages/session/handoff" @@ -86,7 +85,6 @@ import { } from "./layout/pawwork-session-source" import { type WorkspaceSidebarContext } from "./layout/sidebar-workspace" import { PawworkSidebar, type PawworkSidebarSession } from "./layout/pawwork-sidebar" -import { ProjectDragOverlay, type ProjectSidebarContext } from "./layout/sidebar-project" import { PawworkTitlebar } from "./layout/pawwork-titlebar" import { SettingsPage, type SettingsPageTab } from "@/components/settings-page" import { DialogDeleteSession } from "@/components/dialog-delete-session" @@ -158,12 +156,9 @@ export default function Layout(props: ParentProps) { const [state, setState] = createStore({ autoselect: !initialDirectory, busyWorkspaces: {} as Record, - hoverProject: undefined as string | undefined, scrollSessionKey: undefined as string | undefined, nav: undefined as HTMLElement | undefined, sizing: false, - peek: undefined as string | undefined, - peeked: false, }) const editor = createInlineEditorController() @@ -181,110 +176,19 @@ export default function Layout(props: ParentProps) { ) } const isBusy = (directory: string) => !!state.busyWorkspaces[workspaceKey(directory)] - const navLeave = { current: undefined as number | undefined } let sizet: number | undefined - const aim = createAim({ - enabled: () => !layout.sidebar.opened(), - active: () => state.hoverProject, - el: () => state.nav?.querySelector("[data-component='sidebar-rail']") ?? state.nav, - onActivate: (directory) => { - globalSync.child(directory) - setState("hoverProject", directory) - }, - }) - onCleanup(() => { dialogDead = true dialogRun += 1 - if (navLeave.current !== undefined) clearTimeout(navLeave.current) if (sizet !== undefined) clearTimeout(sizet) - if (peekt !== undefined) clearTimeout(peekt) - aim.reset() }) onMount(() => { const stop = () => setState("sizing", false) - const blur = () => reset() - const hide = () => { - if (document.visibilityState !== "hidden") return - reset() - } makeEventListener(window, "pointerup", stop) makeEventListener(window, "pointercancel", stop) makeEventListener(window, "blur", stop) - makeEventListener(window, "blur", blur) - makeEventListener(document, "visibilitychange", hide) - }) - - const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined) - const sidebarExpanded = createMemo(() => layout.sidebar.opened() || sidebarHovering()) - const setHoverProject = (value: string | undefined) => { - setState("hoverProject", value) - if (value !== undefined) return - aim.reset() - } - const clearHoverProjectSoon = () => queueMicrotask(() => setHoverProject(undefined)) - - const disarm = () => { - if (navLeave.current === undefined) return - clearTimeout(navLeave.current) - navLeave.current = undefined - } - - const reset = () => { - disarm() - setHoverProject(undefined) - } - - const arm = () => { - if (layout.sidebar.opened()) return - if (state.hoverProject === undefined) return - disarm() - navLeave.current = window.setTimeout(() => { - navLeave.current = undefined - setHoverProject(undefined) - }, 300) - } - - let peekt: number | undefined - - const hoverProjectData = createMemo(() => { - const id = state.hoverProject - if (!id) return - return layout.projects.list().find((project) => project.worktree === id) - }) - - const peekProject = createMemo(() => { - const id = state.peek - 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 - } - setState("peek", p.worktree) - setState("peeked", true) - return - } - - setState("peeked", false) - if (state.peek === undefined) return - if (peekt !== undefined) clearTimeout(peekt) - peekt = window.setTimeout(() => { - peekt = undefined - setState("peek", undefined) - }, 180) - }) - - createEffect(() => { - if (!layout.sidebar.opened()) return - setHoverProject(undefined) }) createEffect(() => { @@ -302,17 +206,6 @@ export default function Layout(props: ParentProps) { const setEditor = editor.setEditor const InlineEditor = editor.InlineEditor - const clearSidebarHoverState = () => { - if (layout.sidebar.opened()) return - reset() - } - - const navigateWithSidebarReset = (href: string) => { - clearSidebarHoverState() - navigate(href) - layout.mobileSidebar.hide() - } - function cycleTheme(direction = 1) { const ids = availableThemeEntries().map(([id]) => id) if (ids.length === 0) return @@ -668,11 +561,6 @@ export default function Layout(props: ParentProps) { } const pawworkSessions = createMemo(() => collectPawworkSessions(layout.projects.list())) - const pawworkPeekSessions = createMemo(() => { - const project = peekProject() - if (!project) return [] as PawworkSidebarSession[] - return collectPawworkSessions([project]) - }) type PrefetchQueue = { inflight: Set @@ -1342,7 +1230,6 @@ export default function Layout(props: ParentProps) { function openSettings() { setSettingsTab("general") setSettingsOpen(true) - layout.mobileSidebar.hide() } createEffect(() => { @@ -1432,7 +1319,7 @@ export default function Layout(props: ParentProps) { 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}`) + navigate(`/${base64Encode(target.directory)}/session/${target.id}`) return true } const resolved = await globalSDK.client.session @@ -1442,7 +1329,7 @@ export default function Layout(props: ParentProps) { if (!resolved?.directory) return false if (!canOpen(resolved.directory)) return false setStore("lastProjectSession", root, { directory: resolved.directory, id: resolved.id, at: Date.now() }) - navigateWithSidebarReset(`/${base64Encode(resolved.directory)}/session/${resolved.id}`) + navigate(`/${base64Encode(resolved.directory)}/session/${resolved.id}`) return true } @@ -1474,12 +1361,12 @@ export default function Layout(props: ParentProps) { return } - navigateWithSidebarReset(`/${base64Encode(root)}/session`) + navigate(`/${base64Encode(root)}/session`) } function navigateToSession(session: Session | undefined) { if (!session) return - navigateWithSidebarReset(`/${base64Encode(session.directory)}/session/${session.id}`) + navigate(`/${base64Encode(session.directory)}/session/${session.id}`) } function openPawworkHome(directory?: string) { @@ -1488,7 +1375,7 @@ export default function Layout(props: ParentProps) { chooseProject() return } - navigateWithSidebarReset(`/${base64Encode(root)}/session`) + navigate(`/${base64Encode(root)}/session`) } function openProject(directory: string, navigate = true) { @@ -1510,7 +1397,7 @@ export default function Layout(props: ParentProps) { setSessionHandoff(slug, { prompt: link.prompt }) } const href = link.prompt ? `/${slug}/session?prompt=${encodeURIComponent(link.prompt)}` : `/${slug}/session` - navigateWithSidebarReset(href) + navigate(href) } } @@ -1564,7 +1451,7 @@ export default function Layout(props: ParentProps) { return } - navigateWithSidebarReset(`/${base64Encode(next.worktree)}/session`) + navigate(`/${base64Encode(next.worktree)}/session`) layout.projects.close(directory) queueMicrotask(() => { void navigateToProject(next.worktree) @@ -1627,7 +1514,7 @@ export default function Layout(props: ParentProps) { const deletedKey = workspaceKey(directory) const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey) if (!leaveDeletedWorkspace && shouldLeave) { - navigateWithSidebarReset(`/${base64Encode(root)}/session`) + navigate(`/${base64Encode(root)}/session`) } setBusy(directory, true) @@ -1675,7 +1562,7 @@ export default function Layout(props: ParentProps) { const valid = dirs.some((item) => workspaceKey(item) === nextKey) if (params.dir && projectRoot(nextCurrent) === root && !valid) { - navigateWithSidebarReset(`/${base64Encode(root)}/session`) + navigate(`/${base64Encode(root)}/session`) } } @@ -1746,7 +1633,6 @@ export default function Layout(props: ParentProps) { onClick: () => { const href = `/${base64Encode(directory)}/session` navigate(href) - layout.mobileSidebar.hide() }, }, { @@ -1780,7 +1666,7 @@ export default function Layout(props: ParentProps) { const handleDelete = () => { const leaveDeletedWorkspace = !!params.dir && workspaceKey(currentDir()) === workspaceKey(props.directory) if (leaveDeletedWorkspace) { - navigateWithSidebarReset(`/${base64Encode(props.root)}/session`) + navigate(`/${base64Encode(props.root)}/session`) } dialog.close() void deleteWorkspace(props.root, props.directory, leaveDeletedWorkspace) @@ -1980,7 +1866,6 @@ export default function Layout(props: ParentProps) { function handleDragStart(event: unknown) { const id = getDraggableId(event) if (!id) return - setHoverProject(undefined) setStore("activeProject", id) } @@ -2009,12 +1894,7 @@ export default function Layout(props: ParentProps) { }) } - const sidebarProject = createMemo(() => { - if (layout.sidebar.opened()) return currentProject() - const hovered = hoverProjectData() - if (hovered) return hovered - return currentProject() - }) + const sidebarProject = createMemo(() => currentProject()) function handleWorkspaceDragStart(event: unknown) { const id = getDraggableId(event) @@ -2051,7 +1931,6 @@ export default function Layout(props: ParentProps) { } const createWorkspace = async (project: LocalProject) => { - clearSidebarHoverState() const created = await globalSDK.client.worktree .create({ directory: project.worktree }) .then((x) => x.data) @@ -2087,15 +1966,12 @@ export default function Layout(props: ParentProps) { }) globalSync.child(created.directory) - navigateWithSidebarReset(`/${base64Encode(created.directory)}/session`) + navigate(`/${base64Encode(created.directory)}/session`) } const workspaceSidebarCtx: WorkspaceSidebarContext = { currentDir, navList: currentSessions, - sidebarExpanded, - sidebarHovering, - clearHoverProjectSoon, prefetchSession, workspaceName, renameWorkspace, @@ -2111,57 +1987,24 @@ export default function Layout(props: ParentProps) { dialog.show(() => ), showDeleteWorkspaceDialog: (root, directory) => dialog.show(() => ), - setScrollContainerRef: (el, mobile) => { - if (!mobile) scrollContainerRef = el - }, - } - - const projectSidebarCtx: ProjectSidebarContext = { - currentDir, - currentProject, - sidebarOpened: () => layout.sidebar.opened(), - sidebarHovering, - hoverProject: () => state.hoverProject, - onProjectMouseEnter: (worktree, event) => aim.enter(worktree, event), - onProjectMouseLeave: (worktree) => aim.leave(worktree), - onProjectFocus: (worktree) => aim.activate(worktree), - onHoverOpenChanged: (worktree, hoverOpen) => { - if (!hoverOpen && state.hoverProject && state.hoverProject !== worktree) return - setState("hoverProject", hoverOpen ? worktree : undefined) - }, - navigateToProject, - openSidebar: () => layout.sidebar.open(), - closeProject, - showEditProjectDialog, - toggleProjectWorkspaces, - workspacesEnabled: (project) => project.vcs === "git" && layout.sidebar.workspaces(project.worktree)(), - workspaceIds, - workspaceLabel, - sessionProps: { - navList: currentSessions, - sidebarExpanded, - clearHoverProjectSoon, - prefetchSession, + setScrollContainerRef: (el) => { + scrollContainerRef = el }, } const projects = () => layout.projects.list() - const projectOverlay = () => store.activeProject} /> const renderPawworkPanel = ( sessions: Accessor, - options?: { mobile?: boolean; directory?: string; scope?: "main" | "peek" }, + options?: { directory?: string; scope?: "main" | "peek" }, ) => ( params.id} pinnedIDs={() => store.pawworkPinnedSessions} sortMode={() => store.pawworkSortMode} - sidebarExpanded={sidebarExpanded} setScrollContainerRef={workspaceSidebarCtx.setScrollContainerRef} - clearHoverProjectSoon={clearHoverProjectSoon} prefetchSession={prefetchSession} onRenameSession={renamePawworkSession} onTogglePinnedSession={togglePinnedSession} @@ -2177,8 +2020,8 @@ export default function Layout(props: ParentProps) { settingsKeybind={() => command.keybind("settings.open")} /> ) - const sidebarContent = (mobile?: boolean) => - renderPawworkPanel(pawworkSessions, { mobile, directory: currentProject()?.worktree, scope: "main" }) + const sidebarContent = () => + renderPawworkPanel(pawworkSessions, { directory: currentProject()?.worktree, scope: "main" }) return (
- - + + -
-
{ - if (e.target === e.currentTarget) layout.mobileSidebar.hide() - }} - /> - -
+
- -
{ - disarm() - aim.reset() - }} - onPointerDown={disarm} - onMouseLeave={() => { - arm() - }} - > - - {(project) => - renderPawworkPanel(pawworkPeekSessions, { - mobile: false, - directory: project().worktree, - scope: "peek", - }) - } - -
- -
-
-
{import.meta.env.DEV && diff --git a/packages/app/src/pages/layout/pawwork-sidebar.tsx b/packages/app/src/pages/layout/pawwork-sidebar.tsx index 7bb812e50..9b0b77f88 100644 --- a/packages/app/src/pages/layout/pawwork-sidebar.tsx +++ b/packages/app/src/pages/layout/pawwork-sidebar.tsx @@ -31,15 +31,12 @@ const FilterIcon = (props: { size?: number }) => { export const PawworkSidebar = (props: { scope?: "main" | "peek" - mobile?: boolean sessions: Accessor showProjectEmptyState: boolean activeSessionID?: Accessor pinnedIDs: Accessor sortMode: Accessor - sidebarExpanded: Accessor - setScrollContainerRef: (el: HTMLDivElement | undefined, mobile?: boolean) => void - clearHoverProjectSoon: () => void + setScrollContainerRef: (el: HTMLDivElement | undefined) => void prefetchSession: (session: Session, priority?: "high" | "low") => void onRenameSession: (session: Session, next: string) => Promise onTogglePinnedSession: (sessionID: string) => void @@ -107,10 +104,7 @@ export const PawworkSidebar = (props: { list={navList()} navList={navList} slug={entry.item.slug} - mobile={props.mobile} showChild - sidebarExpanded={props.sidebarExpanded} - clearHoverProjectSoon={props.clearHoverProjectSoon} prefetchSession={props.prefetchSession} pinned={() => isPinned()} timeText={() => @@ -231,7 +225,7 @@ export const PawworkSidebar = (props: { }) }) - const tooltipPlacement = () => (props.mobile ? "bottom" : "right") + const tooltipPlacement = () => "right" as const const sortAriaLabel = () => props.sortMode() === "time" ? language.t("sidebar.pawwork.sort.byProject") : language.t("sidebar.pawwork.sort.byTime") @@ -285,15 +279,12 @@ export const PawworkSidebar = (props: {
{ scrollEl = el - props.setScrollContainerRef(el, props.mobile) + props.setScrollContainerRef(el) }} data-component="pawwork-session-scroll" class="flex-1 min-h-0 overflow-y-auto px-3 pb-3" > - 0} - fallback={
{language.t("sidebar.pawwork.empty.sessions")}
} - > + 0}>