diff --git a/.project-docs/30-worklog/tasks/20261013-canvas-workspace-b6d9e2a4.md b/.project-docs/30-worklog/tasks/20261013-canvas-workspace-b6d9e2a4.md index ef248328..33a318b3 100644 --- a/.project-docs/30-worklog/tasks/20261013-canvas-workspace-b6d9e2a4.md +++ b/.project-docs/30-worklog/tasks/20261013-canvas-workspace-b6d9e2a4.md @@ -25,7 +25,7 @@ ## Outcome - Canvas implementation complete: Agents-style navigation/composer/Markdown, 64px identity header, resizable 42/58 workspace, current output preview and historical selection, one persistent editable production card, <=899px compact panel switching. Hidden videos pause; modal Escape does not change split focus; compact creation no longer opens two dialogs. -- Coordinated integration, normal main push, live temporary-application update and worktree retirement remain pending. Canvas source2ca6e31; Code-navigation chat completed product source d287d5ac (handoff15848cd). The committed effective Renderer non-project-doc delta from baseline70166d2 was applied through Git three-way merge, preserving Canvas and all current Sidebar/TOOL/SKILL/knowledge-upload fixes; its live verification is blocked by Mac lock. Channel-adapter chat completed source979596f. Its non-project-doc product/test/README delta was also three-way merged cleanly. Combined typecheck/scoped ESLint and all443 selected units passed. The accepted non-project-doc code deltas were three-way merged before recording source ancestry with a metadata-only merge of Code15848cd and channel979596f. This preserves source commit history without changing their read-only task records or importing canonical-memory changes into feature mode; no Yuxi files were changed. Required unlock question is pending; no app mutation has occurred in this task. +- Coordinated source integration complete. The final product tree includes Canvas2ca6e31, effective Code/sidebar/advanced source d287d5ac / handoff15848cd and channel979596f; ancestry mergea106dc1 preserves both delivered histories. Normal main push and immutable packaging follow final evidence commit. Live temporary-application activation and worktree retirement still await native saved/quit/UI verification. Canvas source2ca6e31; Code-navigation chat completed product source d287d5ac (handoff15848cd). The committed effective Renderer non-project-doc delta from baseline70166d2 was applied through Git three-way merge, preserving Canvas and all current Sidebar/TOOL/SKILL/knowledge-upload fixes; its live verification is blocked by Mac lock. Channel-adapter chat completed source979596f. Its non-project-doc product/test/README delta was also three-way merged cleanly. Combined typecheck/scoped ESLint and all443 selected units passed. The accepted non-project-doc code deltas were three-way merged before recording source ancestry with a metadata-only merge of Code15848cd and channel979596f. This preserves source commit history without changing their read-only task records or importing canonical-memory changes into feature mode; no Yuxi files were changed. Required unlock question is pending; no app mutation has occurred in this task. ## Verification @@ -36,6 +36,9 @@ ## Follow-ups +- Combined actual Electron/browser fixtures: 18 unique cases passed (Canvas/channels4, community9, Code/navigation5). Three-way integration exposed inherited assertions for the replaced community entry, current attachment thumbnails, the existing filter query and the new toolbar/record-dialog layout; test-only fixes retain behavior/geometry/first-send/draft/attachment/navigation checks. Type/scoped lint and whitespace checks passed. Controlled screenshots and complete passed logs are preserved outside the source worktree. +- Native CUA independently confirmed Mac locked on 2026-10-14; the user unlock question remains unanswered. Do not activate files, quit/launch the app, finalize a false UI receipt or retire the owned source tree until the required native confirmation is possible. Other selected chats are idle/completed. + - Finish both user-selected chat dependencies before staging combined runtime. Inspect fresh pointer, actual compatibility symlink and Main source; never reuse the rejected broad Code visual variant. Preserve unrelated primary checkout task record and stable profile/runtime/dependency links during cleanup. ## Promotion Candidates diff --git a/tests/e2e/main-navigation.spec.ts b/tests/e2e/main-navigation.spec.ts index eb48b6f0..bb181987 100644 --- a/tests/e2e/main-navigation.spec.ts +++ b/tests/e2e/main-navigation.spec.ts @@ -330,10 +330,11 @@ test.describe('Makelore module navigation without setup flow', () => { expect(returnBox!.y + returnBox!.height).toBeLessThanOrEqual(brandBox!.y); const headerBox = await page.getByTestId('coding-conversation-header').boundingBox(); const tabsBox = await page.getByRole('tablist', { name: '当前工作' }).boundingBox(); + const toolbarBox = await page.locator('.classroom-workspace-toolbar').boundingBox(); expect(Math.abs(returnBox!.y + returnBox!.height / 2 - headerBox!.y - headerBox!.height / 2)).toBeLessThan(1); expect(Math.abs(brandBox!.y + brandBox!.height / 2 - tabsBox!.y - tabsBox!.height / 2)).toBeLessThan(1); expect(brandBox!.y - sidebarBox!.y).toBe(0); - expect(brandBox!.height).toBe(tabsBox!.height); + expect(brandBox!.height).toBe(toolbarBox!.height); expect(returnBox!.width).toBe(32); expect(Math.abs(returnBox!.y + returnBox!.height / 2 - collapseBox!.y - collapseBox!.height / 2)).toBeLessThan(1); expect(returnBox!.x + returnBox!.width).toBeLessThanOrEqual(collapseBox!.x); @@ -347,9 +348,9 @@ test.describe('Makelore module navigation without setup flow', () => { expect(communityBox!.y).toBeGreaterThanOrEqual(brandBox!.y + brandBox!.height); expect(communityBox!.y - brandBox!.y - brandBox!.height).toBe(10); expect(createBox!.y - communityBox!.y - communityBox!.height).toBe(10); - expect(createBox!.y - brandBox!.y - brandBox!.height).toBe(61); + expect(createBox!.y - brandBox!.y - brandBox!.height).toBe(communityBox!.height + 20); expect(communityBox!.height).toBe(createBox!.height); - await expect(community).toHaveCSS('background-color', 'rgb(248, 250, 245)'); + await expect(community).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); const buttonStyles = await page.evaluate(() => { const styles = (selector: string) => { const s = getComputedStyle(document.querySelector(selector)!); @@ -414,7 +415,9 @@ test.describe('Makelore module navigation without setup flow', () => { await expect(sidebar.getByTestId('sidebar-module-switcher')).toHaveCount(0); await page.screenshot({ path: test.info().outputPath('project-config-plugins-without-module-return.png') }); await plugins.getByRole('button', { name: '关闭', exact: true }).click(); - await expect(page).toHaveURL(/\/project-config$/); + await expect(page).toHaveURL(/\/project-config(?:\?|$)/); + await expect(plugins).toHaveCount(0); + await expect(page.getByTestId('project-configuration-page')).toBeVisible(); await expect(projectBack).toBeVisible(); await sidebar.getByTestId('sidebar-course-project-project-storybook').click(); await expect(page).toHaveURL(/\/chat$/); diff --git a/tests/e2e/pi-coding-first-chat.spec.ts b/tests/e2e/pi-coding-first-chat.spec.ts index b05db950..e550476d 100644 --- a/tests/e2e/pi-coding-first-chat.spec.ts +++ b/tests/e2e/pi-coding-first-chat.spec.ts @@ -1261,16 +1261,23 @@ test('first PI Conversation is editable under 500 ms and submits before runtime await expect(page.getByTestId('coding-message-composer')).toBeInViewport({ ratio: 1 }); expect(await page.getByTestId('coding-message-composer').evaluate(el => innerHeight - el.getBoundingClientRect().bottom)).toBeLessThan(40); - await expect(page.getByTestId('community-invitation')).toBeVisible(); + const welcomeCommunity = page.getByTestId('community-welcome-link'); + await expect(welcomeCommunity).toBeVisible(); + await expect(page.getByTestId('community-invitation')).toHaveCount(0); expect(await page.getByTestId('coding-message-composer').evaluate(el => { const body = document.querySelector('.coding-chat-body')!.getBoundingClientRect(); const input = el.getBoundingClientRect(); return Math.abs(input.left + input.width / 2 - body.left - body.width / 2); })).toBeLessThan(2); - expect(await page.getByTestId('community-invitation').evaluate(el => { + expect(await welcomeCommunity.evaluate(el => { + const body = document.querySelector('.coding-chat-body')!.getBoundingClientRect(); + const welcome = el.getBoundingClientRect(); + return Math.abs(welcome.left + welcome.width / 2 - body.left - body.width / 2); + })).toBeLessThan(2); + expect(await welcomeCommunity.evaluate(el => { const composer = document.querySelector('[data-testid="coding-message-composer"]')!; return composer.getBoundingClientRect().top - el.getBoundingClientRect().bottom; - })).toBeCloseTo(12, 0); + })).toBeGreaterThan(12); const pixelPng = Buffer.from( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=', @@ -1288,20 +1295,21 @@ test('first PI Conversation is editable under 500 ms and submits before runtime scope.__communityOpened = []; shell.openExternal = async url => { scope.__communityOpened!.push(url); }; }); - await page.getByRole('button', { name: '作品广场', exact: true }).click(); - await page.getByRole('button', { name: '去作品广场逛逛' }).click(); + await page.getByTestId('sidebar').getByRole('button', { name: '作品广场', exact: true }).click(); + await welcomeCommunity.click(); await expect.poll(() => electronApp.evaluate(() => ( (globalThis as typeof globalThis & { __communityOpened?: string[] }).__communityOpened ))).toEqual(['https://square.nianxx.cn/#explore', 'https://square.nianxx.cn/#explore']); await expect(composer).toHaveValue('Build the first PI scene'); - await expect(page.getByText('large.png', { exact: true })).toBeVisible(); + await expect(page.getByRole('img', { name: 'large.png', exact: true })).toBeVisible(); + await expect(page.getByRole('button', { name: '移除图片 large.png', exact: true })).toBeEnabled(); await expect(page.getByRole('dialog')).toHaveCount(0); expect((await readState(electronApp)).captured.filter(item => item.path.endsWith('/prompt') && item.method === 'POST')).toHaveLength(0); await expect(page.getByRole('button', { name: '发送' })).toBeEnabled(); await page.getByTestId('coding-message-composer').evaluate( (form: HTMLFormElement) => form.requestSubmit(), ); - await expect(page.getByTestId('community-invitation')).toHaveCount(0); + await expect(welcomeCommunity).toHaveCount(0); await expect.poll(async () => { const state = await readState(electronApp);