@dimina-kit/devtools 0.4.0-dev.20260803105710 → 0.4.0-dev.20260824071518

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 (113) hide show
  1. package/dist/main/app/app.js +12 -1
  2. package/dist/main/index.bundle.js +584 -323
  3. package/dist/main/ipc/index.d.ts +1 -0
  4. package/dist/main/ipc/index.js +1 -0
  5. package/dist/main/ipc/session.js +11 -0
  6. package/dist/main/ipc/tooltip.d.ts +13 -0
  7. package/dist/main/ipc/tooltip.js +28 -0
  8. package/dist/main/services/layout/index.d.ts +11 -0
  9. package/dist/main/services/layout/index.js +23 -0
  10. package/dist/main/services/network-forward/frontend-dispatch.js +2 -1
  11. package/dist/main/services/notifications/renderer-notifier.d.ts +2 -0
  12. package/dist/main/services/notifications/renderer-notifier.js +7 -1
  13. package/dist/main/services/simulator-current-page/index.d.ts +5 -3
  14. package/dist/main/services/simulator-current-page/index.js +8 -1
  15. package/dist/main/services/views/clear-console-filter.d.ts +44 -0
  16. package/dist/main/services/views/clear-console-filter.js +86 -0
  17. package/dist/main/services/views/console-filter-test-fixtures.d.ts +94 -0
  18. package/dist/main/services/views/console-filter-test-fixtures.js +82 -0
  19. package/dist/main/services/views/console-filter.d.ts +79 -130
  20. package/dist/main/services/views/console-filter.js +140 -183
  21. package/dist/main/services/views/devtools-tabs.d.ts +24 -4
  22. package/dist/main/services/views/devtools-tabs.js +40 -21
  23. package/dist/main/services/views/host-toolbar-view.js +5 -18
  24. package/dist/main/services/views/native-simulator-devtools-host.js +64 -45
  25. package/dist/main/services/views/native-simulator-view.js +6 -24
  26. package/dist/main/services/views/native-view-tree.d.ts +25 -0
  27. package/dist/main/services/views/native-view-tree.js +69 -0
  28. package/dist/main/services/views/overlay-panels-view.d.ts +27 -4
  29. package/dist/main/services/views/overlay-panels-view.js +138 -109
  30. package/dist/main/services/views/placement-reconciler.d.ts +4 -16
  31. package/dist/main/services/views/placement-reconciler.js +64 -29
  32. package/dist/main/services/views/placement-test-driver.d.ts +26 -0
  33. package/dist/main/services/views/placement-test-driver.js +26 -0
  34. package/dist/main/services/views/view-manager.d.ts +5 -9
  35. package/dist/main/services/views/view-manager.js +7 -1
  36. package/dist/main/services/views/workbench-view.js +27 -20
  37. package/dist/main/services/workbench-coi-server.d.ts +11 -0
  38. package/dist/main/services/workbench-coi-server.js +32 -0
  39. package/dist/main/services/workspace/rebuild-status.d.ts +21 -8
  40. package/dist/main/services/workspace/rebuild-status.js +24 -8
  41. package/dist/main/utils/sender-policy.d.ts +1 -1
  42. package/dist/main/utils/sender-policy.js +6 -1
  43. package/dist/main/utils/theme.d.ts +7 -2
  44. package/dist/main/utils/theme.js +8 -3
  45. package/dist/native-host/container/pageFrame.css +1 -1
  46. package/dist/native-host/render/render.js +1966 -1816
  47. package/dist/native-host/service/service.js +1 -1
  48. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  49. package/dist/preload/windows/main.cjs +3176 -14
  50. package/dist/preload/windows/main.cjs.map +4 -4
  51. package/dist/preload/windows/main.css +428 -0
  52. package/dist/preload/windows/main.css.map +7 -0
  53. package/dist/preload/windows/simulator.cjs +5 -2
  54. package/dist/preload/windows/simulator.cjs.map +2 -2
  55. package/dist/preload/windows/simulator.js +5 -2
  56. package/dist/renderer/assets/constants-Bdz0LHdy.js +2 -0
  57. package/dist/renderer/assets/index-CtznrmHR.js +49 -0
  58. package/dist/renderer/assets/{input-xdmfv-eH.js → input-Dmm6nob5.js} +2 -2
  59. package/dist/renderer/assets/ipc-transport-BOg0gR9W.js +9 -0
  60. package/dist/renderer/assets/ipc-transport-DgQhZaE9.css +1 -0
  61. package/dist/renderer/assets/popover-Cd1FEGOX.js +2 -0
  62. package/dist/renderer/assets/select-BaYox7If.js +2 -0
  63. package/dist/renderer/assets/settings-BXvwuFaz.js +2 -0
  64. package/dist/renderer/assets/settings-api-Bo9GbYqs.js +2 -0
  65. package/dist/renderer/assets/settings-tab-bar-Beia6lMr.js +2 -0
  66. package/dist/renderer/assets/tooltip-DfQqK3Sk.js +2 -0
  67. package/dist/renderer/assets/view-api-nn69je7A.js +2 -0
  68. package/dist/renderer/assets/workbenchSettings-oq_EyfKf.js +8 -0
  69. package/dist/renderer/entries/main/index.html +8 -7
  70. package/dist/renderer/entries/popover/index.html +7 -6
  71. package/dist/renderer/entries/settings/index.html +7 -5
  72. package/dist/renderer/entries/tooltip/index.html +20 -0
  73. package/dist/renderer/entries/workbench-settings/index.html +7 -5
  74. package/dist/service-host/sync-impls/menu-button-geometry.js +1 -0
  75. package/dist/shared/ipc-channels.d.ts +11 -0
  76. package/dist/shared/ipc-channels.js +28 -2
  77. package/dist/shared/ipc-schemas.d.ts +19 -0
  78. package/dist/shared/ipc-schemas.js +23 -0
  79. package/dist/shared/simulator-route.d.ts +8 -0
  80. package/dist/shared/simulator-route.js +15 -0
  81. package/dist/shared/types.d.ts +7 -0
  82. package/dist/shared/view-ids.d.ts +2 -0
  83. package/dist/shared/view-ids.js +11 -0
  84. package/dist/simulator/assets/device-shell-CmtM1QxP.js +2 -0
  85. package/dist/simulator/assets/device-shell-iFZo1igL.css +1 -0
  86. package/dist/simulator/assets/{simulator-COIE6R49.js → simulator-CIobn3Mh.js} +6 -6
  87. package/dist/simulator/assets/{simulator-mini-app-BLn7XV8e.js → simulator-mini-app-CRe6n9br.js} +2 -2
  88. package/dist/simulator/assets/simulator-ui-BupATgKo.css +1 -0
  89. package/dist/simulator/assets/simulator-ui-C_cP602X.js +2 -0
  90. package/dist/simulator/simulator.html +3 -2
  91. package/dist/vscode-workbench/assets/__vite-browser-external-Bc1vizTf.js +1 -0
  92. package/dist/vscode-workbench/assets/{dist-oHcwN53f.js → dist-CMfmHflW.js} +3 -3
  93. package/dist/vscode-workbench/assets/{iconv-lite-umd-CLMLpdGm.js → iconv-lite-umd-Bx8CgaZq.js} +1 -1
  94. package/dist/vscode-workbench/assets/{index-BE3UJdoH.js → index-CdG6YWhW.js} +436 -436
  95. package/dist/vscode-workbench/assets/{jschardet-BICW8PLJ.js → jschardet-M0qgd2lN.js} +1 -1
  96. package/dist/vscode-workbench/index.html +21 -1
  97. package/package.json +7 -6
  98. package/dist/main/services/views/destroy-child-view.d.ts +0 -9
  99. package/dist/main/services/views/destroy-child-view.js +0 -23
  100. package/dist/renderer/assets/constants-EAMiwL9_.js +0 -2
  101. package/dist/renderer/assets/index-BBg3YI5b.js +0 -49
  102. package/dist/renderer/assets/ipc-transport-B3EpmfNS.js +0 -9
  103. package/dist/renderer/assets/ipc-transport-Blac3sOj.css +0 -1
  104. package/dist/renderer/assets/popover-B88e2hkg.js +0 -2
  105. package/dist/renderer/assets/select-CD9CJyhI.js +0 -2
  106. package/dist/renderer/assets/settings-44hf0QtO.js +0 -2
  107. package/dist/renderer/assets/settings-api-pJKPuP1M.js +0 -2
  108. package/dist/renderer/assets/workbenchSettings-DKD5U1Ql.js +0 -8
  109. package/dist/simulator/assets/device-shell-Cw7c-IkG.js +0 -2
  110. package/dist/simulator/assets/device-shell-DbC5ccs8.css +0 -1
  111. package/dist/simulator/assets/jsx-runtime-DkKmJyBb.js +0 -2
  112. package/dist/simulator/device-shell/menu-button-geometry.js +0 -18
  113. package/dist/vscode-workbench/assets/__vite-browser-external-BON7qP59.js +0 -1
