@dimina-kit/devtools 0.4.0-dev.20260828161241 → 0.4.0-dev.20260907055341
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -729
- package/dist/main/app/app.d.ts +27 -7
- package/dist/main/app/app.js +285 -681
- package/dist/main/app/editor-view.d.ts +21 -0
- package/dist/main/app/editor-view.js +71 -0
- package/dist/main/app/global-mirrors.d.ts +23 -0
- package/dist/main/app/global-mirrors.js +37 -0
- package/dist/main/app/host-sidebar-default.d.ts +19 -0
- package/dist/main/app/host-sidebar-default.js +44 -0
- package/dist/main/app/menu-setup.d.ts +13 -0
- package/dist/main/app/menu-setup.js +49 -0
- package/dist/main/app/open-project-ipc.d.ts +13 -0
- package/dist/main/app/open-project-ipc.js +21 -0
- package/dist/main/app/project-list-surface.d.ts +31 -0
- package/dist/main/app/project-list-surface.js +20 -0
- package/dist/main/app/project-window.d.ts +36 -0
- package/dist/main/app/project-window.js +93 -0
- package/dist/main/app/servers.d.ts +17 -0
- package/dist/main/app/servers.js +60 -0
- package/dist/main/app/ui-extension-targets.d.ts +42 -0
- package/dist/main/app/ui-extension-targets.js +101 -0
- package/dist/main/app/window-close-reveal.harness.d.ts +34 -0
- package/dist/main/app/window-close-reveal.harness.js +65 -0
- package/dist/main/app/window-events.d.ts +15 -0
- package/dist/main/app/window-events.js +45 -0
- package/dist/main/app/window-runtime-services.d.ts +41 -0
- package/dist/main/app/window-runtime-services.js +178 -0
- package/dist/main/app/workbench-modules.d.ts +24 -0
- package/dist/main/app/workbench-modules.js +51 -0
- package/dist/main/app/workbench-window.d.ts +85 -0
- package/dist/main/app/workbench-window.js +310 -0
- package/dist/main/index.bundle.js +10669 -9096
- package/dist/main/ipc/app.d.ts +4 -1
- package/dist/main/ipc/app.js +4 -3
- package/dist/main/ipc/device-picker.d.ts +16 -0
- package/dist/main/ipc/device-picker.js +27 -0
- package/dist/main/ipc/index.d.ts +1 -0
- package/dist/main/ipc/index.js +1 -0
- package/dist/main/ipc/internal-devtools.d.ts +2 -1
- package/dist/main/ipc/internal-devtools.js +4 -3
- package/dist/main/ipc/popover.d.ts +4 -1
- package/dist/main/ipc/popover.js +25 -9
- package/dist/main/ipc/project-create.d.ts +2 -1
- package/dist/main/ipc/project-create.js +6 -5
- package/dist/main/ipc/project-fs.d.ts +3 -1
- package/dist/main/ipc/project-fs.js +17 -17
- package/dist/main/ipc/projects.d.ts +3 -3
- package/dist/main/ipc/projects.js +13 -12
- package/dist/main/ipc/session.d.ts +4 -1
- package/dist/main/ipc/session.js +16 -11
- package/dist/main/ipc/settings.d.ts +4 -1
- package/dist/main/ipc/settings.js +12 -19
- package/dist/main/ipc/simulator-module.d.ts +5 -0
- package/dist/main/ipc/simulator-module.js +9 -5
- package/dist/main/ipc/simulator.d.ts +4 -1
- package/dist/main/ipc/simulator.js +12 -18
- package/dist/main/ipc/tooltip.d.ts +2 -1
- package/dist/main/ipc/tooltip.js +8 -7
- package/dist/main/ipc/views.d.ts +4 -1
- package/dist/main/ipc/views.js +20 -9
- package/dist/main/runtime/devtools-backend-before-quit.harness.d.ts +2 -20
- package/dist/main/runtime/devtools-backend-before-quit.harness.js +14 -283
- package/dist/main/runtime/devtools-backend.js +14 -3
- package/dist/main/runtime/devtools-runtime-mock.harness.d.ts +21 -0
- package/dist/main/runtime/devtools-runtime-mock.harness.js +313 -0
- package/dist/main/services/app-services.d.ts +62 -0
- package/dist/main/services/app-services.js +64 -0
- package/dist/main/services/automation/connection-target.d.ts +36 -0
- package/dist/main/services/automation/connection-target.js +29 -0
- package/dist/main/services/automation/console-bridge.d.ts +28 -0
- package/dist/main/services/automation/console-bridge.js +91 -0
- package/dist/main/services/automation/handlers/app.js +4 -4
- package/dist/main/services/automation/handlers/element.js +55 -21
- package/dist/main/services/automation/index.d.ts +12 -1
- package/dist/main/services/automation/index.js +54 -133
- package/dist/main/services/default-adapter.js +1 -1
- package/dist/main/services/fs-bridge-protocol.d.ts +61 -0
- package/dist/main/services/fs-bridge-protocol.js +34 -0
- package/dist/main/services/fs-watch-sse.d.ts +8 -1
- package/dist/main/services/fs-watch-sse.js +19 -5
- package/dist/main/services/mcp/connection-owner.d.ts +38 -0
- package/dist/main/services/mcp/connection-owner.js +79 -0
- package/dist/main/services/mcp/index.d.ts +3 -1
- package/dist/main/services/mcp/index.js +4 -3
- package/dist/main/services/mcp/opened-project.d.ts +88 -0
- package/dist/main/services/mcp/opened-project.js +60 -0
- package/dist/main/services/mcp/target-manager.d.ts +71 -23
- package/dist/main/services/mcp/target-manager.js +269 -142
- package/dist/main/services/mcp/target-selection.d.ts +47 -0
- package/dist/main/services/mcp/target-selection.js +71 -0
- package/dist/main/services/mcp/tools/context-tools.js +20 -5
- package/dist/main/services/mcp/tools/project-tools.d.ts +29 -6
- package/dist/main/services/mcp/tools/project-tools.js +37 -25
- package/dist/main/services/module.d.ts +13 -5
- package/dist/main/services/notifications/renderer-notifier.d.ts +28 -4
- package/dist/main/services/notifications/renderer-notifier.js +15 -3
- package/dist/main/services/projects/local-provider.js +2 -0
- package/dist/main/services/projects/project-repository.d.ts +31 -1
- package/dist/main/services/projects/project-repository.js +144 -20
- package/dist/main/services/projects/types.d.ts +31 -8
- package/dist/main/services/safe-area/index.d.ts +19 -5
- package/dist/main/services/safe-area/index.js +38 -13
- package/dist/main/services/simulator/ui-extensions.d.ts +8 -0
- package/dist/main/services/simulator/ui-extensions.js +16 -6
- package/dist/main/services/simulator-storage/index.d.ts +12 -1
- package/dist/main/services/simulator-storage/index.js +22 -7
- package/dist/main/services/simulator-storage/window-scope.d.ts +20 -0
- package/dist/main/services/simulator-storage/window-scope.js +20 -0
- package/dist/main/services/views/host-slot-port-channel.d.ts +20 -3
- package/dist/main/services/views/host-slot-port-channel.js +33 -5
- package/dist/main/services/views/managed-web-contents-view.js +21 -1
- package/dist/main/services/views/native-simulator-view.js +13 -16
- package/dist/main/services/views/overlay-panels-view.d.ts +13 -0
- package/dist/main/services/views/overlay-panels-view.js +31 -0
- package/dist/main/services/views/view-manager.d.ts +1 -1
- package/dist/main/services/views/view-manager.js +3 -0
- package/dist/main/services/window-contexts/context-router.d.ts +36 -0
- package/dist/main/services/window-contexts/context-router.js +55 -0
- package/dist/main/services/workbench-coi-host.d.ts +52 -0
- package/dist/main/services/workbench-coi-host.js +227 -0
- package/dist/main/services/workbench-coi-server.js +75 -133
- package/dist/main/services/workbench-coi-shutdown.d.ts +40 -0
- package/dist/main/services/workbench-coi-shutdown.js +69 -0
- package/dist/main/services/workbench-coi-static.d.ts +23 -0
- package/dist/main/services/workbench-coi-static.js +87 -0
- package/dist/main/services/workbench-context.d.ts +27 -3
- package/dist/main/services/workbench-context.js +10 -5
- package/dist/main/services/workspace/compile-mode-adapter.d.ts +18 -0
- package/dist/main/services/workspace/compile-mode-adapter.js +57 -0
- package/dist/main/services/workspace/compile-mode-store.d.ts +25 -0
- package/dist/main/services/workspace/compile-mode-store.js +71 -0
- package/dist/main/services/workspace/workspace-compile-modes.d.ts +59 -0
- package/dist/main/services/workspace/workspace-compile-modes.js +93 -0
- package/dist/main/services/workspace/workspace-referer.d.ts +17 -0
- package/dist/main/services/workspace/workspace-referer.js +26 -0
- package/dist/main/services/workspace/workspace-service.d.ts +4 -0
- package/dist/main/services/workspace/workspace-service.js +51 -84
- package/dist/main/services/workspace/workspace-thumbnails.d.ts +22 -0
- package/dist/main/services/workspace/workspace-thumbnails.js +67 -0
- package/dist/main/utils/ipc-context-source.d.ts +40 -0
- package/dist/main/utils/ipc-context-source.js +23 -0
- package/dist/main/utils/ipc-registry.d.ts +42 -36
- package/dist/main/utils/ipc-registry.js +117 -50
- package/dist/main/utils/sender-policy.d.ts +9 -0
- package/dist/main/utils/sender-policy.js +26 -3
- package/dist/main/utils/theme.d.ts +3 -2
- package/dist/main/utils/theme.js +3 -2
- package/dist/main/windows/main-window/create.d.ts +16 -0
- package/dist/main/windows/main-window/create.js +19 -11
- package/dist/main/windows/main-window/events.d.ts +1 -1
- package/dist/main/windows/main-window/events.js +26 -13
- package/dist/main/windows/main-window/index.d.ts +1 -1
- package/dist/main/windows/main-window/index.js +1 -1
- package/dist/native-host/common/common.js +94 -73
- package/dist/native-host/render/render.js +4079 -3171
- package/dist/native-host/service/service.js +2 -2
- package/dist/preload/runtime/native-host.d.ts +4 -0
- package/dist/preload/runtime/temp-files.js +9 -2
- package/dist/preload/runtime/wait-for-slot-root.d.ts +7 -4
- package/dist/preload/runtime/wait-for-slot-root.js +36 -8
- package/dist/preload/windows/host-dialog-runtime.cjs +10 -3
- package/dist/preload/windows/host-dialog-runtime.cjs.map +2 -2
- package/dist/preload/windows/host-sidebar-runtime.cjs +10 -3
- package/dist/preload/windows/host-sidebar-runtime.cjs.map +2 -2
- package/dist/preload/windows/host-toolbar-runtime.cjs +10 -3
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +181 -4
- package/dist/preload/windows/main.cjs.map +4 -4
- package/dist/preload/windows/simulator.cjs +10 -0
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +10 -0
- package/dist/render-host/pageFrame.html +1 -1
- package/dist/render-host/preload.cjs +16 -4
- package/dist/renderer/assets/button-CB2MWjHY.js +2 -0
- package/dist/renderer/assets/constants-CkoGPQTS.js +2 -0
- package/dist/renderer/assets/constants-zQZ2-y21.js +2 -0
- package/dist/renderer/assets/{createLucideIcon-Bqi8hpdi.js → createLucideIcon-cZ9FlZZw.js} +2 -2
- package/dist/renderer/assets/devicePicker-H2eIO4e1.js +2 -0
- package/dist/renderer/assets/dialog-pHzEA8Vx.js +46 -0
- package/dist/renderer/assets/dist-B5HADirS.js +2 -0
- package/dist/renderer/assets/{dist-DKWdE1TK.js → dist-DIFlRMKe.js} +2 -2
- package/dist/renderer/assets/hostSidebarDefault-DRWh6OCt.js +2 -0
- package/dist/renderer/assets/index-CEL_XFge.js +2 -0
- package/dist/renderer/assets/{input-Ct3alvkq.js → input-CAObw95L.js} +2 -2
- package/dist/renderer/assets/ipc-channels-overlays-gIPUFnqc.js +2 -0
- package/dist/renderer/assets/jsx-runtime-DICEQH_0.css +1 -0
- package/dist/renderer/assets/{jsx-runtime-DGLHmmOq.js → jsx-runtime-DIw5wnYl.js} +1 -1
- package/dist/renderer/assets/plus-BQ9u5EbL.js +2 -0
- package/dist/renderer/assets/popover-Cw7G2Ctp.js +2 -0
- package/dist/renderer/assets/presets-DMZOb9ay.js +2 -0
- package/dist/renderer/assets/project-api-YTOr_Yvo.js +2 -0
- package/dist/renderer/assets/{projectCreateDialog-Bbjz75bP.js → projectCreateDialog-BmLqiYxn.js} +2 -2
- package/dist/renderer/assets/search-BMvuYF48.js +2 -0
- package/dist/renderer/assets/select-nFP95TxM.js +2 -0
- package/dist/renderer/assets/settings-CnpVfSWt.js +2 -0
- package/dist/renderer/assets/settings-api-Ccsf-Nsj.js +2 -0
- package/dist/renderer/assets/settings-tab-bar-CAzrv32m.js +2 -0
- package/dist/renderer/assets/smartphone-Bb2GdIKy.js +2 -0
- package/dist/renderer/assets/tabs-CPKN3Mmq.js +2 -0
- package/dist/renderer/assets/{tooltip-B_-_YVbw.js → tooltip-D6AxXRk5.js} +2 -2
- package/dist/renderer/assets/types-Bh0hA7El.js +2 -0
- package/dist/renderer/assets/updateDialog-CwndJ0Bl.js +2 -0
- package/dist/renderer/assets/{utils-kfQov6Pa.js → utils-sEB5OZEQ.js} +2 -2
- package/dist/renderer/assets/view-api-B-2zPBR5.js +2 -0
- package/dist/renderer/assets/view-ids-BfaDVV5P.js +2 -0
- package/dist/renderer/assets/workbench-BLJ0wKsg.js +5 -0
- package/dist/renderer/assets/{workbenchSettings-CV4LVA9b.js → workbenchSettings-YD0Ctbm_.js} +3 -3
- package/dist/renderer/entries/device-picker/index.html +28 -0
- package/dist/renderer/entries/host-sidebar-default/index.html +6 -5
- package/dist/renderer/entries/main/index.html +16 -17
- package/dist/renderer/entries/popover/index.html +16 -10
- package/dist/renderer/entries/project-create-dialog/index.html +12 -12
- package/dist/renderer/entries/settings/index.html +10 -10
- package/dist/renderer/entries/tooltip/index.html +5 -5
- package/dist/renderer/entries/update-dialog/index.html +10 -10
- package/dist/renderer/entries/workbench/index.html +49 -0
- package/dist/renderer/entries/workbench-settings/index.html +12 -11
- package/dist/service-host/host-env-update.cjs +23 -0
- package/dist/service-host/preload.cjs +24 -49
- package/dist/service-host/service-message-queue.cjs +71 -0
- package/dist/service-host/sync-impls/system-info.js +31 -9
- package/dist/shared/compile-mode-state.d.ts +73 -0
- package/dist/shared/compile-mode-state.js +100 -0
- package/dist/shared/compile-modes.d.ts +152 -0
- package/dist/shared/compile-modes.js +237 -0
- package/dist/shared/ipc-channels-overlays.d.ts +8 -2
- package/dist/shared/ipc-channels-overlays.js +19 -2
- package/dist/shared/ipc-channels.d.ts +5 -10
- package/dist/shared/ipc-channels.js +17 -14
- package/dist/shared/ipc-schemas.d.ts +76 -24
- package/dist/shared/ipc-schemas.js +55 -10
- package/dist/shared/project-window-types.d.ts +42 -0
- package/dist/shared/project-window-types.js +2 -0
- package/dist/shared/types.d.ts +43 -17
- package/dist/shared/view-ids.d.ts +1 -0
- package/dist/shared/view-ids.js +3 -1
- package/dist/simulator/assets/bridge-channels-B3JAmRfl.js +2 -0
- package/dist/simulator/assets/device-shell-ATEhHV2_.css +1 -0
- package/dist/simulator/assets/device-shell-pxnZZp10.js +431 -0
- package/dist/simulator/assets/simulator-Dwp7lnvu.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-DFo7fhi5.js +2 -0
- package/dist/simulator/assets/simulator-ui-B2ddJB1R.js +2 -0
- package/dist/simulator/simulator.html +3 -3
- package/dist/simulator/temp-files.d.ts +6 -0
- package/dist/simulator/temp-files.js +46 -4
- package/dist/simulator/types.d.ts +7 -0
- package/dist/vscode-workbench/assets/__vite-browser-external-JIkWewFf.js +1 -0
- package/dist/vscode-workbench/assets/{dist-CMfmHflW.js → dist-CXG-u1VB.js} +3 -3
- package/dist/vscode-workbench/assets/{iconv-lite-umd-Bx8CgaZq.js → iconv-lite-umd-Dxx9Qyb4.js} +1 -1
- package/dist/vscode-workbench/assets/{index-CdG6YWhW.js → index-6VJMsaWj.js} +460 -460
- package/dist/vscode-workbench/assets/{jschardet-M0qgd2lN.js → jschardet-Ba3D16vt.js} +1 -1
- package/dist/vscode-workbench/index.html +1 -1
- package/package.json +11 -8
- package/dist/renderer/assets/button-D5Q2wkXq.js +0 -2
- package/dist/renderer/assets/constants-D7DfVqMp.js +0 -2
- package/dist/renderer/assets/constants-D8zsejFq.js +0 -2
- package/dist/renderer/assets/dialog-e1TecTOY.js +0 -46
- package/dist/renderer/assets/hostSidebarDefault-B3yLfy8j.js +0 -2
- package/dist/renderer/assets/index-CsPD68N_.js +0 -5
- package/dist/renderer/assets/ipc-channels-overlays-LsDzGAtV.js +0 -2
- package/dist/renderer/assets/jsx-runtime-2vXHouwr.css +0 -1
- package/dist/renderer/assets/popover-Dix5cm_b.js +0 -2
- package/dist/renderer/assets/project-api-k7Oq9iaq.js +0 -2
- package/dist/renderer/assets/select-acGb0AmJ.js +0 -2
- package/dist/renderer/assets/settings-CQrTMr43.js +0 -2
- package/dist/renderer/assets/settings-api-CrUQ9rYV.js +0 -2
- package/dist/renderer/assets/settings-tab-bar-Cw1kVIf2.js +0 -2
- package/dist/renderer/assets/types-CsEKtFyr.js +0 -2
- package/dist/renderer/assets/updateDialog-NsF8223A.js +0 -2
- package/dist/renderer/assets/view-api-CLn3rcQE.js +0 -2
- package/dist/simulator/assets/bridge-channels-zhsumfky.js +0 -2
- package/dist/simulator/assets/device-shell-CmtM1QxP.js +0 -2
- package/dist/simulator/assets/device-shell-iFZo1igL.css +0 -1
- package/dist/simulator/assets/simulator-CIobn3Mh.js +0 -10
- package/dist/simulator/assets/simulator-mini-app-CRe6n9br.js +0 -2
- package/dist/simulator/assets/simulator-ui-C_cP602X.js +0 -2
- package/dist/vscode-workbench/assets/__vite-browser-external-Bc1vizTf.js +0 -1
package/dist/main/app/app.js
CHANGED
|
@@ -1,338 +1,97 @@
|
|
|
1
1
|
import { setupCdpPort, registerDifileScheme, suppressInsecureCspWarnings } from './bootstrap.js';
|
|
2
2
|
import { installMaxListenersWarningDiagnostic } from './max-listeners-diagnostic.js';
|
|
3
|
+
import { app, session } from 'electron';
|
|
4
|
+
import { rendererDir as defaultRendererDir } from '../utils/paths.js';
|
|
5
|
+
import { createAppServices, registerTrustedWindow } from '../services/app-services.js';
|
|
6
|
+
import { createWindowContextRouter } from '../services/window-contexts/context-router.js';
|
|
7
|
+
import { createInternalDevtoolsWindow } from '../windows/internal-devtools-window/index.js';
|
|
8
|
+
import { registerAppIpc, registerInternalDevtoolsIpc, registerTooltipIpc, registerProjectCreateIpc, registerDevicePickerIpc, registerViewsIpc, } from '../ipc/index.js';
|
|
3
9
|
import { registerProjectFsIpc } from '../ipc/project-fs.js';
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { HOST_SIDEBAR_DEFAULT_WIDTH } from '../../shared/constants.js';
|
|
8
|
-
import { rendererDir as defaultRendererDir, defaultPreloadPath, devtoolsPackageRoot } from '../utils/paths.js';
|
|
10
|
+
import { setupSimulatorSessionPolicy } from '../services/views/simulator-session-policy.js';
|
|
11
|
+
import { setupCompileWorkerStandby } from '../services/compile-standby.js';
|
|
9
12
|
import { installThemeBackgroundSync } from '../utils/theme.js';
|
|
10
|
-
import { createMainWindow, wireMainWindowEvents } from '../windows/main-window/index.js';
|
|
11
|
-
import { createInternalDevtoolsWindow } from '../windows/internal-devtools-window/index.js';
|
|
12
|
-
import { createGlobalConsoleMirror } from '../services/console-forward/global-console-mirror.js';
|
|
13
|
-
import { createGlobalDiagnosticsMirror } from '../services/console-forward/global-diagnostics-mirror.js';
|
|
14
13
|
import { isAppQuitting } from './lifecycle.js';
|
|
15
|
-
import { resolveNativeAppDataKeys, resolveNativeStorageOverview } from './native-overview.js';
|
|
16
|
-
// eslint-disable-next-line no-restricted-syntax -- grandfathered(workbench-context): shrink-only
|
|
17
|
-
import { createWorkbenchContext } from '../services/workbench-context.js';
|
|
18
|
-
import { setupCompileWorkerStandby } from '../services/compile-standby.js';
|
|
19
14
|
import { loadWorkbenchSettings, applyTheme } from '../services/settings/index.js';
|
|
20
|
-
import { installAppMenu } from '../menu/index.js';
|
|
21
|
-
import { registerAppIpc, registerInternalDevtoolsIpc, registerTooltipIpc, registerProjectCreateIpc, registerViewsIpc, popoverModule, projectsModule, sessionModule, settingsModule, simulatorModule, } from '../ipc/index.js';
|
|
22
|
-
import { startAutomationServer } from '../services/automation/index.js';
|
|
23
|
-
import { startMcpServer, setNativeHost, setActiveBridgeId, setNativeOverviewProvider, } from '../services/mcp/index.js';
|
|
24
|
-
import { setupSimulatorStorage } from '../services/simulator-storage/index.js';
|
|
25
|
-
import { createNetworkForwarder } from '../services/network-forward/index.js';
|
|
26
|
-
import { setupSimulatorWxml } from '../services/simulator-wxml/index.js';
|
|
27
|
-
import { setupSimulatorAppData } from '../services/simulator-appdata/index.js';
|
|
28
|
-
import { setupSimulatorCurrentPage } from '../services/simulator-current-page/index.js';
|
|
29
|
-
import { createRenderInspector } from '../services/render-inspect/index.js';
|
|
30
|
-
import { setupSimulatorTempFiles } from '../services/simulator-temp-files/index.js';
|
|
31
15
|
import { SHARED_MINIAPP_PARTITION } from '../services/views/miniapp-partition.js';
|
|
32
|
-
import {
|
|
33
|
-
import { startWorkbenchCoiServer } from '../services/workbench-coi-server.js';
|
|
16
|
+
import { setupSimulatorTempFiles } from '../services/simulator-temp-files/index.js';
|
|
34
17
|
import { UpdateManager } from '../services/update/index.js';
|
|
35
|
-
import { toDisposable } from '@dimina-kit/electron-deck/main';
|
|
18
|
+
import { toDisposable, DisposableRegistry } from '@dimina-kit/electron-deck/main';
|
|
36
19
|
import { IpcRegistry } from '../utils/ipc-registry.js';
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* Adds `win.webContents` to the context's trusted-sender set and returns a
|
|
54
|
-
* Disposable that removes it again.
|
|
55
|
-
*
|
|
56
|
-
* Trust is reference-counted: registering the SAME window N times keeps it
|
|
57
|
-
* trusted until every one of the N returned Disposables has been disposed.
|
|
58
|
-
* `trustedWindowSenderIds` is a `Map<webContents.id, refCount>`; each register
|
|
59
|
-
* bumps the count, each dispose decrements it, and the window is un-trusted
|
|
60
|
-
* only when the count reaches zero.
|
|
61
|
-
*
|
|
62
|
-
* The window's `closed` event short-circuits the ref-count: it deletes the
|
|
63
|
-
* map entry outright (the window is dead, so it must be un-trusted
|
|
64
|
-
* immediately regardless of how many Disposables are still outstanding).
|
|
65
|
-
* After `closed`, disposing any leftover Disposable is a safe no-op — the
|
|
66
|
-
* map entry is already gone, so the decrement finds `undefined` and bails
|
|
67
|
-
* without driving the count negative or resurrecting trust.
|
|
68
|
-
*
|
|
69
|
-
* Each returned Disposable is itself idempotent (`removed` flag), and its
|
|
70
|
-
* `closed` listener removes itself once fired so a long-lived context
|
|
71
|
-
* doesn't accumulate dead listeners on closed windows.
|
|
72
|
-
*/
|
|
73
|
-
function registerTrustedWindow(context, win) {
|
|
74
|
-
const senderId = win.webContents.id;
|
|
75
|
-
const counts = context.trustedWindowSenderIds;
|
|
76
|
-
counts.set(senderId, (counts.get(senderId) ?? 0) + 1);
|
|
77
|
-
let removed = false;
|
|
78
|
-
const onClosed = () => {
|
|
79
|
-
// The window is gone: zero the ref-count for this sender id outright,
|
|
80
|
-
// regardless of any other outstanding registrations for the same window.
|
|
81
|
-
counts.delete(senderId);
|
|
82
|
-
win.removeListener('closed', onClosed);
|
|
83
|
-
};
|
|
84
|
-
function remove() {
|
|
85
|
-
if (removed)
|
|
86
|
-
return;
|
|
87
|
-
removed = true;
|
|
88
|
-
win.removeListener('closed', onClosed);
|
|
89
|
-
const count = counts.get(senderId);
|
|
90
|
-
// `undefined` → the entry was already cleared (e.g. by `closed` or by a
|
|
91
|
-
// prior sibling's decrement that hit zero). Nothing to do — never go
|
|
92
|
-
// negative, never resurrect trust.
|
|
93
|
-
if (count === undefined)
|
|
94
|
-
return;
|
|
95
|
-
if (count <= 1)
|
|
96
|
-
counts.delete(senderId);
|
|
97
|
-
else
|
|
98
|
-
counts.set(senderId, count - 1);
|
|
99
|
-
}
|
|
100
|
-
win.once('closed', onClosed);
|
|
101
|
-
return toDisposable(remove);
|
|
102
|
-
}
|
|
103
|
-
/** Parse --auto, --auto-port, --project from process.argv. */
|
|
104
|
-
function parseAutoArgs() {
|
|
105
|
-
const argv = process.argv;
|
|
106
|
-
let auto = false;
|
|
107
|
-
let autoPort = 9420;
|
|
108
|
-
let projectPath = '';
|
|
109
|
-
for (let i = 0; i < argv.length; i++) {
|
|
110
|
-
if (argv[i] === 'auto' || argv[i] === '--auto')
|
|
111
|
-
auto = true;
|
|
112
|
-
if ((argv[i] === '--auto-port' || argv[i] === '--auto_port') && argv[i + 1]) {
|
|
113
|
-
const parsed = parseInt(argv[i + 1], 10);
|
|
114
|
-
// 0 → OS-assigned free port (used by parallel e2e workers)
|
|
115
|
-
if (Number.isFinite(parsed) && parsed >= 0)
|
|
116
|
-
autoPort = parsed;
|
|
117
|
-
}
|
|
118
|
-
if (argv[i] === '--project' && argv[i + 1]) {
|
|
119
|
-
projectPath = argv[i + 1];
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return { auto, autoPort, projectPath };
|
|
123
|
-
}
|
|
124
|
-
function resolveModules(config) {
|
|
125
|
-
return {
|
|
126
|
-
...DEFAULT_MODULES,
|
|
127
|
-
...config.modules,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
async function disposeContext(ctx) {
|
|
131
|
-
await ctx.workspace.closeProject();
|
|
132
|
-
await ctx.registry.dispose().catch((err) => {
|
|
133
|
-
console.warn('[workbench] dispose registry encountered errors:', err);
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
function createConfiguredMainWindow(config, rendererDir) {
|
|
137
|
-
const mainWindow = createMainWindow({
|
|
138
|
-
title: config.appName ?? 'Dimina DevTools',
|
|
139
|
-
indexHtml: path.join(rendererDir, 'entries/main/index.html'),
|
|
140
|
-
width: config.window?.width,
|
|
141
|
-
height: config.window?.height,
|
|
142
|
-
minWidth: config.window?.minWidth,
|
|
143
|
-
minHeight: config.window?.minHeight,
|
|
144
|
-
autoShow: config.window?.autoShow,
|
|
145
|
-
});
|
|
146
|
-
// Set window/taskbar icon if provided (Linux/Windows; macOS uses app bundle icon)
|
|
147
|
-
if (config.icon) {
|
|
148
|
-
const icon = nativeImage.createFromPath(config.icon);
|
|
149
|
-
if (!icon.isEmpty())
|
|
150
|
-
mainWindow.setIcon(icon);
|
|
151
|
-
}
|
|
152
|
-
return mainWindow;
|
|
153
|
-
}
|
|
154
|
-
function createContext(config, mainWindow, rendererDir) {
|
|
155
|
-
return createWorkbenchContext({
|
|
156
|
-
mainWindow,
|
|
157
|
-
adapter: config.adapter,
|
|
158
|
-
preloadPath: config.preloadPath ?? defaultPreloadPath,
|
|
159
|
-
rendererDir,
|
|
160
|
-
appName: config.appName,
|
|
161
|
-
apiNamespaces: config.apiNamespaces,
|
|
162
|
-
fileTypes: config.fileTypes,
|
|
163
|
-
brandingProvider: config.brandingProvider,
|
|
164
|
-
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
165
|
-
// are structurally compatible with the main-process equivalents —
|
|
166
|
-
// these casts are safe; we re-narrow at the workspace-service /
|
|
167
|
-
// create-project-service boundary.
|
|
168
|
-
projectsProvider: config.projectsProvider,
|
|
169
|
-
projectTemplates: config.projectTemplates,
|
|
170
|
-
builtinTemplates: config.builtinTemplates,
|
|
171
|
-
customCreateProjectDialog: config.customCreateProjectDialog,
|
|
172
|
-
// No cast needed here: the hook receives the project record, so the
|
|
173
|
-
// main-process `Project` the context hands it satisfies the structural
|
|
174
|
-
// `EditableProject` the config declares.
|
|
175
|
-
customEditProjectDialog: config.customEditProjectDialog,
|
|
176
|
-
onBeforeOpenProject: config.onBeforeOpenProject,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
function registerBuiltinModules(config, context) {
|
|
180
|
-
const modules = resolveModules(config);
|
|
181
|
-
Object.keys(modules).forEach((moduleId) => {
|
|
182
|
-
if (modules[moduleId])
|
|
183
|
-
context.registry.add(BUILTIN_MODULES[moduleId].setup(context));
|
|
184
|
-
});
|
|
185
|
-
}
|
|
20
|
+
import { createLauncherWindow } from './project-window.js';
|
|
21
|
+
import { createProjectListSurface } from './project-list-surface.js';
|
|
22
|
+
import { createWorkbenchWindowManager } from './workbench-window.js';
|
|
23
|
+
import { registerModuleIpc, setupWindowModules } from './workbench-modules.js';
|
|
24
|
+
import { registerOpenProjectWindowIpc } from './open-project-ipc.js';
|
|
25
|
+
import { createUiExtensionTargets } from './ui-extension-targets.js';
|
|
26
|
+
import { installGlobalMirrors } from './global-mirrors.js';
|
|
27
|
+
import { installHostSidebarDefault } from './host-sidebar-default.js';
|
|
28
|
+
import { installMenu } from './menu-setup.js';
|
|
29
|
+
import { createOpenForMcp, createTargetForMcp, noteActiveMcpWindowChanged, setActiveMcpWindowResolver, } from '../services/mcp/index.js';
|
|
30
|
+
import { setupAutomation, setupMcp } from './servers.js';
|
|
31
|
+
import { enableDevRendererAutoReload } from './window-events.js';
|
|
32
|
+
import { wireMainWindowEvents } from '../windows/main-window/index.js';
|
|
33
|
+
export { setupWindowModules } from './workbench-modules.js';
|
|
186
34
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* EXACTLY the contract members and nothing else. Every member is a lazy
|
|
190
|
-
* closure over the live context: a host monkey-patch of
|
|
191
|
-
* `context.workspace.openProject` (the documented permission-gate pattern)
|
|
192
|
-
* still intercepts calls made through this menu surface.
|
|
35
|
+
* Registers the workbench's IPC surface against the router, so each message is
|
|
36
|
+
* answered with the context of the window it came from.
|
|
193
37
|
*/
|
|
194
|
-
function
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// would re-enter and tear the same session down twice. Swallow re-entrancy.
|
|
252
|
-
let closing = false;
|
|
253
|
-
return wireMainWindowEvents(mainWindow, {
|
|
254
|
-
context,
|
|
255
|
-
onResize: () => context.views.repositionAll(),
|
|
256
|
-
onClose: async (e) => {
|
|
257
|
-
// A real application quit (⌘Q / menu "Quit" / app.quit()) fires
|
|
258
|
-
// `before-quit` first, then this window's `close`. Let it through so the
|
|
259
|
-
// app actually exits — do NOT convert it into "close the project".
|
|
260
|
-
// Without this, `hasActiveSession()` would be true and the quit gets
|
|
261
|
-
// swallowed into closeProject(), so the app can never be quit while a
|
|
262
|
-
// project is open.
|
|
263
|
-
if (isAppQuitting())
|
|
264
|
-
return;
|
|
265
|
-
// A close arriving while a project teardown is already in flight (the
|
|
266
|
-
// user rapid-double-clicked the close button) MUST keep the window open.
|
|
267
|
-
// This guard runs BEFORE hasActiveSession() on purpose: `closeProject()`
|
|
268
|
-
// → `disposeSession()` synchronously nulls the active session before it
|
|
269
|
-
// finishes awaiting `session.close()`, so by the time the second close
|
|
270
|
-
// arrives `hasActiveSession()` is already false. With the old guard order
|
|
271
|
-
// that second close fell straight through the hasActiveSession() check
|
|
272
|
-
// WITHOUT `preventDefault()`, so Electron destroyed the last window →
|
|
273
|
-
// `window-all-closed` → `app.quit()`, quitting the whole app on a
|
|
274
|
-
// double-click. Swallow the re-entrant close and keep the window.
|
|
275
|
-
if (closing) {
|
|
276
|
-
e.preventDefault();
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
// Keep the app alive on close whenever EITHER a live compiled session
|
|
280
|
-
// exists OR the renderer reports it is on a project screen. The renderer
|
|
281
|
-
// enters the project screen (and reports it) before openProject resolves,
|
|
282
|
-
// so a FAILED open — invalid/non-existent project, no session ever
|
|
283
|
-
// created — still parks the renderer there showing the compile-failed
|
|
284
|
-
// overlay. Keying off `hasActiveSession()` alone let that close fall
|
|
285
|
-
// through with no preventDefault → the last window is destroyed →
|
|
286
|
-
// `window-all-closed` → `app.quit()`, quitting the whole app instead of
|
|
287
|
-
// returning to the list. The two signals answer one question ("is the
|
|
288
|
-
// user inside a project, so close means back-to-list?"); the renderer's
|
|
289
|
-
// screen is the authority and hasActiveSession is a resource-safety belt
|
|
290
|
-
// (never destroy the window while a live session runs behind it).
|
|
291
|
-
if (!context.workspace.hasActiveSession() && !isOnProjectScreen())
|
|
292
|
-
return;
|
|
293
|
-
// Close button while inside a project: stay in the workbench and surface
|
|
294
|
-
// the project list. Tear down only the session — do NOT dispose
|
|
295
|
-
// `context.registry`, which owns every IPC handler (projects, dialog,
|
|
296
|
-
// settings…). Disposing it would leave the renderer alive but unable to
|
|
297
|
-
// invoke anything, so subsequent clicks on Import/etc. would raise
|
|
298
|
-
// `No handler registered for ...`.
|
|
299
|
-
e.preventDefault();
|
|
300
|
-
closing = true;
|
|
301
|
-
try {
|
|
302
|
-
if (config.onBeforeClose) {
|
|
303
|
-
await config.onBeforeClose(instance);
|
|
304
|
-
}
|
|
305
|
-
await context.workspace.closeProject();
|
|
306
|
-
context.notify.windowNavigateBack();
|
|
307
|
-
}
|
|
308
|
-
finally {
|
|
309
|
-
closing = false;
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
function enableDevRendererAutoReload(rendererDir) {
|
|
315
|
-
// Auto-reload renderer windows when dist files change during development
|
|
316
|
-
if (app.isPackaged) {
|
|
317
|
-
return toDisposable(() => { });
|
|
318
|
-
}
|
|
319
|
-
let reloadTimer = null;
|
|
320
|
-
const watcher = fs.watch(rendererDir, { recursive: true }, () => {
|
|
321
|
-
if (reloadTimer)
|
|
322
|
-
clearTimeout(reloadTimer);
|
|
323
|
-
reloadTimer = setTimeout(() => {
|
|
324
|
-
for (const win of BrowserWindow.getAllWindows()) {
|
|
325
|
-
win.webContents.reload();
|
|
326
|
-
}
|
|
327
|
-
}, 300);
|
|
328
|
-
});
|
|
329
|
-
return toDisposable(() => {
|
|
330
|
-
if (reloadTimer) {
|
|
331
|
-
clearTimeout(reloadTimer);
|
|
332
|
-
reloadTimer = null;
|
|
333
|
-
}
|
|
334
|
-
watcher.close();
|
|
335
|
-
});
|
|
38
|
+
function registerWorkbenchIpc(config, router, context, mainWindow, appRegistry) {
|
|
39
|
+
appRegistry.add(registerAppIpc(router));
|
|
40
|
+
// Sandboxed project file-system IPC (the renderer-side project:fs:* surface).
|
|
41
|
+
appRegistry.add(registerProjectFsIpc(router));
|
|
42
|
+
// Standalone internal (app-wide) DevTools debug window controller — the
|
|
43
|
+
// independent floating CDP panel that debugs the whole Electron app (as
|
|
44
|
+
// opposed to the right-panel CDP, which inspects only the user's
|
|
45
|
+
// mini-program). Assembled before the IPC handler below so a request
|
|
46
|
+
// arriving right after boot always finds it.
|
|
47
|
+
// `isAppQuitting` lets the controller stop intercepting 'close' during a
|
|
48
|
+
// real quit — its habitual preventDefault()+hide() would otherwise cancel
|
|
49
|
+
// the quit itself and strand the process with a hidden window.
|
|
50
|
+
// One per window (each debugs its own renderer), so it is parked on the
|
|
51
|
+
// window's context registry, not the app's.
|
|
52
|
+
context.internalDevtoolsWindow = createInternalDevtoolsWindow(mainWindow, { isAppQuitting });
|
|
53
|
+
context.registry.add(toDisposable(() => context.internalDevtoolsWindow?.dispose()));
|
|
54
|
+
// Unconditional (not a toggleable BUILTIN_MODULES entry): it's core dev
|
|
55
|
+
// tooling, not a host-configurable feature.
|
|
56
|
+
appRegistry.add(registerInternalDevtoolsIpc(router));
|
|
57
|
+
// Unconditional (not a toggleable BUILTIN_MODULES entry): the tooltip
|
|
58
|
+
// overlay is core UI chrome (every toolbar in this app relies on it), not a
|
|
59
|
+
// host-configurable feature.
|
|
60
|
+
appRegistry.add(registerTooltipIpc(router));
|
|
61
|
+
// Unconditional: the project-create dialog is core UI chrome (the built-in
|
|
62
|
+
// "新建项目" flow every host falls back to), not a host-configurable feature.
|
|
63
|
+
appRegistry.add(registerProjectCreateIpc(router));
|
|
64
|
+
// Unconditional for the same reason as the tooltip above: the device picker
|
|
65
|
+
// is an overlay WebContentsView the simulator toolbar opens, and `ctx.views`
|
|
66
|
+
// exists regardless of the `simulator` module toggle. A host that turns the
|
|
67
|
+
// simulator off simply never renders the trigger.
|
|
68
|
+
appRegistry.add(registerDevicePickerIpc(router));
|
|
69
|
+
// Unconditional (not a toggleable BUILTIN_MODULES entry): placement/host-
|
|
70
|
+
// slot IPC has no real dependency on the simulator module — `ctx.views`
|
|
71
|
+
// (ViewManager) is constructed unconditionally regardless of
|
|
72
|
+
// `modules.simulator`, and host-sidebar in particular lives on the
|
|
73
|
+
// project-list page, unrelated to the simulator webview. Every renderer
|
|
74
|
+
// entry point also blocks its first render on `AllocatePlacementGeneration`
|
|
75
|
+
// (see renderer-placement-generation.ts) — gating any of this behind the
|
|
76
|
+
// simulator toggle would strand a host that disables it on the fatal
|
|
77
|
+
// boot-failure page, or leave placement silently non-functional.
|
|
78
|
+
appRegistry.add(registerViewsIpc(router));
|
|
79
|
+
// Referer/CORS webRequest policy for the simulator runtime's sessions (shared
|
|
80
|
+
// fallback + every per-project partition). Registered into the app registry so
|
|
81
|
+
// its configurator + per-session listeners are torn down with the app —
|
|
82
|
+
// re-creating the app never leaks a duplicate configurator, and a window
|
|
83
|
+
// closing must not strip the policy off the sessions other windows still use.
|
|
84
|
+
appRegistry.add(setupSimulatorSessionPolicy());
|
|
85
|
+
// One process-wide listener that re-syncs every window's native
|
|
86
|
+
// backgroundColor on theme change — windows otherwise keep the stale
|
|
87
|
+
// creation-time color (see installThemeBackgroundSync).
|
|
88
|
+
appRegistry.add(installThemeBackgroundSync());
|
|
89
|
+
// Warm-standby compile worker: only meaningful for the devkit-backed
|
|
90
|
+
// default adapter (a host-injected adapter has no devkit fork to adopt the
|
|
91
|
+
// spare). Registered into the registry so the spare dies with the app.
|
|
92
|
+
if (!config.adapter)
|
|
93
|
+
appRegistry.add(setupCompileWorkerStandby(context));
|
|
94
|
+
registerModuleIpc(config, router, appRegistry);
|
|
336
95
|
}
|
|
337
96
|
/**
|
|
338
97
|
* Pre-ready bootstrap side effects (app name, CDP port, CSP suppression,
|
|
@@ -367,10 +126,11 @@ export function runDevtoolsBootstrap(config = {}) {
|
|
|
367
126
|
catch { /* electron stub in tests */ }
|
|
368
127
|
}
|
|
369
128
|
/**
|
|
370
|
-
* Domain runtime assembly — the post-whenReady body. Builds the
|
|
371
|
-
* context,
|
|
372
|
-
*
|
|
373
|
-
*
|
|
129
|
+
* Domain runtime assembly — the post-whenReady body. Builds the app-wide
|
|
130
|
+
* services and the context router, opens the project window, registers IPC
|
|
131
|
+
* against the router, stands up simulator/storage/CDP/native-host services, and
|
|
132
|
+
* returns the fat {@link WorkbenchAppInstance}. Extracted so the v2
|
|
133
|
+
* `RuntimeBackend.assemble` reuses the exact same body (parity by shared
|
|
374
134
|
* implementation, not behavioural re-creation).
|
|
375
135
|
*/
|
|
376
136
|
export async function createDevtoolsRuntime(config = {},
|
|
@@ -390,123 +150,159 @@ onInstanceCreated) {
|
|
|
390
150
|
await app.whenReady();
|
|
391
151
|
applyTheme(loadWorkbenchSettings().theme);
|
|
392
152
|
const rendererDir = config.rendererDir ?? defaultRendererDir;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
// slot IPC has no real dependency on the simulator module — `ctx.views`
|
|
424
|
-
// (ViewManager) is constructed unconditionally regardless of
|
|
425
|
-
// `modules.simulator`, and host-sidebar in particular lives on the
|
|
426
|
-
// project-list page, unrelated to the simulator webview. Every renderer
|
|
427
|
-
// entry point also blocks its first render on `AllocatePlacementGeneration`
|
|
428
|
-
// (see renderer-placement-generation.ts) — gating any of this behind the
|
|
429
|
-
// simulator toggle would strand a host that disables it on the fatal
|
|
430
|
-
// boot-failure page, or leave placement silently non-functional.
|
|
431
|
-
context.registry.add(registerViewsIpc(context));
|
|
432
|
-
// Referer/CORS webRequest policy for the simulator runtime's sessions (shared
|
|
433
|
-
// fallback + every per-project partition). Registered into the context
|
|
434
|
-
// registry so its configurator + per-session listeners are torn down with the
|
|
435
|
-
// context — re-creating the app never leaks a duplicate configurator.
|
|
436
|
-
context.registry.add(setupSimulatorSessionPolicy());
|
|
437
|
-
// One process-wide listener that re-syncs every window's native
|
|
438
|
-
// backgroundColor on theme change — windows otherwise keep the stale
|
|
439
|
-
// creation-time color (see installThemeBackgroundSync).
|
|
440
|
-
context.registry.add(installThemeBackgroundSync());
|
|
441
|
-
// Warm-standby compile worker: only meaningful for the devkit-backed
|
|
442
|
-
// default adapter (a host-injected adapter has no devkit fork to adopt the
|
|
443
|
-
// spare). Registered into the registry so the spare dies with the context.
|
|
444
|
-
if (!config.adapter)
|
|
445
|
-
context.registry.add(setupCompileWorkerStandby(context));
|
|
446
|
-
registerBuiltinModules(config, context);
|
|
447
|
-
// Global console mirror: while the standalone
|
|
448
|
-
// internal DevTools window is open, mirror every guest console entry
|
|
449
|
-
// (service + render, UNFILTERED — see global-console-mirror.ts) into it —
|
|
450
|
-
// each open replays the forwarder's current history buffer first (see that
|
|
451
|
-
// module's doc comment for why the subscribe lifecycle is gated on
|
|
452
|
-
// onHostChanged rather than subscribed once at construction time).
|
|
453
|
-
// `context.consoleForwarder` is assembled by the simulator module's
|
|
454
|
-
// installBridgeRouter just above; absent only when that builtin module was
|
|
455
|
-
// disabled via config. The Console panel shows the INSPECTED target's own
|
|
456
|
-
// console (mainWindow, per internal-devtools-window.ts's
|
|
457
|
-
// setDevToolsWebContents relationship) — NOT the front-end host page's
|
|
458
|
-
// console, so `target` is always mainWindow.webContents, never the hostWc
|
|
459
|
-
// onHostChanged hands the mirror.
|
|
460
|
-
if (context.consoleForwarder && context.internalDevtoolsWindow) {
|
|
461
|
-
const consoleMirror = createGlobalConsoleMirror(context.consoleForwarder, mainWindow.webContents, context.internalDevtoolsWindow.onHostChanged,
|
|
462
|
-
// CDP transport (never executeJavaScript) — see the mirror's inject()
|
|
463
|
-
// doc for the setDevToolsWebContents + external-CDP double-attach hang.
|
|
464
|
-
{ broker: context.cdpSessionBroker });
|
|
465
|
-
context.registry.add(toDisposable(() => consoleMirror.dispose()));
|
|
466
|
-
}
|
|
467
|
-
// Global diagnostics mirror (same wiring, same INSPECTED-side
|
|
468
|
-
// target rationale as the console mirror above): every diagnostic —
|
|
469
|
-
// including `audience:'internal'` ones console-forward's own service-host
|
|
470
|
-
// injection now skips (see compile-standby.ts / index.ts's handleDiagnostic
|
|
471
|
-
// gate) — surfaces here instead of vanishing. `context.diagnostics` is
|
|
472
|
-
// assembled alongside `context.consoleForwarder` by the same
|
|
473
|
-
// installBridgeRouter call.
|
|
474
|
-
if (context.diagnostics && context.internalDevtoolsWindow) {
|
|
475
|
-
const diagnosticsMirror = createGlobalDiagnosticsMirror(context.diagnostics, mainWindow.webContents, context.internalDevtoolsWindow.onHostChanged, { broker: context.cdpSessionBroker });
|
|
476
|
-
context.registry.add(toDisposable(() => diagnosticsMirror.dispose()));
|
|
477
|
-
}
|
|
153
|
+
// App-wide, built once: the project list, the templates, the trusted-sender
|
|
154
|
+
// ledger and the host's simulator APIs are properties of the application, not
|
|
155
|
+
// of any single window.
|
|
156
|
+
const appServices = createAppServices({
|
|
157
|
+
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
158
|
+
// are structurally compatible with the main-process equivalents —
|
|
159
|
+
// these casts are safe; we re-narrow at the workspace-service /
|
|
160
|
+
// create-project-service boundary.
|
|
161
|
+
projectsProvider: config.projectsProvider,
|
|
162
|
+
projectTemplates: config.projectTemplates,
|
|
163
|
+
builtinTemplates: config.builtinTemplates,
|
|
164
|
+
});
|
|
165
|
+
const router = createWindowContextRouter();
|
|
166
|
+
// App-lifetime registry. `ipcMain.handle` is a process-wide singleton per
|
|
167
|
+
// channel, so the IPC surface is registered exactly once and answers
|
|
168
|
+
// whichever window a message came from. Keeping it OFF the launcher
|
|
169
|
+
// context's registry is what lets the user close the project list without
|
|
170
|
+
// stripping every handler out from under the workbench windows still open.
|
|
171
|
+
const appRegistry = new DisposableRegistry();
|
|
172
|
+
const launcher = createLauncherWindow({ config, rendererDir, appServices, router });
|
|
173
|
+
const { window: mainWindow, context } = launcher;
|
|
174
|
+
const projectList = createProjectListSurface({ window: mainWindow, context });
|
|
175
|
+
registerWorkbenchIpc(config, router, context, mainWindow, appRegistry);
|
|
176
|
+
// No `setupWindowModules` here: the only per-window module is the simulator's
|
|
177
|
+
// bridge router, and the project list runs no mini-app — it has no simulator,
|
|
178
|
+
// no service host and no page stack. Installing one here would also claim the
|
|
179
|
+
// process-global `dmb:*` invoke channels before the first workbench window
|
|
180
|
+
// could. `installGlobalMirrors` skips the console/diagnostics mirrors when the
|
|
181
|
+
// router never ran, so the list window keeps its own internal DevTools.
|
|
182
|
+
installGlobalMirrors(context, mainWindow);
|
|
478
183
|
// Wire the simulator-side difile:// protocol handler + temp-file IPC
|
|
479
184
|
// before host onSetup so any host-driven simulator boot sees the
|
|
480
185
|
// protocol live. The module installs its own narrow sender-policy
|
|
481
186
|
// (simulator-session-only) — see file header — because the default
|
|
482
187
|
// workbench policy intentionally rejects the simulator <webview>.
|
|
483
188
|
const simSession = session.fromPartition(SHARED_MINIAPP_PARTITION);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
189
|
+
appRegistry.add(setupSimulatorTempFiles(simSession));
|
|
190
|
+
// Gated custom-IPC surface for the host. Admits any sender the router places
|
|
191
|
+
// in a live window, so a host channel invoked from a workbench window is
|
|
192
|
+
// answered the same as one invoked from the list — binding it to the
|
|
193
|
+
// launcher's own policy would reject every workbench renderer.
|
|
194
|
+
const hostIpc = new IpcRegistry((sender) => router.resolve(sender) !== null);
|
|
195
|
+
appRegistry.add(hostIpc);
|
|
196
|
+
// Host-registered simulator UI extensions are app-level, but an extension can
|
|
197
|
+
// only live in a project window, so the ledger keeps one copy per window.
|
|
198
|
+
const uiExtensions = createUiExtensionTargets({
|
|
199
|
+
projectWindows: () => workbenchWindows.list().map((pw) => pw.context),
|
|
200
|
+
activeWindow: () => workbenchWindows.activeContext(),
|
|
201
|
+
});
|
|
202
|
+
// The open-project IPC is registered below, well before `config.onSetup` is
|
|
203
|
+
// awaited, so a renderer that races ahead can ask for a project window while
|
|
204
|
+
// the host is still wiring its extensions up. Those opens park on `ready`
|
|
205
|
+
// instead of producing a window the host never got to extend.
|
|
206
|
+
let markReady;
|
|
207
|
+
const ready = new Promise((resolve) => { markReady = resolve; });
|
|
208
|
+
// The parked open reports an onSetup failure to whoever asked for the
|
|
209
|
+
// window; this keeps the rejection handled when nobody ever asks.
|
|
210
|
+
void ready.catch(() => { });
|
|
211
|
+
const workbenchWindows = createWorkbenchWindowManager({
|
|
212
|
+
config,
|
|
213
|
+
rendererDir,
|
|
214
|
+
appServices,
|
|
215
|
+
router,
|
|
216
|
+
ready,
|
|
217
|
+
// Not wrapped in try/catch the way `onBeforeClose` is: this hook runs on
|
|
218
|
+
// the way IN, so a host that could not configure the window has no window
|
|
219
|
+
// worth keeping — the manager tears it down and the error reaches the
|
|
220
|
+
// caller.
|
|
221
|
+
setupProjectWindow: async (opened, project) => {
|
|
222
|
+
await config.setupProjectWindow?.(instance, {
|
|
223
|
+
path: project.path,
|
|
224
|
+
name: project.name,
|
|
225
|
+
window: opened.window,
|
|
226
|
+
context: opened.context,
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
setupWindowModules: (ctx) => {
|
|
230
|
+
setupWindowModules(config, ctx);
|
|
231
|
+
uiExtensions.attachTo(ctx);
|
|
232
|
+
},
|
|
233
|
+
// MCP's CDP connections are aimed at the active project window and have to
|
|
234
|
+
// follow it when the user moves.
|
|
235
|
+
onActiveContextChanged: noteActiveMcpWindowChanged,
|
|
236
|
+
onBeforeClose: async (closing, project) => {
|
|
237
|
+
try {
|
|
238
|
+
await config.onBeforeClose?.(instance, {
|
|
239
|
+
path: project.path,
|
|
240
|
+
name: project.name,
|
|
241
|
+
window: closing.window,
|
|
242
|
+
context: closing.context,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
// The host gets to react to the close, not to cancel it: revealing the
|
|
247
|
+
// list window below is what keeps something on screen, and it must not
|
|
248
|
+
// be skipped because host code threw.
|
|
249
|
+
console.error('[workbench] host onBeforeClose hook failed:', err);
|
|
250
|
+
}
|
|
251
|
+
// The closing window keeps its place in the manager's list until its
|
|
252
|
+
// teardown finishes, so "the last project" means no OTHER window is
|
|
253
|
+
// open. The list window may be hidden behind it (see the close handler
|
|
254
|
+
// below) — leaving it hidden strands the user with a running app and
|
|
255
|
+
// nothing on screen.
|
|
256
|
+
if (workbenchWindows.list().every((pw) => pw === closing))
|
|
257
|
+
projectList.revealProjectList();
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
// Menu, automation and MCP drive whatever project the user is working in, so
|
|
261
|
+
// they read the active WORKBENCH context, resolved per call — never the list
|
|
262
|
+
// window, which owns no session. With no project open they fall back to the
|
|
263
|
+
// launcher context, whose `hasActiveSession()` is false, while MCP's CDP
|
|
264
|
+
// targets and native-host state live in the window itself: null instead.
|
|
265
|
+
const activeProjectContext = () => workbenchWindows.activeContext() ?? context;
|
|
266
|
+
setActiveMcpWindowResolver(() => workbenchWindows.activeContext());
|
|
267
|
+
appRegistry.add(toDisposable(() => setActiveMcpWindowResolver(() => null)));
|
|
268
|
+
installMenu(config, mainWindow, activeProjectContext, projectList.revealProjectList);
|
|
269
|
+
appRegistry.add(registerOpenProjectWindowIpc(router, (project) => workbenchWindows.open(project)));
|
|
492
270
|
const instance = {
|
|
493
271
|
mainWindow,
|
|
494
272
|
context,
|
|
495
273
|
ipc: hostIpc,
|
|
274
|
+
// `awaitReady: false`: `ready` only resolves once `config.onSetup` itself
|
|
275
|
+
// settles (`markReady(setup)` below), so a host that calls
|
|
276
|
+
// `instance.openProjectWindow` FROM WITHIN its own `onSetup` would
|
|
277
|
+
// otherwise deadlock — `open()` awaiting `ready` while `ready` awaits the
|
|
278
|
+
// very `onSetup` call that is awaiting `open()`. The renderer-facing IPC
|
|
279
|
+
// path (`registerOpenProjectWindowIpc` above) keeps the default: a
|
|
280
|
+
// renderer racing ahead of `onSetup` still has to park.
|
|
281
|
+
openProjectWindow: (project) => workbenchWindows.open(project, { awaitReady: false }),
|
|
282
|
+
projectWindows: () => workbenchWindows.list(),
|
|
283
|
+
disposeViews: () => {
|
|
284
|
+
for (const ctx of router.list())
|
|
285
|
+
ctx.views.disposeAll();
|
|
286
|
+
},
|
|
496
287
|
// Return the registry wrapper, not the raw disposable: disposing the
|
|
497
288
|
// wrapper splices the registry entry out AND drives the underlying
|
|
498
289
|
// teardown, so a single dispose leaves no dead entry behind.
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
290
|
+
// App-level: the trusted-sender ledger and the simulator API map are
|
|
291
|
+
// shared by every window, so they outlive any one of them.
|
|
292
|
+
registerTrustedWindow: (win) => appRegistry.add(registerTrustedWindow(appServices, win)),
|
|
293
|
+
// Simulator APIs live in the ONE registry AppServices owns, which every
|
|
294
|
+
// window's context points at, so a single registration serves every
|
|
295
|
+
// window — including windows opened later, since the service host reads
|
|
296
|
+
// the registered names off it when it spawns. The disposer therefore
|
|
297
|
+
// belongs to the app: parking it on a window would let that window's close
|
|
298
|
+
// delete the handler out from under every other open window.
|
|
299
|
+
registerSimulatorApi: (name, handler) => appRegistry.add(toDisposable(appServices.simulatorApis.register(name, handler))),
|
|
300
|
+
registerSimulatorUiExtension: (registration) => uiExtensions.register(registration),
|
|
301
|
+
dispose: async () => {
|
|
302
|
+
await workbenchWindows.disposeAll();
|
|
303
|
+
await launcher.dispose();
|
|
304
|
+
await appRegistry.dispose();
|
|
508
305
|
},
|
|
509
|
-
dispose: () => disposeContext(context),
|
|
510
306
|
};
|
|
511
307
|
onInstanceCreated?.(instance);
|
|
512
308
|
// Built-in simulator APIs: devtools-supplied wx.* implementations that run
|
|
@@ -515,50 +311,17 @@ onInstanceCreated) {
|
|
|
515
311
|
instance.registerSimulatorApi('login', async () => {
|
|
516
312
|
return 'hello';
|
|
517
313
|
});
|
|
518
|
-
|
|
519
|
-
//
|
|
520
|
-
// `
|
|
521
|
-
//
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const category = payload?.category;
|
|
526
|
-
if (category !== 'miniprogram' && category !== 'minigame')
|
|
527
|
-
return;
|
|
528
|
-
context.notify.hostSidebarCategorySelected(category);
|
|
529
|
-
}));
|
|
530
|
-
context.views.hostSidebar
|
|
531
|
-
.loadFile(path.join(rendererDir, 'entries/host-sidebar-default/index.html'))
|
|
532
|
-
.catch((err) => {
|
|
533
|
-
console.warn('[workbench] failed to load host-sidebar default content:', err);
|
|
534
|
-
});
|
|
535
|
-
// Seed the notified width with the rail's own known intrinsic width
|
|
536
|
-
// (`HOST_SIDEBAR_DEFAULT_WIDTH`) instead of leaving it at 0 until the
|
|
537
|
-
// rail's in-page advertiser reports back. That report can never arrive on
|
|
538
|
-
// its own: the placeholder above only becomes "desired visible" once a
|
|
539
|
-
// nonzero width is known (project-list-screen.tsx), attach only happens
|
|
540
|
-
// for a "desired visible" view (placement-reconciler.ts), and the rail's
|
|
541
|
-
// advertiser (view-anchor's measure-loop.ts) only ever fires via
|
|
542
|
-
// `requestAnimationFrame`, which Chromium never schedules for a
|
|
543
|
-
// WebContentsView that was never attached — a structural deadlock, not a
|
|
544
|
-
// slow-to-resolve race. `setWidthMode({fixed})` pushes the seed
|
|
545
|
-
// synchronously (no attach/rAF dependency) and, critically, retains it in
|
|
546
|
-
// `getHostSidebarWidth()` too, so a renderer that mounts and pulls before
|
|
547
|
-
// any push replays the real seed instead of a stale 0. Flipping back to
|
|
548
|
-
// 'auto' immediately after, before anything has attached or advertised, is
|
|
549
|
-
// a same-tick no-op past the seed (`setExtentMode`'s reapply branch only
|
|
550
|
-
// fires once something has actually been advertised) — it leaves the slot
|
|
551
|
-
// in 'auto' for whichever content ends up loaded (this rail's own later
|
|
552
|
-
// report, or a downstream replacement's), not pinned.
|
|
553
|
-
context.views.hostSidebar.setWidthMode({ fixed: HOST_SIDEBAR_DEFAULT_WIDTH });
|
|
554
|
-
context.views.hostSidebar.setWidthMode('auto');
|
|
555
|
-
if (config.onSetup) {
|
|
556
|
-
await config.onSetup(instance);
|
|
557
|
-
}
|
|
314
|
+
installHostSidebarDefault(context, rendererDir);
|
|
315
|
+
// Run inside an async IIFE so a hook that throws SYNCHRONOUSLY still settles
|
|
316
|
+
// `ready` — leaving it pending would park every queued open forever, and
|
|
317
|
+
// `disposeAll()` waits on those queues.
|
|
318
|
+
const setup = (async () => { await config.onSetup?.(instance); })();
|
|
319
|
+
markReady(setup);
|
|
320
|
+
await setup;
|
|
558
321
|
if (config.updateChecker) {
|
|
559
322
|
instance.updateManager = new UpdateManager({
|
|
560
323
|
checker: config.updateChecker,
|
|
561
|
-
showUpdatePanel: (info) =>
|
|
324
|
+
showUpdatePanel: (info) => projectList.showUpdate(info),
|
|
562
325
|
notifyDownloadProgress: (percent) => context.views.notifyUpdateDownloadProgress(percent),
|
|
563
326
|
hideUpdatePanel: () => context.views.hideUpdateDialog(),
|
|
564
327
|
senderPolicy: context.senderPolicy,
|
|
@@ -566,219 +329,60 @@ onInstanceCreated) {
|
|
|
566
329
|
initialDelay: config.updateOptions?.initialDelay,
|
|
567
330
|
getCurrentVersion: config.updateOptions?.getCurrentVersion,
|
|
568
331
|
});
|
|
569
|
-
|
|
332
|
+
appRegistry.add(() => instance.updateManager.dispose());
|
|
570
333
|
}
|
|
571
|
-
await setupAutomation(instance);
|
|
572
|
-
|
|
334
|
+
const automation = await setupAutomation(instance, activeProjectContext, router);
|
|
335
|
+
if (automation)
|
|
336
|
+
appRegistry.add(automation);
|
|
337
|
+
const mcp = setupMcp(createTargetForMcp({
|
|
338
|
+
list: () => workbenchWindows.list(),
|
|
339
|
+
activeContext: activeProjectContext,
|
|
340
|
+
// `close()` (not `destroy()`) so the window's own close handling runs the
|
|
341
|
+
// same teardown a user-driven close does.
|
|
342
|
+
close: (window) => { if (!window.isDestroyed())
|
|
343
|
+
window.close(); },
|
|
344
|
+
}), createOpenForMcp(workbenchWindows));
|
|
573
345
|
if (mcp)
|
|
574
|
-
|
|
575
|
-
//
|
|
576
|
-
//
|
|
577
|
-
//
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
const overview = {
|
|
595
|
-
currentRoute: top?.pagePath ?? null,
|
|
596
|
-
pageStackDepth: stack.length,
|
|
597
|
-
storageKeys: [],
|
|
598
|
-
storageCount: 0,
|
|
599
|
-
appDataKeys: [],
|
|
600
|
-
};
|
|
601
|
-
if (appId) {
|
|
602
|
-
const storage = await resolveNativeStorageOverview(context, appId);
|
|
603
|
-
overview.storageKeys = storage.storageKeys;
|
|
604
|
-
overview.storageCount = storage.storageCount;
|
|
605
|
-
overview.appDataKeys = resolveNativeAppDataKeys(context, appId);
|
|
346
|
+
appRegistry.add(mcp);
|
|
347
|
+
// The list window opens no project, so its close needs none of the workbench
|
|
348
|
+
// teardown: just unregister its context, or a recycled `webContents.id`
|
|
349
|
+
// could later resolve to a destroyed window.
|
|
350
|
+
context.registry.add(wireMainWindowEvents(mainWindow, {
|
|
351
|
+
context,
|
|
352
|
+
onResize: () => context.views.repositionAll(),
|
|
353
|
+
onClose: (e) => {
|
|
354
|
+
if (isAppQuitting())
|
|
355
|
+
return;
|
|
356
|
+
// With projects still open, this window must survive its own close
|
|
357
|
+
// button: it is the only way back to the project list, and the
|
|
358
|
+
// app-level IPC it registered cannot be registered again on a
|
|
359
|
+
// replacement window. Hide it — every path back calls
|
|
360
|
+
// `projectList.revealProjectList()`, including the last project window
|
|
361
|
+
// closing.
|
|
362
|
+
if (workbenchWindows.list().length > 0) {
|
|
363
|
+
e.preventDefault();
|
|
364
|
+
mainWindow.hide();
|
|
365
|
+
return;
|
|
606
366
|
}
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
const off = context.bridge.onRenderEvent((ev) => setActiveBridgeId(ev.bridgeId));
|
|
610
|
-
context.registry.add(off);
|
|
611
|
-
context.registry.add(() => setNativeHost(false));
|
|
612
|
-
context.registry.add(() => setNativeOverviewProvider(null));
|
|
613
|
-
}
|
|
614
|
-
// Native-host inspector: injects the render-guest IIFE and drives WXML /
|
|
615
|
-
// element-highlight against the active render-host <webview>. Reused by
|
|
616
|
-
// the storage panel (element inspect) and the WXML panel service.
|
|
617
|
-
const renderInspector = createRenderInspector({ connections: context.connections, broker: context.cdpSessionBroker });
|
|
618
|
-
const storage = setupSimulatorStorage(mainWindow.webContents, {
|
|
619
|
-
senderPolicy: context.senderPolicy,
|
|
620
|
-
connections: context.connections,
|
|
621
|
-
broker: context.cdpSessionBroker,
|
|
622
|
-
// Per-project filter for the simulator-storage panel: the simulator
|
|
623
|
-
// uses a fixed `persist:simulator` partition + a fixed simulator.html
|
|
624
|
-
// origin, so localStorage is shared across every project that has
|
|
625
|
-
// ever opened. The dimina runtime isolates writes with `${appId}_`
|
|
626
|
-
// prefixes; this callback lets the storage panel filter the CDP
|
|
627
|
-
// snapshot/event stream to the active appId.
|
|
628
|
-
getActiveAppId,
|
|
629
|
-
// Native-host: route element inspection to the active render guest, and
|
|
630
|
-
// read/write storage from the service-host window's file:// store.
|
|
631
|
-
bridge: context.bridge,
|
|
632
|
-
renderInspector,
|
|
633
|
-
});
|
|
634
|
-
context.registry.add(storage);
|
|
635
|
-
// Native-host: expose the async-storage runtime hook so bridge-router
|
|
636
|
-
// routes async wx.setStorage/etc. to the unified service-host store.
|
|
637
|
-
if (storage.storageApi) {
|
|
638
|
-
context.storageApi = storage.storageApi;
|
|
639
|
-
context.registry.add(() => { context.storageApi = undefined; });
|
|
640
|
-
// SYNC wx storage writes bypass main (they hit the service-host localStorage
|
|
641
|
-
// directly); the service-host posts `storageChanged` and bridge-router routes
|
|
642
|
-
// it here so the Storage panel stays live without a manual reload.
|
|
643
|
-
context.onServiceStorageChanged = storage.onSyncStorageChange;
|
|
644
|
-
context.registry.add(() => { context.onServiceStorageChanged = undefined; });
|
|
645
|
-
}
|
|
646
|
-
// Native-host WXML + AppData panels: main sources the data (WXML pulled
|
|
647
|
-
// from the active render guest; AppData tapped from the service→render
|
|
648
|
-
// setData stream in bridge-router) and pushes it to the renderer. Inert on
|
|
649
|
-
// the default dimina-fe path (which sources both from the simulator
|
|
650
|
-
// miniappSnapshot transport), so only wire them when native-host is on.
|
|
651
|
-
if (context.bridge?.isNativeHost()) {
|
|
652
|
-
// Native-host: surface the simulator WCV's network (wx.request/download/
|
|
653
|
-
// upload run there, not in the service host) in the embedded DevTools by
|
|
654
|
-
// injecting its raw Network.* CDP events into the DevTools front-end so the
|
|
655
|
-
// native Network tab renders them (service-host console line is the
|
|
656
|
-
// fallback). The ViewManager calls the forwarder's attachSimulator +
|
|
657
|
-
// setDevtoolsHost from attachNativeSimulator once the simulator WCV +
|
|
658
|
-
// DevTools host exist; getServiceWc here is the fallback sink target.
|
|
659
|
-
const networkForward = createNetworkForwarder({
|
|
660
|
-
getServiceWc: (appId) => context.bridge?.getServiceWc(appId) ?? null,
|
|
661
|
-
getResourceServerBaseUrl: () => context.bridge?.getResourceBaseUrl?.() ?? null,
|
|
662
|
-
// The simulator shell's own static-asset server (serves simulator.html
|
|
663
|
-
// + its JS/CSS, independent from the resource server above — see
|
|
664
|
-
// NetworkForwarderBridge.getSimulatorServerBaseUrl's doc). Host is
|
|
665
|
-
// always 'localhost' — see shared/simulator-route.ts's
|
|
666
|
-
// buildSimulatorUrlFromSpec default. Absent (null port) when no
|
|
667
|
-
// project is open.
|
|
668
|
-
getSimulatorServerBaseUrl: () => {
|
|
669
|
-
const port = context.workspace?.getSession()?.port;
|
|
670
|
-
return typeof port === 'number' ? `http://localhost:${port}/` : null;
|
|
671
|
-
},
|
|
672
|
-
connections: context.connections,
|
|
673
|
-
broker: context.cdpSessionBroker,
|
|
674
|
-
});
|
|
675
|
-
context.networkForward = networkForward;
|
|
676
|
-
context.registry.add(networkForward);
|
|
677
|
-
context.registry.add(() => { context.networkForward = undefined; });
|
|
678
|
-
// Global mirror: once the standalone internal
|
|
679
|
-
// DevTools window builds its own front-end host, mirror the full
|
|
680
|
-
// unfiltered Network stream into it. Attached AFTER context.networkForward
|
|
681
|
-
// is assigned above — the callback re-reads the mutable field on every
|
|
682
|
-
// fire, so ordering only matters for readability here, not correctness.
|
|
683
|
-
context.registry.add(toDisposable(context.internalDevtoolsWindow?.onHostChanged((hostWc) => {
|
|
684
|
-
context.networkForward?.setGlobalDevtoolsHost(hostWc);
|
|
685
|
-
}) ?? (() => { })));
|
|
686
|
-
// Main-process WebSocket traffic (wx.connectSocket runs on the Node `ws`
|
|
687
|
-
// transport, invisible to any webContents debugger): bridge-router fans
|
|
688
|
-
// the trace stream out here, and the forwarder synthesizes it into
|
|
689
|
-
// Network.webSocket* CDP events for the same Network panel sinks.
|
|
690
|
-
context.registry.add(toDisposable(context.bridge?.onNativeWebSocketTrace?.((ownerId, event) => {
|
|
691
|
-
context.networkForward?.reportWebSocketTrace(ownerId, event);
|
|
692
|
-
}) ?? (() => { })));
|
|
693
|
-
context.registry.add(setupSimulatorWxml(mainWindow.webContents, {
|
|
694
|
-
senderPolicy: context.senderPolicy,
|
|
695
|
-
bridge: context.bridge,
|
|
696
|
-
inspector: renderInspector,
|
|
697
|
-
getActiveAppId,
|
|
698
|
-
}));
|
|
699
|
-
const appDataService = setupSimulatorAppData(mainWindow.webContents, {
|
|
700
|
-
senderPolicy: context.senderPolicy,
|
|
701
|
-
getActiveAppId,
|
|
702
|
-
// AppData-panel edit write-back target: the service-host window owning
|
|
703
|
-
// the edited page bridge.
|
|
704
|
-
bridge: context.bridge,
|
|
705
|
-
});
|
|
706
|
-
// bridge-router feeds this via ctx.appData (service→render tap + evict).
|
|
707
|
-
context.appData = appDataService;
|
|
708
|
-
context.registry.add(appDataService);
|
|
709
|
-
context.registry.add(() => { context.appData = undefined; });
|
|
710
|
-
// Push the visible page route to the toolbar on every navigation (the
|
|
711
|
-
// page stack lives in the DeviceShell WCV, invisible to renderer
|
|
712
|
-
// <webview> nav events).
|
|
713
|
-
context.registry.add(setupSimulatorCurrentPage(mainWindow.webContents, {
|
|
714
|
-
bridge: context.bridge,
|
|
715
|
-
}));
|
|
716
|
-
}
|
|
717
|
-
// Embedded workbench editor — the sole devtools editor. Stand up the COI
|
|
718
|
-
// http server that serves the workbench bundle with the SharedArrayBuffer
|
|
719
|
-
// isolation headers and bridges `/__fs/*` onto the active project, then hand its
|
|
720
|
-
// base URL to the view manager so the 'editor' dock slot mounts the workbench
|
|
721
|
-
// WebContentsView. Both the server and the WCV tear down with the context
|
|
722
|
-
// registry.
|
|
723
|
-
//
|
|
724
|
-
// Default the bundle dir to the devtools package's OWN `dist/vscode-workbench`
|
|
725
|
-
// (resolved from the package root), NOT relative to the caller's rendererDir:
|
|
726
|
-
// a host that overrides `rendererDir` but omits `editorViewConfig.bundleDir`
|
|
727
|
-
// would otherwise compute a path next to ITS renderer, where no workbench
|
|
728
|
-
// bundle exists → 404 / blank editor.
|
|
729
|
-
const bundleDir = config.editorViewConfig?.bundleDir ?? path.join(devtoolsPackageRoot, 'dist/vscode-workbench');
|
|
730
|
-
// Skip the entire editor assembly when the bundle is missing. Starting the COI
|
|
731
|
-
// server and attaching the WCV against a non-existent bundle yields a silent
|
|
732
|
-
// blank editor (the WCV loads index.html → 404); a launchable app with no
|
|
733
|
-
// editor is strictly better. The view manager never gets a source, so the
|
|
734
|
-
// 'editor' slot's lazy attach is a no-op.
|
|
735
|
-
if (!fs.existsSync(path.join(bundleDir, 'index.html'))) {
|
|
736
|
-
console.warn(`[workbench] editor bundle not found at ${bundleDir} — skipping embedded editor assembly`);
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
const coiServer = await startWorkbenchCoiServer({
|
|
740
|
-
rootDir: bundleDir,
|
|
741
|
-
getProjectRoot: () => context.workspace.getProjectPath(),
|
|
742
|
-
extensionsDir: config.editorViewConfig?.extensionsDir,
|
|
743
|
-
getFileTypes: () => context.fileTypes,
|
|
744
|
-
// Names the editor's VS Code workspace after the active miniapp, so each
|
|
745
|
-
// project gets its own open-editors/view-state bucket instead of all of
|
|
746
|
-
// them sharing the one derived from the constant mirror root.
|
|
747
|
-
getProjectIdentity: () => ({
|
|
748
|
-
appId: getActiveAppId(),
|
|
749
|
-
projectPath: context.workspace.getProjectPath(),
|
|
750
|
-
}),
|
|
751
|
-
});
|
|
752
|
-
// Return the close promise so the registry awaits server shutdown on dispose
|
|
753
|
-
// instead of fire-and-forgetting it (a dangling http server would keep the
|
|
754
|
-
// port + event loop alive past teardown).
|
|
755
|
-
context.registry.add(() => coiServer.close());
|
|
756
|
-
// The registry disposes LIFO, so this runs BEFORE the context-level
|
|
757
|
-
// disposeAll: void any in-flight open's attach hold here too, or a stale
|
|
758
|
-
// release / cap firing could rebuild the workbench during the awaited
|
|
759
|
-
// coiServer.close() above, before disposeAll's own cancel runs.
|
|
760
|
-
context.registry.add(() => {
|
|
761
|
-
context.views.cancelWorkbenchAttachHold();
|
|
762
|
-
context.views.detachWorkbench();
|
|
763
|
-
});
|
|
764
|
-
// Only HAND the view manager the COI URL — do NOT load yet. The heavy
|
|
765
|
-
// WebContentsView load (10MB bundle + ext-host) is deferred to the first time
|
|
766
|
-
// the 'editor' dock slot becomes visible (first non-zero bounds), so it never
|
|
767
|
-
// sits on the app boot critical path. Loading it eagerly here delayed
|
|
768
|
-
// preload/window-ready enough to trip the e2e launch health check.
|
|
769
|
-
context.views.setWorkbenchSource(coiServer.baseUrl);
|
|
770
|
-
}
|
|
771
|
-
// Main's mirror of the renderer's top-level screen. The renderer's `page`
|
|
772
|
-
// state is the single authority; it pushes every change over
|
|
773
|
-
// WindowChannel.ScreenState. Main reads this mirror in the window-close
|
|
774
|
-
// decision so a close while the renderer is parked on a project screen (even
|
|
775
|
-
// a failed open with no session) returns to the list instead of quitting.
|
|
776
|
-
let rendererScreen = 'list';
|
|
777
|
-
context.registry.add(new IpcRegistry(context.senderPolicy).handle(WindowChannel.ScreenState, (_e, screen) => {
|
|
778
|
-
rendererScreen = screen === 'project' ? 'project' : 'list';
|
|
367
|
+
void launcher.dispose();
|
|
368
|
+
},
|
|
779
369
|
}));
|
|
780
|
-
|
|
781
|
-
|
|
370
|
+
// macOS keeps the app running with no window on screen. Clicking the dock
|
|
371
|
+
// icon then has to bring something back, and the project list is the app's
|
|
372
|
+
// home — a workbench window only exists for a project the user already
|
|
373
|
+
// chose.
|
|
374
|
+
const onActivate = () => {
|
|
375
|
+
if (mainWindow.isDestroyed())
|
|
376
|
+
return;
|
|
377
|
+
if (mainWindow.isVisible())
|
|
378
|
+
return;
|
|
379
|
+
if (workbenchWindows.list().some((pw) => !pw.window.isDestroyed() && pw.window.isVisible()))
|
|
380
|
+
return;
|
|
381
|
+
projectList.revealProjectList();
|
|
382
|
+
};
|
|
383
|
+
app.on('activate', onActivate);
|
|
384
|
+
appRegistry.add(toDisposable(() => { app.removeListener('activate', onActivate); }));
|
|
385
|
+
appRegistry.add(enableDevRendererAutoReload(rendererDir));
|
|
782
386
|
return instance;
|
|
783
387
|
}
|
|
784
388
|
//# sourceMappingURL=app.js.map
|