From 179983b823c7f5982f6393d9b286404754f7ce3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 3 Oct 2026 19:34:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9AappSurface=20?= =?UTF-8?q?=E7=9A=84=20Tauri=20=E6=9B=BF=E8=BA=AB=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E8=B5=B0=E5=8D=95=E4=B8=80=E5=AE=89=E8=A3=85=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - harness 新增 createTauriEventBridge,只负责事件登记与派发,不碰 window、不做类型转换 - home.suite 44 处内联 window.__TAURI__ 赋值改为 installTauriRuntime,并删除 asTauriInvoke / asTauriListen 两个适配器 - auth.suite 删除 installTauriBridge 的 as never,改为 createTauriEventBridge + installTauriRuntime 组合 --- .../tests/appSurface/auth.suite.ts | 46 +---- .../tests/appSurface/harness.ts | 24 +++ .../tests/appSurface/home.suite.ts | 187 ++++++++---------- 3 files changed, 113 insertions(+), 144 deletions(-) diff --git a/apps/ai-game-creator-shell/tests/appSurface/auth.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/auth.suite.ts index dacba04da..7ed4d0845 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/auth.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/auth.suite.ts @@ -10,8 +10,10 @@ import { installUnhandledRejectionBridge } from '../unhandledRejectionBridge'; import { act, AuthenticatedClient, + createTauriEventBridge, expect, fireEvent, + installTauriRuntime, it, React, render, @@ -21,52 +23,22 @@ import { waitFor, } from './harness'; -type AuthEventBridge = { - listeners: Map void>; - emit: (event: string, payload: unknown) => void; -}; - /** * 注入 Tauri 事件替身。 * * jsdom 没有 `__TAURI_INTERNALS__`,`subscribeTauriEvent` 会退回 - * `window.__TAURI__.event.listen`,所以这里按同一入口登记回调。 + * `window.__TAURI__.event.listen`,所以按同一入口登记回调:安装走 + * harness.installTauriRuntime,事件注册 / 派发由 harness.createTauriEventBridge 负责。 */ -function installTauriBridge( - invoke: (command: string, args?: Record) => Promise, - options: { withEvents?: boolean } = {}, -): AuthEventBridge { - const listeners = new Map void>(); - window.__TAURI__ = { - core: { invoke }, - ...(options.withEvents - ? { - event: { - listen: async ( - event: string, - handler: (payload: { payload: unknown }) => void, - ) => { - listeners.set(event, handler); - return () => listeners.delete(event); - }, - }, - } - : {}), - } as never; - return { - listeners, - emit: (event, payload) => { - const handler = listeners.get(event); - if (handler) handler({ payload }); - }, - }; -} - function renderAuthSurface( invoke: (command: string, args?: Record) => Promise, options: { withEvents?: boolean } = {}, ) { - const bridge = installTauriBridge(invoke, options); + const bridge = createTauriEventBridge(); + installTauriRuntime({ + core: { invoke }, + ...(options.withEvents ? { event: { listen: bridge.listen } } : {}), + }); render( // React 19 的 createElement 的 children 形参只接受 ReactNode,渲染函数子节点改传进 props。 React.createElement(AuthenticatedClient, { diff --git a/apps/ai-game-creator-shell/tests/appSurface/harness.ts b/apps/ai-game-creator-shell/tests/appSurface/harness.ts index 1470cb338..43d3f3e14 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/harness.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/harness.ts @@ -962,6 +962,30 @@ export function installTauriRuntime(runtime: { window.__TAURI__ = runtime as unknown as Window['__TAURI__']; } +/** + * 测试用 Tauri 事件注册表:只登记 `event.listen` 的回调并按事件名派发。 + * + * 不碰 `window.__TAURI__`、不做类型转换;安装由 `installTauriRuntime` 负责,需要事件桥的 + * 用例把 `bridge.listen` 交给它即可。 + */ +export function createTauriEventBridge() { + const listeners = new Map void>(); + return { + listen: async ( + event: string, + handler: (event: { payload: unknown }) => void, + ) => { + listeners.set(event, handler); + return () => { + listeners.delete(event); + }; + }, + emit: (event: string, payload: unknown) => { + listeners.get(event)?.({ payload }); + }, + }; +} + export function installResizeObserverStub() { let observerCount = 0; let observerDisconnected = false; diff --git a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts index 0376b7f37..98627b517 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/home.suite.ts @@ -11,6 +11,7 @@ import { findResourceSelectButton, fireEvent, getResourceSelectButton, + installTauriRuntime, it, nativeClipboardMock, pickProjectFromLauncher, @@ -28,34 +29,6 @@ import { within, } from './harness'; -type TauriInvoke = NonNullable< - NonNullable['core']>['invoke'] ->; - -type TauriListen = NonNullable< - NonNullable['event']>['listen'] ->; - -// Tauri 的 invoke / listen 都是泛型签名,而测试里的 vi.fn mock 只有具体返回类型与 -// 事件负载,非泛型函数无法直接满足泛型签名。下面两个 helper 只做类型收敛,运行时 -// 原样返回同一个 mock,调用方仍持有原引用(mock.calls 等断言不受影响)。 -function asTauriInvoke( - invoke: (command: string, args?: Record) => Promise, -): TauriInvoke { - // 泛型 invoke 与具体的非泛型 mock 结构上不可兼容,只能用 as unknown as 收敛。 - return invoke as unknown as TauriInvoke; -} - -function asTauriListen( - listen: ( - event: string, - handler: (event: { payload: unknown }) => void, - ) => Promise<() => void>, -): TauriListen { - // 泛型 listen 与携带具体负载的 mock 结构上不可兼容,只能用 as unknown as 收敛。 - return listen as unknown as TauriListen; -} - // App 的 props 参数带默认值,`React.ComponentProps` 会带上 undefined, // createElement 因此推断不出 props;这里收窄成非空组件类型,运行时仍是同一个组件。 const TestApp = App as React.ComponentType< @@ -160,7 +133,7 @@ export function registerClientHomeTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher'); // 挂载即加载目录,触发按钮直接落在默认模型上,不出现「选择模型」空态。 @@ -208,7 +181,7 @@ export function registerClientHomeTests() { if (command === 'read_game_creator_app_config') return { config: {} }; throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher'); await waitFor(() => expect(invoke).toHaveBeenCalledWith('get_game_template_library_access'), @@ -290,7 +263,7 @@ export function registerClientHomeTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher'); // 首页推荐位只展示封面、标题、运行时与已下载徽标,本机灵感图库已随模板库上线删除。 @@ -432,7 +405,7 @@ export function registerClientHomeTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); render( React.createElement(WorkspaceLauncherShell, { currentUser: testAuthUser, @@ -574,10 +547,10 @@ export function registerClientHomeTests() { return runtimeHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(runtimeHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: runtimeHarness.listen }, + }); render( React.createElement(WorkspaceLauncherShell, { currentUser: testAuthUser, @@ -747,10 +720,10 @@ export function registerClientHomeTests() { return runtimeHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(runtimeHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: runtimeHarness.listen }, + }); render( React.createElement(WorkspaceLauncherShell, { currentUser: testAuthUser, @@ -854,7 +827,7 @@ export function registerClientHomeTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); expect(screen.getByLabelText('GameAgent 客户端')).not.toBeNull(); @@ -1051,7 +1024,7 @@ export function registerClientHomeTests() { return undefined; }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '充值' })); @@ -1151,7 +1124,7 @@ export function registerClientHomeTests() { } return undefined; }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '我的' })); @@ -1200,9 +1173,9 @@ export function registerClientHomeTests() { export function registerHomeProjectCreationTests() { it('scopes the 策划补全 option to the game entry and clears it on creation-type switch', () => { - window.__TAURI__ = { - core: { invoke: asTauriInvoke(vi.fn(async () => null)) }, - }; + installTauriRuntime({ + core: { invoke: vi.fn(async () => null) }, + }); renderLauncherAt('/?launcher', 'home', true); const creationTypes = screen.getByRole('group', { name: '创作类型' }); @@ -1259,10 +1232,10 @@ export function registerHomeProjectCreationTests() { return chatHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(chatHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: chatHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); fireEvent.click(screen.getByRole('checkbox', { name: '策划补全' })); @@ -1315,7 +1288,7 @@ export function registerHomeProjectCreationTests() { 'genarrative-ai-game-creator.recent-workspaces.v1', JSON.stringify(['/tmp/authorized-game']), ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); expect(await screen.findByText('authorized-game')).not.toBeNull(); @@ -1365,7 +1338,7 @@ export function registerHomeProjectCreationTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); pickProjectFromLauncher('/tmp/missing-game'); @@ -1407,7 +1380,7 @@ export function registerHomeProjectCreationTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); @@ -1460,7 +1433,7 @@ export function registerHomeProjectCreationTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '打开项目' })); @@ -1493,7 +1466,7 @@ export function registerHomeProjectCreationTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '打开项目' })); @@ -1513,7 +1486,7 @@ export function registerHomeProjectCreationTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); const openButton = screen.getByRole('button', { name: '打开项目' }); @@ -1572,10 +1545,10 @@ export function registerHomeProjectCreationTests() { return chatHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(chatHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: chatHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); const creationTypes = screen.getByRole('group', { name: '创作类型' }); @@ -1719,10 +1692,10 @@ export function registerHomeProjectCreationTests() { return chatHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(chatHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: chatHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); const fileInput = @@ -1832,7 +1805,7 @@ export function registerHomeProjectCreationTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher', 'home', true); const promptInput = screen.getByLabelText('创作想法'); @@ -1904,7 +1877,7 @@ export function registerHomeProjectCreationTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher', 'home', true); await act(async () => { await vi.advanceTimersByTimeAsync(0); @@ -1998,10 +1971,10 @@ export function registerHomeProjectCreationTests() { return chatHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(chatHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: chatHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); // 做方案走立项链路:建项之后还要写策划运行时,这一步失败时项目目录已经存在。 @@ -2065,10 +2038,10 @@ export function registerHomeProjectCreationTests() { return chatHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(chatHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: chatHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); if (modeLabel === '做方案') { @@ -2162,10 +2135,10 @@ export function registerHomeProjectCreationTests() { return chatHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(chatHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: chatHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); fireEvent.click(screen.getByRole('button', { name: '做方案' })); @@ -2300,10 +2273,10 @@ export function registerHomeProjectCreationTests() { return chatHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(chatHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: chatHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); fireEvent.click(screen.getByRole('button', { name: '做方案' })); @@ -2366,10 +2339,10 @@ export function registerHomeProjectCreationTests() { return runtimeHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(runtimeHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: runtimeHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); const promptInput = screen.getByLabelText('创作想法'); @@ -2542,7 +2515,7 @@ export function registerHomeProjectCreationTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); render( React.createElement(TestApp, { @@ -2626,7 +2599,7 @@ export function registerHomeProjectCreationTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); render( React.createElement(TestApp, { @@ -2737,7 +2710,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); window.localStorage.setItem( 'genarrative-ai-game-creator.recent-workspaces.v1', JSON.stringify(paths), @@ -2808,7 +2781,7 @@ export function registerRecentProjectsTests() { return undefined; }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher'); expect(screen.getByLabelText('最近项目')).not.toBeNull(); @@ -2898,7 +2871,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); window.localStorage.setItem( 'genarrative-ai-game-creator.recent-workspaces.v1', JSON.stringify([projectPath]), @@ -2946,7 +2919,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); window.localStorage.setItem( 'genarrative-ai-game-creator.recent-workspaces.v1', JSON.stringify([projectPath]), @@ -2995,7 +2968,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); window.localStorage.setItem( 'genarrative-ai-game-creator.recent-workspaces.v1', JSON.stringify(['/tmp/recent-one']), @@ -3034,7 +3007,7 @@ export function registerRecentProjectsTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); pickProjectFromLauncher('/tmp/open-failed-game'); @@ -3084,7 +3057,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); window.localStorage.setItem( 'genarrative-ai-game-creator.recent-workspaces.v1', JSON.stringify([ @@ -3231,7 +3204,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); window.localStorage.setItem( 'genarrative-ai-game-creator.recent-workspaces.v1', JSON.stringify(['/tmp/refreshable-game']), @@ -3296,7 +3269,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherAt('/?launcher'); expect(screen.getByLabelText('最近项目')).not.toBeNull(); @@ -3344,7 +3317,7 @@ export function registerRecentProjectsTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '打开项目' })); @@ -3374,7 +3347,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '新建项目' })); @@ -3415,7 +3388,7 @@ export function registerRecentProjectsTests() { }, ); const confirm = vi.spyOn(window, 'confirm').mockReturnValue(false); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '新建项目' })); @@ -3461,7 +3434,7 @@ export function registerRecentProjectsTests() { }, ); const confirm = vi.spyOn(window, 'confirm').mockReturnValue(true); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '新建项目' })); @@ -3514,7 +3487,7 @@ export function registerRecentProjectsTests() { throw new Error(`unexpected invoke ${command}`); }, ); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '新建项目' })); @@ -3541,7 +3514,7 @@ export function registerRecentProjectsTests() { } throw new Error(`unexpected invoke ${command}`); }); - window.__TAURI__ = { core: { invoke: asTauriInvoke(invoke) } }; + installTauriRuntime({ core: { invoke } }); renderLauncherProjectsAt('/?launcher'); fireEvent.click(screen.getByRole('button', { name: '新建项目' })); @@ -3608,10 +3581,10 @@ export function registerRecentProjectsTests() { return supervisorHarness.invoke(command, args); }, ); - window.__TAURI__ = { - core: { invoke: asTauriInvoke(invoke) }, - event: { listen: asTauriListen(supervisorHarness.listen) }, - }; + installTauriRuntime({ + core: { invoke }, + event: { listen: supervisorHarness.listen }, + }); renderLauncherAt('/?launcher', 'home', true); // 设置 → 工作区:默认位置就是不选目录,仍然落在 AGC 管理的应用数据目录。