@@ -35,10 +35,14 @@
35
35
  */
36
36
  /**
37
37
  * Canonical DevTools panel view ids kept in the DEFAULT tab bar (stable front-end
38
- * ids, unchanged for many Chromium releases): `elements`, `console`, `network`,
39
- * `sources`. Sources stays so a source-link click that isn't routed to Monaco
40
- * (build/runtime chunks, framework frames) still has a panel to reveal in instead
41
- * of silently no-op'ing.
38
+ * ids, unchanged for many Chromium releases): `elements`, `console`, `sources`,
39
+ * `network`. Sources stays so a source-link click NOT routed to Monaco (build/
40
+ * runtime chunks, framework frames) still has a panel to reveal in instead of
41
+ * silently no-op'ing. ORDERING NOTE: these stay in DevTools' DEFAULT visual
42
+ * order (Sources between Console and Network). We deliberately do NOT move
43
+ * Sources after Network: reordering the DOM nodes desyncs the front-end's
44
+ * click→panel mapping (it indexes tabs by child position), which made clicking
45
+ * Network activate Sources.
42
46
  */
43
47
  export declare const DEVTOOLS_KEPT_VIEW_IDS: readonly string[];
44
48
  /**
@@ -47,4 +51,20 @@ export declare const DEVTOOLS_KEPT_VIEW_IDS: readonly string[];
47
51
  * (never interpolated as bare code), same discipline as the other injectors.
48
52
  */
49
53
  export declare function buildCustomizeTabsScript(keptIds?: readonly string[]): string;
54
+ /**
55
+ * Build the `executeJavaScript` source that forces the DevTools front-end to
56
+ * render its context menus as PAGE menus (soft menus) instead of asking the
57
+ * host via `showContextMenuAtPoint()`.
58
+ *
59
+ * The embedded DevTools front-end decides per-menu in `ContextMenu#show()`:
60
+ * `this.useSoftMenu || InspectorFrontendHostInstance.isHostedMode()`. On a
61
+ * self-built WebContentsView Electron leaves both false and does not answer
62
+ * `showContextMenuAtPoint()`, so right-click menus (复制为 cURL / Copy / …)
63
+ * never appear. Forcing the host-wide `isHostedMode()` true instead breaks the
64
+ * front-end's target data flow (Console/Network panels go empty), so we take
65
+ * the narrow path: patch the ContextMenu prototype so every menu shows as a
66
+ * page-rendered soft menu — same rendering the VS Code embedding uses, without
67
+ * touching the host-mode flag or any other front-end behaviour.
68
+ */
69
+ export declare function buildDevtoolsSoftMenuScript(): string;
50
70
  //# sourceMappingURL=devtools-tabs.d.ts.map
