From 97366d2af8994567dd257deb8da3569cc1cb5967 Mon Sep 17 00:00:00 2001 From: M4n5ter Date: Thu, 3 Sep 2026 14:50:14 +0800 Subject: [PATCH] refactor(desktop): retire obsolete Astryx workarounds Astryx now owns the resize-handle hit area and ChatLayout flex contract that Maka still overrode. Remove those stale compatibility rules, delete the compositor-sensitive E2E that duplicated vendor behavior, and retain only the product-owned sidebar geometry check.\n\nGenerated-by: OpenAI Codex --- apps/desktop/e2e/sidebar-geometry.spec.ts | 103 +----------------- .../features/session-navigation/testing.ts | 2 - .../src/renderer/styles/chat-header.css | 15 --- .../styles/module-pages/module-shell.css | 12 -- .../src/renderer/styles/shell-layout.css | 6 +- apps/desktop/src/renderer/styles/sidebar.css | 19 ---- 6 files changed, 7 insertions(+), 150 deletions(-) diff --git a/apps/desktop/e2e/sidebar-geometry.spec.ts b/apps/desktop/e2e/sidebar-geometry.spec.ts index e1f8b3c67f..a0e52cd609 100644 --- a/apps/desktop/e2e/sidebar-geometry.spec.ts +++ b/apps/desktop/e2e/sidebar-geometry.spec.ts @@ -18,47 +18,20 @@ */ /* - * Locks the two invariants of the Astryx sidenav column that live only in - * hand-tuned CSS and were, before this file, verified by nothing (#3834): - * - * 1. Geometry — `.maka-sidenav-motion { height: 100% }` (shell-layout.css). - * Without a definite height the wrapper grows to its unclipped content and - * the footer leaves the window. `sidenav footer stays inside the window` - * is the lock shell-layout.css's comment already names by this path. - * - * 2. The resize handle — the two workaround rules that keep it grabbable: - * `top: var(--h-titlebar)` (shell-layout.css) and `transform: none - * !important` (sidebar.css, the Astryx hitAreaOffsetX fix). A regression - * in either leaves a column that looks right and simply cannot be dragged. - * `resize handle drag ...` grabs the handle at its vertical centre — the - * exact point the bug left ungrabbable — and asserts the width changes. - * - * Both run on `projectSidebarWindow`: the only fixture that boots the shell - * expanded (so the handle is mounted) with a populated, overflowing list (60 - * sessions, so the footer has content to be pushed past). It opens with the - * search modal over an inert shell, so dismiss it first — the same first step - * sidebar-project-row.spec.ts takes. + * Locks `.maka-sidenav-motion { height: 100% }` in shell-layout.css. Without a + * definite height, the sidenav grows to its unclipped content and pushes the + * footer below the window. `projectSidebarWindow` supplies the overflowing + * session list needed to expose that regression. */ import { expect, test } from './fixtures'; -import { - SESSION_LIST_EXPANDED_MAX_WIDTH, - SESSION_LIST_EXPANDED_MIN_WIDTH, -} from '../src/renderer/features/session-navigation/testing'; -import type { Locator, Page } from '@playwright/test'; +import type { Page } from '@playwright/test'; -async function revealPopulatedSidebar(page: Page): Promise { +async function revealPopulatedSidebar(page: Page): Promise { await page.keyboard.press('Escape'); await expect(page.locator('[data-maka-contract="search-modal"]')).not.toBeVisible(); const sidebar = page.getByRole('navigation', { name: '任务列表' }); await expect(sidebar).toBeVisible(); - return sidebar; -} - -async function wrapperWidth(wrapper: Locator): Promise { - const box = await wrapper.boundingBox(); - if (!box) throw new Error('sidenav wrapper (.maka-sidenav-motion) has no visible bounds'); - return box.width; } test('sidenav footer stays inside the window under an overflowing list', async ({ @@ -98,67 +71,3 @@ test('sidenav footer stays inside the window under an overflowing list', async ( expect(wrapperBox!.y + wrapperBox!.height).toBeLessThanOrEqual(innerHeight + 1); expect(footerBox!.y + footerBox!.height).toBeLessThanOrEqual(innerHeight + 1); }); - -test('resize handle drag from its vertical centre changes the column width', async ({ - projectSidebarWindow: page, -}) => { - await revealPopulatedSidebar(page); - - const handle = page.getByTestId('astryx-sidenav-resize-handle'); - await expect(handle).toBeVisible(); - const wrapper = page.locator('.maka-sidenav-motion'); - - const initialWidth = await wrapperWidth(wrapper); - expect(initialWidth).toBeGreaterThanOrEqual(SESSION_LIST_EXPANDED_MIN_WIDTH); - expect(initialWidth).toBeLessThanOrEqual(SESSION_LIST_EXPANDED_MAX_WIDTH); - - // Grab the vertical centre. The Astryx hitAreaOffsetX bug leaves only the top - // half grabbable, so the centre no-ops unless sidebar.css's `transform: none - // !important` is applied — this point is the regression probe for that rule. - // Let locator actionability resolve the handle's live centre before reading - // its endpoint; replaying a box captured before that wait can miss a moving - // 16px handle under load. - await handle.hover(); - const handleBox = await handle.boundingBox(); - if (!handleBox) throw new Error('resize handle has no visible bounds'); - const grabX = handleBox.x + handleBox.width / 2; - const grabY = handleBox.y + handleBox.height / 2; - - await page.mouse.down(); - - // Astryx flags the separator while a drag is in flight; shell-layout.css keys - // its transition-suppression (`:has([data-resizing])`) on it, so the width - // tracks the pointer live rather than easing behind it. Check the second the - // pointer goes down, so a missed grab fails directly instead of timing out on - // an unrelated width assertion. - await expect(handle).toHaveAttribute('data-resizing', /.*/); - await page.mouse.move(grabX + 80, grabY, { steps: 20 }); - await expect.poll(() => wrapperWidth(wrapper)).toBeGreaterThan(initialWidth + 40); - await page.mouse.up(); - await expect(handle).not.toHaveAttribute('data-resizing', /.*/); - - const widenedWidth = await wrapperWidth(wrapper); - expect(widenedWidth).toBeGreaterThan(initialWidth + 40); - expect(widenedWidth).toBeLessThanOrEqual(SESSION_LIST_EXPANDED_MAX_WIDTH + 1); - - // Drag the other way to prove the handle also narrows the column, grabbing - // the centre again at the handle's new right-edge position. - await handle.hover(); - const widenedHandleBox = await handle.boundingBox(); - if (!widenedHandleBox) throw new Error('resize handle lost its bounds after widening'); - const shrinkX = widenedHandleBox.x + widenedHandleBox.width / 2; - const shrinkY = widenedHandleBox.y + widenedHandleBox.height / 2; - - await page.mouse.down(); - await expect(handle).toHaveAttribute('data-resizing', /.*/); - // Undo the 80px widening. Keeping the reverse drag symmetric means every - // width accepted by the precondition stays above the collapsible region. - await page.mouse.move(shrinkX - 80, shrinkY, { steps: 20 }); - await expect.poll(() => wrapperWidth(wrapper)).toBeLessThan(widenedWidth - 40); - await page.mouse.up(); - await expect(handle).not.toHaveAttribute('data-resizing', /.*/); - - const narrowedWidth = await wrapperWidth(wrapper); - expect(narrowedWidth).toBeLessThan(widenedWidth - 40); - expect(narrowedWidth).toBeGreaterThanOrEqual(SESSION_LIST_EXPANDED_MIN_WIDTH); -}); diff --git a/apps/desktop/src/renderer/features/session-navigation/testing.ts b/apps/desktop/src/renderer/features/session-navigation/testing.ts index f20d456de7..980c22f9f5 100644 --- a/apps/desktop/src/renderer/features/session-navigation/testing.ts +++ b/apps/desktop/src/renderer/features/session-navigation/testing.ts @@ -51,8 +51,6 @@ export { } from './model/session-selection.js'; export { readSessionListViewMode, - SESSION_LIST_EXPANDED_MAX_WIDTH, - SESSION_LIST_EXPANDED_MIN_WIDTH, writeSessionListViewMode, } from './model/session-list-layout.js'; export { createSessionRailLayoutStore } from './model/session-rail-layout-store.js'; diff --git a/apps/desktop/src/renderer/styles/chat-header.css b/apps/desktop/src/renderer/styles/chat-header.css index c88f3d25b6..369ef5e7f1 100644 --- a/apps/desktop/src/renderer/styles/chat-header.css +++ b/apps/desktop/src/renderer/styles/chat-header.css @@ -201,21 +201,6 @@ overscroll-behavior: contain; } -/* - * Compatibility with Astryx #2573 until its fixed ChatLayout ships in a - * stable package. Published 0.2.0 gives the message area min-height: 100%, - * then adds the in-flow dock below it, creating a dock-height phantom range. - * These are the upstream flex contracts and can be removed on that upgrade. - */ -.maka-chat-layout > :first-child { - min-height: 0; - flex: 1 0 auto; -} - -.maka-chat-layout > :last-child { - flex-shrink: 0; -} - .maka-chat-shell { position: relative; min-height: 0; diff --git a/apps/desktop/src/renderer/styles/module-pages/module-shell.css b/apps/desktop/src/renderer/styles/module-pages/module-shell.css index 5832c497a2..fad4083048 100644 --- a/apps/desktop/src/renderer/styles/module-pages/module-shell.css +++ b/apps/desktop/src/renderer/styles/module-pages/module-shell.css @@ -65,18 +65,6 @@ background: var(--muted); } -/* The ResizeHandle's wider hit area is absolutely positioned with a vendor - bug: it gets the pill's translateY(-50%) centering without the pill's - top:50%, so the full-height strip lands shifted half its height upward — - over the page header, where it swallows the toolbar's clicks (技能 / MCP - with the inspector open; 定时任务 has the same latent strip). Drop the - stray translate. The !important is load-bearing: the vendor injects the - transform unlayered with :not(#\#) escalation, which beats any layered - product rule. */ -.maka-module-page .astryx-resize-handle > :not(.astryx-resize-handle-pill) { - transform: none !important; -} - /* The page's one control bar: module switch on the left, this page's view + filters on the right. It is the last row of the Layout header. diff --git a/apps/desktop/src/renderer/styles/shell-layout.css b/apps/desktop/src/renderer/styles/shell-layout.css index f4cbd53a55..1ce69237d2 100644 --- a/apps/desktop/src/renderer/styles/shell-layout.css +++ b/apps/desktop/src/renderer/styles/shell-layout.css @@ -79,11 +79,7 @@ Keyed on the sidenav handle's own testid, not on the class: every Astryx ResizeHandle carries that class, and a page's own inspector handle is a `position: relative` flex item, so the same `top` shoved its divider 36px - down the page — it started below the header's rule instead of meeting it. - - e2e/sidebar-geometry.spec.ts drags this handle from its vertical centre and - asserts the column width changes, so this rule and sidebar.css's grab-zone - fix both fail loudly if the handle stops being grabbable. */ + down the page — it started below the header's rule instead of meeting it. */ .maka-shell-astryx [data-testid="astryx-sidenav-resize-handle"] { top: var(--h-titlebar); } diff --git a/apps/desktop/src/renderer/styles/sidebar.css b/apps/desktop/src/renderer/styles/sidebar.css index b0ceeafc71..963cea5be9 100644 --- a/apps/desktop/src/renderer/styles/sidebar.css +++ b/apps/desktop/src/renderer/styles/sidebar.css @@ -25,25 +25,6 @@ height: 100%; } -/* - * Astryx 0.2.0 ResizeHandle: when pillPlacement is not "center", hitAreaOffsetX - * applies translateY(-50%) without a matching top: 50%. The grab zone then only - * covers the top half of the handle, so a pointer drag at the vertical center - * (where the pill sits) hits the handle root — which has no pointerdown — and - * no-ops. SideNav uses pillPlacement="end" (sub-pixel X bias) and mounts the - * handle as a sibling of , not a descendant — target the - * handle test id. Remove once Astryx fixes hitAreaOffsetX. - * - * e2e/sidebar-geometry.spec.ts drags the handle from its vertical centre — the - * half this fix restores — and asserts the column resizes, so removing this - * before Astryx is fixed fails loudly instead of silently killing the grab. - */ -[data-testid="astryx-sidenav-resize-handle"] > div:not(.astryx-resize-handle-pill) { - /* !important: Astryx StyleX injects the bad transform unlayered, which - otherwise beats every rule in @layer maka.legacy. */ - transform: none !important; -} - .maka-session-list { min-width: 0; max-width: 100%;