@dimina-kit/devtools 0.4.0-dev.20260707070110 → 0.4.0-dev.20260707110006

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.
Files changed (28) hide show
  1. package/dist/main/app/app.js +1 -1
  2. package/dist/main/index.bundle.js +3 -3
  3. package/dist/main/ipc/bridge-router.d.ts +1 -1
  4. package/dist/main/ipc/bridge-router.js +14 -11
  5. package/dist/main/runtime/miniapp-runtime.d.ts +4 -3
  6. package/dist/main/services/render-inspect/index.d.ts +1 -1
  7. package/dist/main/services/render-inspect/index.js +1 -1
  8. package/dist/main/services/service-host-pool/pool.d.ts +4 -4
  9. package/dist/main/services/service-host-pool/pool.js +5 -5
  10. package/dist/main/services/simulator-temp-files/disk.d.ts +1 -1
  11. package/dist/main/services/simulator-temp-files/disk.js +1 -1
  12. package/dist/main/services/simulator-temp-files/fs-channels.d.ts +1 -1
  13. package/dist/main/services/simulator-temp-files/fs-channels.js +1 -1
  14. package/dist/main/services/simulator-temp-files/request-handler.d.ts +1 -1
  15. package/dist/main/services/simulator-temp-files/request-handler.js +1 -1
  16. package/dist/preload/windows/host-toolbar-runtime.cjs +1 -1
  17. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  18. package/dist/preload/windows/main.cjs.map +1 -1
  19. package/dist/shared/vpath.d.ts +1 -1
  20. package/dist/shared/vpath.js +1 -1
  21. package/dist/vscode-workbench/assets/__vite-browser-external-D6-QnwSb.js +1 -0
  22. package/dist/vscode-workbench/assets/{dist-wJUFfrkb.js → dist-OuMzPA6f.js} +3 -3
  23. package/dist/vscode-workbench/assets/{iconv-lite-umd-O-GPtKy4.js → iconv-lite-umd-a5yQx8hi.js} +1 -1
  24. package/dist/vscode-workbench/assets/{index-DyxjKeLl.js → index-CVEUw5jw.js} +379 -379
  25. package/dist/vscode-workbench/assets/{jschardet-BorbxV40.js → jschardet-BYkY0M2Q.js} +1 -1
  26. package/dist/vscode-workbench/index.html +1 -1
  27. package/package.json +4 -4
  28. package/dist/vscode-workbench/assets/__vite-browser-external-CjtF3dcZ.js +0 -1