@@ -35,12 +35,16 @@
35
35
  */
36
36
  /**
37
37
  * Canonical DevTools panel view ids kept in the DEFAULT tab bar (stable front-end
38
- * ids, unchanged for many Chromium releases): `elements`, `console`, `network`,
39
- * `sources`. Sources stays so a source-link click that isn't routed to Monaco
40
- * (build/runtime chunks, framework frames) still has a panel to reveal in instead
41
- * of silently no-op'ing.
38
+ * ids, unchanged for many Chromium releases): `elements`, `console`, `sources`,
39
+ * `network`. Sources stays so a source-link click NOT routed to Monaco (build/
40
+ * runtime chunks, framework frames) still has a panel to reveal in instead of
41
+ * silently no-op'ing. ORDERING NOTE: these stay in DevTools' DEFAULT visual
42
+ * order (Sources between Console and Network). We deliberately do NOT move
43
+ * Sources after Network: reordering the DOM nodes desyncs the front-end's
44
+ * click→panel mapping (it indexes tabs by child position), which made clicking
45
+ * Network activate Sources.
42
46
  */
43
- export const DEVTOOLS_KEPT_VIEW_IDS = ['elements', 'console', 'network', 'sources'];
47
+ export const DEVTOOLS_KEPT_VIEW_IDS = ['elements', 'console', 'sources', 'network'];
44
48
  /**
45
49
  * Build the `executeJavaScript` source injected into the DevTools front-end
46
50
  * WebContents. The kept ids + display names are carried as JSON data literals
@@ -50,20 +54,15 @@ export function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
50
54
  // Kept panels are matched by view id (registry path) and by visible NAME (DOM
51
55
  // fallback path); the DevTools UI may be EN or ZH, so include both.
52
56
  const NAME_MAP = {
53
- elements: ['Elements', '元素'], console: ['Console', '控制台'], network: ['Network', '网络'],
54
- sources: ['Sources', '来源', '源代码'],
57
+ elements: ['Elements', '元素'], console: ['Console', '控制台'],
58
+ sources: ['Sources', '来源', '源代码'], network: ['Network', '网络'],
55
59
  };
56
60
  const keepNames = keptIds.flatMap((id) => NAME_MAP[id] ?? [id]);
57
61
  const keepIdsJson = JSON.stringify(JSON.stringify([...keptIds]));
58
62
  const keepNamesJson = JSON.stringify(JSON.stringify(keepNames));
59
- const srcNamesJson = JSON.stringify(JSON.stringify(NAME_MAP.sources));
60
- const netNamesJson = JSON.stringify(JSON.stringify(NAME_MAP.network));
61
63
  return `(function(){try{
62
64
  var KEEPID = new Set(JSON.parse(${keepIdsJson}));
63
65
  var KEEPNAME = new Set(JSON.parse(${keepNamesJson}));
64
- var SRCNAME = new Set(JSON.parse(${srcNamesJson}));
65
- var NETNAME = new Set(JSON.parse(${netNamesJson}));
66
- var WANT_SOURCES_LAST = KEEPID.has('sources');
67
66
  // (1) Locale infobar — official host-preference suppression (NOT localStorage).
68
67
  try { var IFH=globalThis.InspectorFrontendHost; if(IFH&&typeof IFH.setPreference==='function'){ IFH.setPreference('disable-locale-info-bar','true'); } }catch(_){}
69
68
  function deepCollect(sel,cap){ var out=[],seen=0,stack=[document]; while(stack.length&&seen<(cap||40000)){ var root=stack.pop(); try{var m=root.querySelectorAll?root.querySelectorAll(sel):[];for(var i=0;i<m.length;i++)out.push(m[i]);}catch(_){} try{var all=root.querySelectorAll?root.querySelectorAll('*'):[];for(var j=0;j<all.length;j++){seen++;if(all[j].shadowRoot)stack.push(all[j].shadowRoot);}}catch(_){} } return out; }
@@ -94,15 +93,6 @@ export function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
94
93
  if(!handled){ var FALL=['timeline','resources','heap-profiler','security','lighthouse','chrome-recorder','coverage','linear-memory-inspector','sensors','rendering','animations','autofill-view','medias','issues-pane']; for(var fr=0;fr<FALL.length;fr++){ try{ if(!KEEPID.has(FALL[fr])) maybeRemove(FALL[fr]); }catch(_){} } }
95
94
  } else { domFallback(); }
96
95
  })();
97
- // Keep Sources LAST in the bar (after Network). Default DevTools order puts
98
- // Sources between Console and Network; nudge it to just after Network. Runs in
99
- // BOTH paths (registry removal leaves the kept tabs in default order). Self-
100
- // stable: once Sources follows Network the condition no longer holds.
101
- if(WANT_SOURCES_LAST){ var ro=0,rt=setInterval(function(){ ro++; try{
102
- var tabs=deepCollect('[role="tab"]'),src=null,net=null;
103
- for(var i=0;i<tabs.length;i++){ var n=txt(tabs[i]); if(SRCNAME.has(n))src=tabs[i]; else if(NETNAME.has(n))net=tabs[i]; }
104
- if(src&&net&&src.parentElement&&src.parentElement===net.parentElement&&(src.compareDocumentPosition(net)&Node.DOCUMENT_POSITION_FOLLOWING)){ net.parentElement.insertBefore(src,net.nextSibling); }
105
- }catch(_){} if(ro>120)clearInterval(rt); },60); }
106
96
  // DOM fallback: only if the ESM module couldn't be resolved at all.
107
97
  function domFallback(){ var cachedBar=null;
108
98
  function findBar(){ if(cachedBar&&cachedBar.isConnected)return cachedBar; cachedBar=null; var tabs=deepCollect('[role="tab"]'); var a=null; for(var i=0;i<tabs.length;i++){if(KEEPNAME.has(txt(tabs[i]))){a=tabs[i];break;}} if(!a)return null; var p=a.parentElement; while(p){try{if(p.querySelectorAll('[role="tab"]').length>1){cachedBar=p;return p;}}catch(_){} p=p.parentElement;} cachedBar=a.parentElement; return cachedBar; }
@@ -110,4 +100,33 @@ export function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
110
100
  var tr=0,t=setInterval(function(){tr++;try{apply();}catch(_){}if(tr>120)clearInterval(t);},60); try{apply();}catch(_){} }
111
101
  }catch(_){}})()`;
112
102
  }
103
+ /**
104
+ * Build the `executeJavaScript` source that forces the DevTools front-end to
105
+ * render its context menus as PAGE menus (soft menus) instead of asking the
106
+ * host via `showContextMenuAtPoint()`.
107
+ *
108
+ * The embedded DevTools front-end decides per-menu in `ContextMenu#show()`:
109
+ * `this.useSoftMenu || InspectorFrontendHostInstance.isHostedMode()`. On a
110
+ * self-built WebContentsView Electron leaves both false and does not answer
111
+ * `showContextMenuAtPoint()`, so right-click menus (复制为 cURL / Copy / …)
112
+ * never appear. Forcing the host-wide `isHostedMode()` true instead breaks the
113
+ * front-end's target data flow (Console/Network panels go empty), so we take
114
+ * the narrow path: patch the ContextMenu prototype so every menu shows as a
115
+ * page-rendered soft menu — same rendering the VS Code embedding uses, without
116
+ * touching the host-mode flag or any other front-end behaviour.
117
+ */
118
+ export function buildDevtoolsSoftMenuScript() {
119
+ return `(function(){
120
+ try {
121
+ var C = globalThis.EUI && globalThis.EUI.ContextMenu && globalThis.EUI.ContextMenu.ContextMenu;
122
+ if (C && C.prototype && typeof C.prototype.show === 'function') {
123
+ var origShow = C.prototype.show;
124
+ C.prototype.show = function() {
125
+ this.useSoftMenu = true;
126
+ return origShow.apply(this, arguments);
127
+ };
128
+ }
129
+ } catch (_) {}
130
+ })()`;
131
+ }
113
132
  //# sourceMappingURL=devtools-tabs.js.map
