Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
041cc03
feat(desktop): add a scoped assistant with visible UI control
M4n5ter Sep 7, 2026
678cc06
refactor(desktop): smooth assistant cursor movement along gentle arcs
M4n5ter Sep 7, 2026
b36bd21
feat(desktop): operate application controls with a persistent assista…
M4n5ter Sep 7, 2026
0816645
fix(desktop): recover assistant actions through a fixed overlay
M4n5ter Sep 7, 2026
2355a5e
fix(desktop): defer first-task reads until Host creation completes
M4n5ter Sep 7, 2026
9d10900
feat(desktop): batch assistant actions against known controls
M4n5ter Sep 7, 2026
2b06988
fix(ci): refresh desktop assistant surface inventories
M4n5ter Sep 8, 2026
dd5459f
feat(desktop): unify WorkHub conversation and floating controls
M4n5ter Sep 8, 2026
1b01f14
fix(desktop): keep WorkHub navigation readable and cancel observation…
M4n5ter Sep 8, 2026
903d78c
fix(desktop): preserve WorkHub overlays and refine floating defaults
M4n5ter Sep 8, 2026
958040e
fix(desktop): make floating WorkHub expansion respond to native clicks
M4n5ter Sep 8, 2026
a4b58c7
fix(desktop): animate WorkHub resizing and content reveal
M4n5ter Sep 8, 2026
9b817bb
fix(desktop): preserve transcript navigation after WorkHub migration
M4n5ter Sep 8, 2026
f5d1f03
fix(desktop): show WorkHub prompts immediately while agents run
M4n5ter Sep 8, 2026
03ff5eb
test(desktop): register WorkHub send visibility regression
M4n5ter Sep 8, 2026
dc371a5
fix(desktop): keep WorkHub visible without interrupting on pointer mo…
M4n5ter Sep 8, 2026
7eeabfa
fix(desktop): preserve Dock visibility when floating WorkHub
M4n5ter Sep 8, 2026
18cb329
feat(ui): drag the model wheel and snap on release
M4n5ter Sep 8, 2026
5ac82ac
fix(workhub): show waiting feedback immediately on send
M4n5ter Sep 8, 2026
63882a3
refactor(workhub): retire legacy routing and separate floating visibi…
M4n5ter Sep 8, 2026
238f01e
fix(workhub): release retired persisted operation grants
M4n5ter Sep 8, 2026
f546908
fix(workhub): recover crashed views and align observation and selecti…
M4n5ter Sep 8, 2026
abbf4c1
fix(workhub): expose retry for a crashed docked conversation
M4n5ter Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/desktop/e2e-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@
"electron": "the perf budget is measured from CDP wheel input and the browser's own render skipping"
},
"workhub-layout.spec.ts": {
"tests": 1,
"electron": "Coordination startup failure comes from the Host, and recovery needs a default model written back to it"
"tests": 2,
"electron": "WorkHub model configuration and attachment sending cross the Host boundary; native floating preserves macOS Dock visibility and pointer dragging verifies model-wheel scrolling and release snapping; a held-open Host Turn verifies immediate prompt visibility, durable transcript handoff and persistence after renderer reload"
},
"workhub-reconstruction.spec.ts": {
"tests": 2,
"electron": "delegation linkage is rebuilt by the Host across navigation and across Sessions"
"tests": 1,
"electron": "the same WebContentsView and unsent draft survive reparenting between native main and floating windows, shared appearance changes and the explicit return-from-picture-in-picture button"
},
"zh-tw-locale.spec.ts": {
"tests": 1,
Expand Down
22 changes: 9 additions & 13 deletions apps/desktop/e2e/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,15 @@ export async function awaitSendReady(page: Page): Promise<void> {
});
}

