@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/shared/constants.ts", "../../../src/preload/runtime/host-sidebar-advertiser.ts", "../../../../view-anchor/src/measure-loop.ts", "../../../../view-anchor/src/size-advertiser.ts", "../../../src/shared/ipc-channels-overlays.ts", "../../../src/preload/runtime/wait-for-slot-root.ts", "../../../src/preload/runtime/host-slot-port-bridge.ts", "../../../src/preload/runtime/host-sidebar-port.ts", "../../../src/preload/runtime/host-sidebar-runtime.ts", "../../../src/preload/windows/host-sidebar-runtime.ts"],
|
|
4
|
-
"sourcesContent": ["/** Shared cross-process constants for dimina-devtools. */\n\n/** Default Chrome DevTools Protocol (CDP) remote debugging port. */\nexport const DEFAULT_CDP_PORT = 9222\n\n/** Default scene value for mini-app launch. */\nexport const DEFAULT_SCENE = 1001\n\n/**\n * Fixed devtools toolbar header height (px). Single source of truth shared by\n * the main process (overlay view layout) and the renderer (toolbar/popover\n * layout). Not host-configurable \u2014 the deprecated\n * `WorkbenchAppConfig.headerHeight` is ignored; hosts that need their own\n * toolbar use the host toolbar WCV instead.\n */\nexport const HEADER_H = 40\n\n/**\n * Process-level argv marker injected (via `webPreferences.additionalArguments`)\n * into the host-toolbar WebContentsView. The session-registered toolbar-runtime\n * preload (`src/preload/runtime/host-toolbar-runtime.ts`) executes in EVERY\n * defaultSession renderer; its guard activates the height advertiser only when\n * `process.argv` carries this marker AND `process.isMainFrame` is true (the\n * marker is process-level, so subframes of the toolbar window see it too \u2014\n * both guard wings are required).\n */\nexport const HOST_TOOLBAR_RUNTIME_MARKER = '--dimina-host-toolbar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-sidebar WebContentsView (`src/preload/runtime/host-sidebar-runtime.ts`).\n */\nexport const HOST_SIDEBAR_RUNTIME_MARKER = '--dimina-host-sidebar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-dialog WebContentsView (`src/preload/runtime/host-dialog-runtime.ts`).\n */\nexport const HOST_DIALOG_RUNTIME_MARKER = '--dimina-host-dialog'\n\n/**\n * Intrinsic width (px) of devtools' own default host-sidebar content (the\n * project-category icon rail). Single source of truth shared by the renderer\n * (`host-sidebar-default.tsx`, which gives its `[data-host-sidebar-root]` this\n * exact width so the width advertiser reports it) and anything in main that\n * needs to reason about the default rail's size. The slot is NOT left pinned\n * at this width: main pushes it once as a seed and immediately returns the\n * slot to 'auto' (see `createDevtoolsRuntime`), so the rail \u2014 and any\n * downstream `loadURL`/`loadFile` content that replaces it \u2014 advertises its\n * own width from then on, exactly like any other host-sidebar content.\n */\nexport const HOST_SIDEBAR_DEFAULT_WIDTH = 56\n", "/**\n * Reverse size-advertiser preload for the host-controllable sidebar\n * WebContentsView. Runs in that WCV's OWN renderer (the sidebar content's\n * preload), measures the sidebar's intrinsic inline (width) extent and sends it\n * to the main process. Main pushes the value back to the main-window renderer as\n * `HostSidebarWidthChanged`, which resizes the sidebar placeholder, re-measures\n * the forward anchor, and re-overlays this WCV \u2014 closing the dynamic-width loop\n * (see `@dimina-kit/view-anchor` `createSizeAdvertiser`).\n */\n\nimport { ipcRenderer } from 'electron'\nimport { createSizeAdvertiser } from '@dimina-kit/view-anchor'\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { whenSlotRootReady } from './wait-for-slot-root.js'\n\n/**\n * Attach the advertiser to the sidebar content's shrink-to-fit root\n * (`[data-host-sidebar-root]`), waiting for it if necessary \u2014 the root may be\n * mounted asynchronously by a framework (e.g. React) rather than present in\n * the initial HTML. That element MUST be shrink-to-fit on the inline axis \u2014\n * its width must reflect the content, not the host-applied view width, or the\n * cross-process loop never converges (`createSizeAdvertiser`'s footgun).\n */\nexport function installHostSidebarAdvertiserWhenReady(): void {\n whenSlotRootReady(\n '[data-host-sidebar-root]',\n '[host-sidebar-advertiser] no `[data-host-sidebar-root]` element found \u2014 ' +\n 'not advertising a width yet. The sidebar content must wrap itself in a ' +\n 'shrink-to-fit `[data-host-sidebar-root]` element so its inline size is ' +\n 'the content width, not the host-given view size.',\n (root) => {\n createSizeAdvertiser(root, {\n axis: 'inline',\n publish: (size) => {\n ipcRenderer.send(ViewChannel.HostSidebarAdvertiseWidth, size)\n },\n })\n },\n )\n}\n", "/**\n * Internal \u2014 the RAF-coalesced measure/dedupe/dispose engine behind the REVERSE\n * primitive `createSizeAdvertiser`.\n *\n * The forward `createViewAnchor` deliberately does NOT use this: it publishes\n * SYNCHRONOUSLY (a native overlay's `setBounds` already lands a cross-process\n * frame late, and a RAF stacked a second frame of visible trailing). The\n * reverse direction is different \u2014 it is a cross-process FEEDBACK loop\n * (advertise \u2192 host resizes the view \u2192 content re-measures \u2192 re-advertise), so\n * the RAF's one-publish-per-frame coalescing is a useful damper. The two\n * directions thus have different optimal emit timing; this engine serves only\n * the reverse.\n *\n * NOT exported from the package: it is pure mechanism with no knowledge of\n * direction, the DOM, `ResizeObserver`, or the structure of the value `T` it\n * carries. The wrapping primitive injects `produce` / `same` / `sink` and\n * drives the lifecycle.\n *\n * - `schedule()` \u2014 coalesce a burst of triggers into ONE RAF; the frame\n * body re-`produce()`s, dedupes against the last emit (`same`), and `sink`s.\n * Bails if inactive or disposed (stale-RAF safe).\n * - `emitNow(v)` \u2014 explicit synchronous emit (create / update path). Always\n * fires, bypassing the dedupe check, and refreshes the dedupe baseline.\n * - `setActive` \u2014 gate the observer stream; a queued frame bails on `!active`.\n * - `cancel` \u2014 drop any in-flight RAF.\n * - `dispose` \u2014 cancel + go inert; after dispose nothing emits again.\n */\nexport interface MeasureLoop<T> {\n schedule(): void\n emitNow(value: T): void\n setActive(on: boolean): void\n cancel(): void\n dispose(): void\n}\n\nexport function createMeasureLoop<T>(cfg: {\n /** Produce the value to emit in the RAF body. Return `null` to decline the\n * frame entirely (no dedupe, no sink, baseline untouched) \u2014 e.g. a\n * non-finite or unavailable measurement. */\n produce: () => T | null\n same: (a: T, b: T) => boolean\n sink: (value: T) => void\n}): MeasureLoop<T> {\n const { produce, same, sink } = cfg\n let rafId: number | null = null\n let active = false\n let disposed = false\n let last: T | null = null\n\n const cancel = (): void => {\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n rafId = null\n }\n }\n\n return {\n schedule(): void {\n if (disposed || !active || rafId !== null) return\n rafId = requestAnimationFrame(() => {\n rafId = null\n if (disposed || !active) return\n const value = produce()\n if (value === null) return // producer declined this frame\n // last-value dedupe: a frame whose produced value equals the last one\n // we emitted costs nothing (no IPC / setBounds).\n if (last !== null && same(value, last)) return\n last = value\n sink(value)\n })\n },\n emitNow(value: T): void {\n if (disposed) return\n last = value\n sink(value)\n },\n setActive(on: boolean): void {\n active = on\n },\n cancel,\n dispose(): void {\n if (disposed) return\n disposed = true\n cancel()\n },\n }\n}\n", "import type {\n AdvertisedSize,\n SizeAdvertiserOptions,\n SizeAdvertiserHandle,\n} from './types.js'\nimport { createMeasureLoop } from './measure-loop.js'\n\n/**\n * Reverse of `createViewAnchor`: runs in a downstream WebContentsView's own\n * renderer, measures the content's own size on ONE owned axis (from the\n * `ResizeObserver` border-box \u2014 no `getBoundingClientRect`, no forced reflow),\n * and advertises it via the injected `publish`. Shares the forward primitive's\n * measure/coalesce/dedupe/dispose engine (`createMeasureLoop`).\n *\n * The extent is `Math.round`ed and clamped to `>= 0`; non-finite measurements\n * drop the frame.\n *\n * FOOTGUN \u2014 `target` must be shrink-to-fit on the owned axis: its owned-axis\n * size must NOT be driven by the host-applied view size, or the cross-process\n * loop (advertise \u2192 host resizes view \u2192 remeasure) never converges (it\n * oscillates or stays \"stable but wrong\"). Measuring `<body>`/`<html>` is the\n * classic mistake \u2014 their size *is* the view size. See\n * `docs/bidirectional-design.md`'s single-axis-ownership and trust-boundary\n * sections.\n */\nexport function createSizeAdvertiser(\n target: HTMLElement,\n opts: SizeAdvertiserOptions,\n): SizeAdvertiserHandle {\n const axis = opts.axis // immutable for the advertiser's life\n let publish = opts.publish\n let observer: ResizeObserver | null = null\n let disposed = false\n // Latest border-box, stashed by the RO callback and read by `produce` in the\n // RAF body (keep the entry out of the shared, DOM-agnostic loop).\n let latest: ResizeObserverSize | null = null\n\n const produce = (): AdvertisedSize | null => {\n if (!latest) return null\n const raw = axis === 'block' ? latest.blockSize : latest.inlineSize\n if (!Number.isFinite(raw)) return null\n return { axis, extent: Math.max(0, Math.round(raw)) }\n }\n\n const loop = createMeasureLoop<AdvertisedSize>({\n produce,\n same: (a, b) => a.extent === b.extent, // axis is constant\n sink: (size) => publish(size),\n })\n\n const onResize: ResizeObserverCallback = (entries) => {\n const entry = entries[entries.length - 1]\n if (entry) {\n latest = entry.borderBoxSize?.[0] ?? entry.contentBoxSize?.[0] ?? latest\n }\n loop.schedule()\n }\n\n // One cheap, once-per-advertiser guard for the textbook feedback-loop footgun.\n const doc = target.ownerDocument\n if (target === doc.body || target === doc.documentElement) {\n console.warn(\n `[view-anchor] size-advertiser: <${target === doc.body ? 'body' : 'html'}>'s ` +\n `${axis} size is the host-given view size, not the content size \u2014 the ` +\n `advertiser will never shrink to content. Measure a shrink-to-fit wrapper. ` +\n `See bidirectional-design.md's single-axis-ownership section.`,\n )\n }\n\n loop.setActive(true)\n observer = new ResizeObserver(onResize)\n observer.observe(target)\n\n return {\n update(nextPublish: (size: AdvertisedSize) => void): void {\n if (disposed) return\n publish = nextPublish\n // Re-advertise the current size to the new sink immediately (mirrors the\n // forward anchor's re-publish on update) so the new channel is not left\n // sizeless until the next ResizeObserver tick.\n const cur = produce()\n if (cur) loop.emitNow(cur)\n },\n dispose(): void {\n if (disposed) return\n disposed = true\n loop.cancel()\n if (observer) {\n observer.disconnect()\n observer = null\n }\n loop.dispose()\n },\n }\n}\n", "/**\n * IPC channel name constants for dimina-devtools' overlay/panel\n * WebContentsViews \u2014 layout slots (host-toolbar/host-sidebar/host-dialog),\n * the popover, tooltip, project-create dialog and settings overlays, plus\n * the update flow. Split out of `ipc-channels.ts` (see that file's header)\n * once this group alone pushed it past the repo's file-length threshold \u2014\n * these constants share no runtime dependency on the rest of that file, so\n * the split is a pure line-count relief valve, not a behavior change.\n */\n\n// \u2500\u2500 Embedded views (renderer \u2192 main) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The main window's React layout owns the *positions* of the editor +\n// simulator-DevTools WebContentsView overlays \u2014 each visible placeholder\n// `<div>` measures its client rect via ResizeObserver and pushes the\n// rectangle to the main process. The view manager caches the latest\n// payload per kind and applies it to the overlay; no payload means the\n// overlay is hidden.\n//\n// Payload (after schema validation): `{ x, y, width, height }` in CSS\n// pixels relative to the window's content area (origin = top-left,\n// not including the OS chrome).\nexport const ViewChannel = {\n /**\n * Reverse size-advertiser: the host-toolbar WCV's OWN renderer advertises its\n * intrinsic content height so main reserves exactly that much. Payload\n * `{ axis: 'block', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostToolbarAdvertiseHeight: 'view:host-toolbar:advertise-height',\n /**\n * main \u2192 host-toolbar WCV renderer: per-load MessagePort handshake for the\n * gated narrow channel. On every toolbar `did-finish-load` main creates a\n * `MessageChannelMain` and transfers port2 here\n * (`wc.postMessage(HostToolbarPort, null, [port2])`); the session-resident\n * toolbar runtime preload receives it via `event.ports[0]` and bridges it to\n * the page as `window.diminaHostToolbar`. Envelope both directions:\n * `{ channel: string, payload: unknown }`.\n */\n HostToolbarPort: 'view:host-toolbar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-toolbar height so the\n * renderer placeholder div resizes (closing the dynamic-height loop).\n */\n HostToolbarHeightChanged: 'view:host-toolbar:height-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED toolbar\n * height retained in main. Mount-time replay companion to\n * `HostToolbarHeightChanged`: the push listener mounts with the project\n * view and the toolbar's size-advertiser deduplicates (never re-reports),\n * so a height pushed while no project view was mounted would otherwise be\n * lost forever (cold start on the project list races it; close-project \u2192\n * reopen hits it deterministically). No payload; resolves a number.\n */\n HostToolbarGetHeight: 'view:host-toolbar:get-height',\n /**\n * Reverse size-advertiser for the host-sidebar WCV, mirroring\n * `HostToolbarAdvertiseHeight` on the inline (width) axis instead of block.\n * Payload `{ axis: 'inline', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostSidebarAdvertiseWidth: 'view:host-sidebar:advertise-width',\n /**\n * main \u2192 host-sidebar WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostSidebarPort: 'view:host-sidebar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-sidebar width so the\n * renderer placeholder div resizes, mirroring `HostToolbarHeightChanged`.\n */\n HostSidebarWidthChanged: 'view:host-sidebar:width-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED sidebar\n * width, mirroring `HostToolbarGetHeight`'s mount-time replay role.\n */\n HostSidebarGetWidth: 'view:host-sidebar:get-width',\n /**\n * main \u2192 main-window renderer: push the project category (`Project.type`)\n * selected in the host-sidebar's content \u2014 devtools' own default icon rail,\n * or any downstream replacement that sends on the same channel. No\n * pull/replay companion like `HostSidebarGetWidth`: the default category is\n * a stable initial value regardless of load-order, and nothing is ever sent\n * before the main-window renderer (and thus `ProjectListScreen`) is already\n * mounted.\n */\n HostSidebarCategorySelected: 'view:host-sidebar:category-selected',\n /**\n * Reverse size-advertiser for the host-dialog WCV. Unlike the toolbar/sidebar\n * (single-axis, `setBaseDesired`-placed strips), the dialog is a BOTH-AXES\n * overlay positioned by `setOverlayDesired` \u2014 main centers it in the main\n * window once it knows both dimensions. One channel carries either axis'\n * report (`{ axis: 'block' | 'inline', extent }`); the dialog's own content\n * advertises both from the same root (see `host-dialog-advertiser.ts`).\n * fire-and-forget (send), NOT invoke.\n *\n * No `HostDialogSizeChanged` / `HostDialogGetSize` counterparts: those exist\n * for toolbar/sidebar so a renderer-owned PLACEHOLDER can replay a size it\n * missed. The dialog has no such placeholder \u2014 it is a main-driven overlay\n * shown/hidden on demand (`hostDialog.show()`/`hide()`), so there is nothing\n * in the renderer to replay a size into.\n */\n HostDialogAdvertiseSize: 'view:host-dialog:advertise-size',\n /**\n * main \u2192 host-dialog WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostDialogPort: 'view:host-dialog:port',\n /**\n * Renderer \u2192 main: the window-level placement snapshot (one monotonic epoch\n * per commit tick, one generation per renderer lifetime) that drives the view\n * reconciler. The single source of truth for every managed native view's\n * bounds/visibility/z-order \u2014 supersedes the per-view bounds channels above.\n * invoke.\n */\n PlacementSnapshot: 'view:placement-snapshot',\n /**\n * main \u2190 main-window renderer (invoke): allocate a fresh placement\n * generation seed for this renderer bootstrap. See\n * renderer-placement-generation.ts for why the seed comes from main\n * (a wall-clock `Date.now()` seed is not guaranteed to exceed main's\n * still-standing high-water mark across two reloads that happen faster\n * than the clock's resolution). No payload; resolves a number.\n */\n AllocatePlacementGeneration: 'view:allocate-placement-generation',\n} as const\n\nexport interface ViewBounds {\n x: number\n y: number\n width: number\n height: number\n}\n\n// \u2500\u2500 Popover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const PopoverChannel = {\n Show: 'popover:show',\n Hide: 'popover:hide',\n Relaunch: 'popover:relaunch',\n Closed: 'popover:closed',\n Init: 'popover:init',\n} as const\n\n// \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.tooltip \u2014 see view-ids.ts),\n// NOT a DOM tooltip in the main renderer: any DOM-portaled floating UI (Radix\n// Tooltip) or the browser-native `title` attribute lives in the main\n// renderer's own paint surface, which every other WCV (simulator, editor,\n// settings, popover) renders on top of \u2014 CSS cannot reach across that\n// boundary. A trigger reports its anchor rect + label; main computes the\n// tooltip's screen bounds and shows/repositions/hides this overlay.\n\nexport const OverlayChannel = {\n Ready: 'overlay:ready',\n} as const\n\nexport const TooltipChannel = {\n Prepare: 'tooltip:prepare',\n Show: 'tooltip:show',\n Hide: 'tooltip:hide',\n Init: 'tooltip:init',\n Measured: 'tooltip:measured',\n} as const\n\n// \u2500\u2500 Project-create dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.dialog), NOT the Radix\n// `fixed inset-0` DOM portal it replaced \u2014 a DOM overlay paints inside the\n// main window's own renderer, so any native WebContentsView mounted on top\n// (simulator, host-toolbar, host-sidebar) occludes it. main.tsx fetches the\n// template catalog + base-dir defaults, then asks main to show this panel;\n// the panel relays submit/cancel back to main.tsx to run the existing\n// scaffold flow.\n\nexport const ProjectCreateChannel = {\n Show: 'projectCreate:show',\n Init: 'projectCreate:init',\n Submit: 'projectCreate:submit',\n Cancel: 'projectCreate:cancel',\n Submitted: 'projectCreate:submitted',\n} as const\n\n// \u2500\u2500 Embedded settings overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const SettingsChannel = {\n SetVisible: 'settings:setVisible',\n ConfigChanged: 'settings:configChanged',\n ProjectSettingsChanged: 'settings:projectSettingsChanged',\n Init: 'settings:init',\n} as const\n\n// \u2500\u2500 Updates (UpdateManager) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// String values are FROZEN: shipped builds key off them. Add new entries\n// here, never rename existing ones.\nexport const UpdateChannel = {\n Check: 'updates:check',\n Download: 'updates:download',\n Install: 'updates:install',\n DownloadProgress: 'updates:downloadProgress',\n Available: 'updates:available',\n Close: 'updates:close',\n} as const\n", "/**\n * Shared readiness wait for a host-slot content's shrink-to-fit measurement\n * root (`[data-host-*-root]`). A one-shot `document.querySelector` at\n * `DOMContentLoaded` time only works when the root is present in the initial\n * HTML. Framework-rendered content (e.g. a React app) can mount its root\n * asynchronously, after `DOMContentLoaded` has already fired \u2014 a one-shot\n * check misses it permanently and the slot's width/height is never\n * advertised. This keeps watching via `MutationObserver` instead of assuming\n * a synchronous mount, without polling on a fixed delay.\n *\n * The \"root is missing\" warning exists for downstream authors who forgot the\n * element entirely, so it must not fire on the healthy async-mount path \u2014 a\n * warning printed on every normal load is noise that trains its readers to\n * ignore it. It is therefore withheld until the document has finished loading\n * (`window.load`, a real event rather than a guessed delay) with the root\n * still absent; the observer stays armed either way, so a root that mounts\n * even later is still picked up.\n */\nexport function whenSlotRootReady(\n selector: string,\n missingWarning: string,\n install: (root: HTMLElement) => void,\n): void {\n function attempt(): void {\n const root = document.querySelector<HTMLElement>(selector)\n if (root) {\n install(root)\n return\n }\n\n let installed = false\n const observer = new MutationObserver(() => {\n const lateRoot = document.querySelector<HTMLElement>(selector)\n if (!lateRoot) return\n observer.disconnect()\n installed = true\n install(lateRoot)\n })\n observer.observe(document.documentElement, { childList: true, subtree: true })\n\n const warnIfStillMissing = (): void => {\n if (!installed) console.warn(missingWarning)\n }\n if (document.readyState === 'complete') warnIfStillMissing()\n else window.addEventListener('load', warnIfStillMissing, { once: true })\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', attempt, { once: true })\n } else {\n attempt()\n }\n}\n", "/**\n * Shared implementation behind every host-slot page bridge (toolbar/sidebar/\n * dialog): the MessagePort handshake, pending-queue-with-bounded-overflow,\n * handler registry and `{ send, onMessage }` contextBridge exposure are\n * IDENTICAL across slots \u2014 only the bridge key, handshake channel, queue cap\n * and overflow-warning label differ. Each slot's own `host-*-port.ts` stays\n * the frozen public surface (see its own \"PUBLIC SURFACE EVOLUTION RULE\"\n * doc-comment) and calls this factory with its own constants; this module has\n * no public-surface contract of its own.\n */\nimport { contextBridge, ipcRenderer } from 'electron'\n\nexport interface HostSlotPortBridgeOptions {\n /** contextBridge key the page-facing API is exposed under, e.g. 'diminaHostToolbar'. */\n bridgeKey: string\n /** main \u2192 renderer per-load handshake channel, e.g. ViewChannel.HostToolbarPort. */\n channel: string\n /** Cap on pre-handshake queued sends; overflow drops the NEWEST envelope. */\n pendingLimit: number\n /** Slot label used in the overflow console.warn, e.g. 'host-toolbar'. */\n label: string\n}\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing per-slot runtime guard \u2014 a failing guard must leave zero\n * footprint (no bridge key, no IPC listener), so this factory itself does no\n * guard checking; the caller decides whether to call it at all.\n */\nexport function createHostSlotPortBridge(opts: HostSlotPortBridgeOptions): void {\n const { bridgeKey, channel, pendingLimit, label } = opts\n let activePort: MessagePort | null = null\n const pending: Array<{ channel: string; payload: unknown }> = []\n // One overflow warning per load (this installer runs once per document) \u2014\n // a runaway page send-loop must not get per-drop console spam.\n let warnedPendingOverflow = false\n const handlers: Array<{ channel: string; handler: (payload: unknown) => void }> = []\n\n // Entry-waist channel validation, parity with the main side's `onMessage`\n // guard (same TypeError, message names `channel`). Runs BEFORE any state is\n // touched (no queue slot, no registry entry), in both port states \u2014 a page\n // author's typo throws at the call site instead of vanishing into main's\n // silent inbound drop.\n function assertValidChannel(method: string, ch: unknown): asserts ch is string {\n if (typeof ch !== 'string' || ch === '') {\n throw new TypeError(`${bridgeKey}.${method}: channel must be a non-empty string`)\n }\n }\n\n const dispatch = (data: unknown): void => {\n // Defensive symmetry with main: drop anything that is not an object\n // envelope with a string channel \u2014 never throw in the dispatcher.\n if (typeof data !== 'object' || data === null) return\n const { channel: ch, payload } = data as { channel?: unknown; payload?: unknown }\n if (typeof ch !== 'string') return\n for (const entry of [...handlers]) {\n if (entry.channel === ch) entry.handler(payload)\n }\n }\n\n const onPortMessage = (event: MessageEvent): void => {\n dispatch(event.data)\n }\n\n ipcRenderer.on(channel, (event) => {\n const port = event.ports[0]\n if (!port) return\n // Same-load duplicate handshake: the LATER port wins. Main closed (or is\n // about to close) its end of the old pair \u2014 detach and stop writing to it.\n if (activePort) {\n try {\n activePort.removeEventListener('message', onPortMessage)\n activePort.close()\n } catch {\n /* already dead */\n }\n }\n activePort = port\n // addEventListener (not onmessage) keeps the handler removable on\n // re-handshake; it REQUIRES start() or inbound never delivers.\n port.addEventListener('message', onPortMessage)\n port.start()\n // Flush sends issued before the port arrived, in order.\n while (pending.length > 0) {\n const envelope = pending.shift()!\n port.postMessage(envelope)\n }\n })\n\n // EXACTLY { send, onMessage } \u2014 functions only; the port stays in the\n // isolated world.\n contextBridge.exposeInMainWorld(bridgeKey, {\n send(ch: string, payload: unknown): void {\n assertValidChannel('send', ch)\n const envelope = { channel: ch, payload }\n if (activePort) {\n activePort.postMessage(envelope)\n return\n }\n // Bounded queue: drop the NEWEST send on overflow (FIFO first-comers\n // survive), warn once per load, never throw into page code.\n if (pending.length >= pendingLimit) {\n if (!warnedPendingOverflow) {\n warnedPendingOverflow = true\n console.warn(\n `[dimina-devtools] ${label} pending queue full (${pendingLimit}); ` +\n 'dropping further pre-handshake send() calls until the port arrives',\n )\n }\n return\n }\n pending.push(envelope)\n },\n onMessage(ch: string, handler: (payload: unknown) => void): () => void {\n assertValidChannel('onMessage', ch)\n const entry = { channel: ch, handler }\n handlers.push(entry)\n return () => {\n const i = handlers.indexOf(entry)\n if (i >= 0) handlers.splice(i, 1)\n }\n },\n })\n}\n", "/**\n * Preload side of the host-sidebar gated narrow channel.\n *\n * \u2500\u2500 PUBLIC SURFACE EVOLUTION RULE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * `window.diminaHostSidebar` is a published page-facing API, typed by the\n * exported `DiminaHostSidebarPageBridge` (src/main/runtime/miniapp-runtime.ts)\n * \u2014 arbitrary host sidebar pages compile against it. Members may only be\n * ADDED, never changed: any semantic change to an existing member (signature,\n * return shape, queueing/throw behavior) ships under a NEW name instead.\n * Exception: security fixes / compliance corrections may change existing\n * member semantics, and MUST be called out in the release's version notes.\n * Keep the exported bridge type in lockstep with what is exposed here.\n *\n * Receives the per-load MessagePort main transfers on `did-finish-load`\n * (`ViewChannel.HostSidebarPort`, `event.ports[0]`) and bridges it to the page\n * as `window.diminaHostSidebar` \u2014 EXACTLY `{ send, onMessage }`, functions\n * only. The raw MessagePort never crosses into the main world: the page only\n * ever talks through these two functions, so the `{ channel, payload }`\n * envelope stays the single waist (same posture as the main side's\n * host-sidebar-port-channel.ts).\n *\n * Ordering reality this module absorbs \u2014 identical to the toolbar's port\n * bridge (see host-toolbar-port.ts for the full rationale): pre-handshake\n * sends queue and bounded-drop, handlers register before the port exists and\n * re-attach across same-load re-handshakes, inbound dispatch drops malformed\n * envelopes without throwing.\n *\n * Shared implementation: see `host-slot-port-bridge.ts` (the toolbar/sidebar/\n * dialog port bridges are otherwise identical; only the constants below\n * differ).\n */\n\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { createHostSlotPortBridge } from './host-slot-port-bridge.js'\n\n/**\n * Cap on pre-handshake queued sends. Same rationale and value as\n * `HOST_TOOLBAR_PENDING_LIMIT` \u2014 overflow drops the NEWEST envelope, one\n * console.warn per load.\n */\nexport const HOST_SIDEBAR_PENDING_LIMIT = 128\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing sidebar-runtime guard (`activateHostSidebarRuntime`) \u2014 a failing\n * guard must leave zero footprint (no bridge key, no IPC listener).\n */\nexport function installHostSidebarPortBridge(): void {\n createHostSlotPortBridge({\n bridgeKey: 'diminaHostSidebar',\n channel: ViewChannel.HostSidebarPort,\n pendingLimit: HOST_SIDEBAR_PENDING_LIMIT,\n label: 'host-sidebar',\n })\n}\n", "/**\n * Session-resident host-sidebar framework runtime: GUARD + ACTIVATION.\n *\n * Same registration and guard shape as host-toolbar-runtime.ts (see that\n * module for the full rationale): registered once per process on\n * `session.defaultSession`, so it runs in EVERY defaultSession renderer;\n * this guard keeps it inert everywhere except the host-sidebar WCV's main\n * frame, identified by `HOST_SIDEBAR_RUNTIME_MARKER` in that process' argv.\n */\n\nimport { HOST_SIDEBAR_RUNTIME_MARKER } from '../../shared/constants.js'\nimport { installHostSidebarAdvertiserWhenReady } from './host-sidebar-advertiser.js'\nimport { installHostSidebarPortBridge } from './host-sidebar-port.js'\n\n/**\n * Pure guard predicate: should the sidebar runtime activate in a renderer with\n * this `argv` / `isMainFrame`? True only for the MAIN frame of a process whose\n * argv carries the `'--dimina-host-sidebar'` marker.\n */\nexport function shouldActivateHostSidebarRuntime(\n argv: readonly string[],\n isMainFrame: boolean,\n): boolean {\n return isMainFrame && argv.includes(HOST_SIDEBAR_RUNTIME_MARKER)\n}\n\n/**\n * Run the guard; only when it passes, install the width advertiser\n * (`installHostSidebarAdvertiserWhenReady`) and the narrow-channel page\n * bridge (`installHostSidebarPortBridge` \u2014 `window.diminaHostSidebar` +\n * the MessagePort handshake listener). Returns whether the runtime\n * activated. A failed guard installs NOTHING (zero footprint in non-sidebar\n * windows and subframes: no advertiser, no bridge key, no IPC listener).\n */\nexport function activateHostSidebarRuntime(env: {\n argv: readonly string[]\n isMainFrame: boolean\n}): boolean {\n if (!shouldActivateHostSidebarRuntime(env.argv, env.isMainFrame)) return false\n installHostSidebarAdvertiserWhenReady()\n installHostSidebarPortBridge()\n return true\n}\n", "// Session-registered sidebar-runtime preload entry.\n//\n// Bundled into a single CJS file (dist/preload/windows/host-sidebar-runtime.cjs)\n// via build:preload and registered ONCE per process on `session.defaultSession`\n// (`registerPreloadScript({ type: 'frame', \u2026 })` \u2014 see\n// `src/main/services/views/host-sidebar-session-runtime.ts`). It therefore runs\n// in EVERY defaultSession renderer; `activateHostSidebarRuntime`'s guard\n// (`process.isMainFrame && process.argv.includes('--dimina-host-sidebar')`)\n// makes it a zero-footprint no-op everywhere except the host-sidebar WCV's\n// main frame, where it installs the reverse size-advertiser.\nimport { activateHostSidebarRuntime } from '../runtime/host-sidebar-runtime.js'\n\nactivateHostSidebarRuntime({ argv: process.argv, isMainFrame: process.isMainFrame })\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/** Shared cross-process constants for dimina-devtools. */\n\n/** Default Chrome DevTools Protocol (CDP) remote debugging port. */\nexport const DEFAULT_CDP_PORT = 9222\n\n/** Default scene value for mini-app launch. */\nexport const DEFAULT_SCENE = 1001\n\n/**\n * Fixed devtools toolbar header height (px). Single source of truth shared by\n * the main process (overlay view layout) and the renderer (toolbar/popover\n * layout). Not host-configurable \u2014 the deprecated\n * `WorkbenchAppConfig.headerHeight` is ignored; hosts that need their own\n * toolbar use the host toolbar WCV instead.\n */\nexport const HEADER_H = 40\n\n/**\n * Process-level argv marker injected (via `webPreferences.additionalArguments`)\n * into the host-toolbar WebContentsView. The session-registered toolbar-runtime\n * preload (`src/preload/runtime/host-toolbar-runtime.ts`) executes in EVERY\n * defaultSession renderer; its guard activates the height advertiser only when\n * `process.argv` carries this marker AND `process.isMainFrame` is true (the\n * marker is process-level, so subframes of the toolbar window see it too \u2014\n * both guard wings are required).\n */\nexport const HOST_TOOLBAR_RUNTIME_MARKER = '--dimina-host-toolbar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-sidebar WebContentsView (`src/preload/runtime/host-sidebar-runtime.ts`).\n */\nexport const HOST_SIDEBAR_RUNTIME_MARKER = '--dimina-host-sidebar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-dialog WebContentsView (`src/preload/runtime/host-dialog-runtime.ts`).\n */\nexport const HOST_DIALOG_RUNTIME_MARKER = '--dimina-host-dialog'\n\n/**\n * Intrinsic width (px) of devtools' own default host-sidebar content (the\n * project-category icon rail). Single source of truth shared by the renderer\n * (`host-sidebar-default.tsx`, which gives its `[data-host-sidebar-root]` this\n * exact width so the width advertiser reports it) and anything in main that\n * needs to reason about the default rail's size. The slot is NOT left pinned\n * at this width: main pushes it once as a seed and immediately returns the\n * slot to 'auto' (see `createDevtoolsRuntime`), so the rail \u2014 and any\n * downstream `loadURL`/`loadFile` content that replaces it \u2014 advertises its\n * own width from then on, exactly like any other host-sidebar content.\n */\nexport const HOST_SIDEBAR_DEFAULT_WIDTH = 56\n", "/**\n * Reverse size-advertiser preload for the host-controllable sidebar\n * WebContentsView. Runs in that WCV's OWN renderer (the sidebar content's\n * preload), measures the sidebar's intrinsic inline (width) extent and sends it\n * to the main process. Main pushes the value back to the main-window renderer as\n * `HostSidebarWidthChanged`, which resizes the sidebar placeholder, re-measures\n * the forward anchor, and re-overlays this WCV \u2014 closing the dynamic-width loop\n * (see `@dimina-kit/view-anchor` `createSizeAdvertiser`).\n */\n\nimport { ipcRenderer } from 'electron'\nimport { createSizeAdvertiser } from '@dimina-kit/view-anchor'\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { whenSlotRootReady } from './wait-for-slot-root.js'\n\n/**\n * Attach the advertiser to the sidebar content's shrink-to-fit root\n * (`[data-host-sidebar-root]`), waiting for it if necessary \u2014 the root may be\n * mounted asynchronously by a framework (e.g. React) rather than present in\n * the initial HTML. That element MUST be shrink-to-fit on the inline axis \u2014\n * its width must reflect the content, not the host-applied view width, or the\n * cross-process loop never converges (`createSizeAdvertiser`'s footgun).\n */\nexport function installHostSidebarAdvertiserWhenReady(): void {\n whenSlotRootReady(\n '[data-host-sidebar-root]',\n '[host-sidebar-advertiser] no `[data-host-sidebar-root]` element found \u2014 ' +\n 'not advertising a width yet. The sidebar content must wrap itself in a ' +\n 'shrink-to-fit `[data-host-sidebar-root]` element so its inline size is ' +\n 'the content width, not the host-given view size.',\n (root) => {\n createSizeAdvertiser(root, {\n axis: 'inline',\n publish: (size) => {\n ipcRenderer.send(ViewChannel.HostSidebarAdvertiseWidth, size)\n },\n })\n },\n )\n}\n", "/**\n * Internal \u2014 the RAF-coalesced measure/dedupe/dispose engine behind the REVERSE\n * primitive `createSizeAdvertiser`.\n *\n * The forward `createViewAnchor` deliberately does NOT use this: it publishes\n * SYNCHRONOUSLY (a native overlay's `setBounds` already lands a cross-process\n * frame late, and a RAF stacked a second frame of visible trailing). The\n * reverse direction is different \u2014 it is a cross-process FEEDBACK loop\n * (advertise \u2192 host resizes the view \u2192 content re-measures \u2192 re-advertise), so\n * the RAF's one-publish-per-frame coalescing is a useful damper. The two\n * directions thus have different optimal emit timing; this engine serves only\n * the reverse.\n *\n * NOT exported from the package: it is pure mechanism with no knowledge of\n * direction, the DOM, `ResizeObserver`, or the structure of the value `T` it\n * carries. The wrapping primitive injects `produce` / `same` / `sink` and\n * drives the lifecycle.\n *\n * - `schedule()` \u2014 coalesce a burst of triggers into ONE RAF; the frame\n * body re-`produce()`s, dedupes against the last emit (`same`), and `sink`s.\n * Bails if inactive or disposed (stale-RAF safe).\n * - `emitNow(v)` \u2014 explicit synchronous emit (create / update path). Always\n * fires, bypassing the dedupe check, and refreshes the dedupe baseline.\n * - `setActive` \u2014 gate the observer stream; a queued frame bails on `!active`.\n * - `cancel` \u2014 drop any in-flight RAF.\n * - `dispose` \u2014 cancel + go inert; after dispose nothing emits again.\n */\nexport interface MeasureLoop<T> {\n schedule(): void\n emitNow(value: T): void\n setActive(on: boolean): void\n cancel(): void\n dispose(): void\n}\n\nexport function createMeasureLoop<T>(cfg: {\n /** Produce the value to emit in the RAF body. Return `null` to decline the\n * frame entirely (no dedupe, no sink, baseline untouched) \u2014 e.g. a\n * non-finite or unavailable measurement. */\n produce: () => T | null\n same: (a: T, b: T) => boolean\n sink: (value: T) => void\n}): MeasureLoop<T> {\n const { produce, same, sink } = cfg\n let rafId: number | null = null\n let active = false\n let disposed = false\n let last: T | null = null\n\n const cancel = (): void => {\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n rafId = null\n }\n }\n\n return {\n schedule(): void {\n if (disposed || !active || rafId !== null) return\n rafId = requestAnimationFrame(() => {\n rafId = null\n if (disposed || !active) return\n const value = produce()\n if (value === null) return // producer declined this frame\n // last-value dedupe: a frame whose produced value equals the last one\n // we emitted costs nothing (no IPC / setBounds).\n if (last !== null && same(value, last)) return\n last = value\n sink(value)\n })\n },\n emitNow(value: T): void {\n if (disposed) return\n last = value\n sink(value)\n },\n setActive(on: boolean): void {\n active = on\n },\n cancel,\n dispose(): void {\n if (disposed) return\n disposed = true\n cancel()\n },\n }\n}\n", "import type {\n AdvertisedSize,\n SizeAdvertiserOptions,\n SizeAdvertiserHandle,\n} from './types.js'\nimport { createMeasureLoop } from './measure-loop.js'\n\n/**\n * Reverse of `createViewAnchor`: runs in a downstream WebContentsView's own\n * renderer, measures the content's own size on ONE owned axis (from the\n * `ResizeObserver` border-box \u2014 no `getBoundingClientRect`, no forced reflow),\n * and advertises it via the injected `publish`. Shares the forward primitive's\n * measure/coalesce/dedupe/dispose engine (`createMeasureLoop`).\n *\n * The extent is `Math.round`ed and clamped to `>= 0`; non-finite measurements\n * drop the frame.\n *\n * FOOTGUN \u2014 `target` must be shrink-to-fit on the owned axis: its owned-axis\n * size must NOT be driven by the host-applied view size, or the cross-process\n * loop (advertise \u2192 host resizes view \u2192 remeasure) never converges (it\n * oscillates or stays \"stable but wrong\"). Measuring `<body>`/`<html>` is the\n * classic mistake \u2014 their size *is* the view size. See\n * `docs/bidirectional-design.md`'s single-axis-ownership and trust-boundary\n * sections.\n */\nexport function createSizeAdvertiser(\n target: HTMLElement,\n opts: SizeAdvertiserOptions,\n): SizeAdvertiserHandle {\n const axis = opts.axis // immutable for the advertiser's life\n let publish = opts.publish\n let observer: ResizeObserver | null = null\n let disposed = false\n // Latest border-box, stashed by the RO callback and read by `produce` in the\n // RAF body (keep the entry out of the shared, DOM-agnostic loop).\n let latest: ResizeObserverSize | null = null\n\n const produce = (): AdvertisedSize | null => {\n if (!latest) return null\n const raw = axis === 'block' ? latest.blockSize : latest.inlineSize\n if (!Number.isFinite(raw)) return null\n return { axis, extent: Math.max(0, Math.round(raw)) }\n }\n\n const loop = createMeasureLoop<AdvertisedSize>({\n produce,\n same: (a, b) => a.extent === b.extent, // axis is constant\n sink: (size) => publish(size),\n })\n\n const onResize: ResizeObserverCallback = (entries) => {\n const entry = entries[entries.length - 1]\n if (entry) {\n latest = entry.borderBoxSize?.[0] ?? entry.contentBoxSize?.[0] ?? latest\n }\n loop.schedule()\n }\n\n // One cheap, once-per-advertiser guard for the textbook feedback-loop footgun.\n const doc = target.ownerDocument\n if (target === doc.body || target === doc.documentElement) {\n console.warn(\n `[view-anchor] size-advertiser: <${target === doc.body ? 'body' : 'html'}>'s ` +\n `${axis} size is the host-given view size, not the content size \u2014 the ` +\n `advertiser will never shrink to content. Measure a shrink-to-fit wrapper. ` +\n `See bidirectional-design.md's single-axis-ownership section.`,\n )\n }\n\n loop.setActive(true)\n observer = new ResizeObserver(onResize)\n observer.observe(target)\n\n return {\n update(nextPublish: (size: AdvertisedSize) => void): void {\n if (disposed) return\n publish = nextPublish\n // Re-advertise the current size to the new sink immediately (mirrors the\n // forward anchor's re-publish on update) so the new channel is not left\n // sizeless until the next ResizeObserver tick.\n const cur = produce()\n if (cur) loop.emitNow(cur)\n },\n dispose(): void {\n if (disposed) return\n disposed = true\n loop.cancel()\n if (observer) {\n observer.disconnect()\n observer = null\n }\n loop.dispose()\n },\n }\n}\n", "/**\n * IPC channel name constants for dimina-devtools' overlay/panel\n * WebContentsViews \u2014 layout slots (host-toolbar/host-sidebar/host-dialog),\n * the popover, tooltip, project-create dialog and settings overlays, plus\n * the update flow. Split out of `ipc-channels.ts` (see that file's header)\n * once this group alone pushed it past the repo's file-length threshold \u2014\n * these constants share no runtime dependency on the rest of that file, so\n * the split is a pure line-count relief valve, not a behavior change.\n */\n\n// \u2500\u2500 Embedded views (renderer \u2192 main) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The main window's React layout owns the *positions* of the editor +\n// simulator-DevTools WebContentsView overlays \u2014 each visible placeholder\n// `<div>` measures its client rect via ResizeObserver and pushes the\n// rectangle to the main process. The view manager caches the latest\n// payload per kind and applies it to the overlay; no payload means the\n// overlay is hidden.\n//\n// Payload (after schema validation): `{ x, y, width, height }` in CSS\n// pixels relative to the window's content area (origin = top-left,\n// not including the OS chrome).\nexport const ViewChannel = {\n /**\n * Reverse size-advertiser: the host-toolbar WCV's OWN renderer advertises its\n * intrinsic content height so main reserves exactly that much. Payload\n * `{ axis: 'block', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostToolbarAdvertiseHeight: 'view:host-toolbar:advertise-height',\n /**\n * main \u2192 host-toolbar WCV renderer: per-load MessagePort handshake for the\n * gated narrow channel. On every toolbar `did-finish-load` main creates a\n * `MessageChannelMain` and transfers port2 here\n * (`wc.postMessage(HostToolbarPort, null, [port2])`); the session-resident\n * toolbar runtime preload receives it via `event.ports[0]` and bridges it to\n * the page as `window.diminaHostToolbar`. Envelope both directions:\n * `{ channel: string, payload: unknown }`.\n */\n HostToolbarPort: 'view:host-toolbar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-toolbar height so the\n * renderer placeholder div resizes (closing the dynamic-height loop).\n */\n HostToolbarHeightChanged: 'view:host-toolbar:height-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED toolbar\n * height retained in main. Mount-time replay companion to\n * `HostToolbarHeightChanged`: the push listener mounts with the project\n * view and the toolbar's size-advertiser deduplicates (never re-reports),\n * so a height pushed while no project view was mounted would otherwise be\n * lost forever (cold start on the project list races it; close-project \u2192\n * reopen hits it deterministically). No payload; resolves a number.\n */\n HostToolbarGetHeight: 'view:host-toolbar:get-height',\n /**\n * Reverse size-advertiser for the host-sidebar WCV, mirroring\n * `HostToolbarAdvertiseHeight` on the inline (width) axis instead of block.\n * Payload `{ axis: 'inline', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostSidebarAdvertiseWidth: 'view:host-sidebar:advertise-width',\n /**\n * main \u2192 host-sidebar WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostSidebarPort: 'view:host-sidebar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-sidebar width so the\n * renderer placeholder div resizes, mirroring `HostToolbarHeightChanged`.\n */\n HostSidebarWidthChanged: 'view:host-sidebar:width-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED sidebar\n * width, mirroring `HostToolbarGetHeight`'s mount-time replay role.\n */\n HostSidebarGetWidth: 'view:host-sidebar:get-width',\n /**\n * main \u2192 main-window renderer: push the project category (`Project.type`)\n * selected in the host-sidebar's content \u2014 devtools' own default icon rail,\n * or any downstream replacement that sends on the same channel. No\n * pull/replay companion like `HostSidebarGetWidth`: the default category is\n * a stable initial value regardless of load-order, and nothing is ever sent\n * before the main-window renderer (and thus `ProjectListScreen`) is already\n * mounted.\n */\n HostSidebarCategorySelected: 'view:host-sidebar:category-selected',\n /**\n * Reverse size-advertiser for the host-dialog WCV. Unlike the toolbar/sidebar\n * (single-axis, `setBaseDesired`-placed strips), the dialog is a BOTH-AXES\n * overlay positioned by `setOverlayDesired` \u2014 main centers it in the main\n * window once it knows both dimensions. One channel carries either axis'\n * report (`{ axis: 'block' | 'inline', extent }`); the dialog's own content\n * advertises both from the same root (see `host-dialog-advertiser.ts`).\n * fire-and-forget (send), NOT invoke.\n *\n * No `HostDialogSizeChanged` / `HostDialogGetSize` counterparts: those exist\n * for toolbar/sidebar so a renderer-owned PLACEHOLDER can replay a size it\n * missed. The dialog has no such placeholder \u2014 it is a main-driven overlay\n * shown/hidden on demand (`hostDialog.show()`/`hide()`), so there is nothing\n * in the renderer to replay a size into.\n */\n HostDialogAdvertiseSize: 'view:host-dialog:advertise-size',\n /**\n * main \u2192 host-dialog WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostDialogPort: 'view:host-dialog:port',\n /**\n * Renderer \u2192 main: the window-level placement snapshot (one monotonic epoch\n * per commit tick, one generation per renderer lifetime) that drives the view\n * reconciler. The single source of truth for every managed native view's\n * bounds/visibility/z-order \u2014 supersedes the per-view bounds channels above.\n * invoke.\n */\n PlacementSnapshot: 'view:placement-snapshot',\n /**\n * main \u2190 main-window renderer (invoke): allocate a fresh placement\n * generation seed for this renderer bootstrap. See\n * renderer-placement-generation.ts for why the seed comes from main\n * (a wall-clock `Date.now()` seed is not guaranteed to exceed main's\n * still-standing high-water mark across two reloads that happen faster\n * than the clock's resolution). No payload; resolves a number.\n */\n AllocatePlacementGeneration: 'view:allocate-placement-generation',\n} as const\n\nexport interface ViewBounds {\n x: number\n y: number\n width: number\n height: number\n}\n\n// \u2500\u2500 Popover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const PopoverChannel = {\n Show: 'popover:show',\n Hide: 'popover:hide',\n // Popover \u2192 main \u2192 main-renderer: the edited compile modes, plus whether\n // the change affects what is currently running (selecting a mode, or\n // editing the selected one) and so needs a relaunch.\n Apply: 'popover:apply',\n Closed: 'popover:closed',\n Init: 'popover:init',\n} as const\n\n// \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.tooltip \u2014 see view-ids.ts),\n// NOT a DOM tooltip in the main renderer: any DOM-portaled floating UI (Radix\n// Tooltip) or the browser-native `title` attribute lives in the main\n// renderer's own paint surface, which every other WCV (simulator, editor,\n// settings, popover) renders on top of \u2014 CSS cannot reach across that\n// boundary. A trigger reports its anchor rect + label; main computes the\n// tooltip's screen bounds and shows/repositions/hides this overlay.\n\nexport const OverlayChannel = {\n Ready: 'overlay:ready',\n} as const\n\nexport const TooltipChannel = {\n Prepare: 'tooltip:prepare',\n Show: 'tooltip:show',\n Hide: 'tooltip:hide',\n Init: 'tooltip:init',\n Measured: 'tooltip:measured',\n} as const\n\n// \u2500\u2500 Project-create dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.dialog), NOT the Radix\n// `fixed inset-0` DOM portal it replaced \u2014 a DOM overlay paints inside the\n// main window's own renderer, so any native WebContentsView mounted on top\n// (simulator, host-toolbar, host-sidebar) occludes it. main.tsx fetches the\n// template catalog + base-dir defaults, then asks main to show this panel;\n// the panel relays submit/cancel back to main.tsx to run the existing\n// scaffold flow.\n\nexport const ProjectCreateChannel = {\n Show: 'projectCreate:show',\n Init: 'projectCreate:init',\n Submit: 'projectCreate:submit',\n Cancel: 'projectCreate:cancel',\n Submitted: 'projectCreate:submitted',\n} as const\n\n// \u2500\u2500 Device picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.dialog), for the same reason\n// as the project-create dialog above: the simulator's own WebContentsView is\n// mounted on top of the main renderer and overlaps the centred search panel,\n// so a DOM-portaled dialog is cut in half by it. The toolbar keeps only the\n// trigger button; it asks main to show this panel with the currently selected\n// device, and main relays the picked device name back to the toolbar.\n\nexport const DevicePickerChannel = {\n Show: 'devicePicker:show',\n Init: 'devicePicker:init',\n Select: 'devicePicker:select',\n Cancel: 'devicePicker:cancel',\n Selected: 'devicePicker:selected',\n} as const\n\n// \u2500\u2500 Embedded settings overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const SettingsChannel = {\n SetVisible: 'settings:setVisible',\n ProjectSettingsChanged: 'settings:projectSettingsChanged',\n Init: 'settings:init',\n} as const\n\n// \u2500\u2500 Updates (UpdateManager) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// String values are FROZEN: shipped builds key off them. Add new entries\n// here, never rename existing ones.\nexport const UpdateChannel = {\n Check: 'updates:check',\n Download: 'updates:download',\n Install: 'updates:install',\n DownloadProgress: 'updates:downloadProgress',\n Available: 'updates:available',\n Close: 'updates:close',\n} as const\n", "/**\n * Shared readiness wait for a host-slot content's shrink-to-fit measurement\n * root (`[data-host-*-root]`). A one-shot `document.querySelector` at\n * `DOMContentLoaded` time only works when the root is present in the initial\n * HTML. Framework-rendered content (e.g. a React app) can mount its root\n * asynchronously, after `DOMContentLoaded` has already fired \u2014 a one-shot\n * check misses it permanently and the slot's width/height is never\n * advertised. This keeps watching via `MutationObserver` instead of assuming\n * a synchronous mount, without polling on a fixed delay.\n *\n * The \"root is missing\" warning exists for downstream authors who forgot the\n * element entirely, so it must not fire on the healthy async-mount path \u2014 a\n * warning printed on every normal load is noise that trains its readers to\n * ignore it. Two conditions gate it, both signals rather than guessed delays:\n * the document must be one the host actually loaded content into (not the\n * view's own blank document), and it must have finished loading AND run out of\n * pending work (`window.load`, then an idle callback \u2014 a framework's first\n * commit is normal-priority work, so it always lands before idle). The\n * observer stays armed either way, so a root that mounts even later is still\n * picked up.\n */\nexport function whenSlotRootReady(\n selector: string,\n missingWarning: string,\n install: (root: HTMLElement) => void,\n): void {\n function attempt(): void {\n // The slot's WebContentsView holds its own empty document until the host\n // loads content into it, and this preload runs there too. That document\n // has no author: a missing root in it is not a mistake anyone made, and\n // warning about it fires on every app start. The observer below still gets\n // armed, so content that does arrive is picked up.\n const authored = location.href !== 'about:blank' && location.href !== ''\n\n const root = document.querySelector<HTMLElement>(selector)\n if (root) {\n install(root)\n return\n }\n\n let installed = false\n const observer = new MutationObserver(() => {\n const lateRoot = document.querySelector<HTMLElement>(selector)\n if (!lateRoot) return\n observer.disconnect()\n installed = true\n install(lateRoot)\n })\n observer.observe(document.documentElement, { childList: true, subtree: true })\n\n // Re-queries rather than trusting `installed`: the observer delivers on a\n // microtask, so a root that is already in the DOM may not have been\n // installed yet \u2014 it is present either way, which is what the warning is\n // about.\n const warnIfStillMissing = (): void => {\n if (installed || !authored || document.querySelector(selector)) return\n console.warn(missingWarning)\n }\n\n // `load` alone is too early a verdict: a concurrent React root commits its\n // first render on a scheduler task that can land after it, and the warning\n // would then race the healthy mount it is supposed to ignore. Idle\n // callbacks run only once the main thread has no pending work of that\n // kind, so a root still absent then is genuinely absent. The timeout is\n // the cap for a page that never goes idle, not the mechanism.\n const decide = (): void => {\n const idle = (globalThis as { requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number })\n .requestIdleCallback\n if (typeof idle === 'function') idle(warnIfStillMissing, { timeout: 5_000 })\n else warnIfStillMissing()\n }\n if (document.readyState === 'complete') decide()\n else window.addEventListener('load', decide, { once: true })\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', attempt, { once: true })\n } else {\n attempt()\n }\n}\n", "/**\n * Shared implementation behind every host-slot page bridge (toolbar/sidebar/\n * dialog): the MessagePort handshake, pending-queue-with-bounded-overflow,\n * handler registry and `{ send, onMessage }` contextBridge exposure are\n * IDENTICAL across slots \u2014 only the bridge key, handshake channel, queue cap\n * and overflow-warning label differ. Each slot's own `host-*-port.ts` stays\n * the frozen public surface (see its own \"PUBLIC SURFACE EVOLUTION RULE\"\n * doc-comment) and calls this factory with its own constants; this module has\n * no public-surface contract of its own.\n */\nimport { contextBridge, ipcRenderer } from 'electron'\n\nexport interface HostSlotPortBridgeOptions {\n /** contextBridge key the page-facing API is exposed under, e.g. 'diminaHostToolbar'. */\n bridgeKey: string\n /** main \u2192 renderer per-load handshake channel, e.g. ViewChannel.HostToolbarPort. */\n channel: string\n /** Cap on pre-handshake queued sends; overflow drops the NEWEST envelope. */\n pendingLimit: number\n /** Slot label used in the overflow console.warn, e.g. 'host-toolbar'. */\n label: string\n}\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing per-slot runtime guard \u2014 a failing guard must leave zero\n * footprint (no bridge key, no IPC listener), so this factory itself does no\n * guard checking; the caller decides whether to call it at all.\n */\nexport function createHostSlotPortBridge(opts: HostSlotPortBridgeOptions): void {\n const { bridgeKey, channel, pendingLimit, label } = opts\n let activePort: MessagePort | null = null\n const pending: Array<{ channel: string; payload: unknown }> = []\n // One overflow warning per load (this installer runs once per document) \u2014\n // a runaway page send-loop must not get per-drop console spam.\n let warnedPendingOverflow = false\n const handlers: Array<{ channel: string; handler: (payload: unknown) => void }> = []\n\n // Entry-waist channel validation, parity with the main side's `onMessage`\n // guard (same TypeError, message names `channel`). Runs BEFORE any state is\n // touched (no queue slot, no registry entry), in both port states \u2014 a page\n // author's typo throws at the call site instead of vanishing into main's\n // silent inbound drop.\n function assertValidChannel(method: string, ch: unknown): asserts ch is string {\n if (typeof ch !== 'string' || ch === '') {\n throw new TypeError(`${bridgeKey}.${method}: channel must be a non-empty string`)\n }\n }\n\n const dispatch = (data: unknown): void => {\n // Defensive symmetry with main: drop anything that is not an object\n // envelope with a string channel \u2014 never throw in the dispatcher.\n if (typeof data !== 'object' || data === null) return\n const { channel: ch, payload } = data as { channel?: unknown; payload?: unknown }\n if (typeof ch !== 'string') return\n for (const entry of [...handlers]) {\n if (entry.channel === ch) entry.handler(payload)\n }\n }\n\n const onPortMessage = (event: MessageEvent): void => {\n dispatch(event.data)\n }\n\n ipcRenderer.on(channel, (event) => {\n const port = event.ports[0]\n if (!port) return\n // Same-load duplicate handshake: the LATER port wins. Main closed (or is\n // about to close) its end of the old pair \u2014 detach and stop writing to it.\n if (activePort) {\n try {\n activePort.removeEventListener('message', onPortMessage)\n activePort.close()\n } catch {\n /* already dead */\n }\n }\n activePort = port\n // addEventListener (not onmessage) keeps the handler removable on\n // re-handshake; it REQUIRES start() or inbound never delivers.\n port.addEventListener('message', onPortMessage)\n port.start()\n // Flush sends issued before the port arrived, in order.\n while (pending.length > 0) {\n const envelope = pending.shift()!\n port.postMessage(envelope)\n }\n })\n\n // EXACTLY { send, onMessage } \u2014 functions only; the port stays in the\n // isolated world.\n contextBridge.exposeInMainWorld(bridgeKey, {\n send(ch: string, payload: unknown): void {\n assertValidChannel('send', ch)\n const envelope = { channel: ch, payload }\n if (activePort) {\n activePort.postMessage(envelope)\n return\n }\n // Bounded queue: drop the NEWEST send on overflow (FIFO first-comers\n // survive), warn once per load, never throw into page code.\n if (pending.length >= pendingLimit) {\n if (!warnedPendingOverflow) {\n warnedPendingOverflow = true\n console.warn(\n `[dimina-devtools] ${label} pending queue full (${pendingLimit}); ` +\n 'dropping further pre-handshake send() calls until the port arrives',\n )\n }\n return\n }\n pending.push(envelope)\n },\n onMessage(ch: string, handler: (payload: unknown) => void): () => void {\n assertValidChannel('onMessage', ch)\n const entry = { channel: ch, handler }\n handlers.push(entry)\n return () => {\n const i = handlers.indexOf(entry)\n if (i >= 0) handlers.splice(i, 1)\n }\n },\n })\n}\n", "/**\n * Preload side of the host-sidebar gated narrow channel.\n *\n * \u2500\u2500 PUBLIC SURFACE EVOLUTION RULE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * `window.diminaHostSidebar` is a published page-facing API, typed by the\n * exported `DiminaHostSidebarPageBridge` (src/main/runtime/miniapp-runtime.ts)\n * \u2014 arbitrary host sidebar pages compile against it. Members may only be\n * ADDED, never changed: any semantic change to an existing member (signature,\n * return shape, queueing/throw behavior) ships under a NEW name instead.\n * Exception: security fixes / compliance corrections may change existing\n * member semantics, and MUST be called out in the release's version notes.\n * Keep the exported bridge type in lockstep with what is exposed here.\n *\n * Receives the per-load MessagePort main transfers on `did-finish-load`\n * (`ViewChannel.HostSidebarPort`, `event.ports[0]`) and bridges it to the page\n * as `window.diminaHostSidebar` \u2014 EXACTLY `{ send, onMessage }`, functions\n * only. The raw MessagePort never crosses into the main world: the page only\n * ever talks through these two functions, so the `{ channel, payload }`\n * envelope stays the single waist (same posture as the main side's\n * host-sidebar-port-channel.ts).\n *\n * Ordering reality this module absorbs \u2014 identical to the toolbar's port\n * bridge (see host-toolbar-port.ts for the full rationale): pre-handshake\n * sends queue and bounded-drop, handlers register before the port exists and\n * re-attach across same-load re-handshakes, inbound dispatch drops malformed\n * envelopes without throwing.\n *\n * Shared implementation: see `host-slot-port-bridge.ts` (the toolbar/sidebar/\n * dialog port bridges are otherwise identical; only the constants below\n * differ).\n */\n\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { createHostSlotPortBridge } from './host-slot-port-bridge.js'\n\n/**\n * Cap on pre-handshake queued sends. Same rationale and value as\n * `HOST_TOOLBAR_PENDING_LIMIT` \u2014 overflow drops the NEWEST envelope, one\n * console.warn per load.\n */\nexport const HOST_SIDEBAR_PENDING_LIMIT = 128\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing sidebar-runtime guard (`activateHostSidebarRuntime`) \u2014 a failing\n * guard must leave zero footprint (no bridge key, no IPC listener).\n */\nexport function installHostSidebarPortBridge(): void {\n createHostSlotPortBridge({\n bridgeKey: 'diminaHostSidebar',\n channel: ViewChannel.HostSidebarPort,\n pendingLimit: HOST_SIDEBAR_PENDING_LIMIT,\n label: 'host-sidebar',\n })\n}\n", "/**\n * Session-resident host-sidebar framework runtime: GUARD + ACTIVATION.\n *\n * Same registration and guard shape as host-toolbar-runtime.ts (see that\n * module for the full rationale): registered once per process on\n * `session.defaultSession`, so it runs in EVERY defaultSession renderer;\n * this guard keeps it inert everywhere except the host-sidebar WCV's main\n * frame, identified by `HOST_SIDEBAR_RUNTIME_MARKER` in that process' argv.\n */\n\nimport { HOST_SIDEBAR_RUNTIME_MARKER } from '../../shared/constants.js'\nimport { installHostSidebarAdvertiserWhenReady } from './host-sidebar-advertiser.js'\nimport { installHostSidebarPortBridge } from './host-sidebar-port.js'\n\n/**\n * Pure guard predicate: should the sidebar runtime activate in a renderer with\n * this `argv` / `isMainFrame`? True only for the MAIN frame of a process whose\n * argv carries the `'--dimina-host-sidebar'` marker.\n */\nexport function shouldActivateHostSidebarRuntime(\n argv: readonly string[],\n isMainFrame: boolean,\n): boolean {\n return isMainFrame && argv.includes(HOST_SIDEBAR_RUNTIME_MARKER)\n}\n\n/**\n * Run the guard; only when it passes, install the width advertiser\n * (`installHostSidebarAdvertiserWhenReady`) and the narrow-channel page\n * bridge (`installHostSidebarPortBridge` \u2014 `window.diminaHostSidebar` +\n * the MessagePort handshake listener). Returns whether the runtime\n * activated. A failed guard installs NOTHING (zero footprint in non-sidebar\n * windows and subframes: no advertiser, no bridge key, no IPC listener).\n */\nexport function activateHostSidebarRuntime(env: {\n argv: readonly string[]\n isMainFrame: boolean\n}): boolean {\n if (!shouldActivateHostSidebarRuntime(env.argv, env.isMainFrame)) return false\n installHostSidebarAdvertiserWhenReady()\n installHostSidebarPortBridge()\n return true\n}\n", "// Session-registered sidebar-runtime preload entry.\n//\n// Bundled into a single CJS file (dist/preload/windows/host-sidebar-runtime.cjs)\n// via build:preload and registered ONCE per process on `session.defaultSession`\n// (`registerPreloadScript({ type: 'frame', \u2026 })` \u2014 see\n// `src/main/services/views/host-sidebar-session-runtime.ts`). It therefore runs\n// in EVERY defaultSession renderer; `activateHostSidebarRuntime`'s guard\n// (`process.isMainFrame && process.argv.includes('--dimina-host-sidebar')`)\n// makes it a zero-footprint no-op everywhere except the host-sidebar WCV's\n// main frame, where it installs the reverse size-advertiser.\nimport { activateHostSidebarRuntime } from '../runtime/host-sidebar-runtime.js'\n\nactivateHostSidebarRuntime({ argv: process.argv, isMainFrame: process.isMainFrame })\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_electron"]
|
|
7
7
|
}
|
|
@@ -211,6 +211,7 @@ var ViewChannel = {
|
|
|
211
211
|
// src/preload/runtime/wait-for-slot-root.ts
|
|
212
212
|
function whenSlotRootReady(selector, missingWarning, install) {
|
|
213
213
|
function attempt() {
|
|
214
|
+
const authored = location.href !== "about:blank" && location.href !== "";
|
|
214
215
|
const root = document.querySelector(selector);
|
|
215
216
|
if (root) {
|
|
216
217
|
install(root);
|
|
@@ -226,10 +227,16 @@ function whenSlotRootReady(selector, missingWarning, install) {
|
|
|
226
227
|
});
|
|
227
228
|
observer.observe(document.documentElement, { childList: true, subtree: true });
|
|
228
229
|
const warnIfStillMissing = () => {
|
|
229
|
-
if (
|
|
230
|
+
if (installed || !authored || document.querySelector(selector)) return;
|
|
231
|
+
console.warn(missingWarning);
|
|
230
232
|
};
|
|
231
|
-
|
|
232
|
-
|
|
233
|
+
const decide = () => {
|
|
234
|
+
const idle = globalThis.requestIdleCallback;
|
|
235
|
+
if (typeof idle === "function") idle(warnIfStillMissing, { timeout: 5e3 });
|
|
236
|
+
else warnIfStillMissing();
|
|
237
|
+
};
|
|
238
|
+
if (document.readyState === "complete") decide();
|
|
239
|
+
else window.addEventListener("load", decide, { once: true });
|
|
233
240
|
}
|
|
234
241
|
if (document.readyState === "loading") {
|
|
235
242
|
document.addEventListener("DOMContentLoaded", attempt, { once: true });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/shared/constants.ts", "../../../src/preload/runtime/host-toolbar-advertiser.ts", "../../../../view-anchor/src/measure-loop.ts", "../../../../view-anchor/src/size-advertiser.ts", "../../../src/shared/ipc-channels-overlays.ts", "../../../src/preload/runtime/wait-for-slot-root.ts", "../../../src/preload/runtime/host-slot-port-bridge.ts", "../../../src/preload/runtime/host-toolbar-port.ts", "../../../src/preload/runtime/host-toolbar-runtime.ts", "../../../src/preload/windows/host-toolbar-runtime.ts"],
|
|
4
|
-
"sourcesContent": ["/** Shared cross-process constants for dimina-devtools. */\n\n/** Default Chrome DevTools Protocol (CDP) remote debugging port. */\nexport const DEFAULT_CDP_PORT = 9222\n\n/** Default scene value for mini-app launch. */\nexport const DEFAULT_SCENE = 1001\n\n/**\n * Fixed devtools toolbar header height (px). Single source of truth shared by\n * the main process (overlay view layout) and the renderer (toolbar/popover\n * layout). Not host-configurable \u2014 the deprecated\n * `WorkbenchAppConfig.headerHeight` is ignored; hosts that need their own\n * toolbar use the host toolbar WCV instead.\n */\nexport const HEADER_H = 40\n\n/**\n * Process-level argv marker injected (via `webPreferences.additionalArguments`)\n * into the host-toolbar WebContentsView. The session-registered toolbar-runtime\n * preload (`src/preload/runtime/host-toolbar-runtime.ts`) executes in EVERY\n * defaultSession renderer; its guard activates the height advertiser only when\n * `process.argv` carries this marker AND `process.isMainFrame` is true (the\n * marker is process-level, so subframes of the toolbar window see it too \u2014\n * both guard wings are required).\n */\nexport const HOST_TOOLBAR_RUNTIME_MARKER = '--dimina-host-toolbar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-sidebar WebContentsView (`src/preload/runtime/host-sidebar-runtime.ts`).\n */\nexport const HOST_SIDEBAR_RUNTIME_MARKER = '--dimina-host-sidebar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-dialog WebContentsView (`src/preload/runtime/host-dialog-runtime.ts`).\n */\nexport const HOST_DIALOG_RUNTIME_MARKER = '--dimina-host-dialog'\n\n/**\n * Intrinsic width (px) of devtools' own default host-sidebar content (the\n * project-category icon rail). Single source of truth shared by the renderer\n * (`host-sidebar-default.tsx`, which gives its `[data-host-sidebar-root]` this\n * exact width so the width advertiser reports it) and anything in main that\n * needs to reason about the default rail's size. The slot is NOT left pinned\n * at this width: main pushes it once as a seed and immediately returns the\n * slot to 'auto' (see `createDevtoolsRuntime`), so the rail \u2014 and any\n * downstream `loadURL`/`loadFile` content that replaces it \u2014 advertises its\n * own width from then on, exactly like any other host-sidebar content.\n */\nexport const HOST_SIDEBAR_DEFAULT_WIDTH = 56\n", "/**\n * Reverse size-advertiser preload for the host-controllable toolbar\n * WebContentsView. Runs in that WCV's OWN renderer (the toolbar content's\n * preload), measures the toolbar's intrinsic block (height) extent and sends it\n * to the main process. Main pushes the value back to the main-window renderer as\n * `HostToolbarHeightChanged`, which resizes the toolbar placeholder, re-measures\n * the forward anchor, and re-overlays this WCV \u2014 closing the dynamic-height loop\n * (see `@dimina-kit/view-anchor` `createSizeAdvertiser`).\n */\n\nimport { ipcRenderer } from 'electron'\nimport { createSizeAdvertiser } from '@dimina-kit/view-anchor'\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { whenSlotRootReady } from './wait-for-slot-root.js'\n\n/**\n * Attach the advertiser to the toolbar content's shrink-to-fit root\n * (`[data-host-toolbar-root]`), waiting for it if necessary \u2014 the root may be\n * mounted asynchronously by a framework rather than present in the initial\n * HTML. That element MUST be shrink-to-fit on the block axis \u2014 its height\n * must reflect the content, not the host-applied view height, or the\n * cross-process loop never converges (`createSizeAdvertiser`'s footgun).\n */\nexport function installHostToolbarAdvertiserWhenReady(): void {\n whenSlotRootReady(\n '[data-host-toolbar-root]',\n '[host-toolbar-advertiser] no `[data-host-toolbar-root]` element found \u2014 ' +\n 'not advertising a height yet. The toolbar content must wrap itself in a ' +\n 'shrink-to-fit `[data-host-toolbar-root]` element so its block size is ' +\n 'the content height, not the host-given view size.',\n (root) => {\n createSizeAdvertiser(root, {\n axis: 'block',\n publish: (size) => {\n ipcRenderer.send(ViewChannel.HostToolbarAdvertiseHeight, size)\n },\n })\n },\n )\n}\n", "/**\n * Internal \u2014 the RAF-coalesced measure/dedupe/dispose engine behind the REVERSE\n * primitive `createSizeAdvertiser`.\n *\n * The forward `createViewAnchor` deliberately does NOT use this: it publishes\n * SYNCHRONOUSLY (a native overlay's `setBounds` already lands a cross-process\n * frame late, and a RAF stacked a second frame of visible trailing). The\n * reverse direction is different \u2014 it is a cross-process FEEDBACK loop\n * (advertise \u2192 host resizes the view \u2192 content re-measures \u2192 re-advertise), so\n * the RAF's one-publish-per-frame coalescing is a useful damper. The two\n * directions thus have different optimal emit timing; this engine serves only\n * the reverse.\n *\n * NOT exported from the package: it is pure mechanism with no knowledge of\n * direction, the DOM, `ResizeObserver`, or the structure of the value `T` it\n * carries. The wrapping primitive injects `produce` / `same` / `sink` and\n * drives the lifecycle.\n *\n * - `schedule()` \u2014 coalesce a burst of triggers into ONE RAF; the frame\n * body re-`produce()`s, dedupes against the last emit (`same`), and `sink`s.\n * Bails if inactive or disposed (stale-RAF safe).\n * - `emitNow(v)` \u2014 explicit synchronous emit (create / update path). Always\n * fires, bypassing the dedupe check, and refreshes the dedupe baseline.\n * - `setActive` \u2014 gate the observer stream; a queued frame bails on `!active`.\n * - `cancel` \u2014 drop any in-flight RAF.\n * - `dispose` \u2014 cancel + go inert; after dispose nothing emits again.\n */\nexport interface MeasureLoop<T> {\n schedule(): void\n emitNow(value: T): void\n setActive(on: boolean): void\n cancel(): void\n dispose(): void\n}\n\nexport function createMeasureLoop<T>(cfg: {\n /** Produce the value to emit in the RAF body. Return `null` to decline the\n * frame entirely (no dedupe, no sink, baseline untouched) \u2014 e.g. a\n * non-finite or unavailable measurement. */\n produce: () => T | null\n same: (a: T, b: T) => boolean\n sink: (value: T) => void\n}): MeasureLoop<T> {\n const { produce, same, sink } = cfg\n let rafId: number | null = null\n let active = false\n let disposed = false\n let last: T | null = null\n\n const cancel = (): void => {\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n rafId = null\n }\n }\n\n return {\n schedule(): void {\n if (disposed || !active || rafId !== null) return\n rafId = requestAnimationFrame(() => {\n rafId = null\n if (disposed || !active) return\n const value = produce()\n if (value === null) return // producer declined this frame\n // last-value dedupe: a frame whose produced value equals the last one\n // we emitted costs nothing (no IPC / setBounds).\n if (last !== null && same(value, last)) return\n last = value\n sink(value)\n })\n },\n emitNow(value: T): void {\n if (disposed) return\n last = value\n sink(value)\n },\n setActive(on: boolean): void {\n active = on\n },\n cancel,\n dispose(): void {\n if (disposed) return\n disposed = true\n cancel()\n },\n }\n}\n", "import type {\n AdvertisedSize,\n SizeAdvertiserOptions,\n SizeAdvertiserHandle,\n} from './types.js'\nimport { createMeasureLoop } from './measure-loop.js'\n\n/**\n * Reverse of `createViewAnchor`: runs in a downstream WebContentsView's own\n * renderer, measures the content's own size on ONE owned axis (from the\n * `ResizeObserver` border-box \u2014 no `getBoundingClientRect`, no forced reflow),\n * and advertises it via the injected `publish`. Shares the forward primitive's\n * measure/coalesce/dedupe/dispose engine (`createMeasureLoop`).\n *\n * The extent is `Math.round`ed and clamped to `>= 0`; non-finite measurements\n * drop the frame.\n *\n * FOOTGUN \u2014 `target` must be shrink-to-fit on the owned axis: its owned-axis\n * size must NOT be driven by the host-applied view size, or the cross-process\n * loop (advertise \u2192 host resizes view \u2192 remeasure) never converges (it\n * oscillates or stays \"stable but wrong\"). Measuring `<body>`/`<html>` is the\n * classic mistake \u2014 their size *is* the view size. See\n * `docs/bidirectional-design.md`'s single-axis-ownership and trust-boundary\n * sections.\n */\nexport function createSizeAdvertiser(\n target: HTMLElement,\n opts: SizeAdvertiserOptions,\n): SizeAdvertiserHandle {\n const axis = opts.axis // immutable for the advertiser's life\n let publish = opts.publish\n let observer: ResizeObserver | null = null\n let disposed = false\n // Latest border-box, stashed by the RO callback and read by `produce` in the\n // RAF body (keep the entry out of the shared, DOM-agnostic loop).\n let latest: ResizeObserverSize | null = null\n\n const produce = (): AdvertisedSize | null => {\n if (!latest) return null\n const raw = axis === 'block' ? latest.blockSize : latest.inlineSize\n if (!Number.isFinite(raw)) return null\n return { axis, extent: Math.max(0, Math.round(raw)) }\n }\n\n const loop = createMeasureLoop<AdvertisedSize>({\n produce,\n same: (a, b) => a.extent === b.extent, // axis is constant\n sink: (size) => publish(size),\n })\n\n const onResize: ResizeObserverCallback = (entries) => {\n const entry = entries[entries.length - 1]\n if (entry) {\n latest = entry.borderBoxSize?.[0] ?? entry.contentBoxSize?.[0] ?? latest\n }\n loop.schedule()\n }\n\n // One cheap, once-per-advertiser guard for the textbook feedback-loop footgun.\n const doc = target.ownerDocument\n if (target === doc.body || target === doc.documentElement) {\n console.warn(\n `[view-anchor] size-advertiser: <${target === doc.body ? 'body' : 'html'}>'s ` +\n `${axis} size is the host-given view size, not the content size \u2014 the ` +\n `advertiser will never shrink to content. Measure a shrink-to-fit wrapper. ` +\n `See bidirectional-design.md's single-axis-ownership section.`,\n )\n }\n\n loop.setActive(true)\n observer = new ResizeObserver(onResize)\n observer.observe(target)\n\n return {\n update(nextPublish: (size: AdvertisedSize) => void): void {\n if (disposed) return\n publish = nextPublish\n // Re-advertise the current size to the new sink immediately (mirrors the\n // forward anchor's re-publish on update) so the new channel is not left\n // sizeless until the next ResizeObserver tick.\n const cur = produce()\n if (cur) loop.emitNow(cur)\n },\n dispose(): void {\n if (disposed) return\n disposed = true\n loop.cancel()\n if (observer) {\n observer.disconnect()\n observer = null\n }\n loop.dispose()\n },\n }\n}\n", "/**\n * IPC channel name constants for dimina-devtools' overlay/panel\n * WebContentsViews \u2014 layout slots (host-toolbar/host-sidebar/host-dialog),\n * the popover, tooltip, project-create dialog and settings overlays, plus\n * the update flow. Split out of `ipc-channels.ts` (see that file's header)\n * once this group alone pushed it past the repo's file-length threshold \u2014\n * these constants share no runtime dependency on the rest of that file, so\n * the split is a pure line-count relief valve, not a behavior change.\n */\n\n// \u2500\u2500 Embedded views (renderer \u2192 main) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The main window's React layout owns the *positions* of the editor +\n// simulator-DevTools WebContentsView overlays \u2014 each visible placeholder\n// `<div>` measures its client rect via ResizeObserver and pushes the\n// rectangle to the main process. The view manager caches the latest\n// payload per kind and applies it to the overlay; no payload means the\n// overlay is hidden.\n//\n// Payload (after schema validation): `{ x, y, width, height }` in CSS\n// pixels relative to the window's content area (origin = top-left,\n// not including the OS chrome).\nexport const ViewChannel = {\n /**\n * Reverse size-advertiser: the host-toolbar WCV's OWN renderer advertises its\n * intrinsic content height so main reserves exactly that much. Payload\n * `{ axis: 'block', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostToolbarAdvertiseHeight: 'view:host-toolbar:advertise-height',\n /**\n * main \u2192 host-toolbar WCV renderer: per-load MessagePort handshake for the\n * gated narrow channel. On every toolbar `did-finish-load` main creates a\n * `MessageChannelMain` and transfers port2 here\n * (`wc.postMessage(HostToolbarPort, null, [port2])`); the session-resident\n * toolbar runtime preload receives it via `event.ports[0]` and bridges it to\n * the page as `window.diminaHostToolbar`. Envelope both directions:\n * `{ channel: string, payload: unknown }`.\n */\n HostToolbarPort: 'view:host-toolbar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-toolbar height so the\n * renderer placeholder div resizes (closing the dynamic-height loop).\n */\n HostToolbarHeightChanged: 'view:host-toolbar:height-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED toolbar\n * height retained in main. Mount-time replay companion to\n * `HostToolbarHeightChanged`: the push listener mounts with the project\n * view and the toolbar's size-advertiser deduplicates (never re-reports),\n * so a height pushed while no project view was mounted would otherwise be\n * lost forever (cold start on the project list races it; close-project \u2192\n * reopen hits it deterministically). No payload; resolves a number.\n */\n HostToolbarGetHeight: 'view:host-toolbar:get-height',\n /**\n * Reverse size-advertiser for the host-sidebar WCV, mirroring\n * `HostToolbarAdvertiseHeight` on the inline (width) axis instead of block.\n * Payload `{ axis: 'inline', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostSidebarAdvertiseWidth: 'view:host-sidebar:advertise-width',\n /**\n * main \u2192 host-sidebar WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostSidebarPort: 'view:host-sidebar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-sidebar width so the\n * renderer placeholder div resizes, mirroring `HostToolbarHeightChanged`.\n */\n HostSidebarWidthChanged: 'view:host-sidebar:width-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED sidebar\n * width, mirroring `HostToolbarGetHeight`'s mount-time replay role.\n */\n HostSidebarGetWidth: 'view:host-sidebar:get-width',\n /**\n * main \u2192 main-window renderer: push the project category (`Project.type`)\n * selected in the host-sidebar's content \u2014 devtools' own default icon rail,\n * or any downstream replacement that sends on the same channel. No\n * pull/replay companion like `HostSidebarGetWidth`: the default category is\n * a stable initial value regardless of load-order, and nothing is ever sent\n * before the main-window renderer (and thus `ProjectListScreen`) is already\n * mounted.\n */\n HostSidebarCategorySelected: 'view:host-sidebar:category-selected',\n /**\n * Reverse size-advertiser for the host-dialog WCV. Unlike the toolbar/sidebar\n * (single-axis, `setBaseDesired`-placed strips), the dialog is a BOTH-AXES\n * overlay positioned by `setOverlayDesired` \u2014 main centers it in the main\n * window once it knows both dimensions. One channel carries either axis'\n * report (`{ axis: 'block' | 'inline', extent }`); the dialog's own content\n * advertises both from the same root (see `host-dialog-advertiser.ts`).\n * fire-and-forget (send), NOT invoke.\n *\n * No `HostDialogSizeChanged` / `HostDialogGetSize` counterparts: those exist\n * for toolbar/sidebar so a renderer-owned PLACEHOLDER can replay a size it\n * missed. The dialog has no such placeholder \u2014 it is a main-driven overlay\n * shown/hidden on demand (`hostDialog.show()`/`hide()`), so there is nothing\n * in the renderer to replay a size into.\n */\n HostDialogAdvertiseSize: 'view:host-dialog:advertise-size',\n /**\n * main \u2192 host-dialog WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostDialogPort: 'view:host-dialog:port',\n /**\n * Renderer \u2192 main: the window-level placement snapshot (one monotonic epoch\n * per commit tick, one generation per renderer lifetime) that drives the view\n * reconciler. The single source of truth for every managed native view's\n * bounds/visibility/z-order \u2014 supersedes the per-view bounds channels above.\n * invoke.\n */\n PlacementSnapshot: 'view:placement-snapshot',\n /**\n * main \u2190 main-window renderer (invoke): allocate a fresh placement\n * generation seed for this renderer bootstrap. See\n * renderer-placement-generation.ts for why the seed comes from main\n * (a wall-clock `Date.now()` seed is not guaranteed to exceed main's\n * still-standing high-water mark across two reloads that happen faster\n * than the clock's resolution). No payload; resolves a number.\n */\n AllocatePlacementGeneration: 'view:allocate-placement-generation',\n} as const\n\nexport interface ViewBounds {\n x: number\n y: number\n width: number\n height: number\n}\n\n// \u2500\u2500 Popover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const PopoverChannel = {\n Show: 'popover:show',\n Hide: 'popover:hide',\n Relaunch: 'popover:relaunch',\n Closed: 'popover:closed',\n Init: 'popover:init',\n} as const\n\n// \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.tooltip \u2014 see view-ids.ts),\n// NOT a DOM tooltip in the main renderer: any DOM-portaled floating UI (Radix\n// Tooltip) or the browser-native `title` attribute lives in the main\n// renderer's own paint surface, which every other WCV (simulator, editor,\n// settings, popover) renders on top of \u2014 CSS cannot reach across that\n// boundary. A trigger reports its anchor rect + label; main computes the\n// tooltip's screen bounds and shows/repositions/hides this overlay.\n\nexport const OverlayChannel = {\n Ready: 'overlay:ready',\n} as const\n\nexport const TooltipChannel = {\n Prepare: 'tooltip:prepare',\n Show: 'tooltip:show',\n Hide: 'tooltip:hide',\n Init: 'tooltip:init',\n Measured: 'tooltip:measured',\n} as const\n\n// \u2500\u2500 Project-create dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.dialog), NOT the Radix\n// `fixed inset-0` DOM portal it replaced \u2014 a DOM overlay paints inside the\n// main window's own renderer, so any native WebContentsView mounted on top\n// (simulator, host-toolbar, host-sidebar) occludes it. main.tsx fetches the\n// template catalog + base-dir defaults, then asks main to show this panel;\n// the panel relays submit/cancel back to main.tsx to run the existing\n// scaffold flow.\n\nexport const ProjectCreateChannel = {\n Show: 'projectCreate:show',\n Init: 'projectCreate:init',\n Submit: 'projectCreate:submit',\n Cancel: 'projectCreate:cancel',\n Submitted: 'projectCreate:submitted',\n} as const\n\n// \u2500\u2500 Embedded settings overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const SettingsChannel = {\n SetVisible: 'settings:setVisible',\n ConfigChanged: 'settings:configChanged',\n ProjectSettingsChanged: 'settings:projectSettingsChanged',\n Init: 'settings:init',\n} as const\n\n// \u2500\u2500 Updates (UpdateManager) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// String values are FROZEN: shipped builds key off them. Add new entries\n// here, never rename existing ones.\nexport const UpdateChannel = {\n Check: 'updates:check',\n Download: 'updates:download',\n Install: 'updates:install',\n DownloadProgress: 'updates:downloadProgress',\n Available: 'updates:available',\n Close: 'updates:close',\n} as const\n", "/**\n * Shared readiness wait for a host-slot content's shrink-to-fit measurement\n * root (`[data-host-*-root]`). A one-shot `document.querySelector` at\n * `DOMContentLoaded` time only works when the root is present in the initial\n * HTML. Framework-rendered content (e.g. a React app) can mount its root\n * asynchronously, after `DOMContentLoaded` has already fired \u2014 a one-shot\n * check misses it permanently and the slot's width/height is never\n * advertised. This keeps watching via `MutationObserver` instead of assuming\n * a synchronous mount, without polling on a fixed delay.\n *\n * The \"root is missing\" warning exists for downstream authors who forgot the\n * element entirely, so it must not fire on the healthy async-mount path \u2014 a\n * warning printed on every normal load is noise that trains its readers to\n * ignore it. It is therefore withheld until the document has finished loading\n * (`window.load`, a real event rather than a guessed delay) with the root\n * still absent; the observer stays armed either way, so a root that mounts\n * even later is still picked up.\n */\nexport function whenSlotRootReady(\n selector: string,\n missingWarning: string,\n install: (root: HTMLElement) => void,\n): void {\n function attempt(): void {\n const root = document.querySelector<HTMLElement>(selector)\n if (root) {\n install(root)\n return\n }\n\n let installed = false\n const observer = new MutationObserver(() => {\n const lateRoot = document.querySelector<HTMLElement>(selector)\n if (!lateRoot) return\n observer.disconnect()\n installed = true\n install(lateRoot)\n })\n observer.observe(document.documentElement, { childList: true, subtree: true })\n\n const warnIfStillMissing = (): void => {\n if (!installed) console.warn(missingWarning)\n }\n if (document.readyState === 'complete') warnIfStillMissing()\n else window.addEventListener('load', warnIfStillMissing, { once: true })\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', attempt, { once: true })\n } else {\n attempt()\n }\n}\n", "/**\n * Shared implementation behind every host-slot page bridge (toolbar/sidebar/\n * dialog): the MessagePort handshake, pending-queue-with-bounded-overflow,\n * handler registry and `{ send, onMessage }` contextBridge exposure are\n * IDENTICAL across slots \u2014 only the bridge key, handshake channel, queue cap\n * and overflow-warning label differ. Each slot's own `host-*-port.ts` stays\n * the frozen public surface (see its own \"PUBLIC SURFACE EVOLUTION RULE\"\n * doc-comment) and calls this factory with its own constants; this module has\n * no public-surface contract of its own.\n */\nimport { contextBridge, ipcRenderer } from 'electron'\n\nexport interface HostSlotPortBridgeOptions {\n /** contextBridge key the page-facing API is exposed under, e.g. 'diminaHostToolbar'. */\n bridgeKey: string\n /** main \u2192 renderer per-load handshake channel, e.g. ViewChannel.HostToolbarPort. */\n channel: string\n /** Cap on pre-handshake queued sends; overflow drops the NEWEST envelope. */\n pendingLimit: number\n /** Slot label used in the overflow console.warn, e.g. 'host-toolbar'. */\n label: string\n}\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing per-slot runtime guard \u2014 a failing guard must leave zero\n * footprint (no bridge key, no IPC listener), so this factory itself does no\n * guard checking; the caller decides whether to call it at all.\n */\nexport function createHostSlotPortBridge(opts: HostSlotPortBridgeOptions): void {\n const { bridgeKey, channel, pendingLimit, label } = opts\n let activePort: MessagePort | null = null\n const pending: Array<{ channel: string; payload: unknown }> = []\n // One overflow warning per load (this installer runs once per document) \u2014\n // a runaway page send-loop must not get per-drop console spam.\n let warnedPendingOverflow = false\n const handlers: Array<{ channel: string; handler: (payload: unknown) => void }> = []\n\n // Entry-waist channel validation, parity with the main side's `onMessage`\n // guard (same TypeError, message names `channel`). Runs BEFORE any state is\n // touched (no queue slot, no registry entry), in both port states \u2014 a page\n // author's typo throws at the call site instead of vanishing into main's\n // silent inbound drop.\n function assertValidChannel(method: string, ch: unknown): asserts ch is string {\n if (typeof ch !== 'string' || ch === '') {\n throw new TypeError(`${bridgeKey}.${method}: channel must be a non-empty string`)\n }\n }\n\n const dispatch = (data: unknown): void => {\n // Defensive symmetry with main: drop anything that is not an object\n // envelope with a string channel \u2014 never throw in the dispatcher.\n if (typeof data !== 'object' || data === null) return\n const { channel: ch, payload } = data as { channel?: unknown; payload?: unknown }\n if (typeof ch !== 'string') return\n for (const entry of [...handlers]) {\n if (entry.channel === ch) entry.handler(payload)\n }\n }\n\n const onPortMessage = (event: MessageEvent): void => {\n dispatch(event.data)\n }\n\n ipcRenderer.on(channel, (event) => {\n const port = event.ports[0]\n if (!port) return\n // Same-load duplicate handshake: the LATER port wins. Main closed (or is\n // about to close) its end of the old pair \u2014 detach and stop writing to it.\n if (activePort) {\n try {\n activePort.removeEventListener('message', onPortMessage)\n activePort.close()\n } catch {\n /* already dead */\n }\n }\n activePort = port\n // addEventListener (not onmessage) keeps the handler removable on\n // re-handshake; it REQUIRES start() or inbound never delivers.\n port.addEventListener('message', onPortMessage)\n port.start()\n // Flush sends issued before the port arrived, in order.\n while (pending.length > 0) {\n const envelope = pending.shift()!\n port.postMessage(envelope)\n }\n })\n\n // EXACTLY { send, onMessage } \u2014 functions only; the port stays in the\n // isolated world.\n contextBridge.exposeInMainWorld(bridgeKey, {\n send(ch: string, payload: unknown): void {\n assertValidChannel('send', ch)\n const envelope = { channel: ch, payload }\n if (activePort) {\n activePort.postMessage(envelope)\n return\n }\n // Bounded queue: drop the NEWEST send on overflow (FIFO first-comers\n // survive), warn once per load, never throw into page code.\n if (pending.length >= pendingLimit) {\n if (!warnedPendingOverflow) {\n warnedPendingOverflow = true\n console.warn(\n `[dimina-devtools] ${label} pending queue full (${pendingLimit}); ` +\n 'dropping further pre-handshake send() calls until the port arrives',\n )\n }\n return\n }\n pending.push(envelope)\n },\n onMessage(ch: string, handler: (payload: unknown) => void): () => void {\n assertValidChannel('onMessage', ch)\n const entry = { channel: ch, handler }\n handlers.push(entry)\n return () => {\n const i = handlers.indexOf(entry)\n if (i >= 0) handlers.splice(i, 1)\n }\n },\n })\n}\n", "/**\n * Preload side of the host-toolbar gated narrow channel.\n *\n * \u2500\u2500 PUBLIC SURFACE EVOLUTION RULE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * `window.diminaHostToolbar` is a published page-facing API, typed by the\n * exported `DiminaHostToolbarPageBridge` (src/main/runtime/miniapp-runtime.ts)\n * \u2014 arbitrary host toolbar pages compile against it. Members may only be\n * ADDED, never changed: any semantic change to an existing member (signature,\n * return shape, queueing/throw behavior) ships under a NEW name instead.\n * Exception: security fixes / compliance corrections may change existing\n * member semantics, and MUST be called out in the release's version notes.\n * Keep the exported bridge type in lockstep with what is exposed here.\n *\n * Receives the per-load MessagePort main transfers on `did-finish-load`\n * (`ViewChannel.HostToolbarPort`, `event.ports[0]`) and bridges it to the page\n * as `window.diminaHostToolbar` \u2014 EXACTLY `{ send, onMessage }`, functions\n * only. The raw MessagePort never crosses into the main world: the page only\n * ever talks through these two functions, so the `{ channel, payload }`\n * envelope stays the single waist (same posture as the main side's\n * host-toolbar-port-channel.ts).\n *\n * Ordering reality this module absorbs:\n * - The page script runs BEFORE the handshake can complete (the port is\n * posted on did-finish-load). Page `send()`s issued before the port\n * arrives go into a PENDING QUEUE and flush in order on handshake \u2014\n * without it the first message of every load is silently dropped. The\n * queue is BOUNDED at `HOST_TOOLBAR_PENDING_LIMIT` (128): the toolbar page\n * is arbitrary host content, and a page whose handshake never arrives must\n * not grow main-world-driven memory without limit. Overflow drops the\n * NEWEST send (boot sequences front-load their important messages; the\n * first-comers survive), warns ONCE per load, and never throws into page\n * code.\n * - Page handlers likewise register before the port exists; the registry is\n * module-level (per-load \u2014 the preload re-runs on every navigation) and is\n * re-attached to each newly delivered port, so a same-load re-handshake\n * keeps existing handlers alive. The LATER port wins for sends.\n * - Inbound dispatch uses `addEventListener('message')` + `start()`\n * (without `start()` a DOM MessagePort never delivers) and DROPS malformed\n * envelopes without throwing.\n *\n * Shared implementation: see `host-slot-port-bridge.ts` (the toolbar/sidebar/\n * dialog port bridges are otherwise identical; only the constants below\n * differ).\n */\n\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { createHostSlotPortBridge } from './host-slot-port-bridge.js'\n\n/**\n * Cap on pre-handshake queued sends. 128 comfortably covers any sane toolbar\n * boot sequence while bounding what a page whose handshake never completes\n * can make the isolated world retain. Overflow policy: drop the NEWEST\n * envelope (queued first-comers survive), one console.warn per load.\n */\nexport const HOST_TOOLBAR_PENDING_LIMIT = 128\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing toolbar-runtime guard (`activateHostToolbarRuntime`) \u2014 a failing\n * guard must leave zero footprint (no bridge key, no IPC listener).\n */\nexport function installHostToolbarPortBridge(): void {\n createHostSlotPortBridge({\n bridgeKey: 'diminaHostToolbar',\n channel: ViewChannel.HostToolbarPort,\n pendingLimit: HOST_TOOLBAR_PENDING_LIMIT,\n label: 'host-toolbar',\n })\n}\n", "/**\n * Session-resident host-toolbar framework runtime: GUARD + ACTIVATION.\n *\n * The toolbar-runtime preload is registered on `session.defaultSession` via\n * `registerPreloadScript({ type: 'frame', \u2026 })`, so it executes in EVERY\n * defaultSession renderer \u2014 the main window, settings/popover overlays, and\n * (with `nodeIntegrationInSubFrames`) even subframes. This module is the guard\n * that keeps it inert everywhere except the host-toolbar WCV's main frame:\n *\n * - the toolbar WCV's creation injects `HOST_TOOLBAR_RUNTIME_MARKER` into its\n * process argv via `webPreferences.additionalArguments`;\n * - the marker is PROCESS-level (subframes of the toolbar window carry it\n * too), so the guard needs BOTH wings: `isMainFrame` AND\n * `argv.includes(marker)`;\n * - a renderer that fails the guard returns immediately with ZERO footprint\n * (no advertiser, no listeners, no globals).\n *\n * Why session-resident at all: riding the toolbar WCV's\n * `webPreferences.preload` lets a host calling `setPreloadPath(<its own\n * preload>)` REPLACE the advertiser, collapsing the strip height to 0. With the\n * runtime on the session layer, the host preload and the framework runtime\n * coexist.\n */\n\nimport { HOST_TOOLBAR_RUNTIME_MARKER } from '../../shared/constants.js'\nimport { installHostToolbarAdvertiserWhenReady } from './host-toolbar-advertiser.js'\nimport { installHostToolbarPortBridge } from './host-toolbar-port.js'\n\n/**\n * Pure guard predicate: should the toolbar runtime activate in a renderer with\n * this `argv` / `isMainFrame`? True only for the MAIN frame of a process whose\n * argv carries the `'--dimina-host-toolbar'` marker.\n */\nexport function shouldActivateHostToolbarRuntime(\n argv: readonly string[],\n isMainFrame: boolean,\n): boolean {\n return isMainFrame && argv.includes(HOST_TOOLBAR_RUNTIME_MARKER)\n}\n\n/**\n * Run the guard; only when it passes, install the height advertiser\n * (`installHostToolbarAdvertiserWhenReady`) and the narrow-channel page\n * bridge (`installHostToolbarPortBridge` \u2014 `window.diminaHostToolbar` +\n * the MessagePort handshake listener). Returns whether the runtime\n * activated. A failed guard installs NOTHING (zero footprint in non-toolbar\n * windows and subframes: no advertiser, no bridge key, no IPC listener).\n */\nexport function activateHostToolbarRuntime(env: {\n argv: readonly string[]\n isMainFrame: boolean\n}): boolean {\n if (!shouldActivateHostToolbarRuntime(env.argv, env.isMainFrame)) return false\n installHostToolbarAdvertiserWhenReady()\n installHostToolbarPortBridge()\n return true\n}\n", "// Session-registered toolbar-runtime preload entry.\n//\n// Bundled into a single CJS file (dist/preload/windows/host-toolbar-runtime.cjs)\n// via build:preload and registered ONCE per process on `session.defaultSession`\n// (`registerPreloadScript({ type: 'frame', \u2026 })` \u2014 see\n// `src/main/services/views/host-toolbar-session-runtime.ts`). It therefore runs\n// in EVERY defaultSession renderer; `activateHostToolbarRuntime`'s guard\n// (`process.isMainFrame && process.argv.includes('--dimina-host-toolbar')`)\n// makes it a zero-footprint no-op everywhere except the host-toolbar WCV's\n// main frame, where it installs the reverse size-advertiser.\nimport { activateHostToolbarRuntime } from '../runtime/host-toolbar-runtime.js'\n\nactivateHostToolbarRuntime({ argv: process.argv, isMainFrame: process.isMainFrame })\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/** Shared cross-process constants for dimina-devtools. */\n\n/** Default Chrome DevTools Protocol (CDP) remote debugging port. */\nexport const DEFAULT_CDP_PORT = 9222\n\n/** Default scene value for mini-app launch. */\nexport const DEFAULT_SCENE = 1001\n\n/**\n * Fixed devtools toolbar header height (px). Single source of truth shared by\n * the main process (overlay view layout) and the renderer (toolbar/popover\n * layout). Not host-configurable \u2014 the deprecated\n * `WorkbenchAppConfig.headerHeight` is ignored; hosts that need their own\n * toolbar use the host toolbar WCV instead.\n */\nexport const HEADER_H = 40\n\n/**\n * Process-level argv marker injected (via `webPreferences.additionalArguments`)\n * into the host-toolbar WebContentsView. The session-registered toolbar-runtime\n * preload (`src/preload/runtime/host-toolbar-runtime.ts`) executes in EVERY\n * defaultSession renderer; its guard activates the height advertiser only when\n * `process.argv` carries this marker AND `process.isMainFrame` is true (the\n * marker is process-level, so subframes of the toolbar window see it too \u2014\n * both guard wings are required).\n */\nexport const HOST_TOOLBAR_RUNTIME_MARKER = '--dimina-host-toolbar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-sidebar WebContentsView (`src/preload/runtime/host-sidebar-runtime.ts`).\n */\nexport const HOST_SIDEBAR_RUNTIME_MARKER = '--dimina-host-sidebar'\n\n/**\n * Same guard-marker contract as {@link HOST_TOOLBAR_RUNTIME_MARKER}, for the\n * host-dialog WebContentsView (`src/preload/runtime/host-dialog-runtime.ts`).\n */\nexport const HOST_DIALOG_RUNTIME_MARKER = '--dimina-host-dialog'\n\n/**\n * Intrinsic width (px) of devtools' own default host-sidebar content (the\n * project-category icon rail). Single source of truth shared by the renderer\n * (`host-sidebar-default.tsx`, which gives its `[data-host-sidebar-root]` this\n * exact width so the width advertiser reports it) and anything in main that\n * needs to reason about the default rail's size. The slot is NOT left pinned\n * at this width: main pushes it once as a seed and immediately returns the\n * slot to 'auto' (see `createDevtoolsRuntime`), so the rail \u2014 and any\n * downstream `loadURL`/`loadFile` content that replaces it \u2014 advertises its\n * own width from then on, exactly like any other host-sidebar content.\n */\nexport const HOST_SIDEBAR_DEFAULT_WIDTH = 56\n", "/**\n * Reverse size-advertiser preload for the host-controllable toolbar\n * WebContentsView. Runs in that WCV's OWN renderer (the toolbar content's\n * preload), measures the toolbar's intrinsic block (height) extent and sends it\n * to the main process. Main pushes the value back to the main-window renderer as\n * `HostToolbarHeightChanged`, which resizes the toolbar placeholder, re-measures\n * the forward anchor, and re-overlays this WCV \u2014 closing the dynamic-height loop\n * (see `@dimina-kit/view-anchor` `createSizeAdvertiser`).\n */\n\nimport { ipcRenderer } from 'electron'\nimport { createSizeAdvertiser } from '@dimina-kit/view-anchor'\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { whenSlotRootReady } from './wait-for-slot-root.js'\n\n/**\n * Attach the advertiser to the toolbar content's shrink-to-fit root\n * (`[data-host-toolbar-root]`), waiting for it if necessary \u2014 the root may be\n * mounted asynchronously by a framework rather than present in the initial\n * HTML. That element MUST be shrink-to-fit on the block axis \u2014 its height\n * must reflect the content, not the host-applied view height, or the\n * cross-process loop never converges (`createSizeAdvertiser`'s footgun).\n */\nexport function installHostToolbarAdvertiserWhenReady(): void {\n whenSlotRootReady(\n '[data-host-toolbar-root]',\n '[host-toolbar-advertiser] no `[data-host-toolbar-root]` element found \u2014 ' +\n 'not advertising a height yet. The toolbar content must wrap itself in a ' +\n 'shrink-to-fit `[data-host-toolbar-root]` element so its block size is ' +\n 'the content height, not the host-given view size.',\n (root) => {\n createSizeAdvertiser(root, {\n axis: 'block',\n publish: (size) => {\n ipcRenderer.send(ViewChannel.HostToolbarAdvertiseHeight, size)\n },\n })\n },\n )\n}\n", "/**\n * Internal \u2014 the RAF-coalesced measure/dedupe/dispose engine behind the REVERSE\n * primitive `createSizeAdvertiser`.\n *\n * The forward `createViewAnchor` deliberately does NOT use this: it publishes\n * SYNCHRONOUSLY (a native overlay's `setBounds` already lands a cross-process\n * frame late, and a RAF stacked a second frame of visible trailing). The\n * reverse direction is different \u2014 it is a cross-process FEEDBACK loop\n * (advertise \u2192 host resizes the view \u2192 content re-measures \u2192 re-advertise), so\n * the RAF's one-publish-per-frame coalescing is a useful damper. The two\n * directions thus have different optimal emit timing; this engine serves only\n * the reverse.\n *\n * NOT exported from the package: it is pure mechanism with no knowledge of\n * direction, the DOM, `ResizeObserver`, or the structure of the value `T` it\n * carries. The wrapping primitive injects `produce` / `same` / `sink` and\n * drives the lifecycle.\n *\n * - `schedule()` \u2014 coalesce a burst of triggers into ONE RAF; the frame\n * body re-`produce()`s, dedupes against the last emit (`same`), and `sink`s.\n * Bails if inactive or disposed (stale-RAF safe).\n * - `emitNow(v)` \u2014 explicit synchronous emit (create / update path). Always\n * fires, bypassing the dedupe check, and refreshes the dedupe baseline.\n * - `setActive` \u2014 gate the observer stream; a queued frame bails on `!active`.\n * - `cancel` \u2014 drop any in-flight RAF.\n * - `dispose` \u2014 cancel + go inert; after dispose nothing emits again.\n */\nexport interface MeasureLoop<T> {\n schedule(): void\n emitNow(value: T): void\n setActive(on: boolean): void\n cancel(): void\n dispose(): void\n}\n\nexport function createMeasureLoop<T>(cfg: {\n /** Produce the value to emit in the RAF body. Return `null` to decline the\n * frame entirely (no dedupe, no sink, baseline untouched) \u2014 e.g. a\n * non-finite or unavailable measurement. */\n produce: () => T | null\n same: (a: T, b: T) => boolean\n sink: (value: T) => void\n}): MeasureLoop<T> {\n const { produce, same, sink } = cfg\n let rafId: number | null = null\n let active = false\n let disposed = false\n let last: T | null = null\n\n const cancel = (): void => {\n if (rafId !== null) {\n cancelAnimationFrame(rafId)\n rafId = null\n }\n }\n\n return {\n schedule(): void {\n if (disposed || !active || rafId !== null) return\n rafId = requestAnimationFrame(() => {\n rafId = null\n if (disposed || !active) return\n const value = produce()\n if (value === null) return // producer declined this frame\n // last-value dedupe: a frame whose produced value equals the last one\n // we emitted costs nothing (no IPC / setBounds).\n if (last !== null && same(value, last)) return\n last = value\n sink(value)\n })\n },\n emitNow(value: T): void {\n if (disposed) return\n last = value\n sink(value)\n },\n setActive(on: boolean): void {\n active = on\n },\n cancel,\n dispose(): void {\n if (disposed) return\n disposed = true\n cancel()\n },\n }\n}\n", "import type {\n AdvertisedSize,\n SizeAdvertiserOptions,\n SizeAdvertiserHandle,\n} from './types.js'\nimport { createMeasureLoop } from './measure-loop.js'\n\n/**\n * Reverse of `createViewAnchor`: runs in a downstream WebContentsView's own\n * renderer, measures the content's own size on ONE owned axis (from the\n * `ResizeObserver` border-box \u2014 no `getBoundingClientRect`, no forced reflow),\n * and advertises it via the injected `publish`. Shares the forward primitive's\n * measure/coalesce/dedupe/dispose engine (`createMeasureLoop`).\n *\n * The extent is `Math.round`ed and clamped to `>= 0`; non-finite measurements\n * drop the frame.\n *\n * FOOTGUN \u2014 `target` must be shrink-to-fit on the owned axis: its owned-axis\n * size must NOT be driven by the host-applied view size, or the cross-process\n * loop (advertise \u2192 host resizes view \u2192 remeasure) never converges (it\n * oscillates or stays \"stable but wrong\"). Measuring `<body>`/`<html>` is the\n * classic mistake \u2014 their size *is* the view size. See\n * `docs/bidirectional-design.md`'s single-axis-ownership and trust-boundary\n * sections.\n */\nexport function createSizeAdvertiser(\n target: HTMLElement,\n opts: SizeAdvertiserOptions,\n): SizeAdvertiserHandle {\n const axis = opts.axis // immutable for the advertiser's life\n let publish = opts.publish\n let observer: ResizeObserver | null = null\n let disposed = false\n // Latest border-box, stashed by the RO callback and read by `produce` in the\n // RAF body (keep the entry out of the shared, DOM-agnostic loop).\n let latest: ResizeObserverSize | null = null\n\n const produce = (): AdvertisedSize | null => {\n if (!latest) return null\n const raw = axis === 'block' ? latest.blockSize : latest.inlineSize\n if (!Number.isFinite(raw)) return null\n return { axis, extent: Math.max(0, Math.round(raw)) }\n }\n\n const loop = createMeasureLoop<AdvertisedSize>({\n produce,\n same: (a, b) => a.extent === b.extent, // axis is constant\n sink: (size) => publish(size),\n })\n\n const onResize: ResizeObserverCallback = (entries) => {\n const entry = entries[entries.length - 1]\n if (entry) {\n latest = entry.borderBoxSize?.[0] ?? entry.contentBoxSize?.[0] ?? latest\n }\n loop.schedule()\n }\n\n // One cheap, once-per-advertiser guard for the textbook feedback-loop footgun.\n const doc = target.ownerDocument\n if (target === doc.body || target === doc.documentElement) {\n console.warn(\n `[view-anchor] size-advertiser: <${target === doc.body ? 'body' : 'html'}>'s ` +\n `${axis} size is the host-given view size, not the content size \u2014 the ` +\n `advertiser will never shrink to content. Measure a shrink-to-fit wrapper. ` +\n `See bidirectional-design.md's single-axis-ownership section.`,\n )\n }\n\n loop.setActive(true)\n observer = new ResizeObserver(onResize)\n observer.observe(target)\n\n return {\n update(nextPublish: (size: AdvertisedSize) => void): void {\n if (disposed) return\n publish = nextPublish\n // Re-advertise the current size to the new sink immediately (mirrors the\n // forward anchor's re-publish on update) so the new channel is not left\n // sizeless until the next ResizeObserver tick.\n const cur = produce()\n if (cur) loop.emitNow(cur)\n },\n dispose(): void {\n if (disposed) return\n disposed = true\n loop.cancel()\n if (observer) {\n observer.disconnect()\n observer = null\n }\n loop.dispose()\n },\n }\n}\n", "/**\n * IPC channel name constants for dimina-devtools' overlay/panel\n * WebContentsViews \u2014 layout slots (host-toolbar/host-sidebar/host-dialog),\n * the popover, tooltip, project-create dialog and settings overlays, plus\n * the update flow. Split out of `ipc-channels.ts` (see that file's header)\n * once this group alone pushed it past the repo's file-length threshold \u2014\n * these constants share no runtime dependency on the rest of that file, so\n * the split is a pure line-count relief valve, not a behavior change.\n */\n\n// \u2500\u2500 Embedded views (renderer \u2192 main) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The main window's React layout owns the *positions* of the editor +\n// simulator-DevTools WebContentsView overlays \u2014 each visible placeholder\n// `<div>` measures its client rect via ResizeObserver and pushes the\n// rectangle to the main process. The view manager caches the latest\n// payload per kind and applies it to the overlay; no payload means the\n// overlay is hidden.\n//\n// Payload (after schema validation): `{ x, y, width, height }` in CSS\n// pixels relative to the window's content area (origin = top-left,\n// not including the OS chrome).\nexport const ViewChannel = {\n /**\n * Reverse size-advertiser: the host-toolbar WCV's OWN renderer advertises its\n * intrinsic content height so main reserves exactly that much. Payload\n * `{ axis: 'block', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostToolbarAdvertiseHeight: 'view:host-toolbar:advertise-height',\n /**\n * main \u2192 host-toolbar WCV renderer: per-load MessagePort handshake for the\n * gated narrow channel. On every toolbar `did-finish-load` main creates a\n * `MessageChannelMain` and transfers port2 here\n * (`wc.postMessage(HostToolbarPort, null, [port2])`); the session-resident\n * toolbar runtime preload receives it via `event.ports[0]` and bridges it to\n * the page as `window.diminaHostToolbar`. Envelope both directions:\n * `{ channel: string, payload: unknown }`.\n */\n HostToolbarPort: 'view:host-toolbar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-toolbar height so the\n * renderer placeholder div resizes (closing the dynamic-height loop).\n */\n HostToolbarHeightChanged: 'view:host-toolbar:height-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED toolbar\n * height retained in main. Mount-time replay companion to\n * `HostToolbarHeightChanged`: the push listener mounts with the project\n * view and the toolbar's size-advertiser deduplicates (never re-reports),\n * so a height pushed while no project view was mounted would otherwise be\n * lost forever (cold start on the project list races it; close-project \u2192\n * reopen hits it deterministically). No payload; resolves a number.\n */\n HostToolbarGetHeight: 'view:host-toolbar:get-height',\n /**\n * Reverse size-advertiser for the host-sidebar WCV, mirroring\n * `HostToolbarAdvertiseHeight` on the inline (width) axis instead of block.\n * Payload `{ axis: 'inline', extent }`. fire-and-forget (send), NOT invoke.\n */\n HostSidebarAdvertiseWidth: 'view:host-sidebar:advertise-width',\n /**\n * main \u2192 host-sidebar WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostSidebarPort: 'view:host-sidebar:port',\n /**\n * main \u2192 main-window renderer: push the reserved host-sidebar width so the\n * renderer placeholder div resizes, mirroring `HostToolbarHeightChanged`.\n */\n HostSidebarWidthChanged: 'view:host-sidebar:width-changed',\n /**\n * main \u2190 main-window renderer (invoke): pull the last NOTIFIED sidebar\n * width, mirroring `HostToolbarGetHeight`'s mount-time replay role.\n */\n HostSidebarGetWidth: 'view:host-sidebar:get-width',\n /**\n * main \u2192 main-window renderer: push the project category (`Project.type`)\n * selected in the host-sidebar's content \u2014 devtools' own default icon rail,\n * or any downstream replacement that sends on the same channel. No\n * pull/replay companion like `HostSidebarGetWidth`: the default category is\n * a stable initial value regardless of load-order, and nothing is ever sent\n * before the main-window renderer (and thus `ProjectListScreen`) is already\n * mounted.\n */\n HostSidebarCategorySelected: 'view:host-sidebar:category-selected',\n /**\n * Reverse size-advertiser for the host-dialog WCV. Unlike the toolbar/sidebar\n * (single-axis, `setBaseDesired`-placed strips), the dialog is a BOTH-AXES\n * overlay positioned by `setOverlayDesired` \u2014 main centers it in the main\n * window once it knows both dimensions. One channel carries either axis'\n * report (`{ axis: 'block' | 'inline', extent }`); the dialog's own content\n * advertises both from the same root (see `host-dialog-advertiser.ts`).\n * fire-and-forget (send), NOT invoke.\n *\n * No `HostDialogSizeChanged` / `HostDialogGetSize` counterparts: those exist\n * for toolbar/sidebar so a renderer-owned PLACEHOLDER can replay a size it\n * missed. The dialog has no such placeholder \u2014 it is a main-driven overlay\n * shown/hidden on demand (`hostDialog.show()`/`hide()`), so there is nothing\n * in the renderer to replay a size into.\n */\n HostDialogAdvertiseSize: 'view:host-dialog:advertise-size',\n /**\n * main \u2192 host-dialog WCV renderer: per-load MessagePort handshake, same\n * contract as `HostToolbarPort` (see that entry).\n */\n HostDialogPort: 'view:host-dialog:port',\n /**\n * Renderer \u2192 main: the window-level placement snapshot (one monotonic epoch\n * per commit tick, one generation per renderer lifetime) that drives the view\n * reconciler. The single source of truth for every managed native view's\n * bounds/visibility/z-order \u2014 supersedes the per-view bounds channels above.\n * invoke.\n */\n PlacementSnapshot: 'view:placement-snapshot',\n /**\n * main \u2190 main-window renderer (invoke): allocate a fresh placement\n * generation seed for this renderer bootstrap. See\n * renderer-placement-generation.ts for why the seed comes from main\n * (a wall-clock `Date.now()` seed is not guaranteed to exceed main's\n * still-standing high-water mark across two reloads that happen faster\n * than the clock's resolution). No payload; resolves a number.\n */\n AllocatePlacementGeneration: 'view:allocate-placement-generation',\n} as const\n\nexport interface ViewBounds {\n x: number\n y: number\n width: number\n height: number\n}\n\n// \u2500\u2500 Popover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const PopoverChannel = {\n Show: 'popover:show',\n Hide: 'popover:hide',\n // Popover \u2192 main \u2192 main-renderer: the edited compile modes, plus whether\n // the change affects what is currently running (selecting a mode, or\n // editing the selected one) and so needs a relaunch.\n Apply: 'popover:apply',\n Closed: 'popover:closed',\n Init: 'popover:init',\n} as const\n\n// \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.tooltip \u2014 see view-ids.ts),\n// NOT a DOM tooltip in the main renderer: any DOM-portaled floating UI (Radix\n// Tooltip) or the browser-native `title` attribute lives in the main\n// renderer's own paint surface, which every other WCV (simulator, editor,\n// settings, popover) renders on top of \u2014 CSS cannot reach across that\n// boundary. A trigger reports its anchor rect + label; main computes the\n// tooltip's screen bounds and shows/repositions/hides this overlay.\n\nexport const OverlayChannel = {\n Ready: 'overlay:ready',\n} as const\n\nexport const TooltipChannel = {\n Prepare: 'tooltip:prepare',\n Show: 'tooltip:show',\n Hide: 'tooltip:hide',\n Init: 'tooltip:init',\n Measured: 'tooltip:measured',\n} as const\n\n// \u2500\u2500 Project-create dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.dialog), NOT the Radix\n// `fixed inset-0` DOM portal it replaced \u2014 a DOM overlay paints inside the\n// main window's own renderer, so any native WebContentsView mounted on top\n// (simulator, host-toolbar, host-sidebar) occludes it. main.tsx fetches the\n// template catalog + base-dir defaults, then asks main to show this panel;\n// the panel relays submit/cancel back to main.tsx to run the existing\n// scaffold flow.\n\nexport const ProjectCreateChannel = {\n Show: 'projectCreate:show',\n Init: 'projectCreate:init',\n Submit: 'projectCreate:submit',\n Cancel: 'projectCreate:cancel',\n Submitted: 'projectCreate:submitted',\n} as const\n\n// \u2500\u2500 Device picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A top-tier overlay WebContentsView (VIEW_LAYER.dialog), for the same reason\n// as the project-create dialog above: the simulator's own WebContentsView is\n// mounted on top of the main renderer and overlaps the centred search panel,\n// so a DOM-portaled dialog is cut in half by it. The toolbar keeps only the\n// trigger button; it asks main to show this panel with the currently selected\n// device, and main relays the picked device name back to the toolbar.\n\nexport const DevicePickerChannel = {\n Show: 'devicePicker:show',\n Init: 'devicePicker:init',\n Select: 'devicePicker:select',\n Cancel: 'devicePicker:cancel',\n Selected: 'devicePicker:selected',\n} as const\n\n// \u2500\u2500 Embedded settings overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const SettingsChannel = {\n SetVisible: 'settings:setVisible',\n ProjectSettingsChanged: 'settings:projectSettingsChanged',\n Init: 'settings:init',\n} as const\n\n// \u2500\u2500 Updates (UpdateManager) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// String values are FROZEN: shipped builds key off them. Add new entries\n// here, never rename existing ones.\nexport const UpdateChannel = {\n Check: 'updates:check',\n Download: 'updates:download',\n Install: 'updates:install',\n DownloadProgress: 'updates:downloadProgress',\n Available: 'updates:available',\n Close: 'updates:close',\n} as const\n", "/**\n * Shared readiness wait for a host-slot content's shrink-to-fit measurement\n * root (`[data-host-*-root]`). A one-shot `document.querySelector` at\n * `DOMContentLoaded` time only works when the root is present in the initial\n * HTML. Framework-rendered content (e.g. a React app) can mount its root\n * asynchronously, after `DOMContentLoaded` has already fired \u2014 a one-shot\n * check misses it permanently and the slot's width/height is never\n * advertised. This keeps watching via `MutationObserver` instead of assuming\n * a synchronous mount, without polling on a fixed delay.\n *\n * The \"root is missing\" warning exists for downstream authors who forgot the\n * element entirely, so it must not fire on the healthy async-mount path \u2014 a\n * warning printed on every normal load is noise that trains its readers to\n * ignore it. Two conditions gate it, both signals rather than guessed delays:\n * the document must be one the host actually loaded content into (not the\n * view's own blank document), and it must have finished loading AND run out of\n * pending work (`window.load`, then an idle callback \u2014 a framework's first\n * commit is normal-priority work, so it always lands before idle). The\n * observer stays armed either way, so a root that mounts even later is still\n * picked up.\n */\nexport function whenSlotRootReady(\n selector: string,\n missingWarning: string,\n install: (root: HTMLElement) => void,\n): void {\n function attempt(): void {\n // The slot's WebContentsView holds its own empty document until the host\n // loads content into it, and this preload runs there too. That document\n // has no author: a missing root in it is not a mistake anyone made, and\n // warning about it fires on every app start. The observer below still gets\n // armed, so content that does arrive is picked up.\n const authored = location.href !== 'about:blank' && location.href !== ''\n\n const root = document.querySelector<HTMLElement>(selector)\n if (root) {\n install(root)\n return\n }\n\n let installed = false\n const observer = new MutationObserver(() => {\n const lateRoot = document.querySelector<HTMLElement>(selector)\n if (!lateRoot) return\n observer.disconnect()\n installed = true\n install(lateRoot)\n })\n observer.observe(document.documentElement, { childList: true, subtree: true })\n\n // Re-queries rather than trusting `installed`: the observer delivers on a\n // microtask, so a root that is already in the DOM may not have been\n // installed yet \u2014 it is present either way, which is what the warning is\n // about.\n const warnIfStillMissing = (): void => {\n if (installed || !authored || document.querySelector(selector)) return\n console.warn(missingWarning)\n }\n\n // `load` alone is too early a verdict: a concurrent React root commits its\n // first render on a scheduler task that can land after it, and the warning\n // would then race the healthy mount it is supposed to ignore. Idle\n // callbacks run only once the main thread has no pending work of that\n // kind, so a root still absent then is genuinely absent. The timeout is\n // the cap for a page that never goes idle, not the mechanism.\n const decide = (): void => {\n const idle = (globalThis as { requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number })\n .requestIdleCallback\n if (typeof idle === 'function') idle(warnIfStillMissing, { timeout: 5_000 })\n else warnIfStillMissing()\n }\n if (document.readyState === 'complete') decide()\n else window.addEventListener('load', decide, { once: true })\n }\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', attempt, { once: true })\n } else {\n attempt()\n }\n}\n", "/**\n * Shared implementation behind every host-slot page bridge (toolbar/sidebar/\n * dialog): the MessagePort handshake, pending-queue-with-bounded-overflow,\n * handler registry and `{ send, onMessage }` contextBridge exposure are\n * IDENTICAL across slots \u2014 only the bridge key, handshake channel, queue cap\n * and overflow-warning label differ. Each slot's own `host-*-port.ts` stays\n * the frozen public surface (see its own \"PUBLIC SURFACE EVOLUTION RULE\"\n * doc-comment) and calls this factory with its own constants; this module has\n * no public-surface contract of its own.\n */\nimport { contextBridge, ipcRenderer } from 'electron'\n\nexport interface HostSlotPortBridgeOptions {\n /** contextBridge key the page-facing API is exposed under, e.g. 'diminaHostToolbar'. */\n bridgeKey: string\n /** main \u2192 renderer per-load handshake channel, e.g. ViewChannel.HostToolbarPort. */\n channel: string\n /** Cap on pre-handshake queued sends; overflow drops the NEWEST envelope. */\n pendingLimit: number\n /** Slot label used in the overflow console.warn, e.g. 'host-toolbar'. */\n label: string\n}\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing per-slot runtime guard \u2014 a failing guard must leave zero\n * footprint (no bridge key, no IPC listener), so this factory itself does no\n * guard checking; the caller decides whether to call it at all.\n */\nexport function createHostSlotPortBridge(opts: HostSlotPortBridgeOptions): void {\n const { bridgeKey, channel, pendingLimit, label } = opts\n let activePort: MessagePort | null = null\n const pending: Array<{ channel: string; payload: unknown }> = []\n // One overflow warning per load (this installer runs once per document) \u2014\n // a runaway page send-loop must not get per-drop console spam.\n let warnedPendingOverflow = false\n const handlers: Array<{ channel: string; handler: (payload: unknown) => void }> = []\n\n // Entry-waist channel validation, parity with the main side's `onMessage`\n // guard (same TypeError, message names `channel`). Runs BEFORE any state is\n // touched (no queue slot, no registry entry), in both port states \u2014 a page\n // author's typo throws at the call site instead of vanishing into main's\n // silent inbound drop.\n function assertValidChannel(method: string, ch: unknown): asserts ch is string {\n if (typeof ch !== 'string' || ch === '') {\n throw new TypeError(`${bridgeKey}.${method}: channel must be a non-empty string`)\n }\n }\n\n const dispatch = (data: unknown): void => {\n // Defensive symmetry with main: drop anything that is not an object\n // envelope with a string channel \u2014 never throw in the dispatcher.\n if (typeof data !== 'object' || data === null) return\n const { channel: ch, payload } = data as { channel?: unknown; payload?: unknown }\n if (typeof ch !== 'string') return\n for (const entry of [...handlers]) {\n if (entry.channel === ch) entry.handler(payload)\n }\n }\n\n const onPortMessage = (event: MessageEvent): void => {\n dispatch(event.data)\n }\n\n ipcRenderer.on(channel, (event) => {\n const port = event.ports[0]\n if (!port) return\n // Same-load duplicate handshake: the LATER port wins. Main closed (or is\n // about to close) its end of the old pair \u2014 detach and stop writing to it.\n if (activePort) {\n try {\n activePort.removeEventListener('message', onPortMessage)\n activePort.close()\n } catch {\n /* already dead */\n }\n }\n activePort = port\n // addEventListener (not onmessage) keeps the handler removable on\n // re-handshake; it REQUIRES start() or inbound never delivers.\n port.addEventListener('message', onPortMessage)\n port.start()\n // Flush sends issued before the port arrived, in order.\n while (pending.length > 0) {\n const envelope = pending.shift()!\n port.postMessage(envelope)\n }\n })\n\n // EXACTLY { send, onMessage } \u2014 functions only; the port stays in the\n // isolated world.\n contextBridge.exposeInMainWorld(bridgeKey, {\n send(ch: string, payload: unknown): void {\n assertValidChannel('send', ch)\n const envelope = { channel: ch, payload }\n if (activePort) {\n activePort.postMessage(envelope)\n return\n }\n // Bounded queue: drop the NEWEST send on overflow (FIFO first-comers\n // survive), warn once per load, never throw into page code.\n if (pending.length >= pendingLimit) {\n if (!warnedPendingOverflow) {\n warnedPendingOverflow = true\n console.warn(\n `[dimina-devtools] ${label} pending queue full (${pendingLimit}); ` +\n 'dropping further pre-handshake send() calls until the port arrives',\n )\n }\n return\n }\n pending.push(envelope)\n },\n onMessage(ch: string, handler: (payload: unknown) => void): () => void {\n assertValidChannel('onMessage', ch)\n const entry = { channel: ch, handler }\n handlers.push(entry)\n return () => {\n const i = handlers.indexOf(entry)\n if (i >= 0) handlers.splice(i, 1)\n }\n },\n })\n}\n", "/**\n * Preload side of the host-toolbar gated narrow channel.\n *\n * \u2500\u2500 PUBLIC SURFACE EVOLUTION RULE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * `window.diminaHostToolbar` is a published page-facing API, typed by the\n * exported `DiminaHostToolbarPageBridge` (src/main/runtime/miniapp-runtime.ts)\n * \u2014 arbitrary host toolbar pages compile against it. Members may only be\n * ADDED, never changed: any semantic change to an existing member (signature,\n * return shape, queueing/throw behavior) ships under a NEW name instead.\n * Exception: security fixes / compliance corrections may change existing\n * member semantics, and MUST be called out in the release's version notes.\n * Keep the exported bridge type in lockstep with what is exposed here.\n *\n * Receives the per-load MessagePort main transfers on `did-finish-load`\n * (`ViewChannel.HostToolbarPort`, `event.ports[0]`) and bridges it to the page\n * as `window.diminaHostToolbar` \u2014 EXACTLY `{ send, onMessage }`, functions\n * only. The raw MessagePort never crosses into the main world: the page only\n * ever talks through these two functions, so the `{ channel, payload }`\n * envelope stays the single waist (same posture as the main side's\n * host-toolbar-port-channel.ts).\n *\n * Ordering reality this module absorbs:\n * - The page script runs BEFORE the handshake can complete (the port is\n * posted on did-finish-load). Page `send()`s issued before the port\n * arrives go into a PENDING QUEUE and flush in order on handshake \u2014\n * without it the first message of every load is silently dropped. The\n * queue is BOUNDED at `HOST_TOOLBAR_PENDING_LIMIT` (128): the toolbar page\n * is arbitrary host content, and a page whose handshake never arrives must\n * not grow main-world-driven memory without limit. Overflow drops the\n * NEWEST send (boot sequences front-load their important messages; the\n * first-comers survive), warns ONCE per load, and never throws into page\n * code.\n * - Page handlers likewise register before the port exists; the registry is\n * module-level (per-load \u2014 the preload re-runs on every navigation) and is\n * re-attached to each newly delivered port, so a same-load re-handshake\n * keeps existing handlers alive. The LATER port wins for sends.\n * - Inbound dispatch uses `addEventListener('message')` + `start()`\n * (without `start()` a DOM MessagePort never delivers) and DROPS malformed\n * envelopes without throwing.\n *\n * Shared implementation: see `host-slot-port-bridge.ts` (the toolbar/sidebar/\n * dialog port bridges are otherwise identical; only the constants below\n * differ).\n */\n\nimport { ViewChannel } from '../../shared/ipc-channels-overlays.js'\nimport { createHostSlotPortBridge } from './host-slot-port-bridge.js'\n\n/**\n * Cap on pre-handshake queued sends. 128 comfortably covers any sane toolbar\n * boot sequence while bounding what a page whose handshake never completes\n * can make the isolated world retain. Overflow policy: drop the NEWEST\n * envelope (queued first-comers survive), one console.warn per load.\n */\nexport const HOST_TOOLBAR_PENDING_LIMIT = 128\n\n/**\n * Subscribe the handshake channel and expose the page bridge. Call ONLY from\n * a passing toolbar-runtime guard (`activateHostToolbarRuntime`) \u2014 a failing\n * guard must leave zero footprint (no bridge key, no IPC listener).\n */\nexport function installHostToolbarPortBridge(): void {\n createHostSlotPortBridge({\n bridgeKey: 'diminaHostToolbar',\n channel: ViewChannel.HostToolbarPort,\n pendingLimit: HOST_TOOLBAR_PENDING_LIMIT,\n label: 'host-toolbar',\n })\n}\n", "/**\n * Session-resident host-toolbar framework runtime: GUARD + ACTIVATION.\n *\n * The toolbar-runtime preload is registered on `session.defaultSession` via\n * `registerPreloadScript({ type: 'frame', \u2026 })`, so it executes in EVERY\n * defaultSession renderer \u2014 the main window, settings/popover overlays, and\n * (with `nodeIntegrationInSubFrames`) even subframes. This module is the guard\n * that keeps it inert everywhere except the host-toolbar WCV's main frame:\n *\n * - the toolbar WCV's creation injects `HOST_TOOLBAR_RUNTIME_MARKER` into its\n * process argv via `webPreferences.additionalArguments`;\n * - the marker is PROCESS-level (subframes of the toolbar window carry it\n * too), so the guard needs BOTH wings: `isMainFrame` AND\n * `argv.includes(marker)`;\n * - a renderer that fails the guard returns immediately with ZERO footprint\n * (no advertiser, no listeners, no globals).\n *\n * Why session-resident at all: riding the toolbar WCV's\n * `webPreferences.preload` lets a host calling `setPreloadPath(<its own\n * preload>)` REPLACE the advertiser, collapsing the strip height to 0. With the\n * runtime on the session layer, the host preload and the framework runtime\n * coexist.\n */\n\nimport { HOST_TOOLBAR_RUNTIME_MARKER } from '../../shared/constants.js'\nimport { installHostToolbarAdvertiserWhenReady } from './host-toolbar-advertiser.js'\nimport { installHostToolbarPortBridge } from './host-toolbar-port.js'\n\n/**\n * Pure guard predicate: should the toolbar runtime activate in a renderer with\n * this `argv` / `isMainFrame`? True only for the MAIN frame of a process whose\n * argv carries the `'--dimina-host-toolbar'` marker.\n */\nexport function shouldActivateHostToolbarRuntime(\n argv: readonly string[],\n isMainFrame: boolean,\n): boolean {\n return isMainFrame && argv.includes(HOST_TOOLBAR_RUNTIME_MARKER)\n}\n\n/**\n * Run the guard; only when it passes, install the height advertiser\n * (`installHostToolbarAdvertiserWhenReady`) and the narrow-channel page\n * bridge (`installHostToolbarPortBridge` \u2014 `window.diminaHostToolbar` +\n * the MessagePort handshake listener). Returns whether the runtime\n * activated. A failed guard installs NOTHING (zero footprint in non-toolbar\n * windows and subframes: no advertiser, no bridge key, no IPC listener).\n */\nexport function activateHostToolbarRuntime(env: {\n argv: readonly string[]\n isMainFrame: boolean\n}): boolean {\n if (!shouldActivateHostToolbarRuntime(env.argv, env.isMainFrame)) return false\n installHostToolbarAdvertiserWhenReady()\n installHostToolbarPortBridge()\n return true\n}\n", "// Session-registered toolbar-runtime preload entry.\n//\n// Bundled into a single CJS file (dist/preload/windows/host-toolbar-runtime.cjs)\n// via build:preload and registered ONCE per process on `session.defaultSession`\n// (`registerPreloadScript({ type: 'frame', \u2026 })` \u2014 see\n// `src/main/services/views/host-toolbar-session-runtime.ts`). It therefore runs\n// in EVERY defaultSession renderer; `activateHostToolbarRuntime`'s guard\n// (`process.isMainFrame && process.argv.includes('--dimina-host-toolbar')`)\n// makes it a zero-footprint no-op everywhere except the host-toolbar WCV's\n// main frame, where it installs the reverse size-advertiser.\nimport { activateHostToolbarRuntime } from '../runtime/host-toolbar-runtime.js'\n\nactivateHostToolbarRuntime({ argv: process.argv, isMainFrame: process.isMainFrame })\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_electron"]
|
|
7
7
|
}
|