@@ -4,11 +4,9 @@ import { HOST_TOOLBAR_RUNTIME_MARKER } from '../../../shared/constants.js';
4
4
  import { VIEW_ID, VIEW_LAYER } from '../../../shared/view-ids.js';
5
5
  import { acquireHostToolbarSessionRuntime, releaseHostToolbarSessionRuntime, } from './host-toolbar-session-runtime.js';
6
6
  import { createHostToolbarPortChannel } from './host-toolbar-port-channel.js';
7
- import { destroyChildView } from './destroy-child-view.js';
8
7
  export function createHostToolbarView(ctx, reconciler, deps) {
9
8
  let hostToolbarView = null;
10
9
  let hostToolbarPreloadOverride = null;
11
- let hostToolbarViewAdded = false;
12
10
  // Whether THIS manager holds a reference on the shared defaultSession
13
11
  // registration of the toolbar-runtime preload (see
14
12
  // host-toolbar-session-runtime.ts). Acquired on first toolbar need,
@@ -50,20 +48,11 @@ export function createHostToolbarView(ctx, reconciler, deps) {
50
48
  if (hostToolbarView && liveHostToolbarWebContents()) {
51
49
  return hostToolbarView;
52
50
  }
53
- // Rebuilding after the host destroyed the underlying webContents: detach the
54
- // dead view from the contentView and reset the added-flag so the new view
55
- // gets re-mounted (otherwise the `hostToolbarViewAdded` guard would skip the
56
- // addChildView and the toolbar would silently disappear).
57
- if (hostToolbarView && hostToolbarViewAdded) {
58
- try {
59
- ctx.windows.mainWindow.contentView.removeChildView(hostToolbarView);
60
- }
61
- catch { /* already removed */ }
62
- hostToolbarViewAdded = false;
51
+ // Rebuilding after the host destroyed the underlying webContents first
52
+ // retires the concrete instance from the native-tree ledger.
53
+ if (hostToolbarView) {
54
+ reconciler.destroyView(VIEW_ID.hostToolbar, hostToolbarView);
63
55
  }
64
- // The instance is being replaced — forget its reconciled mount state so the
65
- // rebuilt view is treated as a fresh attach (not skipped as already-attached).
66
- reconciler.forgetActual(VIEW_ID.hostToolbar);
67
56
  // The framework's height-advertiser runtime is SESSION-resident: register
68
57
  // it on session.defaultSession (ref-counted across coexisting managers)
69
58
  // BEFORE the view exists, so the very first load already runs it. The
@@ -213,7 +202,6 @@ export function createHostToolbarView(ctx, reconciler, deps) {
213
202
  reconciler.registerView(VIEW_ID.hostToolbar, {
214
203
  getView: () => hostToolbarView,
215
204
  ensureView: () => ensureHostToolbarView(),
216
- setAdded: (added) => { hostToolbarViewAdded = added; },
217
205
  ensureLazy: (desired) => {
218
206
  if (desired?.placement.visible && !liveHostToolbarWebContents())
219
207
  ensureHostToolbarView();
@@ -226,9 +214,8 @@ export function createHostToolbarView(ctx, reconciler, deps) {
226
214
  hostToolbarPort.dispose();
227
215
  // Host-controllable toolbar view: removed from the contentView + its
228
216
  // WebContents closed (the host's loaded content is torn down on app exit).
229
- destroyChildView(ctx.windows.mainWindow, hostToolbarView);
217
+ reconciler.destroyView(VIEW_ID.hostToolbar, hostToolbarView);
230
218
  hostToolbarView = null;
231
- hostToolbarViewAdded = false;
232
219
  // Release this manager's reference on the shared defaultSession
233
220
  // toolbar-runtime registration (only if it ever acquired one — a manager
234
221
  // that never used the toolbar must not drive the shared count to zero).
@@ -1,13 +1,13 @@
1
1
  import { WebContentsView, webContents as electronWebContents } from 'electron';
2
2
  import { buildDevtoolsProjectSourceLinksScript, decodeOpenInEditorUrl, projectSourceContextFromServiceHostUrl, } from '../../../shared/open-in-editor.js';
3
- import { buildCustomizeTabsScript } from './devtools-tabs.js';
4
- import { buildConsoleFilterScript, buildLiveConsoleFilterScript } from './console-filter.js';
3
+ import { buildCustomizeTabsScript, buildDevtoolsSoftMenuScript } from './devtools-tabs.js';
4
+ import { buildInternalLogHideScript } from './console-filter.js';
5
+ import { buildClearConsoleFilterScript } from './clear-console-filter.js';
5
6
  import { whenFrontendBootstrapped } from './frontend-bootstrap-gate.js';
6
7
  import { installElementsForward } from '../elements-forward/index.js';
7
8
  import { installServiceConsoleForward } from '../service-console/index.js';
8
9
  import { VIEW_ID } from '../../../shared/view-ids.js';
9
10
  import { resolveProjectEditorTarget } from './resolve-project-editor-target.js';
10
- import { destroyChildView } from './destroy-child-view.js';
11
11
  import { createLoadDeferredInjector } from './inject-when-ready.js';
12
12
  // `webContents.executeJavaScript()` registers a transient `did-stop-loading`
13
13
  // listener on the target wc whenever that wc is still loading (Electron's
@@ -39,7 +39,6 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
39
39
  // The right-panel Chrome DevTools front-end HOST WebContentsView. Created by
40
40
  // rebuildDevtoolsHostView; positioned by the renderer's simulatorDevtools anchor.
41
41
  let simulatorView = null;
42
- let simulatorViewAdded = false;
43
42
  // The webContents the right-panel Chrome DevTools front-end currently inspects.
44
43
  // We point it at the SERVICE HOST (logic layer) — the hidden BrowserWindow
45
44
  // where the mini-app's page code runs (`console.log`, `wx.request`,
@@ -67,6 +66,14 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
67
66
  let stopServiceConsole = null;
68
67
  let nativeDevtoolsRetryTimer = null;
69
68
  let nativeDevtoolsRetryToken = 0;
69
+ // When the NEXT host build happens it is a FRESH PROJECT OPEN, so the stale
70
+ // Console filter (DevTools' own persisted localStorage setting, possibly
71
+ // leftover from the old filter-box implementation) must be cleared once.
72
+ // Armed by `attach()` (the project-open path) and disarmed by every re-point,
73
+ // so a service-host pool swap (hot-reload respawn) rebuilds the host WITHOUT
74
+ // wiping a filter the developer typed during the session. See
75
+ // clear-console-filter.ts.
76
+ let clearFilterOnNextHostBuild = false;
70
77
  // Front-end injects re-fire on EVERY re-point; while the host is loading each
71
78
  // raw executeJavaScript would queue one did-stop-loading waiter per call.
72
79
  // The deferred injector keeps one hook per (wc, kind) with latest-wins.
@@ -172,11 +179,10 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
172
179
  // framework internal log lines from THIS right-panel Console — the
173
180
  // standalone internal DevTools window (native-only, wired elsewhere) shows
174
181
  // them unfiltered. Re-applied on every re-point, same policy as
175
- // customizeDevtoolsTabs. Runs BOTH the persisted-preference write (for a
176
- // not-yet-constructed panel) AND the live-object drive (for a panel that's
177
- // already constructed by the time this injection lands, or finishes
178
- // constructing shortly after — see console-filter.ts's header comment for
179
- // why the persisted write alone cannot cover that case).
182
+ // customizeDevtoolsTabs (the injected script wraps once and is idempotent
183
+ // across re-points). Drives the panel's own visibility judge rather than
184
+ // its text-filter box, so the developer's filter input stays empty and
185
+ // entirely theirs — see console-filter.ts's header comment.
180
186
  // Bootstrap-gated: the live script touches `Console.ConsoleView.instance()`,
181
187
  // which — fired before the front-end's own bootstrap completes —
182
188
  // constructs IssuesManager early and permanently kills that bootstrap (see
@@ -191,8 +197,7 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
191
197
  if (!ready || devtoolsWc.isDestroyed())
192
198
  return;
193
199
  try {
194
- void devtoolsWc.executeJavaScript(buildConsoleFilterScript()).catch(() => { });
195
- void devtoolsWc.executeJavaScript(buildLiveConsoleFilterScript()).catch(() => { });
200
+ void devtoolsWc.executeJavaScript(buildInternalLogHideScript()).catch(() => { });
196
201
  }
197
202
  catch { /* wc torn down mid-call */ }
198
203
  });
@@ -254,6 +259,9 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
254
259
  function pointNativeDevtoolsAtServiceWc(next) {
255
260
  if (!simulatorView || simulatorView.webContents.isDestroyed())
256
261
  return true;
262
+ // Any host rebuild triggered from a re-point is a service-host swap, NOT a
263
+ // fresh project open — the stale Console filter must survive it.
264
+ clearFilterOnNextHostBuild = false;
257
265
  if (nativeDevtoolsSourceWc?.id === next.id && !nativeDevtoolsSourceWc.isDestroyed()) {
258
266
  return true;
259
267
  }
@@ -432,15 +440,9 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
432
440
  // service-host pool swap must repoint onto a fresh host, not reuse the
433
441
  // already-navigated one).
434
442
  function rebuildDevtoolsHostView() {
435
- // Destroy old simulatorView to prevent WebContentsView leak
443
+ // Destroy the previous host through the native-tree owner before replacing it.
436
444
  if (simulatorView) {
437
- removeSimulatorDevtoolsView();
438
- try {
439
- if (!simulatorView.webContents.isDestroyed()) {
440
- simulatorView.webContents.close();
441
- }
442
- }
443
- catch { /* ignore */ }
445
+ reconciler.destroyView(VIEW_ID.simulatorDevtools, simulatorView);
444
446
  simulatorView = null;
445
447
  }
446
448
  simulatorView = new WebContentsView();
@@ -464,6 +466,27 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
464
466
  // queue a pending `did-stop-loading` waiter while the front-end is still
465
467
  // loading (see raiseExecuteJavaScriptListenerCeiling).
466
468
  raiseExecuteJavaScriptListenerCeiling(devtoolsWc);
469
+ // The embedded Chrome DevTools front-end renders its context menus ONLY
470
+ // when the host reports “hosted mode” (page-rendered soft menus — the
471
+ // VS Code / Web-embedding path). Electron's own devtools:// host injection
472
+ // leaves `isHostedMode()` false and does not handle
473
+ // `showContextMenuAtPoint()` for a SELF-BUILT WebContentsView, so
474
+ // right-click menus (复制为 cURL / Copy / …) never appear — the front-end
475
+ // asks the host for a menu and nobody answers. Force hosted mode so the
476
+ // front-end draws its native page menus itself. Re-injected on every
477
+ // The embedded DevTools front-end asks the host for its context menus via
478
+ // `showContextMenuAtPoint()` (Electron leaves it unhandled on a self-built
479
+ // WCV), so right-click menus never appear. Force page-rendered soft menus
480
+ // via a narrow ContextMenu#show patch (NOT the host-wide isHostedMode()
481
+ // flag — that one breaks the front-end's target data flow, emptying the
482
+ // Console/Network panels). Needs the front-end bootstrap for EUI to exist.
483
+ injectWhenReady(devtoolsWc, 'soft-menu', () => {
484
+ void whenFrontendBootstrapped(devtoolsWc).then((ready) => {
485
+ if (!ready || devtoolsWc.isDestroyed())
486
+ return;
487
+ devtoolsWc.executeJavaScript(buildDevtoolsSoftMenuScript()).catch(() => { });
488
+ });
489
+ });
467
490
  // Hand the network forwarder THIS front-end host wc — it injects the
468
491
  // simulator WCV's Network.* CDP events into `window.DevToolsAPI.dispatchMessage`
469
492
  // here so the native Network tab renders them (falls back to the service-host
@@ -529,6 +552,23 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
529
552
  `).catch(() => { });
530
553
  });
531
554
  });
555
+ // Clear the stale Console filter box on a FRESH project open only (armed by
556
+ // attach(), disarmed by re-points — see clearFilterOnNextHostBuild). The
557
+ // value is captured at build time so a later swap cannot re-arm/clear this
558
+ // host; bootstrap-gated the same way as the console-default inject above.
559
+ const clearStaleFilter = clearFilterOnNextHostBuild;
560
+ injectWhenReady(devtoolsWc, 'clear-console-filter', () => {
561
+ if (!clearStaleFilter)
562
+ return;
563
+ void whenFrontendBootstrapped(devtoolsWc).then((ready) => {
564
+ if (!ready || devtoolsWc.isDestroyed())
565
+ return;
566
+ try {
567
+ void devtoolsWc.executeJavaScript(buildClearConsoleFilterScript()).catch(() => { });
568
+ }
569
+ catch { /* wc torn down mid-call */ }
570
+ });
571
+ });
532
572
  // Anchor-only mount: the renderer's published rect is the SOLE authority.
533
573
  // If a non-zero rect was already published (it can land before this attach
534
574
  // on the project-open ordering), replay it; otherwise the view stays
@@ -543,6 +583,10 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
543
583
  devtoolsHostUsed = false;
544
584
  }
545
585
  function attachNativeSimulatorDevtoolsHost() {
586
+ // This host build is a FRESH PROJECT OPEN: clear the stale Console filter
587
+ // once (see clearFilterOnNextHostBuild / clear-console-filter.ts). A later
588
+ // re-point (service-host pool swap) disarms it before ITS rebuild.
589
+ clearFilterOnNextHostBuild = true;
546
590
  stopFollowingNativeServiceHost();
547
591
  rebuildDevtoolsHostView();
548
592
  if (ctx.bridge?.isNativeHost()) {
@@ -551,32 +595,8 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
551
595
  followNativeDevtoolsServiceHost();
552
596
  }
553
597
  }
554
- // Remove (but do not destroy) the DevTools overlay from the contentView.
555
- // Internal teardown helper for re-attach; user-facing visibility is the
556
- // anchor 0×0 single path (`setSimulatorDevtoolsBounds`).
557
- function removeSimulatorDevtoolsView() {
558
- if (simulatorView && simulatorViewAdded) {
559
- try {
560
- ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
561
- }
562
- catch (e) {
563
- console.error('[workbench] removeSimulatorDevtoolsView error', e);
564
- }
565
- simulatorViewAdded = false;
566
- }
567
- // The instance is being replaced/destroyed — forget its reconciled mount
568
- // state so the next rebuilt host is treated as a fresh attach. Without this,
569
- // the level-triggered reconciler still records `simulatorDevtools` as
570
- // `attached` (the manual removeChildView above bypasses it), so it never
571
- // emits the `attach` op for the rebuilt view and the new host is never
572
- // addChildView'd — embedded but invisible. Mirrors the hostToolbar /
573
- // simulator instance-replacement handling (ensureHostToolbarView /
574
- // tearDownNativeSimulatorView).
575
- reconciler.forgetActual(VIEW_ID.simulatorDevtools);
576
- }
577
598
  reconciler.registerView(VIEW_ID.simulatorDevtools, {
578
599
  getView: () => simulatorView,
579
- setAdded: (added) => { simulatorViewAdded = added; },
580
600
  gateHidden: () => !simulatorView,
581
601
  });
582
602
  return {
@@ -594,9 +614,8 @@ export function createDevtoolsHost(ctx, reconciler, deps) {
594
614
  stopElementsForward = null;
595
615
  },
596
616
  destroyHostView: () => {
597
- destroyChildView(ctx.windows.mainWindow, simulatorView);
617
+ reconciler.destroyView(VIEW_ID.simulatorDevtools, simulatorView);
598
618
  simulatorView = null;
599
- simulatorViewAdded = false;
600
619
  },
601
620
  };
602
621
  }
@@ -13,7 +13,6 @@ import { VIEW_ID } from '../../../shared/view-ids.js';
13
13
  export function createNativeSimulatorView(ctx, reconciler, deps) {
14
14
  const { safeArea, devtoolsHost, overlayPanels } = deps;
15
15
  let nativeSimulatorView = null;
16
- let nativeSimulatorViewAdded = false;
17
16
  let nativeSimulatorProjectPath = null;
18
17
  let settleNativeSimulatorReady = null;
19
18
  // Whether the CURRENT simulator view's shell finished its first boot (first
@@ -68,36 +67,20 @@ export function createNativeSimulatorView(ctx, reconciler, deps) {
68
67
  * double-dispose. `label` only tags the diagnostic on the async-tail failure.
69
68
  */
70
69
  function tearDownNativeSimulatorView(label) {
71
- if (!nativeSimulatorView)
70
+ const view = nativeSimulatorView;
71
+ if (!view)
72
72
  return;
73
- ctx.simulatorUiExtensions?.detach(nativeSimulatorView.webContents);
74
- if (nativeSimulatorViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
75
- try {
76
- ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
77
- }
78
- catch { /* already removed */ }
79
- }
73
+ ctx.simulatorUiExtensions?.detach(view.webContents);
80
74
  try {
81
- if (!nativeSimulatorView.webContents.isDestroyed()) {
82
- ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)
75
+ if (!view.webContents.isDestroyed()) {
76
+ ctx.bridge?.disposeSessionsForSimulator?.(view.webContents.id)
83
77
  ?.catch((err) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err));
84
- nativeSimulatorView.webContents.close();
85
78
  }
86
79
  }
87
80
  catch { /* ignore */ }
81
+ reconciler.destroyView(VIEW_ID.simulator, view);
88
82
  nativeSimulatorView = null;
89
- nativeSimulatorViewAdded = false;
90
83
  nativeSimulatorShellReady = false;
91
- // The instance is being replaced/destroyed — forget its reconciled mount
92
- // state so the NEXT rebuilt view is treated as a fresh attach. Without this,
93
- // the level-triggered reconciler still records `simulator` as `attached`
94
- // (the manual removeChildView above bypasses it), so the next reconcile
95
- // classifies the freshly-built WebContentsView as already-attached, never
96
- // emits the `attach` op, and the new view is never addChildView'd — a sticky
97
- // 100%-invisible simulator after every recompile. Mirrors the hostToolbar
98
- // instance-replacement handling in ensureHostToolbarView.
99
- reconciler.forgetActual(VIEW_ID.simulator);
100
- // The view is gone: gateReadiness now hides it, so reconcile detaches it.
101
84
  reconciler.reconcileNow();
102
85
  }
103
86
  /**
@@ -435,7 +418,6 @@ export function createNativeSimulatorView(ctx, reconciler, deps) {
435
418
  }
436
419
  reconciler.registerView(VIEW_ID.simulator, {
437
420
  getView: () => nativeSimulatorView,
438
- setAdded: (added) => { nativeSimulatorViewAdded = added; },
439
421
  gateHidden: () => !nativeSimulatorView,
440
422
  applyBounds: (view, bounds, extra) => applyNativeSimulatorBounds(view, bounds, extra?.zoom ?? 1),
441
423
  });
@@ -0,0 +1,25 @@
1
+ import type { BrowserWindow, WebContentsView } from 'electron';
2
+ import type { ViewSlot } from './placement-reconciler.js';
3
+ /**
4
+ * Single native child-tree owner for devtools. It resolves the current concrete
5
+ * instance dynamically because a stable viewId may be rebuilt, and updates its
6
+ * order ledger only after the corresponding Electron call succeeds.
7
+ */
8
+ interface NativeViewRef {
9
+ readonly id: string;
10
+ }
11
+ export interface NativeViewTreeHost {
12
+ addChildView(ref: NativeViewRef): boolean;
13
+ removeChildView(ref: NativeViewRef): boolean;
14
+ readonly isDestroyed: boolean;
15
+ children(): readonly NativeViewRef[];
16
+ /** Detach and close a concrete instance, then remove it from the ledger. */
17
+ destroyView(viewId: string, view: WebContentsView | null): void;
18
+ }
19
+ export declare function createNativeViewTreeHost(ctx: {
20
+ windows: {
21
+ mainWindow: BrowserWindow;
22
+ };
23
+ }, slots: Map<string, ViewSlot>): NativeViewTreeHost;
24
+ export {};
25
+ //# sourceMappingURL=native-view-tree.d.ts.map
@@ -0,0 +1,69 @@
1
+ export function createNativeViewTreeHost(ctx, slots) {
2
+ const order = [];
3
+ const mounted = new Map();
4
+ function resolve(id) {
5
+ return slots.get(id)?.getView() ?? null;
6
+ }
7
+ return {
8
+ addChildView(ref) {
9
+ const view = resolve(ref.id);
10
+ if (!view || view.webContents.isDestroyed() || ctx.windows.mainWindow.isDestroyed())
11
+ return false;
12
+ try {
13
+ ctx.windows.mainWindow.contentView.addChildView(view);
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ const i = order.indexOf(ref.id);
19
+ if (i >= 0)
20
+ order.splice(i, 1);
21
+ order.push(ref.id);
22
+ mounted.set(ref.id, view);
23
+ return true;
24
+ },
25
+ removeChildView(ref) {
26
+ const view = mounted.get(ref.id) ?? null;
27
+ if (!view || ctx.windows.mainWindow.isDestroyed())
28
+ return false;
29
+ try {
30
+ ctx.windows.mainWindow.contentView.removeChildView(view);
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ const i = order.indexOf(ref.id);
36
+ if (i >= 0)
37
+ order.splice(i, 1);
38
+ mounted.delete(ref.id);
39
+ return true;
40
+ },
41
+ get isDestroyed() {
42
+ return ctx.windows.mainWindow.isDestroyed();
43
+ },
44
+ children: () => order.map((id) => ({ id })),
45
+ destroyView(viewId, view) {
46
+ const isMountedInstance = view !== null && mounted.get(viewId) === view;
47
+ if (view && !ctx.windows.mainWindow.isDestroyed()) {
48
+ try {
49
+ ctx.windows.mainWindow.contentView.removeChildView(view);
50
+ }
51
+ catch { /* detached */ }
52
+ }
53
+ if (isMountedInstance) {
54
+ const i = order.indexOf(viewId);
55
+ if (i >= 0)
56
+ order.splice(i, 1);
57
+ mounted.delete(viewId);
58
+ }
59
+ if (view) {
60
+ try {
61
+ if (!view.webContents.isDestroyed())
62
+ view.webContents.close();
63
+ }
64
+ catch { /* destroyed concurrently */ }
65
+ }
66
+ },
67
+ };
68
+ }
69
+ //# sourceMappingURL=native-view-tree.js.map
@@ -1,9 +1,22 @@
1
1
  import type { WebContents } from 'electron';
2
2
  import type { PlacementReconciler } from './placement-reconciler.js';
3
3
  import type { ViewManagerContext } from './view-manager.js';
4
+ export interface TooltipShowPayload {
5
+ anchor: {
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ };
11
+ text: string;
12
+ }
4
13
  /**
5
- * The two main-owned overlay panels: the settings sheet (right-side panel over
6
- * a transparent backdrop) and the transient popover. Their bounds are
14
+ * The main-owned overlay panels: the settings sheet (right-side panel over a
15
+ * transparent backdrop), the transient compile-mode popover, and the tooltip.
16
+ * Each is an `OverlayPanel` (`@dimina-kit/electron-deck/main`) — the shared
17
+ * lazy-create/reuse/destroy WebContentsView lifecycle — wired into THIS
18
+ * module's own placement reconciler (the panel factory owns no z-order/bounds
19
+ * authority itself; see overlay-panel.ts's doc-comment). Bounds are
7
20
  * main-computed and published into the reconciler's `overlayDesired` (top-tier
8
21
  * layers, so a reorder keeps them above every base overlay).
9
22
  */
@@ -12,15 +25,25 @@ export interface OverlayPanelsView {
12
25
  hideSettings(): void;
13
26
  showPopover(data: unknown): void;
14
27
  hidePopover(): void;
15
- /** Re-apply whichever of settings/popover is currently present (window resize / toolbar height change). */
28
+ prepareTooltip(): void;
29
+ showTooltip(data: TooltipShowPayload): void;
30
+ hideTooltip(): void;
31
+ markOverlayReady(webContentsId: number): void;
32
+ applyTooltipMeasurement(webContentsId: number, measurement: {
33
+ requestId: number;
34
+ width: number;
35
+ height: number;
36
+ }): void;
37
+ /** Re-apply whichever of settings/popover is currently present (window resize / toolbar height change). Tooltip excluded — it re-anchors on its own next show, a stale position while hidden doesn't matter. */
16
38
  reapplyPresentOverlays(): void;
17
39
  /** Re-apply the settings overlay only (the resize entry point re-applies settings, not popover). */
18
40
  applySettingsBoundsIfPresent(): void;
19
- /** Destroy the cached settings view (aggregate simulator detach). */
41
+ /** Destroy the cached settings + tooltip views (aggregate simulator detach). */
20
42
  destroySettings(): void;
21
43
  getSettingsWebContents(): WebContents | null;
22
44
  getSettingsWebContentsId(): number | null;
23
45
  getPopoverWebContentsId(): number | null;
46
+ getTooltipWebContentsId(): number | null;
24
47
  }
25
48
  export declare function createOverlayPanelsView(ctx: ViewManagerContext, reconciler: PlacementReconciler, deps: {
26
49
  /** The host-toolbar strip height that offsets the overlay top edge. */