/**
* Wait for the default Host's Coordination Session and the WorkHub projection
* to agree that the surface is ready. A mounted WorkHub main is not sufficient:
* it is also rendered while the Host reconnects and the projection reloads.
*/
export async function waitForWorkHubReady(page: Page, workCount: number): Promise<void> {
await expect
.poll(async () => {
const snapshot = await page.evaluate(() => window.maka.runtimeHostProfiles.getSnapshot());
return snapshot.entries.find(({ isDefault }) => isDefault)?.readiness;
})
.toBe('ready');
await expect(page.getByText(`${workCount} 项工作`, { exact: true })).toBeVisible();
/** The persistent WorkHub WebContentsView is a distinct renderer, not part of the main DOM. */
export async function getWorkHubPage(app: ElectronApplication): Promise<Page> {
let view: Page | undefined;
await expect.poll(() => {
view = app.context().pages().find((candidate) => new URL(candidate.url()).searchParams.get('surface') === 'workhub');
return Boolean(view);
}).toBe(true);
await expect(view!.locator('.workHubLive .maka-composer-editor')).toBeVisible();
return view!;
}

/**
Expand Down
297 changes: 280 additions & 17 deletions apps/desktop/e2e/workhub-layout.spec.ts

Large diffs are not rendered by default.

191 changes: 67 additions & 124 deletions apps/desktop/e2e/workhub-reconstruction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,134 +17,77 @@
* under the License.
*/

import {
COMPOSER_INPUT,
ensureSidebarExpanded,
expect,
test,
waitForWorkHubReady,
} from './fixtures';
import { expect, test, getWorkHubPage } from './fixtures';

test('WorkHub rebuilds delegated execution feedback after navigating away and back', async ({
window: page,
}) => {
const initialPrompt = '检查支付回调重复投递时的幂等性';
const composer = page.locator(COMPOSER_INPUT);
await composer.fill(initialPrompt);
await composer.press('Enter');
await expect(page.getByRole('button', { name: '重新生成' })).toHaveCount(1, {
timeout: 20_000,
test('WorkHub moves the same renderer and draft between the main window and floating window', async ({ sessionLocalWindow: { page, app } }, testInfo) => {
await page.evaluate(() => window.maka.settings.updateClient({ workHub: { enabled: true } }));
const workhub = await getWorkHubPage(app);
const appearance = await page.evaluate(async () => (await window.maka.settings.getClient()).appearance);
await page.evaluate(() => window.maka.settings.updateClient({ appearance: { theme: 'dark', palette: 'nord', uiFontSize: 18 } }));
const readAppearance = () => ({
dark: document.documentElement.classList.contains('dark'),
palette: document.documentElement.dataset.makaTheme,
fontSize: document.documentElement.style.fontSize,
});

const sessionName = await page.evaluate(async () =>
(await window.maka.sessions.list())[0]?.name,
);
expect(sessionName).toBeTruthy();
await page.evaluate(async () => {
await window.maka.settings.updateClient({ workHub: { enabled: true } });
});
await expect(page.getByRole('region', { name: 'WorkHub' })).toBeVisible();
// The conversation is the Coordination Session transcript. An ordinary
// Session is a routing target and a status row, never a turn in WorkHub.
await waitForWorkHubReady(page, 1);
await expect(page.locator('.workhub-turn')).toHaveCount(0);
await expect(page.locator('.workhub-empty h2')).toHaveText('从这里继续所有工作');

const routedPrompt = '继续这个工作,补充重复投递测试点。';
const workHubComposer = page.locator(
'.workhub-surface .maka-composer-editor [contenteditable="true"]',
);
await workHubComposer.fill(routedPrompt);
await workHubComposer.press('Enter');
const routedTurn = page.locator('.workhub-turn', { hasText: routedPrompt });
await expect(routedTurn.locator('.workhub-submitted')).toBeVisible();
await expect(routedTurn.locator('.workhub-message-identity')).toContainText(sessionName!);
const workIdentity = await routedTurn.getAttribute('data-work-session-id');
expect(workIdentity).toBeTruthy();
const identityColor = await routedTurn.evaluate((element) =>
getComputedStyle(element).getPropertyValue('--workhub-work-color'),
);
expect(identityColor).toContain('oklch');

await routedTurn.locator('.workhub-submitted > button').click();
await expect(page.getByRole('region', { name: 'WorkHub' })).toBeHidden();

await ensureSidebarExpanded(page);
await page.getByRole('button', { name: 'WorkHub', exact: true }).click();
await waitForWorkHubReady(page, 1);
await expect(page.getByRole('region', { name: 'WorkHub' })).toBeVisible();
await expect(
page.locator('.workhub-projected-turn .workhub-user-bubble > p', {
hasText: routedPrompt,
}),
).toBeVisible();
await expect(
page.locator('.workhub-projected-turn', { hasText: routedPrompt })
.locator('.workhub-submitted-state'),
).toHaveText('关联有效 · 已完成');
await expect(routedTurn).toHaveAttribute('data-work-session-id', workIdentity!);
expect(await routedTurn.evaluate((element) =>
getComputedStyle(element).getPropertyValue('--workhub-work-color'),
)).toBe(identityColor);

});

test('WorkHub replaces the exact linked delegation across Sessions', async ({
window: page,
}) => {
const sourceSessionName = '检查支付回调重复投递时的幂等性';
const composer = page.locator(COMPOSER_INPUT);
await composer.fill(sourceSessionName);
await composer.press('Enter');
await expect(page.getByRole('button', { name: '重新生成' })).toHaveCount(1, {
timeout: 20_000,
});
await page.evaluate(async (name) => {
const sourceSession = (await window.maka.sessions.list())[0];
if (!sourceSession) throw new Error('Source Session was not found');
await window.maka.sessions.rename(sourceSession.id, name);
}, sourceSessionName);
await page.evaluate(async () => {
await window.maka.settings.updateClient({ workHub: { enabled: true } });
await expect.poll(() => workhub.evaluate(readAppearance)).toMatchObject({ dark: true, palette: 'nord' });
await expect.poll(() => page.evaluate(readAppearance)).toEqual(await workhub.evaluate(readAppearance));
await page.evaluate((appearance) => window.maka.settings.updateClient({ appearance }), appearance);
const editor = workhub.locator('.maka-composer-editor [contenteditable="true"]');
await editor.fill('Keep this unsent WorkHub draft');
const marker = await editor.evaluate((element) => {
const value = crypto.randomUUID();
element.setAttribute('data-test-instance', value);
return value;
});
await expect(page.getByRole('region', { name: 'WorkHub' })).toBeVisible();
await page.evaluate(async () => {
await window.maka.sessions.create({ name: '登录稳定性' });
const webContentsId = await workhub.evaluate(() => window.maka.workHub.resolveCoordinationSession());
await workhub.emulateMedia({ reducedMotion: 'no-preference' });
await editor.evaluate((element) => {
element.addEventListener('input', () => {
if (document.querySelector('.workHubRevealMark')?.getAnimations().some((animation) => animation.playState === 'running')) {
element.setAttribute('data-input-during-reveal', 'true');
}
});
});
await waitForWorkHubReady(page, 2);

const workHubComposer = page.locator(
'.workhub-surface .maka-composer-editor [contenteditable="true"]',
);
await workHubComposer.fill('继续这个工作,补充重复投递测试点。');
await workHubComposer.press('Enter');
const continuedTurn = page.locator('.workhub-turn', {
hasText: '继续这个工作,补充重复投递测试点。',
});
await expect(
continuedTurn.locator('.workhub-submitted-session strong'),
).toHaveText(sourceSessionName);
await waitForWorkHubReady(page, 2);

await workHubComposer.fill('不是这个,换成登录稳定性,补充刷新令牌失败判定。');
await expect(
page.locator('.workhub-surface').getByRole('button', { name: '发送' }),
).toBeEnabled();
await workHubComposer.press('Enter');
await workhub.getByRole('button', { name: /^(Float WorkHub|浮出工作台)$/ }).click();
await workhub.keyboard.type(' — typed during opening');
await expect(editor).toHaveAttribute('data-input-during-reveal', 'true');
await expect(page.locator('.workHubDockPlaceholder')).toBeVisible();
await expect.poll(() => workhub.evaluate(() => window.maka.workHubPresentation.getSnapshot())).toMatchObject({ placement: 'floating', floatingVisible: true });
await expect(editor).toHaveText('Keep this unsent WorkHub draft — typed during opening');
await expect(editor).toHaveAttribute('data-test-instance', marker);

const correctionTurn = page.locator('.workhub-turn', {
hasText: '不是这个,换成登录稳定性,补充刷新令牌失败判定。',
await expect(workhub.locator('.workHubLive')).toHaveAttribute('data-conversation-expanded', 'false');
const capabilities = await app.evaluate(({ BrowserWindow }) => {
const floating = BrowserWindow.getAllWindows().find((window) => window.getTitle() === 'WorkHub')!;
return { maximizable: floating.isMaximizable(), fullscreenable: floating.isFullScreenable() };
});
await expect(
correctionTurn.locator('.workhub-submitted-session strong'),
).toHaveText('登录稳定性');
await expect(correctionTurn.locator('.workhub-error')).toHaveCount(0);
await expect(
continuedTurn.locator('.workhub-submitted-state'),
).toHaveText('已被更正');
await expect(
correctionTurn.locator('.workhub-submitted-state'),
).toContainText(
/^关联有效 · (?:已接收|进行中|等待你|已完成|失败|已中止|正在恢复)$/u,
);
expect(capabilities.fullscreenable).toBe(false);
if (process.platform !== 'linux') expect(capabilities.maximizable).toBe(false);
await expect(workhub.locator('.workHubHistory')).toBeHidden();
await expect.poll(() => workhub.evaluate(() => Math.abs(innerHeight - document.querySelector('.workHubComposerSurface')!.getBoundingClientRect().height))).toBeLessThanOrEqual(1);
await workhub.getByRole('button', { name: /展开对话|Expand conversation/ }).click();
await expect(workhub.locator('.workHubHistory')).toBeVisible();
await workhub.getByRole('button', { name: /^(Hide|隐藏|隱藏)$/ }).click();
await expect.poll(() => workhub.evaluate(() => window.maka.workHubPresentation.getSnapshot())).toMatchObject({ placement: 'floating', floatingVisible: false });
await page.getByRole('button', { name: /^(Bring WorkHub back|收回工作台)$/ }).click();
await expect(page.locator('.workHubDockPlaceholder')).toBeHidden();
await expect.poll(() => workhub.evaluate(() => window.maka.workHubPresentation.getSnapshot())).toMatchObject({ placement: 'docked' });
await expect(editor).toHaveText('Keep this unsent WorkHub draft — typed during opening');
await expect(editor).toHaveAttribute('data-test-instance', marker);
expect(await workhub.evaluate(() => window.maka.workHub.resolveCoordinationSession())).toBe(webContentsId);
expect(app.context().pages().filter((candidate) => candidate.url().includes('surface=workhub'))).toHaveLength(1);
await workhub.getByRole('button', { name: /^(Float WorkHub|浮出工作台)$/ }).click();
await expect(workhub.locator('.workHubLive')).toHaveAttribute('data-conversation-expanded', 'false');
await expect(workhub.getByRole('button', { name: /^(Return to Maka|收回 Maka)$/ })).toHaveCount(0);
await workhub.getByRole('button', { name: /展开对话|Expand conversation/ }).click();
await workhub.locator('.workHubWindowControls').screenshot({ path: testInfo.outputPath('workhub-return-from-pip.png') });
await workhub.getByRole('button', { name: /^(Return to Maka|收回 Maka)$/ }).click();
await expect.poll(() => workhub.evaluate(() => window.maka.workHubPresentation.getSnapshot())).toMatchObject({ placement: 'docked' });
await expect(editor).toHaveAttribute('data-test-instance', marker);
await expect(editor).toHaveText('Keep this unsent WorkHub draft — typed during opening');
await workhub.getByRole('button', { name: /^(Float WorkHub|浮出工作台)$/ }).click();
await expect(workhub.getByRole('button', { name: '发送', exact: true })).toBeEnabled();
await workhub.getByRole('button', { name: '发送', exact: true }).click();
await expect(workhub.locator('.workHubLive')).toHaveAttribute('data-conversation-expanded', 'true');
await expect(workhub.locator('article').filter({ hasText: 'Keep this unsent WorkHub draft' }).first()).toBeVisible();
});
Loading