@@ -356,7 +356,7 @@ export async function createDevtoolsRuntime(config = {}) {
356
356
  const context = createContext(config, mainWindow, rendererDir);
357
357
  // Anchor the main window's renderer as the first Connection. Resources
358
358
  // scoped to the main webContents (acquired by later wiring) tear down with
359
- // it; see packages/electron-deck/docs/foundation.md §4.
359
+ // it; see packages/electron-deck/docs/foundation.md (teardown paths).
360
360
  context.connections.acquire(mainWindow.webContents);
361
361
  context.registry.add(registerAppIpc(context));
362
362
  // Sandboxed project file-system IPC (the renderer-side project:fs:* surface).
@@ -5298,7 +5298,7 @@ var ServiceHostPool = class {
5298
5298
  /**
5299
5299
  * Hand a ready window matching `spec` to the caller. If none is ready, fall
5300
5300
  * back to synchronously constructing a fresh window — never blocking on warm
5301
- * (doc §3.1 "绝不阻塞 acquire"). Fallback windows carry `entryId === null` and
5301
+ * (see prewarm-webview.md's invariants section). Fallback windows carry `entryId === null` and
5302
5302
  * are destroyed (not pooled) on release.
5303
5303
  *
5304
5304
  * A spec change (different `preloadPath`) tears down all *pooled* entries and
@@ -5374,7 +5374,7 @@ var ServiceHostPool = class {
5374
5374
  }
5375
5375
  /**
5376
5376
  * Re-target the pool. `target` is clamped to `[0, maxPoolSize]`. Pooled
5377
- * entries beyond the target are disposed OLDEST-FIRST (doc §3.3/§3.6); in-use
5377
+ * entries beyond the target are disposed OLDEST-FIRST (see prewarm-webview.md); in-use
5378
5378
  * entries are untouched. Does not warm new entries up (use `init` / refill).
5379
5379
  */
5380
5380
  resize(target) {
@@ -5503,7 +5503,7 @@ var ServiceHostPool = class {
5503
5503
  }
5504
5504
  /**
5505
5505
  * Reset a released window back to a clean, blank state. Order matters
5506
- * (doc §3.5.3 / §3.4「等待时序」): navigate to blank FIRST so the old document
5506
+ * (see prewarm-webview.md's state-machine and reset-ordering sections): navigate to blank FIRST so the old document
5507
5507
  * stops running and in-flight requests are aborted, THEN clear storage — so a
5508
5508
  * still-live old page cannot re-populate storage after the clear.
5509
5509
  */
@@ -108,7 +108,7 @@ export interface BridgeRouterHandle {
108
108
  */
109
109
  census?(): BridgeResourceCensus;
110
110
  /**
111
- * The flag-gated debugTap (§7) over the bridge message stream. Exposed so a
111
+ * The flag-gated debugTap (see foundation.md) over the bridge message stream. Exposed so a
112
112
  * hidden devtools panel / automation can read `.entries()` when
113
113
  * `DIMINA_DEBUG_TAP=1`; a no-op snapshot otherwise. Optional so the many
114
114
  * partial `BridgeRouterHandle` test mocks don't each have to stub it.
@@ -63,10 +63,11 @@ const STACK_ID = 'stack_0';
63
63
  * constant instead of a magic number.
64
64
  */
65
65
  export const LAUNCH_TIMEOUT_MS = 20_000;
66
- /** Hard ceiling on the pre-warm pool, mirroring ServiceHostPool/doc §3.3. */
66
+ /** Hard ceiling on the pre-warm pool, mirroring ServiceHostPool/prewarm-webview.md. */
67
67
  const PREWARM_MAX_POOL_SIZE = 4;
68
68
  /**
69
- * Resolve the pre-warm pool size from env (doc §6). Returns 0 (OFF) unless
69
+ * Resolve the pre-warm pool size from env (see prewarm-webview.md's config
70
+ * section). Returns 0 (OFF) unless
70
71
  * `DIMINA_PREWARM_POOL_SIZE` is a positive integer and `DIMINA_PREWARM_DISABLE`
71
72
  * is not set. Default OFF — pooling is opt-in so the spawn path is unchanged
72
73
  * unless explicitly enabled.
@@ -82,7 +83,7 @@ function resolvePrewarmPoolSize() {
82
83
  return 0;
83
84
  return Math.min(n, PREWARM_MAX_POOL_SIZE);
84
85
  }
85
- /** debugTap (§7) is opt-in via `DIMINA_DEBUG_TAP=1` — off everywhere else. */
86
+ /** debugTap (see foundation.md) is opt-in via `DIMINA_DEBUG_TAP=1` — off everywhere else. */
86
87
  function resolveDebugTapEnabled() {
87
88
  return process.env.DIMINA_DEBUG_TAP === '1';
88
89
  }
@@ -181,8 +182,8 @@ export function installBridgeRouter(ctx) {
181
182
  if (prewarmPoolSize > 0) {
182
183
  const pool = new ServiceHostPool();
183
184
  state.pool = pool;
184
- // Defer warm-up off the cold-start critical path (doc §3.3: app.ready +
185
- // idle). Warming eagerly here races main-window startup — it creates an
185
+ // Defer warm-up off the cold-start critical path (see prewarm-webview.md:
186
+ // app.ready + idle). Warming eagerly here races main-window startup — it creates an
186
187
  // extra hidden BrowserWindow before the workbench renderer has settled,
187
188
  // which made the e2e's first-window/preload-readiness flaky. The timer is
188
189
  // cancelled on teardown so it can't warm a disposed pool.
@@ -451,7 +452,7 @@ export function installBridgeRouter(ctx) {
451
452
  };
452
453
  ipcMain.on(C.DISPOSE, onDispose);
453
454
  ctx.registry.add(() => { ipcMain.removeListener(C.DISPOSE, onDispose); });
454
- // debugTap (§7) ingress recorder — near-free no-op unless DIMINA_DEBUG_TAP=1.
455
+ // debugTap (see foundation.md) ingress recorder — near-free no-op unless DIMINA_DEBUG_TAP=1.
455
456
  // Hung on the bridge dispatch chokepoint so the cross-wc message flow is
456
457
  // inspectable (the first real consumer of the workbench debugTap primitive).
457
458
  const tapIn = (channel, sender, payload) => {
@@ -753,7 +754,8 @@ async function handleSpawn(state, ctx, event, opts) {
753
754
  appSession.pages.set(bridgeId, rootPage);
754
755
  bindWc(state.serviceWcIdToAppSessionId, serviceWindow.webContents, appSessionId);
755
756
  bindSimulatorWc(state.simulatorWcIdToAppSessionIds, simulatorWc, appSessionId);
756
- // Track the service-host webContents as a Connection (foundation.md §4.3) and
757
+ // Track the service-host webContents as a Connection (the soft-reuse
758
+ // teardown path documented in foundation.md) and
757
759
  // own this session's serviceWc→appSessionId binding on its CURRENT lifetime
758
760
  // segment. On a pooled-window REUSE, `disposeAppSession` calls
759
761
  // `connections.reset(serviceWc.id)` → this owned cleanup fires + a fresh
@@ -761,7 +763,8 @@ async function handleSpawn(state, ctx, event, opts) {
761
763
  // real destroy the connection closes and fires it too. The cleanup is guarded
762
764
  // by `appSessionId` so it only clears a binding that is still THIS session's.
763
765
  // (The simulatorWc binding stays manual in `disposeAppSession`: the simulator
764
- // wc outlives individual app sessions — cross-connection state, §4.4 — so it
766
+ // wc outlives individual app sessions — cross-connection state documented in
767
+ // foundation.md — so it
765
768
  // cannot hang off any single session's segment.)
766
769
  state.connections.acquire(serviceWindow.webContents).own(() => {
767
770
  if (state.serviceWcIdToAppSessionId.get(appSession.serviceWc.id) === appSessionId) {
@@ -1732,8 +1735,8 @@ function ensureRenderBound(state, sender, bridgeId) {
1732
1735
  page.renderWc = sender;
1733
1736
  state.wcIdToBridgeId.set(sender.id, bridgeId);
1734
1737
  // Consolidate the render-guest bookkeeping teardown onto the connection
1735
- // layer (foundation.md §4 / P2): the render guest is its own webContents =
1736
- // its own connection (§4.4); own() ties this cleanup to its lifetime so it
1738
+ // layer (see foundation.md): the render guest is its own webContents =
1739
+ // its own connection; own() ties this cleanup to its lifetime so it
1737
1740
  // fires on destroy, replacing the bespoke `once('destroyed')`. acquire() is
1738
1741
  // idempotent — re-binding the same sender re-uses its connection.
1739
1742
  state.connections.acquire(sender).own(() => {
@@ -1874,7 +1877,7 @@ function closeSessionPages(state, ap) {
1874
1877
  // the unpooled path.
1875
1878
  async function releaseServiceWindow(state, ap, opts) {
1876
1879
  if (ap.poolEntryId !== null && state.pool && !opts.serviceAlreadyClosed) {
1877
- // Soft reuse (foundation.md §4.3): the pooled service-host webContents keeps
1880
+ // Soft reuse (see foundation.md): the pooled service-host webContents keeps
1878
1881
  // its wc.id but is about to run a NEW app session. Reset its Connection
1879
1882
  // BEFORE returning it to the pool — dispose this session's owned segment
1880
1883
  // (the serviceWc binding cleanup) and swap a fresh one — so an old in-flight
@@ -1,6 +1,6 @@
1
1
  /**
2
- * MiniappRuntime — the stable, host-facing contract surface (foundation.md §3
3
- * "MiniappRuntime 契约" layer).
2
+ * MiniappRuntime — the stable, host-facing contract surface documented in
3
+ * host-migration.md's public-contract section.
4
4
  *
5
5
  * HAND-WRITTEN, not derived from `WorkbenchContext`. A `Pick`-style projection
6
6
  * would drag every nested internal service type (ViewManager,
@@ -18,7 +18,8 @@
18
18
  * - `workspace.openProject` stays writable (no `readonly`) for backward
19
19
  * compat: hosts MAY still gate permissions by reassigning it. The preferred
20
20
  * path is now the declarative `WorkbenchAppConfig.onBeforeOpenProject` hook
21
- * (runs before any side effect, throw to veto) — see host-migration.md §7.
21
+ * (runs before any side effect, throw to veto) — see host-migration.md's
22
+ * `onBeforeOpenProject` hook section.
22
23
  *
23
24
  * `asMiniappRuntime(ctx)` is an identity return — the contract is a typed
24
25
  * VIEW onto the live context, not a snapshot/projection. That is what makes
@@ -38,7 +38,7 @@ export interface RenderInspectorOptions {
38
38
  /** Override the injected IIFE source (default: read the built bundle). Test seam. */
39
39
  loadSource?: () => string;
40
40
  /**
41
- * Connection registry (foundation.md §4 / P2). When provided, the per-wc
41
+ * Connection registry (see foundation.md's teardown-paths section). When provided, the per-wc
42
42
  * `injected` bookkeeping is torn down via the wc's connection (deterministic
43
43
  * with the rest of that webContents's resources) instead of a bespoke
44
44
  * `once('destroyed')`. Optional so focused unit tests can omit it (they fall
@@ -71,7 +71,7 @@ export function createRenderInspector(options = {}) {
71
71
  }
72
72
  injected.add(wc.id);
73
73
  // Consolidate the per-wc bookkeeping teardown onto the connection layer
74
- // (foundation.md §4 / P2) when a registry is available; fall back to the
74
+ // (see foundation.md) when a registry is available; fall back to the
75
75
  // bespoke `once('destroyed')` only when omitted (focused unit tests).
76
76
  const forget = () => {
77
77
  injected.delete(wc.id);
@@ -94,7 +94,7 @@ export interface ServiceHostPoolInitOptions {
94
94
  defaultPoolSize: number;
95
95
  /** Spec used to warm the initial entries. */
96
96
  defaultSpec: ServiceHostSpec;
97
- /** Hard ceiling on pooled entries. Clamped to ≤ 4 (doc §3.3). Default 3. */
97
+ /** Hard ceiling on pooled entries. Clamped to ≤ 4 (see prewarm-webview.md). Default 3. */
98
98
  maxPoolSize?: number;
99
99
  }
100
100
  export declare class ServiceHostPool {
@@ -116,7 +116,7 @@ export declare class ServiceHostPool {
116
116
  /**
117
117
  * Hand a ready window matching `spec` to the caller. If none is ready, fall
118
118
  * back to synchronously constructing a fresh window — never blocking on warm
119
- * (doc §3.1 "绝不阻塞 acquire"). Fallback windows carry `entryId === null` and
119
+ * (see prewarm-webview.md's invariants section). Fallback windows carry `entryId === null` and
120
120
  * are destroyed (not pooled) on release.
121
121
  *
122
122
  * A spec change (different `preloadPath`) tears down all *pooled* entries and
@@ -144,7 +144,7 @@ export declare class ServiceHostPool {
144
144
  releaseDestroyed(entryId: string | null): void;
145
145
  /**
146
146
  * Re-target the pool. `target` is clamped to `[0, maxPoolSize]`. Pooled
147
- * entries beyond the target are disposed OLDEST-FIRST (doc §3.3/§3.6); in-use
147
+ * entries beyond the target are disposed OLDEST-FIRST (see prewarm-webview.md); in-use
148
148
  * entries are untouched. Does not warm new entries up (use `init` / refill).
149
149
  */
150
150
  resize(target: number): void;
@@ -170,7 +170,7 @@ export declare class ServiceHostPool {
170
170
  private warmOne;
171
171
  /**
172
172
  * Reset a released window back to a clean, blank state. Order matters
173
- * (doc §3.5.3 / §3.4「等待时序」): navigate to blank FIRST so the old document
173
+ * (see prewarm-webview.md's state-machine and reset-ordering sections): navigate to blank FIRST so the old document
174
174
  * stops running and in-flight requests are aborted, THEN clear storage — so a
175
175
  * still-live old page cannot re-populate storage after the clear.
176
176
  */
@@ -1,9 +1,9 @@
1
1
  import { BrowserWindow } from 'electron';
2
2
  /** Blank placeholder URL loaded while warming / after reset. */
3
3
  const BLANK_URL = 'about:blank';
4
- /** Hard ceiling on pool size regardless of requested size (doc §3.3). */
4
+ /** Hard ceiling on pool size regardless of requested size (see prewarm-webview.md). */
5
5
  const HARD_MAX_POOL_SIZE = 4;
6
- /** Storage buckets cleared on every release (doc §3.4 "必须" rows). */
6
+ /** Storage buckets cleared on every release (see prewarm-webview.md's reset checklist). */
7
7
  const RESET_STORAGES = [
8
8
  'cookies',
9
9
  'localstorage',
@@ -37,7 +37,7 @@ export class ServiceHostPool {
37
37
  /**
38
38
  * Hand a ready window matching `spec` to the caller. If none is ready, fall
39
39
  * back to synchronously constructing a fresh window — never blocking on warm
40
- * (doc §3.1 "绝不阻塞 acquire"). Fallback windows carry `entryId === null` and
40
+ * (see prewarm-webview.md's invariants section). Fallback windows carry `entryId === null` and
41
41
  * are destroyed (not pooled) on release.
42
42
  *
43
43
  * A spec change (different `preloadPath`) tears down all *pooled* entries and
@@ -129,7 +129,7 @@ export class ServiceHostPool {
129
129
  }
130
130
  /**
131
131
  * Re-target the pool. `target` is clamped to `[0, maxPoolSize]`. Pooled
132
- * entries beyond the target are disposed OLDEST-FIRST (doc §3.3/§3.6); in-use
132
+ * entries beyond the target are disposed OLDEST-FIRST (see prewarm-webview.md); in-use
133
133
  * entries are untouched. Does not warm new entries up (use `init` / refill).
134
134
  */
135
135
  resize(target) {
@@ -275,7 +275,7 @@ export class ServiceHostPool {
275
275
  }
276
276
  /**
277
277
  * Reset a released window back to a clean, blank state. Order matters
278
- * (doc §3.5.3 / §3.4「等待时序」): navigate to blank FIRST so the old document
278
+ * (see prewarm-webview.md's state-machine and reset-ordering sections): navigate to blank FIRST so the old document
279
279
  * stops running and in-flight requests are aborted, THEN clear storage — so a
280
280
  * still-live old page cannot re-populate storage after the clear.
281
281
  */
@@ -2,7 +2,7 @@
2
2
  * Main-process disk reader/writer backing `difile://_store/*` and
3
3
  * `difile://<usr-rel>` URLs.
4
4
  *
5
- * Spec: `packages/devtools/docs/file-system.md` §6 P1-1 / P1-3.
5
+ * Spec: `packages/devtools/docs/file-system.md`.
6
6
  *
7
7
  * - `readDiskFile(realPath, opts?)`: read a (possibly ranged) slice of a
8
8
  * file inside the USER_DATA_PATH sandbox. Returns `bytes` plus the
@@ -2,7 +2,7 @@
2
2
  * Main-process disk reader/writer backing `difile://_store/*` and
3
3
  * `difile://<usr-rel>` URLs.
4
4
  *
5
- * Spec: `packages/devtools/docs/file-system.md` §6 P1-1 / P1-3.
5
+ * Spec: `packages/devtools/docs/file-system.md`.
6
6
  *
7
7
  * - `readDiskFile(realPath, opts?)`: read a (possibly ranged) slice of a
8
8
  * file inside the USER_DATA_PATH sandbox. Returns `bytes` plus the
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Main-process `simulator:fs:*` IPC channel handlers.
3
3
  *
4
- * Spec: `packages/devtools/docs/file-system.md` §4.4, §6 P1-7.
4
+ * Spec: `packages/devtools/docs/file-system.md`.
5
5
  *
6
6
  * Each handler is the pure function that backs one IPC channel:
7
7
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Main-process `simulator:fs:*` IPC channel handlers.
3
3
  *
4
- * Spec: `packages/devtools/docs/file-system.md` §4.4, §6 P1-7.
4
+ * Spec: `packages/devtools/docs/file-system.md`.
5
5
  *
6
6
  * Each handler is the pure function that backs one IPC channel:
7
7
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Pure dispatcher for `difile://` URL requests.
3
3
  *
4
- * Spec: `packages/devtools/docs/file-system.md` §4.3.
4
+ * Spec: `packages/devtools/docs/file-system.md`.
5
5
  *
6
6
  * The shipping implementation in `index.ts` registers a thin
7
7
  * `simSession.protocol.handle('difile')` wrapper that delegates here; the race
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Pure dispatcher for `difile://` URL requests.
3
3
  *
4
- * Spec: `packages/devtools/docs/file-system.md` §4.3.
4
+ * Spec: `packages/devtools/docs/file-system.md`.
5
5
  *
6
6
  * The shipping implementation in `index.ts` registers a thin
7
7
  * `simSession.protocol.handle('difile')` wrapper that delegates here; the race
@@ -78,7 +78,7 @@ function createSizeAdvertiser(target, opts) {
78
78
  const doc = target.ownerDocument;
79
79
  if (target === doc.body || target === doc.documentElement) {
80
80
  console.warn(
81
- `[view-anchor] size-advertiser: <${target === doc.body ? "body" : "html"}>'s ${axis} size is the host-given view size, not the content size \u2014 the advertiser will never shrink to content. Measure a shrink-to-fit wrapper. See bidirectional-design.md \xA74.`
81
+ `[view-anchor] size-advertiser: <${target === doc.body ? "body" : "html"}>'s ${axis} size is the host-given view size, not the content size \u2014 the advertiser will never shrink to content. Measure a shrink-to-fit wrapper. See bidirectional-design.md's single-axis-ownership section.`
82
82
  );
83
83
  }
84
84
  loop.setActive(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.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 * 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.js'\n\n/**\n * Attach the advertiser to the toolbar content's shrink-to-fit root\n * (`[data-host-toolbar-root]`). That element MUST be shrink-to-fit on the block\n * axis \u2014 its height must reflect the content, not the host-applied view height,\n * or the cross-process loop never converges (`createSizeAdvertiser`'s footgun).\n * If the element is missing we warn and no-op rather than measure `<body>`,\n * whose block size IS the view size and would advertise nonsense.\n *\n * Returns a disposer that tears the advertiser down.\n */\nexport function installHostToolbarAdvertiser(): () => void {\n const root = document.querySelector<HTMLElement>('[data-host-toolbar-root]')\n if (!root) {\n console.warn(\n '[host-toolbar-advertiser] no `[data-host-toolbar-root]` element found \u2014 ' +\n 'not advertising a height. 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 )\n return () => {}\n }\n\n const advertiser = createSizeAdvertiser(root, {\n axis: 'block',\n publish: (size) => {\n ipcRenderer.send(ViewChannel.HostToolbarAdvertiseHeight, size)\n },\n })\n\n return () => advertiser.dispose()\n}\n\n/**\n * Install once the DOM is ready (the root element must exist before we query).\n * Self-gating: if the document is already past `loading`, install synchronously;\n * otherwise wait for `DOMContentLoaded`.\n */\nexport function installHostToolbarAdvertiserWhenReady(): void {\n if (document.readyState === 'loading') {\n document.addEventListener(\n 'DOMContentLoaded',\n () => {\n installHostToolbarAdvertiser()\n },\n { once: true },\n )\n } else {\n installHostToolbarAdvertiser()\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` \u00A74/\u00A75.\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 \u00A74.`,\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 * Centralised IPC channel name constants for dimina-devtools.\n *\n * Every raw channel string used across main, renderer, preload and e2e code\n * should reference one of these constants so that renaming a channel only\n * requires a single edit.\n */\n\n// \u2500\u2500 Simulator (preload \u2192 host / renderer \u2194 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\n\nexport const SimulatorChannel = {\n // Ask main to create the simulator as a top-level WebContentsView (so nested\n // render-host <webview>s can attach). Native-host is the sole runtime.\n AttachNative: 'simulator:attach-native',\n // Renderer pushes the selected device's LOGICAL metrics (screen size,\n // pixelRatio, statusBarHeight, \u2026) when the device dropdown changes. Main\n // maps it to a HostEnvSnapshot and live-updates the running service-host\n // window \u2014 the authoritative `wx.getSystemInfoSync()` source \u2014 so the\n // mini-app sees the selected device without a relaunch.\n SetDeviceInfo: 'simulator:set-device-info',\n // Ask main to soft-reload the LIVE simulator WCV after a watcher rebuild:\n // main forwards a SIMULATOR_EVENTS.RELAUNCH into the shell (which boots a\n // new app session and swaps when ready) instead of destroying the view.\n // Resolves false when there is no live+ready shell \u2014 the renderer then falls\n // back to the hard AttachNative rebuild.\n SoftReload: 'simulator:soft-reload',\n Detach: 'simulator:detach',\n Console: 'simulator:console',\n // Main \u2192 renderer push of the visible top-of-stack page route whenever the\n // mini-app navigates (navigateTo / switchTab / back). Payload: the page path\n // string (same bare format as `getCurrentPagePath`), or '' when unknown.\n CurrentPage: 'simulator:current-page',\n} as const\n\n/** iPhone bezel cutout family driving the device-shell notch visual. */\nexport type NotchType = 'none' | 'notch' | 'dynamic-island'\n\n/** Per-device safe-area insets in CSS px (portrait). */\nexport interface SafeAreaInsets {\n top: number\n right: number\n bottom: number\n left: number\n}\n\n/**\n * Logical device metrics pushed by the renderer device dropdown under\n * native-host (`SimulatorChannel.SetDeviceInfo`). Mirrors a row of the renderer\n * `DEVICES` table; main maps it onto a `HostEnvSnapshot` for the service-host\n * window so `wx.getSystemInfoSync()` reflects the selected device, relays it to\n * the simulator WCV (DeviceShell: bezel size + status bar + notch), and drives\n * the CSS `env(safe-area-inset-*)` override on render-host guests.\n */\nexport interface NativeDeviceInfo {\n brand: string\n model: string\n system: string\n platform: string\n pixelRatio: number\n screenWidth: number\n screenHeight: number\n statusBarHeight: number\n notchType: NotchType\n safeAreaInsets: SafeAreaInsets\n}\n\n// \u2500\u2500 Service host (main \u2192 hidden service-host window) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const ServiceHostChannel = {\n /**\n * NATIVE-HOST ONLY. Live-update the service-host window's host-env snapshot\n * (device metrics) so subsequent `wx.getSystemInfoSync()` reflects a device\n * change without a relaunch. The service-host preload mutates\n * `__diminaSpawnContext.hostEnvSnapshot` in place (see `service-host/preload.cjs`).\n */\n HostEnvUpdate: 'service-host:host-env:update',\n} as const\n\n// \u2500\u2500 Custom simulator APIs (downstream-registered, main-process handlers) \u2500\u2500\n// invoke: forwards an API call to the registry; result/reject propagates.\n//\n// This is an ipcMain.handle channel invoked by the **main-window renderer**\n// only (trusted host). The simulator guest never reaches it directly \u2014 it\n// reaches the host via the bridge channels below, so the sender-policy can keep\n// the simulator off the IPC white-list.\nexport const SimulatorCustomApiChannel = {\n Invoke: 'simulator:custom-apis:invoke',\n} as const\n\n// \u2500\u2500 Custom APIs bridge (simulator \u2192 host) \u2500\u2500\n// payload = { id, op: 'list' } | { id, op: 'invoke', name, params } for Request,\n// { id, result } | { id, error } for Response. Transport (in\n// `src/preload/runtime/custom-apis.ts`): native-host \u2014 the simulator is a\n// top-level WebContentsView (no embedder), so Request goes via\n// `ipcRenderer.send` \u2192 `ipcMain.on` dispatcher bound to that simWc (view-manager\n// `attachNativeCustomApiBridge`) \u2192 Response via `simWc.send`. (The old\n// renderer-proxied `<webview>` transport, `ipcRenderer.sendToHost` \u2192\n// `<webview>.send`, no longer exists \u2014 native-host is the sole runtime.)\n//\n// Request/response are correlated by `id` so multiple concurrent invokes can\n// be in flight at once.\nexport const SimulatorCustomApiBridgeChannel = {\n Request: 'simulator:custom-apis:bridge-request',\n Response: 'simulator:custom-apis:bridge-response',\n} as const\n\n// \u2500\u2500 Storage (CDP-backed; main process attaches the debugger to the\n// simulator guest and forwards DOMStorage events to the renderer) \u2500\u2500\nexport const SimulatorStorageChannel = {\n GetSnapshot: 'simulator:storage:snapshot',\n GetActivePrefix: 'simulator:storage:activePrefix',\n Set: 'simulator:storage:set',\n Remove: 'simulator:storage:remove',\n Clear: 'simulator:storage:clear',\n ClearAll: 'simulator:storage:clearAll',\n Event: 'simulator:storage:event',\n} as const\n\nexport interface StorageItem { key: string; value: string }\nexport type StorageEvent =\n | { type: 'added'; key: string; newValue: string }\n | { type: 'updated'; key: string; oldValue: string; newValue: string }\n | { type: 'removed'; key: string }\n | { type: 'cleared' }\n\nexport type StorageWriteResult =\n | { ok: true }\n | { ok: false; error: string }\n\n/**\n * A storage mutation reported by the service-host's SYNC wx storage APIs\n * (`setStorageSync`/`removeStorageSync`/`clearStorageSync`). Those run inside the\n * service-host window and write `localStorage` directly, so \u2014 unlike the async\n * path (`runtimeInvoke`) and the panel's own writes \u2014 they never pass through\n * main and would otherwise leave the Storage panel stale until a manual reload.\n * The service-host posts this over `DiminaServiceBridge` as a `storageChanged`\n * container message; bridge-router hands it to `onServiceStorageChanged`, which\n * pushes the matching `StorageEvent` to the panel. `key` carries the full\n * `${appId}_` prefix (same wire shape as the CDP / async paths).\n */\nexport type SyncStorageChange =\n | { op: 'set'; key: string; value: string }\n | { op: 'remove'; key: string }\n | { op: 'clear' }\n\n// \u2500\u2500 Element inspection (CDP-backed; WXML tree nodes map to real DOM by sid) \u2500\u2500\nexport const SimulatorElementChannel = {\n Inspect: 'simulator:element:inspect',\n Clear: 'simulator:element:clear',\n} as const\n\n// \u2500\u2500 WXML tree (native-host: main pulls the tree from the active render-host\n// <webview> guest via render-inspect, and pushes/answers here \u2014 mirroring the\n// Storage panel's main\u2192renderer contract so the renderer panel is unchanged) \u2500\u2500\nexport const SimulatorWxmlChannel = {\n GetSnapshot: 'simulator:wxml:snapshot',\n Event: 'simulator:wxml:event',\n // renderer\u2192main: whether the WXML panel is currently visible/active. Main\n // only installs the render-guest DOM MutationObserver + pushes live tree\n // updates while active, so an unseen panel never drives a full Vue-tree walk.\n SetActive: 'simulator:wxml:setActive',\n} as const\n\n// \u2500\u2500 AppData (native-host: main taps the service\u2192render setData stream in\n// bridge-router and pushes the cumulative snapshot here \u2014 the service logic runs\n// in the hidden service-host window, not a Worker in the simulator guest) \u2500\u2500\nexport const SimulatorAppDataChannel = {\n GetSnapshot: 'simulator:appdata:snapshot',\n Event: 'simulator:appdata:event',\n} as const\n\nexport interface ElementInspection {\n sid: string\n rect: {\n x: number\n y: number\n width: number\n height: number\n }\n style: {\n display: string\n position: string\n boxSizing: string\n margin: string\n padding: string\n color: string\n backgroundColor: string\n fontSize: string\n }\n}\n\n// \u2500\u2500 Workbench settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 WorkbenchSettingsChannel = {\n Get: 'workbenchSettings:get',\n Save: 'workbenchSettings:save',\n SetTheme: 'workbenchSettings:setTheme',\n GetCdpStatus: 'workbenchSettings:getCdpStatus',\n GetMcpStatus: 'workbenchSettings:getMcpStatus',\n Init: 'workbenchSettings:init',\n // Main \u2192 renderer push: the active color scheme flipped (OS change or in-app\n // SetTheme). Payload is `isDark: boolean`. The app's CSS reacts to\n // `prefers-color-scheme` automatically; this exists for the few JS consumers\n // (Monaco's theme) that can't observe that media change \u2014 Electron does NOT\n // dispatch the renderer's `matchMedia('(prefers-color-scheme)')` change event\n // for programmatic `nativeTheme.themeSource` assignments.\n ThemeChanged: 'workbenchSettings:themeChanged',\n} as const\n\n// \u2500\u2500 Project session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 ProjectChannel = {\n Open: 'project:open',\n Close: 'project:close',\n GetPages: 'project:getPages',\n GetCompileConfig: 'project:getCompileConfig',\n SaveCompileConfig: 'project:saveCompileConfig',\n Status: 'project:status',\n // Main \u2192 renderer push of per-line dmcc compile logs (devkit `onLog`).\n // Dedicated channel: `project:status` keeps its one-event-per-payload\n // contract (compileEvents), this one carries the line stream (compileLogs).\n CompileLog: 'project:compileLog',\n CaptureThumbnail: 'project:captureThumbnail',\n GetThumbnail: 'project:getThumbnail',\n} as const\n\n// \u2500\u2500 Session runtime status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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// Main \u2192 renderer push of the post-compile SESSION lifecycle (spawn \u2192 running\n// \u2192 crash/timeout), distinct from `ProjectChannel.Status` which only tracks\n// compile outcomes. Compile succeeding tells the renderer nothing about\n// whether the simulator actually booted \u2014 this channel closes that gap.\n\nexport const SessionChannel = {\n RuntimeStatus: 'session:runtimeStatus',\n} as const\n\n// \u2500\u2500 Project file system (sandboxed to active project root) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Read/write access to the active project's files, used by the in-renderer\n// Monaco editor. Every path is verified against the active project root in\n// the main process (see `services/project-fs`). Replaces the OpenSumi\n// editor's `editor:fs:*` bridge \u2014 same sandbox, callable from the main\n// renderer instead of a separate WebContentsView.\n\nexport const ProjectFsChannel = {\n GetRoot: 'project:fs:getRoot',\n ReadFile: 'project:fs:readFile',\n WriteFile: 'project:fs:writeFile',\n /**\n * Synchronous (blocking) write, used ONLY by the editor's `beforeunload`\n * flush: a hard window/app close tears the renderer down before an async\n * `WriteFile` IPC can round-trip, so the last in-debounce-window edit would\n * be lost. `sendSync` blocks page teardown until the bytes hit disk. Runs the\n * SAME sandbox as `WriteFile` (see project-fs `writeFileSync`).\n */\n WriteFileSync: 'project:fs:writeFileSync',\n ListFiles: 'project:fs:listFiles',\n} as const\n\n// \u2500\u2500 Editor (main \u2192 renderer) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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// Drives the in-renderer Monaco editor from the main process. Used by the\n// \"click a console file link \u2192 open the file at line:col\" pipeline: the\n// embedded DevTools front-end routes a source-link click through an open-\n// resource handler \u2192 Electron `devtools-open-url` on the service host \u2192 main\n// maps the resource URL to a project-relative path \u2192 this event opens it in\n// Monaco. Payload: `EditorOpenFilePayload`.\n\nexport const EditorChannel = {\n /** main \u2192 renderer: open `path` (project-relative POSIX) at `line`/`column`. */\n OpenFile: 'editor:openFile',\n} as const\n\n/** Payload for `editor:openFile`. `line`/`column` are 1-based for Monaco. */\nexport interface EditorOpenFilePayload {\n /** Project-relative POSIX path (the same key Monaco opens files by). */\n path: string\n /** 1-based line to reveal; omitted/<=0 means open without moving the cursor. */\n line?: number\n /** 1-based column to reveal; defaults to 1 when a line is given. */\n column?: number\n}\n\n// \u2500\u2500 Project list / workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 ProjectsChannel = {\n List: 'projects:list',\n Add: 'projects:add',\n Remove: 'projects:remove',\n /** Merged + sanitized template catalog for the create-project dialog. */\n ListTemplates: 'projects:listTemplates',\n /** Host-supplied create-project dialog hook (returns input or null). */\n OpenCreateDialog: 'projects:openCreateDialog',\n /** Server-side scaffold + register (delegates to create-project-service). */\n Create: 'projects:create',\n /** Default values used to pre-fill the create-project dialog (baseDir). */\n GetCreateDefaults: 'projects:getCreateDefaults',\n} as const\n\n/** Renderer-facing payload for `projects:getCreateDefaults`. */\nexport interface ProjectCreateDefaults {\n /** Absolute directory used as the parent for new projects. */\n baseDir: string\n}\n\n// \u2500\u2500 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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const DialogChannel = {\n OpenDirectory: 'dialog:openDirectory',\n} as const\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 * 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} 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 Window \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 WindowChannel = {\n NavigateBack: 'window:navigateBack',\n} as const\n\n// \u2500\u2500 App \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 AppChannel = {\n GetBranding: 'app:getBranding',\n} as const\n\n// \u2500\u2500 miniappSnapshot (unified panel snapshot framework) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Two generic channels shared by every snapshot source (AppData, WXML, \u2026).\n// Adding a new panel needs no new channel.\n// Push: preload \u2192 renderer (`sendToHost`), payload: SnapshotEnvelope\n// Pull: renderer \u2192 preload (`webview.send`), payload: { id }\n\nexport const MiniappSnapshotChannel = {\n Push: 'miniapp-snapshot:push',\n Pull: 'miniapp-snapshot:pull',\n} as const\n\n// \u2500\u2500 Automation (WebSocket server) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 AutomationChannel = {\n GetPort: 'automation:port',\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} as const\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\nimport { contextBridge, ipcRenderer } from 'electron'\nimport { ViewChannel } from '../../shared/ipc-channels.js'\n\nconst BRIDGE_KEY = 'diminaHostToolbar'\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 */\n/**\n * Entry-waist channel validation, parity with the main side's\n * `hostToolbar.onMessage` guard (same TypeError, message names `channel`).\n * Runs BEFORE any state is touched (no queue slot, no registry entry), in\n * both port states \u2014 a page author's typo throws at the call site instead of\n * vanishing into main's silent inbound drop.\n */\nfunction assertValidChannel(method: string, channel: unknown): asserts channel is string {\n if (typeof channel !== 'string' || channel === '') {\n throw new TypeError(\n `diminaHostToolbar.${method}: channel must be a non-empty string`,\n )\n }\n}\n\nexport function installHostToolbarPortBridge(): void {\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 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, payload } = data as { channel?: unknown; payload?: unknown }\n if (typeof channel !== 'string') return\n for (const entry of [...handlers]) {\n if (entry.channel === channel) entry.handler(payload)\n }\n }\n\n const onPortMessage = (event: MessageEvent): void => {\n dispatch(event.data)\n }\n\n ipcRenderer.on(ViewChannel.HostToolbarPort, (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(BRIDGE_KEY, {\n send(channel: string, payload: unknown): void {\n assertValidChannel('send', channel)\n const envelope = { channel, 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 >= HOST_TOOLBAR_PENDING_LIMIT) {\n if (!warnedPendingOverflow) {\n warnedPendingOverflow = true\n console.warn(\n `[dimina-devtools] host-toolbar pending queue full (${HOST_TOOLBAR_PENDING_LIMIT}); ` +\n 'dropping further pre-handshake send() calls until the port arrives',\n )\n }\n return\n }\n pending.push(envelope)\n },\n onMessage(channel: string, handler: (payload: unknown) => void): () => void {\n assertValidChannel('onMessage', channel)\n const entry = { channel, 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 * 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 * 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.js'\n\n/**\n * Attach the advertiser to the toolbar content's shrink-to-fit root\n * (`[data-host-toolbar-root]`). That element MUST be shrink-to-fit on the block\n * axis \u2014 its height must reflect the content, not the host-applied view height,\n * or the cross-process loop never converges (`createSizeAdvertiser`'s footgun).\n * If the element is missing we warn and no-op rather than measure `<body>`,\n * whose block size IS the view size and would advertise nonsense.\n *\n * Returns a disposer that tears the advertiser down.\n */\nexport function installHostToolbarAdvertiser(): () => void {\n const root = document.querySelector<HTMLElement>('[data-host-toolbar-root]')\n if (!root) {\n console.warn(\n '[host-toolbar-advertiser] no `[data-host-toolbar-root]` element found \u2014 ' +\n 'not advertising a height. 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 )\n return () => {}\n }\n\n const advertiser = createSizeAdvertiser(root, {\n axis: 'block',\n publish: (size) => {\n ipcRenderer.send(ViewChannel.HostToolbarAdvertiseHeight, size)\n },\n })\n\n return () => advertiser.dispose()\n}\n\n/**\n * Install once the DOM is ready (the root element must exist before we query).\n * Self-gating: if the document is already past `loading`, install synchronously;\n * otherwise wait for `DOMContentLoaded`.\n */\nexport function installHostToolbarAdvertiserWhenReady(): void {\n if (document.readyState === 'loading') {\n document.addEventListener(\n 'DOMContentLoaded',\n () => {\n installHostToolbarAdvertiser()\n },\n { once: true },\n )\n } else {\n installHostToolbarAdvertiser()\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 * Centralised IPC channel name constants for dimina-devtools.\n *\n * Every raw channel string used across main, renderer, preload and e2e code\n * should reference one of these constants so that renaming a channel only\n * requires a single edit.\n */\n\n// \u2500\u2500 Simulator (preload \u2192 host / renderer \u2194 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\n\nexport const SimulatorChannel = {\n // Ask main to create the simulator as a top-level WebContentsView (so nested\n // render-host <webview>s can attach). Native-host is the sole runtime.\n AttachNative: 'simulator:attach-native',\n // Renderer pushes the selected device's LOGICAL metrics (screen size,\n // pixelRatio, statusBarHeight, \u2026) when the device dropdown changes. Main\n // maps it to a HostEnvSnapshot and live-updates the running service-host\n // window \u2014 the authoritative `wx.getSystemInfoSync()` source \u2014 so the\n // mini-app sees the selected device without a relaunch.\n SetDeviceInfo: 'simulator:set-device-info',\n // Ask main to soft-reload the LIVE simulator WCV after a watcher rebuild:\n // main forwards a SIMULATOR_EVENTS.RELAUNCH into the shell (which boots a\n // new app session and swaps when ready) instead of destroying the view.\n // Resolves false when there is no live+ready shell \u2014 the renderer then falls\n // back to the hard AttachNative rebuild.\n SoftReload: 'simulator:soft-reload',\n Detach: 'simulator:detach',\n Console: 'simulator:console',\n // Main \u2192 renderer push of the visible top-of-stack page route whenever the\n // mini-app navigates (navigateTo / switchTab / back). Payload: the page path\n // string (same bare format as `getCurrentPagePath`), or '' when unknown.\n CurrentPage: 'simulator:current-page',\n} as const\n\n/** iPhone bezel cutout family driving the device-shell notch visual. */\nexport type NotchType = 'none' | 'notch' | 'dynamic-island'\n\n/** Per-device safe-area insets in CSS px (portrait). */\nexport interface SafeAreaInsets {\n top: number\n right: number\n bottom: number\n left: number\n}\n\n/**\n * Logical device metrics pushed by the renderer device dropdown under\n * native-host (`SimulatorChannel.SetDeviceInfo`). Mirrors a row of the renderer\n * `DEVICES` table; main maps it onto a `HostEnvSnapshot` for the service-host\n * window so `wx.getSystemInfoSync()` reflects the selected device, relays it to\n * the simulator WCV (DeviceShell: bezel size + status bar + notch), and drives\n * the CSS `env(safe-area-inset-*)` override on render-host guests.\n */\nexport interface NativeDeviceInfo {\n brand: string\n model: string\n system: string\n platform: string\n pixelRatio: number\n screenWidth: number\n screenHeight: number\n statusBarHeight: number\n notchType: NotchType\n safeAreaInsets: SafeAreaInsets\n}\n\n// \u2500\u2500 Service host (main \u2192 hidden service-host window) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const ServiceHostChannel = {\n /**\n * NATIVE-HOST ONLY. Live-update the service-host window's host-env snapshot\n * (device metrics) so subsequent `wx.getSystemInfoSync()` reflects a device\n * change without a relaunch. The service-host preload mutates\n * `__diminaSpawnContext.hostEnvSnapshot` in place (see `service-host/preload.cjs`).\n */\n HostEnvUpdate: 'service-host:host-env:update',\n} as const\n\n// \u2500\u2500 Custom simulator APIs (downstream-registered, main-process handlers) \u2500\u2500\n// invoke: forwards an API call to the registry; result/reject propagates.\n//\n// This is an ipcMain.handle channel invoked by the **main-window renderer**\n// only (trusted host). The simulator guest never reaches it directly \u2014 it\n// reaches the host via the bridge channels below, so the sender-policy can keep\n// the simulator off the IPC white-list.\nexport const SimulatorCustomApiChannel = {\n Invoke: 'simulator:custom-apis:invoke',\n} as const\n\n// \u2500\u2500 Custom APIs bridge (simulator \u2192 host) \u2500\u2500\n// payload = { id, op: 'list' } | { id, op: 'invoke', name, params } for Request,\n// { id, result } | { id, error } for Response. Transport (in\n// `src/preload/runtime/custom-apis.ts`): native-host \u2014 the simulator is a\n// top-level WebContentsView (no embedder), so Request goes via\n// `ipcRenderer.send` \u2192 `ipcMain.on` dispatcher bound to that simWc (view-manager\n// `attachNativeCustomApiBridge`) \u2192 Response via `simWc.send`. (The old\n// renderer-proxied `<webview>` transport, `ipcRenderer.sendToHost` \u2192\n// `<webview>.send`, no longer exists \u2014 native-host is the sole runtime.)\n//\n// Request/response are correlated by `id` so multiple concurrent invokes can\n// be in flight at once.\nexport const SimulatorCustomApiBridgeChannel = {\n Request: 'simulator:custom-apis:bridge-request',\n Response: 'simulator:custom-apis:bridge-response',\n} as const\n\n// \u2500\u2500 Storage (CDP-backed; main process attaches the debugger to the\n// simulator guest and forwards DOMStorage events to the renderer) \u2500\u2500\nexport const SimulatorStorageChannel = {\n GetSnapshot: 'simulator:storage:snapshot',\n GetActivePrefix: 'simulator:storage:activePrefix',\n Set: 'simulator:storage:set',\n Remove: 'simulator:storage:remove',\n Clear: 'simulator:storage:clear',\n ClearAll: 'simulator:storage:clearAll',\n Event: 'simulator:storage:event',\n} as const\n\nexport interface StorageItem { key: string; value: string }\nexport type StorageEvent =\n | { type: 'added'; key: string; newValue: string }\n | { type: 'updated'; key: string; oldValue: string; newValue: string }\n | { type: 'removed'; key: string }\n | { type: 'cleared' }\n\nexport type StorageWriteResult =\n | { ok: true }\n | { ok: false; error: string }\n\n/**\n * A storage mutation reported by the service-host's SYNC wx storage APIs\n * (`setStorageSync`/`removeStorageSync`/`clearStorageSync`). Those run inside the\n * service-host window and write `localStorage` directly, so \u2014 unlike the async\n * path (`runtimeInvoke`) and the panel's own writes \u2014 they never pass through\n * main and would otherwise leave the Storage panel stale until a manual reload.\n * The service-host posts this over `DiminaServiceBridge` as a `storageChanged`\n * container message; bridge-router hands it to `onServiceStorageChanged`, which\n * pushes the matching `StorageEvent` to the panel. `key` carries the full\n * `${appId}_` prefix (same wire shape as the CDP / async paths).\n */\nexport type SyncStorageChange =\n | { op: 'set'; key: string; value: string }\n | { op: 'remove'; key: string }\n | { op: 'clear' }\n\n// \u2500\u2500 Element inspection (CDP-backed; WXML tree nodes map to real DOM by sid) \u2500\u2500\nexport const SimulatorElementChannel = {\n Inspect: 'simulator:element:inspect',\n Clear: 'simulator:element:clear',\n} as const\n\n// \u2500\u2500 WXML tree (native-host: main pulls the tree from the active render-host\n// <webview> guest via render-inspect, and pushes/answers here \u2014 mirroring the\n// Storage panel's main\u2192renderer contract so the renderer panel is unchanged) \u2500\u2500\nexport const SimulatorWxmlChannel = {\n GetSnapshot: 'simulator:wxml:snapshot',\n Event: 'simulator:wxml:event',\n // renderer\u2192main: whether the WXML panel is currently visible/active. Main\n // only installs the render-guest DOM MutationObserver + pushes live tree\n // updates while active, so an unseen panel never drives a full Vue-tree walk.\n SetActive: 'simulator:wxml:setActive',\n} as const\n\n// \u2500\u2500 AppData (native-host: main taps the service\u2192render setData stream in\n// bridge-router and pushes the cumulative snapshot here \u2014 the service logic runs\n// in the hidden service-host window, not a Worker in the simulator guest) \u2500\u2500\nexport const SimulatorAppDataChannel = {\n GetSnapshot: 'simulator:appdata:snapshot',\n Event: 'simulator:appdata:event',\n} as const\n\nexport interface ElementInspection {\n sid: string\n rect: {\n x: number\n y: number\n width: number\n height: number\n }\n style: {\n display: string\n position: string\n boxSizing: string\n margin: string\n padding: string\n color: string\n backgroundColor: string\n fontSize: string\n }\n}\n\n// \u2500\u2500 Workbench settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 WorkbenchSettingsChannel = {\n Get: 'workbenchSettings:get',\n Save: 'workbenchSettings:save',\n SetTheme: 'workbenchSettings:setTheme',\n GetCdpStatus: 'workbenchSettings:getCdpStatus',\n GetMcpStatus: 'workbenchSettings:getMcpStatus',\n Init: 'workbenchSettings:init',\n // Main \u2192 renderer push: the active color scheme flipped (OS change or in-app\n // SetTheme). Payload is `isDark: boolean`. The app's CSS reacts to\n // `prefers-color-scheme` automatically; this exists for the few JS consumers\n // (Monaco's theme) that can't observe that media change \u2014 Electron does NOT\n // dispatch the renderer's `matchMedia('(prefers-color-scheme)')` change event\n // for programmatic `nativeTheme.themeSource` assignments.\n ThemeChanged: 'workbenchSettings:themeChanged',\n} as const\n\n// \u2500\u2500 Project session \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 ProjectChannel = {\n Open: 'project:open',\n Close: 'project:close',\n GetPages: 'project:getPages',\n GetCompileConfig: 'project:getCompileConfig',\n SaveCompileConfig: 'project:saveCompileConfig',\n Status: 'project:status',\n // Main \u2192 renderer push of per-line dmcc compile logs (devkit `onLog`).\n // Dedicated channel: `project:status` keeps its one-event-per-payload\n // contract (compileEvents), this one carries the line stream (compileLogs).\n CompileLog: 'project:compileLog',\n CaptureThumbnail: 'project:captureThumbnail',\n GetThumbnail: 'project:getThumbnail',\n} as const\n\n// \u2500\u2500 Session runtime status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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// Main \u2192 renderer push of the post-compile SESSION lifecycle (spawn \u2192 running\n// \u2192 crash/timeout), distinct from `ProjectChannel.Status` which only tracks\n// compile outcomes. Compile succeeding tells the renderer nothing about\n// whether the simulator actually booted \u2014 this channel closes that gap.\n\nexport const SessionChannel = {\n RuntimeStatus: 'session:runtimeStatus',\n} as const\n\n// \u2500\u2500 Project file system (sandboxed to active project root) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Read/write access to the active project's files, used by the in-renderer\n// Monaco editor. Every path is verified against the active project root in\n// the main process (see `services/project-fs`). Replaces the OpenSumi\n// editor's `editor:fs:*` bridge \u2014 same sandbox, callable from the main\n// renderer instead of a separate WebContentsView.\n\nexport const ProjectFsChannel = {\n GetRoot: 'project:fs:getRoot',\n ReadFile: 'project:fs:readFile',\n WriteFile: 'project:fs:writeFile',\n /**\n * Synchronous (blocking) write, used ONLY by the editor's `beforeunload`\n * flush: a hard window/app close tears the renderer down before an async\n * `WriteFile` IPC can round-trip, so the last in-debounce-window edit would\n * be lost. `sendSync` blocks page teardown until the bytes hit disk. Runs the\n * SAME sandbox as `WriteFile` (see project-fs `writeFileSync`).\n */\n WriteFileSync: 'project:fs:writeFileSync',\n ListFiles: 'project:fs:listFiles',\n} as const\n\n// \u2500\u2500 Editor (main \u2192 renderer) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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// Drives the in-renderer Monaco editor from the main process. Used by the\n// \"click a console file link \u2192 open the file at line:col\" pipeline: the\n// embedded DevTools front-end routes a source-link click through an open-\n// resource handler \u2192 Electron `devtools-open-url` on the service host \u2192 main\n// maps the resource URL to a project-relative path \u2192 this event opens it in\n// Monaco. Payload: `EditorOpenFilePayload`.\n\nexport const EditorChannel = {\n /** main \u2192 renderer: open `path` (project-relative POSIX) at `line`/`column`. */\n OpenFile: 'editor:openFile',\n} as const\n\n/** Payload for `editor:openFile`. `line`/`column` are 1-based for Monaco. */\nexport interface EditorOpenFilePayload {\n /** Project-relative POSIX path (the same key Monaco opens files by). */\n path: string\n /** 1-based line to reveal; omitted/<=0 means open without moving the cursor. */\n line?: number\n /** 1-based column to reveal; defaults to 1 when a line is given. */\n column?: number\n}\n\n// \u2500\u2500 Project list / workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 ProjectsChannel = {\n List: 'projects:list',\n Add: 'projects:add',\n Remove: 'projects:remove',\n /** Merged + sanitized template catalog for the create-project dialog. */\n ListTemplates: 'projects:listTemplates',\n /** Host-supplied create-project dialog hook (returns input or null). */\n OpenCreateDialog: 'projects:openCreateDialog',\n /** Server-side scaffold + register (delegates to create-project-service). */\n Create: 'projects:create',\n /** Default values used to pre-fill the create-project dialog (baseDir). */\n GetCreateDefaults: 'projects:getCreateDefaults',\n} as const\n\n/** Renderer-facing payload for `projects:getCreateDefaults`. */\nexport interface ProjectCreateDefaults {\n /** Absolute directory used as the parent for new projects. */\n baseDir: string\n}\n\n// \u2500\u2500 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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const DialogChannel = {\n OpenDirectory: 'dialog:openDirectory',\n} as const\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 * 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} 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 Window \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 WindowChannel = {\n NavigateBack: 'window:navigateBack',\n} as const\n\n// \u2500\u2500 App \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 AppChannel = {\n GetBranding: 'app:getBranding',\n} as const\n\n// \u2500\u2500 miniappSnapshot (unified panel snapshot framework) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Two generic channels shared by every snapshot source (AppData, WXML, \u2026).\n// Adding a new panel needs no new channel.\n// Push: preload \u2192 renderer (`sendToHost`), payload: SnapshotEnvelope\n// Pull: renderer \u2192 preload (`webview.send`), payload: { id }\n\nexport const MiniappSnapshotChannel = {\n Push: 'miniapp-snapshot:push',\n Pull: 'miniapp-snapshot:pull',\n} as const\n\n// \u2500\u2500 Automation (WebSocket server) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 AutomationChannel = {\n GetPort: 'automation:port',\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} as const\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\nimport { contextBridge, ipcRenderer } from 'electron'\nimport { ViewChannel } from '../../shared/ipc-channels.js'\n\nconst BRIDGE_KEY = 'diminaHostToolbar'\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 */\n/**\n * Entry-waist channel validation, parity with the main side's\n * `hostToolbar.onMessage` guard (same TypeError, message names `channel`).\n * Runs BEFORE any state is touched (no queue slot, no registry entry), in\n * both port states \u2014 a page author's typo throws at the call site instead of\n * vanishing into main's silent inbound drop.\n */\nfunction assertValidChannel(method: string, channel: unknown): asserts channel is string {\n if (typeof channel !== 'string' || channel === '') {\n throw new TypeError(\n `diminaHostToolbar.${method}: channel must be a non-empty string`,\n )\n }\n}\n\nexport function installHostToolbarPortBridge(): void {\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 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, payload } = data as { channel?: unknown; payload?: unknown }\n if (typeof channel !== 'string') return\n for (const entry of [...handlers]) {\n if (entry.channel === channel) entry.handler(payload)\n }\n }\n\n const onPortMessage = (event: MessageEvent): void => {\n dispatch(event.data)\n }\n\n ipcRenderer.on(ViewChannel.HostToolbarPort, (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(BRIDGE_KEY, {\n send(channel: string, payload: unknown): void {\n assertValidChannel('send', channel)\n const envelope = { channel, 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 >= HOST_TOOLBAR_PENDING_LIMIT) {\n if (!warnedPendingOverflow) {\n warnedPendingOverflow = true\n console.warn(\n `[dimina-devtools] host-toolbar pending queue full (${HOST_TOOLBAR_PENDING_LIMIT}); ` +\n 'dropping further pre-handshake send() calls until the port arrives',\n )\n }\n return\n }\n pending.push(envelope)\n },\n onMessage(channel: string, handler: (payload: unknown) => void): () => void {\n assertValidChannel('onMessage', channel)\n const entry = { channel, 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 * 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": ";;;AA0BO,IAAM,8BAA8B;;;AChB3C,sBAA4B;;;ACyBrB,SAAS,kBAAqB,KAOlB;AACjB,QAAM,EAAE,SAAS,MAAM,KAAK,IAAI;AAChC,MAAI,QAAuB;AAC3B,MAAI,SAAS;AACb,MAAI,WAAW;AACf,MAAI,OAAiB;AAErB,QAAM,SAAS,MAAY;AACzB,QAAI,UAAU,MAAM;AAClB,2BAAqB,KAAK;AAC1B,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO;AAAA,IACL,WAAiB;AACf,UAAI,YAAY,CAAC,UAAU,UAAU,KAAM;AAC3C,cAAQ,sBAAsB,MAAM;AAClC,gBAAQ;AACR,YAAI,YAAY,CAAC,OAAQ;AACzB,cAAM,QAAQ,QAAQ;AACtB,YAAI,UAAU,KAAM;AAGpB,YAAI,SAAS,QAAQ,KAAK,OAAO,IAAI,EAAG;AACxC,eAAO;AACP,aAAK,KAAK;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,IACA,QAAQ,OAAgB;AACtB,UAAI,SAAU;AACd,aAAO;AACP,WAAK,KAAK;AAAA,IACZ;AAAA,IACA,UAAU,IAAmB;AAC3B,eAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA,UAAgB;AACd,UAAI,SAAU;AACd,iBAAW;AACX,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC7DO,SAAS,qBACd,QACA,MACsB;AACtB,QAAM,OAAO,KAAK;AAClB,MAAI,UAAU,KAAK;AACnB,MAAI,WAAkC;AACtC,MAAI,WAAW;AAGf,MAAI,SAAoC;AAExC,QAAM,UAAU,MAA6B;AAC3C,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,MAAM,SAAS,UAAU,OAAO,YAAY,OAAO;AACzD,QAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAClC,WAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,CAAC,EAAE;AAAA,EACtD;AAEA,QAAM,OAAO,kBAAkC;AAAA,IAC7C;AAAA,IACA,MAAM,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE;AAAA;AAAA,IAC/B,MAAM,CAAC,SAAS,QAAQ,IAAI;AAAA,EAC9B,CAAC;AAED,QAAM,WAAmC,CAAC,YAAY;AACpD,UAAM,QAAQ,QAAQ,QAAQ,SAAS,CAAC;AACxC,QAAI,OAAO;AACT,eAAS,MAAM,gBAAgB,CAAC,KAAK,MAAM,iBAAiB,CAAC,KAAK;AAAA,IACpE;AACA,SAAK,SAAS;AAAA,EAChB;AAGA,QAAM,MAAM,OAAO;AACnB,MAAI,WAAW,IAAI,QAAQ,WAAW,IAAI,iBAAiB;AACzD,YAAQ;AAAA,MACN,mCAAmC,WAAW,IAAI,OAAO,SAAS,MAAM,OACnE,IAAI;AAAA,IAGX;AAAA,EACF;AAEA,OAAK,UAAU,IAAI;AACnB,aAAW,IAAI,eAAe,QAAQ;AACtC,WAAS,QAAQ,MAAM;AAEvB,SAAO;AAAA,IACL,OAAO,aAAmD;AACxD,UAAI,SAAU;AACd,gBAAU;AAIV,YAAM,MAAM,QAAQ;AACpB,UAAI,IAAK,MAAK,QAAQ,GAAG;AAAA,IAC3B;AAAA,IACA,UAAgB;AACd,UAAI,SAAU;AACd,iBAAW;AACX,WAAK,OAAO;AACZ,UAAI,UAAU;AACZ,iBAAS,WAAW;AACpB,mBAAW;AAAA,MACb;AACA,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AACF;;;ACsOO,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzB,4BAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU5B,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKjB,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU1B,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtB,mBAAmB;AACrB;;;AHpVO,SAAS,+BAA2C;AACzD,QAAM,OAAO,SAAS,cAA2B,0BAA0B;AAC3E,MAAI,CAAC,MAAM;AACT,YAAQ;AAAA,MACN;AAAA,IAIF;AACA,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,QAAM,aAAa,qBAAqB,MAAM;AAAA,IAC5C,MAAM;AAAA,IACN,SAAS,CAAC,SAAS;AACjB,kCAAY,KAAK,YAAY,4BAA4B,IAAI;AAAA,IAC/D;AAAA,EACF,CAAC;AAED,SAAO,MAAM,WAAW,QAAQ;AAClC;AAOO,SAAS,wCAA8C;AAC5D,MAAI,SAAS,eAAe,WAAW;AACrC,aAAS;AAAA,MACP;AAAA,MACA,MAAM;AACJ,qCAA6B;AAAA,MAC/B;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AAAA,EACF,OAAO;AACL,iCAA6B;AAAA,EAC/B;AACF;;;AItBA,IAAAA,mBAA2C;AAG3C,IAAM,aAAa;AAQZ,IAAM,6BAA6B;AAc1C,SAAS,mBAAmB,QAAgB,SAA6C;AACvF,MAAI,OAAO,YAAY,YAAY,YAAY,IAAI;AACjD,UAAM,IAAI;AAAA,MACR,qBAAqB,MAAM;AAAA,IAC7B;AAAA,EACF;AACF;AAEO,SAAS,+BAAqC;AACnD,MAAI,aAAiC;AACrC,QAAM,UAAwD,CAAC;AAG/D,MAAI,wBAAwB;AAC5B,QAAM,WAA4E,CAAC;AAEnF,QAAM,WAAW,CAAC,SAAwB;AAGxC,QAAI,OAAO,SAAS,YAAY,SAAS,KAAM;AAC/C,UAAM,EAAE,SAAS,QAAQ,IAAI;AAC7B,QAAI,OAAO,YAAY,SAAU;AACjC,eAAW,SAAS,CAAC,GAAG,QAAQ,GAAG;AACjC,UAAI,MAAM,YAAY,QAAS,OAAM,QAAQ,OAAO;AAAA,IACtD;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,UAA8B;AACnD,aAAS,MAAM,IAAI;AAAA,EACrB;AAEA,+BAAY,GAAG,YAAY,iBAAiB,CAAC,UAAU;AACrD,UAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,QAAI,CAAC,KAAM;AAGX,QAAI,YAAY;AACd,UAAI;AACF,mBAAW,oBAAoB,WAAW,aAAa;AACvD,mBAAW,MAAM;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AACA,iBAAa;AAGb,SAAK,iBAAiB,WAAW,aAAa;AAC9C,SAAK,MAAM;AAEX,WAAO,QAAQ,SAAS,GAAG;AACzB,YAAM,WAAW,QAAQ,MAAM;AAC/B,WAAK,YAAY,QAAQ;AAAA,IAC3B;AAAA,EACF,CAAC;AAID,iCAAc,kBAAkB,YAAY;AAAA,IAC1C,KAAK,SAAiB,SAAwB;AAC5C,yBAAmB,QAAQ,OAAO;AAClC,YAAM,WAAW,EAAE,SAAS,QAAQ;AACpC,UAAI,YAAY;AACd,mBAAW,YAAY,QAAQ;AAC/B;AAAA,MACF;AAGA,UAAI,QAAQ,UAAU,4BAA4B;AAChD,YAAI,CAAC,uBAAuB;AAC1B,kCAAwB;AACxB,kBAAQ;AAAA,YACN,sDAAsD,0BAA0B;AAAA,UAElF;AAAA,QACF;AACA;AAAA,MACF;AACA,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,IACA,UAAU,SAAiB,SAAiD;AAC1E,yBAAmB,aAAa,OAAO;AACvC,YAAM,QAAQ,EAAE,SAAS,QAAQ;AACjC,eAAS,KAAK,KAAK;AACnB,aAAO,MAAM;AACX,cAAM,IAAI,SAAS,QAAQ,KAAK;AAChC,YAAI,KAAK,EAAG,UAAS,OAAO,GAAG,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AC3HO,SAAS,iCACd,MACA,aACS;AACT,SAAO,eAAe,KAAK,SAAS,2BAA2B;AACjE;AAUO,SAAS,2BAA2B,KAG/B;AACV,MAAI,CAAC,iCAAiC,IAAI,MAAM,IAAI,WAAW,EAAG,QAAO;AACzE,wCAAsC;AACtC,+BAA6B;AAC7B,SAAO;AACT;;;AC5CA,2BAA2B,EAAE,MAAM,QAAQ,MAAM,aAAa,QAAQ,YAAY,CAAC;",
6
6
  "names": ["import_electron"]
7
7
  }