@dimina-kit/devtools 0.4.0-dev.20260618090552 → 0.4.0-dev.20260624084417

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 (57) hide show
  1. package/README.md +5 -1
  2. package/dist/main/app/app.js +14 -2
  3. package/dist/main/index.bundle.js +596 -112
  4. package/dist/main/ipc/bridge-router.js +13 -8
  5. package/dist/main/ipc/projects.js +1 -1
  6. package/dist/main/ipc/simulator.js +1 -1
  7. package/dist/main/runtime/miniapp-runtime.d.ts +3 -3
  8. package/dist/main/services/elements-forward/index.js +42 -8
  9. package/dist/main/services/network-forward/index.d.ts +5 -6
  10. package/dist/main/services/network-forward/index.js +9 -10
  11. package/dist/main/services/notifications/renderer-notifier.d.ts +3 -4
  12. package/dist/main/services/projects/index.js +1 -1
  13. package/dist/main/services/render-inspect/index.js +182 -6
  14. package/dist/main/services/service-console/console-api.d.ts +5 -6
  15. package/dist/main/services/service-console/console-api.js +5 -6
  16. package/dist/main/services/service-host-pool/pool.d.ts +2 -3
  17. package/dist/main/services/simulator-storage/index.d.ts +2 -4
  18. package/dist/main/services/simulator-storage/index.js +2 -4
  19. package/dist/main/services/simulator-temp-files/index.js +2 -2
  20. package/dist/main/services/views/devtools-tabs.d.ts +4 -1
  21. package/dist/main/services/views/devtools-tabs.js +21 -3
  22. package/dist/main/services/views/host-toolbar-session-runtime.d.ts +4 -4
  23. package/dist/main/services/views/host-toolbar-session-runtime.js +4 -4
  24. package/dist/main/services/views/view-manager.d.ts +17 -1
  25. package/dist/main/services/views/view-manager.js +74 -47
  26. package/dist/main/services/workspace/workspace-service.js +11 -0
  27. package/dist/main/windows/service-host-window/create.d.ts +1 -0
  28. package/dist/main/windows/service-host-window/create.js +1 -0
  29. package/dist/preload/instrumentation/wxml-extract.js +53 -7
  30. package/dist/preload/runtime/host-toolbar-port.d.ts +1 -1
  31. package/dist/preload/runtime/host-toolbar-port.js +1 -1
  32. package/dist/preload/runtime/host-toolbar-runtime.d.ts +8 -7
  33. package/dist/preload/runtime/host-toolbar-runtime.js +8 -7
  34. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  35. package/dist/preload/windows/main.cjs.map +2 -2
  36. package/dist/preload/windows/simulator.cjs +1 -1
  37. package/dist/preload/windows/simulator.cjs.map +1 -1
  38. package/dist/preload/windows/simulator.js +1 -1
  39. package/dist/render-host/render-inspect.js +24 -23
  40. package/dist/renderer/assets/index-BMqriQNr.js +50 -0
  41. package/dist/renderer/entries/main/index.html +1 -1
  42. package/dist/shared/appdata-accumulator.d.ts +2 -2
  43. package/dist/shared/appdata-accumulator.js +3 -3
  44. package/dist/shared/constants.d.ts +1 -1
  45. package/dist/shared/constants.js +1 -1
  46. package/dist/shared/ipc-channels.d.ts +3 -3
  47. package/dist/shared/ipc-channels.js +3 -3
  48. package/dist/shared/open-in-editor.d.ts +27 -1
  49. package/dist/shared/open-in-editor.js +369 -3
  50. package/dist/shared/vpath.d.ts +2 -2
  51. package/dist/shared/vpath.js +2 -2
  52. package/dist/simulator/assets/device-shell-CPCnCp1L.js +2 -0
  53. package/dist/simulator/assets/{simulator-sf-D0mhw.js → simulator-gMBWKDeO.js} +3 -3
  54. package/dist/simulator/simulator.html +1 -1
  55. package/package.json +6 -4
  56. package/dist/renderer/assets/index-DmgWoK8N.js +0 -50
  57. package/dist/simulator/assets/device-shell-BWX7Yopg.js +0 -2
@@ -28,7 +28,7 @@
28
28
  overflow: hidden;
29
29
  }
30
30
  </style>
31
- <script type="module" crossorigin src="../../assets/index-DmgWoK8N.js"></script>
31
+ <script type="module" crossorigin src="../../assets/index-BMqriQNr.js"></script>
32
32
  <link rel="modulepreload" crossorigin href="../../assets/chunk-DPI0Y_ll.js">
33
33
  <link rel="modulepreload" crossorigin href="../../assets/ipc-transport-DSBmaWpJ.js">
34
34
  <link rel="modulepreload" crossorigin href="../../assets/editor.api2-BTaYmRq7.js">
@@ -11,7 +11,7 @@
11
11
  * Default (dimina-fe) path: the simulator preload sniffs these as Worker
12
12
  * `message` events (app-data.ts). Native-host path: they flow service→render
13
13
  * through bridge-router (SERVICE_PUBLISH). Both feed this one accumulator so the
14
- * decode/merge/page-only/R6 policy can't drift between modes.
14
+ * decode/merge/page-only/init-gate policy can't drift between modes.
15
15
  */
16
16
  /**
17
17
  * The AppData snapshot — the full, cumulative panel state.
@@ -74,7 +74,7 @@ export declare class AppDataAccumulator {
74
74
  private recordBridge;
75
75
  /**
76
76
  * Apply one entry. Returns true if it was accepted (a mutation worth
77
- * republishing), false if dropped (missing ids, or the R6 gate).
77
+ * republishing), false if dropped (missing ids, or the init-gate).
78
78
  */
79
79
  apply(input: AppDataInput): boolean;
80
80
  /** Evict every entry for a bridge (page teardown). */
@@ -11,7 +11,7 @@
11
11
  * Default (dimina-fe) path: the simulator preload sniffs these as Worker
12
12
  * `message` events (app-data.ts). Native-host path: they flow service→render
13
13
  * through bridge-router (SERVICE_PUBLISH). Both feed this one accumulator so the
14
- * decode/merge/page-only/R6 policy can't drift between modes.
14
+ * decode/merge/page-only/init-gate policy can't drift between modes.
15
15
  */
16
16
  /**
17
17
  * Decode a service→render message into AppData entries, or null when it is not
@@ -116,12 +116,12 @@ export class AppDataAccumulator {
116
116
  }
117
117
  /**
118
118
  * Apply one entry. Returns true if it was accepted (a mutation worth
119
- * republishing), false if dropped (missing ids, or the R6 gate).
119
+ * republishing), false if dropped (missing ids, or the init-gate).
120
120
  */
121
121
  apply(input) {
122
122
  if (!input.bridgeId || !input.moduleId)
123
123
  return false;
124
- // R6: drop ub patches whose bridge has never been initialised. dimina
124
+ // Drop ub patches whose bridge has never been initialised. dimina
125
125
  // dispatches pageUnload → onUnload, whose setData produces a late `ub`
126
126
  // arriving AFTER clearBridge; without this gate it would resurrect the
127
127
  // unloaded bridge as a ghost tab.
@@ -18,7 +18,7 @@ export declare const HEADER_H = 40;
18
18
  * defaultSession renderer; its guard activates the height advertiser only when
19
19
  * `process.argv` carries this marker AND `process.isMainFrame` is true (the
20
20
  * marker is process-level, so subframes of the toolbar window see it too —
21
- * both guard wings are required; see .repro/wave3-spike/RESULTS.md items 3/4).
21
+ * both guard wings are required).
22
22
  */
23
23
  export declare const HOST_TOOLBAR_RUNTIME_MARKER = "--dimina-host-toolbar";
24
24
  //# sourceMappingURL=constants.d.ts.map
@@ -18,7 +18,7 @@ export const HEADER_H = 40;
18
18
  * defaultSession renderer; its guard activates the height advertiser only when
19
19
  * `process.argv` carries this marker AND `process.isMainFrame` is true (the
20
20
  * marker is process-level, so subframes of the toolbar window see it too —
21
- * both guard wings are required; see .repro/wave3-spike/RESULTS.md items 3/4).
21
+ * both guard wings are required).
22
22
  */
23
23
  export const HOST_TOOLBAR_RUNTIME_MARKER = '--dimina-host-toolbar';
24
24
  //# sourceMappingURL=constants.js.map
@@ -174,11 +174,11 @@ export declare const ProjectsChannel: {
174
174
  readonly List: "projects:list";
175
175
  readonly Add: "projects:add";
176
176
  readonly Remove: "projects:remove";
177
- /** Phase 3: merged + sanitized template catalog for the create-project dialog. */
177
+ /** Merged + sanitized template catalog for the create-project dialog. */
178
178
  readonly ListTemplates: "projects:listTemplates";
179
- /** Phase 3: host-supplied create-project dialog hook (returns input or null). */
179
+ /** Host-supplied create-project dialog hook (returns input or null). */
180
180
  readonly OpenCreateDialog: "projects:openCreateDialog";
181
- /** Phase 3: server-side scaffold + register (delegates to create-project-service). */
181
+ /** Server-side scaffold + register (delegates to create-project-service). */
182
182
  readonly Create: "projects:create";
183
183
  /** Default values used to pre-fill the create-project dialog (baseDir). */
184
184
  readonly GetCreateDefaults: "projects:getCreateDefaults";
@@ -161,11 +161,11 @@ export const ProjectsChannel = {
161
161
  List: 'projects:list',
162
162
  Add: 'projects:add',
163
163
  Remove: 'projects:remove',
164
- /** Phase 3: merged + sanitized template catalog for the create-project dialog. */
164
+ /** Merged + sanitized template catalog for the create-project dialog. */
165
165
  ListTemplates: 'projects:listTemplates',
166
- /** Phase 3: host-supplied create-project dialog hook (returns input or null). */
166
+ /** Host-supplied create-project dialog hook (returns input or null). */
167
167
  OpenCreateDialog: 'projects:openCreateDialog',
168
- /** Phase 3: server-side scaffold + register (delegates to create-project-service). */
168
+ /** Server-side scaffold + register (delegates to create-project-service). */
169
169
  Create: 'projects:create',
170
170
  /** Default values used to pre-fill the create-project dialog (baseDir). */
171
171
  GetCreateDefaults: 'projects:getCreateDefaults',
@@ -32,6 +32,15 @@ export interface OpenInEditorRequest {
32
32
  /** 0-based column as reported by DevTools (may be undefined). */
33
33
  column?: number;
34
34
  }
35
+ export interface ProjectSourceContext {
36
+ /** Absolute project root on disk. */
37
+ projectRoot: string;
38
+ /** Resource server base URL used by the compiled miniapp. */
39
+ resourceBaseUrl: string;
40
+ appId: string;
41
+ /** Compiler output package root (`main` for the primary package). */
42
+ outputRoot: string;
43
+ }
35
44
  /**
36
45
  * Encode an open-in-editor request as a sentinel URL the DevTools front-end can
37
46
  * hand to `InspectorFrontendHost.openInNewTab(...)`. All fields ride in the
@@ -67,5 +76,22 @@ export declare function decodeOpenInEditorUrl(raw: string): OpenInEditorRequest
67
76
  * @returns the project-relative POSIX path, or null when the URL is not a
68
77
  * mappable dev-server source.
69
78
  */
70
- export declare function resourceUrlToProjectRelativePath(resourceUrl: string, expectedOrigin?: string): string | null;
79
+ export declare function resourceUrlToProjectRelativePath(resourceUrl: string, expectedOriginOrContext?: string | ProjectSourceContext): string | null;
80
+ /** Read the project/source routing metadata carried by a service-host spawn URL. */
81
+ export declare function projectSourceContextFromServiceHostUrl(serviceHostUrl: string, activeProjectRoot?: string): ProjectSourceContext | null;
82
+ /**
83
+ * Build the DevTools-front-end glue for project source links.
84
+ *
85
+ * Primary transport is a capture-phase click interceptor: a project-source link
86
+ * click is redirected to `InspectorFrontendHost.openInNewTab(<sentinel>)` (which
87
+ * Electron surfaces as `devtools-open-url` on the inspected wc) and the default
88
+ * DevTools Sources reveal is suppressed. This survives current Chromium DevTools,
89
+ * where the legacy `Host.InspectorFrontendHost.setOpenResourceHandler` hook is
90
+ * gone (the host moved to the top-level `InspectorFrontendHost` global and that
91
+ * method no longer exists). The `setOpenResourceHandler` registration below is
92
+ * kept as a no-op-when-absent fallback for older fronts. Only locations that map
93
+ * to the active project are forwarded; a DOM observer expands Chromium's
94
+ * basename-only console labels to project-relative paths.
95
+ */
96
+ export declare function buildDevtoolsProjectSourceLinksScript(context: ProjectSourceContext): string;
71
97
  //# sourceMappingURL=open-in-editor.d.ts.map
@@ -96,9 +96,12 @@ export function decodeOpenInEditorUrl(raw) {
96
96
  * @returns the project-relative POSIX path, or null when the URL is not a
97
97
  * mappable dev-server source.
98
98
  */
99
- export function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
99
+ export function resourceUrlToProjectRelativePath(resourceUrl, expectedOriginOrContext) {
100
100
  if (typeof resourceUrl !== 'string' || !resourceUrl)
101
101
  return null;
102
+ if (expectedOriginOrContext && typeof expectedOriginOrContext === 'object') {
103
+ return projectAwareResourcePath(resourceUrl, expectedOriginOrContext);
104
+ }
102
105
  let resource;
103
106
  try {
104
107
  resource = new URL(resourceUrl);
@@ -110,10 +113,10 @@ export function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
110
113
  // `webpack://`, `node:`, `data:` are framework/runtime frames with no file.
111
114
  if (resource.protocol !== 'http:' && resource.protocol !== 'https:')
112
115
  return null;
113
- if (expectedOrigin) {
116
+ if (expectedOriginOrContext) {
114
117
  let base;
115
118
  try {
116
- base = new URL(expectedOrigin);
119
+ base = new URL(expectedOriginOrContext);
117
120
  }
118
121
  catch {
119
122
  return null;
@@ -135,4 +138,367 @@ export function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
135
138
  return null;
136
139
  return segments.slice(1).join('/');
137
140
  }
141
+ function decodePathname(pathname) {
142
+ return pathname.split('/').map((segment) => {
143
+ try {
144
+ return decodeURIComponent(segment);
145
+ }
146
+ catch {
147
+ return segment;
148
+ }
149
+ }).join('/');
150
+ }
151
+ function normalizeSlashPath(value) {
152
+ return decodePathname(value.replace(/\\/g, '/')).replace(/\/+/g, '/');
153
+ }
154
+ function safeRelativePath(segments) {
155
+ if (segments.length === 0 || segments.some((segment) => !segment || segment === '.' || segment === '..')) {
156
+ return null;
157
+ }
158
+ return segments.join('/');
159
+ }
160
+ function projectAwareResourcePath(resourceUrl, context) {
161
+ let base;
162
+ try {
163
+ base = new URL(context.resourceBaseUrl);
164
+ }
165
+ catch {
166
+ return null;
167
+ }
168
+ const projectRoot = normalizeSlashPath(context.projectRoot).replace(/\/$/, '');
169
+ const raw = resourceUrl.trim();
170
+ if (!raw)
171
+ return null;
172
+ // Taro/webpack build + runtime chunks are compiled output, not hand-written
173
+ // source. They also collide: dimina's own service runtime attributes its
174
+ // `log()` helper to a `common.js` on the SAME dev-server origin as the
175
+ // project's compiled `common.js` chunk, so the two are URL-indistinguishable
176
+ // and a dimina-core frame would otherwise open the project's chunk. Drop these
177
+ // well-known chunk basenames so a click falls through to the DevTools Sources
178
+ // panel instead of opening the wrong (or generated) file. Inlined (not a module
179
+ // const) because this function is `.toString()`-injected into the DevTools realm.
180
+ const excludeBuildChunk = (rel) => {
181
+ if (!rel)
182
+ return rel;
183
+ const base = rel.split('/').pop();
184
+ return base === 'common.js' || base === 'vendors.js' || base === 'runtime.js'
185
+ || base === 'taro.js' || base === 'babelHelpers.js'
186
+ ? null
187
+ : rel;
188
+ };
189
+ // Chromium may hand the open-resource hook a raw absolute filesystem path.
190
+ const normalizedRaw = normalizeSlashPath(raw);
191
+ if (projectRoot && (normalizedRaw === projectRoot || normalizedRaw.startsWith(`${projectRoot}/`))) {
192
+ const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, '');
193
+ return excludeBuildChunk(safeRelativePath(relative.split('/').filter(Boolean)));
194
+ }
195
+ // Sourcemap sources commonly use virtual root paths such as `/pages/x.js`.
196
+ // Real host filesystem paths use well-known absolute roots; when they are
197
+ // outside the active project they are runtime/devtools frames, not sources.
198
+ if (/^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw)) {
199
+ return null;
200
+ }
201
+ // Reject relative runtime labels such as `service.js` or
202
+ // `electron/js2c/renderer_init`; project source locations must be an
203
+ // absolute URL/path so they can be scoped to the active project.
204
+ if (!raw.startsWith('/') && !/^[a-z][a-z0-9+.-]*:/i.test(raw))
205
+ return null;
206
+ let resource;
207
+ try {
208
+ resource = raw.startsWith('/') && !raw.startsWith('//')
209
+ ? new URL(raw, base.origin)
210
+ : new URL(raw);
211
+ }
212
+ catch {
213
+ return null;
214
+ }
215
+ if (resource.protocol === 'file:') {
216
+ const filePath = normalizeSlashPath(resource.pathname);
217
+ if (!projectRoot || (filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)))
218
+ return null;
219
+ return excludeBuildChunk(safeRelativePath(filePath.slice(projectRoot.length).replace(/^\/+/, '').split('/').filter(Boolean)));
220
+ }
221
+ if (resource.protocol !== 'http:' && resource.protocol !== 'https:')
222
+ return null;
223
+ if (resource.origin !== base.origin)
224
+ return null;
225
+ let segments = decodePathname(resource.pathname).split('/').filter(Boolean);
226
+ if (context.appId && segments[0] === context.appId) {
227
+ segments = segments.slice(1);
228
+ if (context.outputRoot && segments[0] === context.outputRoot)
229
+ segments = segments.slice(1);
230
+ }
231
+ return excludeBuildChunk(safeRelativePath(segments));
232
+ }
233
+ /** Read the project/source routing metadata carried by a service-host spawn URL. */
234
+ export function projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot) {
235
+ let url;
236
+ try {
237
+ url = new URL(serviceHostUrl);
238
+ }
239
+ catch {
240
+ return null;
241
+ }
242
+ const projectRoot = url.searchParams.get('pkgRoot') || '';
243
+ const resourceBaseUrl = url.searchParams.get('resourceBaseUrl') ?? '';
244
+ const appId = url.searchParams.get('appId') ?? '';
245
+ const outputRoot = url.searchParams.get('root') ?? 'main';
246
+ if (!projectRoot || !resourceBaseUrl || !appId)
247
+ return null;
248
+ if (activeProjectRoot) {
249
+ const authoritativeRoot = normalizeSlashPath(projectRoot).replace(/\/+$/, '');
250
+ const currentRoot = normalizeSlashPath(activeProjectRoot).replace(/\/+$/, '');
251
+ if (!authoritativeRoot || authoritativeRoot !== currentRoot)
252
+ return null;
253
+ }
254
+ try {
255
+ new URL(resourceBaseUrl);
256
+ }
257
+ catch {
258
+ return null;
259
+ }
260
+ return { projectRoot, resourceBaseUrl, appId, outputRoot };
261
+ }
262
+ /**
263
+ * Build the DevTools-front-end glue for project source links.
264
+ *
265
+ * Primary transport is a capture-phase click interceptor: a project-source link
266
+ * click is redirected to `InspectorFrontendHost.openInNewTab(<sentinel>)` (which
267
+ * Electron surfaces as `devtools-open-url` on the inspected wc) and the default
268
+ * DevTools Sources reveal is suppressed. This survives current Chromium DevTools,
269
+ * where the legacy `Host.InspectorFrontendHost.setOpenResourceHandler` hook is
270
+ * gone (the host moved to the top-level `InspectorFrontendHost` global and that
271
+ * method no longer exists). The `setOpenResourceHandler` registration below is
272
+ * kept as a no-op-when-absent fallback for older fronts. Only locations that map
273
+ * to the active project are forwarded; a DOM observer expands Chromium's
274
+ * basename-only console labels to project-relative paths.
275
+ */
276
+ export function buildDevtoolsProjectSourceLinksScript(context) {
277
+ const config = JSON.stringify(context);
278
+ const scheme = JSON.stringify(OPEN_IN_EDITOR_SCHEME);
279
+ return `
280
+ (function() {
281
+ try {
282
+ const stateKey = '__diminaProjectSourceLinksState__'
283
+ const previousState = globalThis[stateKey]
284
+ if (previousState && typeof previousState.dispose === 'function') {
285
+ try { previousState.dispose() } catch (_) {}
286
+ }
287
+
288
+ const cfg = ${config}
289
+ const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
290
+ const decodePathname = ${decodePathname.toString()}
291
+ const normalizeSlashPath = ${normalizeSlashPath.toString()}
292
+ const safeRelativePath = ${safeRelativePath.toString()}
293
+ const observedRoots = new Set()
294
+ const observers = []
295
+ const clickBindings = []
296
+ let timer = null
297
+ const state = {
298
+ dispose() {
299
+ if (timer !== null) {
300
+ clearInterval(timer)
301
+ timer = null
302
+ }
303
+ for (const observer of observers.splice(0)) {
304
+ try { observer.disconnect() } catch (_) {}
305
+ }
306
+ for (const binding of clickBindings.splice(0)) {
307
+ try { binding.target.removeEventListener('click', binding.fn, true) } catch (_) {}
308
+ }
309
+ observedRoots.clear()
310
+ },
311
+ }
312
+ globalThis[stateKey] = state
313
+
314
+ // The InspectorFrontendHost that owns openInNewTab. Across DevTools
315
+ // versions it lives on the top-level \`InspectorFrontendHost\` global, the
316
+ // legacy \`Host.InspectorFrontendHost\`, or \`DevToolsHost\`; resolve at call
317
+ // time so a late-bootstrapped host is still found.
318
+ function resolveOpenInNewTabHost() {
319
+ const candidates = [
320
+ globalThis.InspectorFrontendHost,
321
+ globalThis.Host && globalThis.Host.InspectorFrontendHost,
322
+ globalThis.DevToolsHost,
323
+ ]
324
+ for (const host of candidates) {
325
+ if (host && typeof host.openInNewTab === 'function') return host
326
+ }
327
+ return null
328
+ }
329
+
330
+ // Encode (url, 0-based line, 0-based column) as the sentinel the main
331
+ // process decodes from \`devtools-open-url\` and routes to Monaco.
332
+ function openInEditor(url, line, column) {
333
+ const host = resolveOpenInNewTabHost()
334
+ if (!host) return false
335
+ const p = new URLSearchParams()
336
+ p.set('u', String(url))
337
+ if (typeof line === 'number' && isFinite(line)) p.set('l', String(line))
338
+ if (typeof column === 'number' && isFinite(column)) p.set('c', String(column))
339
+ try { host.openInNewTab(${scheme} + ':?' + p.toString()) } catch (_) { return false }
340
+ return true
341
+ }
342
+
343
+ // Resolve a clicked link element to a project source location. Returns
344
+ // the FULL resource url (the main process re-maps it against the active
345
+ // project) plus its display line/column when present.
346
+ function projectLocationForLink(link) {
347
+ if (!link) return null
348
+ const candidates = [
349
+ link.getAttribute && link.getAttribute('data-url'),
350
+ link.getAttribute && link.getAttribute('href'),
351
+ link.getAttribute && link.getAttribute('title'),
352
+ ]
353
+ for (const candidate of candidates) {
354
+ if (!candidate) continue
355
+ const location = splitLocation(candidate)
356
+ if (!diminaProjectSourcePath(location.url, cfg)) continue
357
+ const suffix = location.suffix
358
+ || ((String(link.textContent || '').match(/(:\\d+(?::\\d+)?)$/) || [])[1] || '')
359
+ return { url: location.url, suffix }
360
+ }
361
+ return null
362
+ }
363
+
364
+ // DevTools shows 1-based line:column in link text; the main process
365
+ // contract expects 0-based (it re-adds 1). Convert here.
366
+ function parseSuffix(suffix) {
367
+ const m = String(suffix || '').match(/^:(\\d+)(?::(\\d+))?$/)
368
+ if (!m) return { line: undefined, column: undefined }
369
+ const line = Math.max(0, parseInt(m[1], 10) - 1)
370
+ const column = m[2] != null ? Math.max(0, parseInt(m[2], 10) - 1) : undefined
371
+ return { line, column }
372
+ }
373
+
374
+ // Capture-phase click handler: route a project-source link click to
375
+ // Monaco instead of the DevTools Sources panel. Plain primary clicks only
376
+ // — modified clicks (cmd/ctrl/shift/alt, middle button) fall through to
377
+ // DevTools so its own "open in new tab" / multi-select still work.
378
+ function onLinkClick(event) {
379
+ if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
380
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : []
381
+ let link = null
382
+ for (const node of path) {
383
+ if (node && node.nodeType === 1 && node.classList && node.classList.contains('devtools-link')) {
384
+ link = node
385
+ break
386
+ }
387
+ }
388
+ if (!link) return
389
+ const location = projectLocationForLink(link)
390
+ if (!location) return
391
+ const { line, column } = parseSuffix(location.suffix)
392
+ if (!openInEditor(location.url, line, column)) return
393
+ event.preventDefault()
394
+ event.stopImmediatePropagation()
395
+ }
396
+
397
+ function bindClicks(target) {
398
+ if (!target || !target.addEventListener) return
399
+ for (const binding of clickBindings) {
400
+ if (binding.target === target) return
401
+ }
402
+ target.addEventListener('click', onLinkClick, true)
403
+ clickBindings.push({ target, fn: onLinkClick })
404
+ }
405
+
406
+ function splitLocation(value) {
407
+ const raw = String(value || '')
408
+ const match = raw.match(/^(.*?)(?::(\\d+))(?::(\\d+))?$/)
409
+ return match
410
+ ? { url: match[1], suffix: ':' + match[2] + (match[3] ? ':' + match[3] : '') }
411
+ : { url: raw, suffix: '' }
412
+ }
413
+
414
+ function rewriteLink(link) {
415
+ if (!link || link.dataset.diminaProjectSourcePath) return
416
+ const candidates = [
417
+ link.getAttribute && link.getAttribute('title'),
418
+ link.getAttribute && link.getAttribute('href'),
419
+ link.getAttribute && link.getAttribute('data-url'),
420
+ ]
421
+ for (const candidate of candidates) {
422
+ if (!candidate) continue
423
+ const location = splitLocation(candidate)
424
+ const relative = diminaProjectSourcePath(location.url, cfg)
425
+ if (!relative) continue
426
+ const textSuffix = location.suffix || ((String(link.textContent || '').match(/(:\\d+(?::\\d+)?)$/) || [])[1] || '')
427
+ link.textContent = relative + textSuffix
428
+ link.dataset.diminaProjectSourcePath = relative
429
+ return
430
+ }
431
+ }
432
+
433
+ function observeRoot(root) {
434
+ if (!root || !root.querySelectorAll || observedRoots.has(root)) return
435
+ observedRoots.add(root)
436
+ bindClicks(root)
437
+
438
+ const observer = new MutationObserver((records) => {
439
+ for (const record of records) {
440
+ if (record.type === 'attributes') rewriteLink(record.target)
441
+ for (const node of record.addedNodes || []) visitNode(node)
442
+ }
443
+ })
444
+ observer.observe(root, {
445
+ subtree: true,
446
+ childList: true,
447
+ attributes: true,
448
+ attributeFilter: ['title', 'href', 'data-url'],
449
+ })
450
+ observers.push(observer)
451
+
452
+ for (const link of root.querySelectorAll('.devtools-link')) rewriteLink(link)
453
+ for (const element of root.querySelectorAll('*')) {
454
+ if (element.shadowRoot) observeRoot(element.shadowRoot)
455
+ }
456
+ }
457
+
458
+ function visitNode(node) {
459
+ if (!node) return
460
+ if (node.matches && node.matches('.devtools-link')) rewriteLink(node)
461
+ if (node.shadowRoot) observeRoot(node.shadowRoot)
462
+ if (!node.querySelectorAll) return
463
+ for (const link of node.querySelectorAll('.devtools-link')) rewriteLink(link)
464
+ for (const element of node.querySelectorAll('*')) {
465
+ if (element.shadowRoot) observeRoot(element.shadowRoot)
466
+ }
467
+ }
468
+
469
+ observeRoot(document)
470
+
471
+ // Fallback for older DevTools fronts that still expose the
472
+ // setOpenResourceHandler hook. On current Chromium the method is gone,
473
+ // so this poll simply times out and the capture-phase click interceptor
474
+ // is the only live path. When the hook IS present the interceptor still
475
+ // wins (it runs in capture and stops propagation), so the two never
476
+ // double-open the same click.
477
+ let tries = 0
478
+ timer = setInterval(() => {
479
+ tries++
480
+ try {
481
+ const host = resolveOpenInNewTabHost()
482
+ if (host && typeof host.setOpenResourceHandler === 'function') {
483
+ host.setOpenResourceHandler((url, lineNumber, columnNumber) => {
484
+ if (!diminaProjectSourcePath(String(url), cfg)) return
485
+ openInEditor(
486
+ String(url),
487
+ typeof lineNumber === 'number' ? lineNumber : undefined,
488
+ typeof columnNumber === 'number' ? columnNumber : undefined,
489
+ )
490
+ })
491
+ clearInterval(timer)
492
+ timer = null
493
+ }
494
+ } catch (_) {}
495
+ if (tries > 80 && timer !== null) {
496
+ clearInterval(timer)
497
+ timer = null
498
+ }
499
+ }, 50)
500
+ } catch (_) {}
501
+ })()
502
+ `;
503
+ }
138
504
  //# sourceMappingURL=open-in-editor.js.map
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Phase 0 vpath resolver.
2
+ * vpath resolver.
3
3
  *
4
4
  * Spec: `docs/file-system.md` §3.3 — single resolver shared by every FSM
5
- * entry, the renderer-side temp store, and (Phase 1) the main-process
5
+ * entry, the renderer-side temp store, and the main-process
6
6
  * protocol.handle / disk reader.
7
7
  *
8
8
  * resolveVPath(url): { kind, writable, realPath? } | null
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Phase 0 vpath resolver.
2
+ * vpath resolver.
3
3
  *
4
4
  * Spec: `docs/file-system.md` §3.3 — single resolver shared by every FSM
5
- * entry, the renderer-side temp store, and (Phase 1) the main-process
5
+ * entry, the renderer-side temp store, and the main-process
6
6
  * protocol.handle / disk reader.
7
7
  *
8
8
  * resolveVPath(url): { kind, writable, realPath? } | null
@@ -0,0 +1,2 @@
1
+ import{n as e,t}from"./jsx-runtime-P6ZegaOf.js";import{t as n}from"./bridge-channels-BUQ5AbvJ.js";var r=e(),i=new Set([`audioListen`]);function a(e){return i.has(e)}function o(e,t,n,r){let i=e.apiRegistry[t];if(!i)return r({ok:!1,errMsg:`${t}:fail no handler`}),Promise.resolve();let o=a(t)||(n&&typeof n==`object`&&!Array.isArray(n)?n.keep===!0:!1);return new Promise(a=>{let s=!1,c=e=>{if(s){o&&e.ok&&r({...e,keep:!0});return}s=!0,r(o&&e.ok?{...e,keep:!0}:e),a()},l=Symbol(`sim-cb-success`),u=Symbol(`sim-cb-fail`),d=Symbol(`sim-cb-complete`),f=Object.create(e);f.createCallbackFunction=e=>{if(e!=null)return(...n)=>{let r=n[0];e===l?c({ok:!0,result:r}):e===u&&c({ok:!1,errMsg:r&&typeof r==`object`&&`errMsg`in r?String(r.errMsg):`${t}:fail`,result:r})}};let p=n&&typeof n==`object`&&!Array.isArray(n)?{...n}:{},m=p.success!==void 0&&p.success!==null,h=p.fail!==void 0&&p.fail!==null,g=p.complete!==void 0&&p.complete!==null;p.success=l,p.fail=u,g&&(p.complete=d);try{let e=i.call(f,p);if(e&&typeof e.then==`function`){Promise.resolve(e).then(e=>c({ok:!0,result:e}),e=>{c({ok:!1,errMsg:`${t}:fail ${e instanceof Error?e.message:String(e)}`})});return}!m&&!h&&(o?a():c({ok:!0,result:e}))}catch(e){c({ok:!1,errMsg:`${t}:fail ${e instanceof Error?e.message:String(e)}`})}})}var s=t();function c({platform:e,statusBarHeight:t,textStyle:n}){let r=e===`ios`?87:95,i=t+(e===`ios`?4:6),a=e===`ios`?7:10;return(0,s.jsxs)(`div`,{className:`menu-capsule menu-capsule--${n}`,style:{width:r,height:32,top:i,right:a},"aria-hidden":`true`,children:[(0,s.jsxs)(`div`,{className:`menu-capsule__more`,children:[(0,s.jsx)(`span`,{}),(0,s.jsx)(`span`,{}),(0,s.jsx)(`span`,{})]}),(0,s.jsx)(`div`,{className:`menu-capsule__divider`}),(0,s.jsx)(`div`,{className:`menu-capsule__close`,children:(0,s.jsx)(`svg`,{viewBox:`0 0 16 16`,width:`12`,height:`12`,"aria-hidden":`true`,children:(0,s.jsx)(`path`,{d:`M4 4l8 8M12 4l-8 8`,stroke:`currentColor`,strokeWidth:`1.5`,fill:`none`,strokeLinecap:`round`})})})]})}var l={linear:`linear`,easeIn:`ease-in`,easeOut:`ease-out`,easeInOut:`ease-in-out`};function u({state:e,stackDepth:t,platform:n,statusBarHeight:i,navBarHeight:a,onBack:o,onHome:u}){let d=(0,r.useMemo)(()=>{if(!e.colorAnimation||e.colorAnimation.durationMs<=0)return;let t=l[e.colorAnimation.timingFunc]??`linear`;return`background-color ${e.colorAnimation.durationMs}ms ${t}, color ${e.colorAnimation.durationMs}ms ${t}`},[e.colorAnimation]),f=e.style===`custom`,p=t>1,m=!p&&e.homeButtonVisible,h=n===`ios`?`center`:`left`,g={backgroundColor:e.backgroundColor,color:e.textStyle===`white`?`#ffffff`:`#000000`,height:i+a,paddingTop:i,transition:d};return(0,s.jsxs)(`header`,{className:`nav-bar nav-bar--${n} nav-bar--${e.textStyle}${f?` nav-bar--custom`:``}`,style:g,"aria-hidden":f,children:[!f&&(0,s.jsxs)(`div`,{className:`nav-bar__row`,style:{height:a,justifyContent:h===`center`?`center`:`flex-start`},children:[(p||m)&&(0,s.jsxs)(`div`,{className:`nav-bar__leading`,children:[p&&(0,s.jsx)(`button`,{type:`button`,className:`nav-bar__back`,"aria-label":`Back`,onClick:o,children:(0,s.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`24`,height:`24`,"aria-hidden":`true`,children:(0,s.jsx)(`path`,{d:`M15 18l-6-6 6-6`,stroke:`currentColor`,strokeWidth:`2`,fill:`none`,strokeLinecap:`round`,strokeLinejoin:`round`})})}),m&&(0,s.jsx)(`button`,{type:`button`,className:`nav-bar__home`,"aria-label":`Home`,onClick:u,children:(0,s.jsx)(`svg`,{viewBox:`0 0 24 24`,width:`22`,height:`22`,"aria-hidden":`true`,children:(0,s.jsx)(`path`,{d:`M3 12l9-9 9 9M5 10v10h14V10`,stroke:`currentColor`,strokeWidth:`2`,fill:`none`,strokeLinecap:`round`,strokeLinejoin:`round`})})})]}),(0,s.jsxs)(`div`,{className:`nav-bar__title nav-bar__title--${h}`,style:{color:e.textStyle===`white`?`#ffffff`:`#000000`},children:[e.loading&&(0,s.jsx)(`span`,{className:`nav-bar__spinner`,"aria-hidden":`true`}),(0,s.jsx)(`span`,{className:`nav-bar__title-text`,children:e.title})]})]}),(0,s.jsx)(c,{platform:n,statusBarHeight:i,textStyle:e.textStyle})]})}function d(e){return{title:e?.title??``,backgroundColor:e?.backgroundColor??`#000000`,textStyle:e?.textStyle??`white`,style:e?.style??`default`,loading:e?.loading??!1,homeButtonVisible:e?.homeButtonVisible??!1,colorAnimation:e?.colorAnimation}}function f(e){switch(e){case`notch`:return{width:164,height:30,top:0,bottomOnly:!0};case`dynamic-island`:return{width:124,height:36,top:11,bottomOnly:!1};default:return null}}function p({height:e,notchType:t,textStyle:n}){let r=f(t);return(0,s.jsxs)(`div`,{className:`device-statusbar`,style:{height:e,color:n===`black`?`#000000`:`#ffffff`},"aria-hidden":`true`,children:[(0,s.jsx)(`span`,{className:`device-statusbar__time`,children:`9:41`}),r&&(0,s.jsx)(`div`,{className:`device-statusbar__notch`,style:{width:r.width,height:r.height,top:r.top,borderRadius:r.bottomOnly?`0 0 ${Math.round(r.height*.6)}px ${Math.round(r.height*.6)}px`:`${r.height/2}px`}}),(0,s.jsxs)(`div`,{className:`device-statusbar__icons`,children:[(0,s.jsx)(`span`,{className:`device-statusbar__signal`}),(0,s.jsx)(`span`,{className:`device-statusbar__wifi`}),(0,s.jsx)(`span`,{className:`device-statusbar__battery`})]})]})}function m({state:e,currentPath:t,resourceBaseUrl:n,appId:r,onSwitch:i,bottomInset:a=0}){if(!e.config||!e.visible)return null;let{color:o,selectedColor:c,backgroundColor:l,borderStyle:u,list:d}=e.config,f=o||`#999999`,p=c||`#1890ff`;return(0,s.jsx)(`div`,{className:`dmb-tab-bar`,style:{backgroundColor:l||`#ffffff`,borderTopColor:u===`white`?`#ffffff`:`#e0e0e0`,paddingBottom:a},role:`tablist`,"aria-label":`TabBar`,children:d.map((a,o)=>{let c=h(a.pagePath),l=c===t,u=g(l?a.selectedIconPath??a.iconPath:a.iconPath,n,r),d=e.badges[o]??``,m=e.redDots[o]??!1;return(0,s.jsxs)(`button`,{type:`button`,className:`dmb-tab-bar__item${l?` is-selected`:``}`,role:`tab`,"aria-selected":l,onClick:()=>i(c),children:[u&&(0,s.jsx)(`span`,{className:`dmb-tab-bar__icon-slot`,children:(0,s.jsx)(`img`,{className:`dmb-tab-bar__icon`,src:u,alt:``,onError:e=>{e.currentTarget.style.display=`none`}})}),(0,s.jsx)(`span`,{className:`dmb-tab-bar__text`,style:{color:l?p:f},children:a.text||``}),d&&(0,s.jsx)(`span`,{className:`dmb-tab-bar__badge`,children:d}),!d&&m&&(0,s.jsx)(`span`,{className:`dmb-tab-bar__red-dot`})]},`${c}-${o}`)})})}function h(e){return e?e.replace(/^\/+/,``):``}function g(e,t,n){if(!e)return null;let r=e.trim();if(!r)return null;if(/^(?:data:|blob:|https?:|\/\/)/i.test(r))return r;if(!t)return null;let i=r.replace(/^\/+/,``).replace(/^\.\//,``);return i.startsWith(`${n}/`)?_(t,i):_(t,`${n}/main/${i}`)}function _(e,t){return`${e.endsWith(`/`)?e:`${e}/`}${t.replace(/^\/+/,``)}`}function v(e){if(!e)return{config:null,visible:!1,badges:[],redDots:[]};let t=e.list.length;return{config:y(e),visible:!0,badges:Array.from({length:t},()=>``),redDots:Array.from({length:t},()=>!1)}}function y(e){return{...e,list:e.list.map(e=>({...e}))}}function b(e,t){switch(t.kind){case`reset`:return{state:v(t.config),ok:!0,errMsg:`reset:ok`};case`visibility`:return{state:{...e,visible:t.visible},ok:!0,errMsg:t.visible?`showTabBar:ok`:`hideTabBar:ok`};case`apply`:return x(e,t.name,t.params)}}function x(e,t,n){if(!e.config)return{state:e,ok:!1,errMsg:`${t}:fail tabBar not configured`};switch(t){case`setTabBarStyle`:return S(e,n);case`setTabBarItem`:return C(e,n);case`showTabBar`:return{state:{...e,visible:!0},ok:!0,errMsg:`showTabBar:ok`};case`hideTabBar`:return{state:{...e,visible:!1},ok:!0,errMsg:`hideTabBar:ok`};case`setTabBarBadge`:return w(e,n);case`removeTabBarBadge`:return T(e,n);case`showTabBarRedDot`:return E(e,n,!0);case`hideTabBarRedDot`:return E(e,n,!1)}}function S(e,t){if(!e.config)return{state:e,ok:!1,errMsg:`setTabBarStyle:fail tabBar not configured`};let n={...e.config},r=(e,t)=>{let r=O(t);r&&(n[e]=r)};return r(`color`,t.color),r(`selectedColor`,t.selectedColor),r(`backgroundColor`,t.backgroundColor),(t.borderStyle===`black`||t.borderStyle===`white`)&&(n.borderStyle=t.borderStyle),{state:{...e,config:n},ok:!0,errMsg:`setTabBarStyle:ok`}}function C(e,t){let n=D(e,t);if(n.err)return{state:e,ok:!1,errMsg:`setTabBarItem:fail ${n.err}`};if(!e.config)return{state:e,ok:!1,errMsg:`setTabBarItem:fail tabBar not configured`};let r=e.config.list[n.index],i={...r,text:typeof t.text==`string`?t.text:r.text,iconPath:typeof t.iconPath==`string`?t.iconPath:r.iconPath,selectedIconPath:typeof t.selectedIconPath==`string`?t.selectedIconPath:r.selectedIconPath},a=[...e.config.list];return a[n.index]=i,{state:{...e,config:{...e.config,list:a}},ok:!0,errMsg:`setTabBarItem:ok`}}function w(e,t){let n=D(e,t);if(n.err)return{state:e,ok:!1,errMsg:`setTabBarBadge:fail ${n.err}`};let r=[...e.badges];r[n.index]=String(t.text??``);let i=[...e.redDots];return i[n.index]=!1,{state:{...e,badges:r,redDots:i},ok:!0,errMsg:`setTabBarBadge:ok`}}function T(e,t){let n=D(e,t);if(n.err)return{state:e,ok:!1,errMsg:`removeTabBarBadge:fail ${n.err}`};let r=[...e.badges];return r[n.index]=``,{state:{...e,badges:r},ok:!0,errMsg:`removeTabBarBadge:ok`}}function E(e,t,n){let r=D(e,t);if(r.err)return{state:e,ok:!1,errMsg:`${n?`showTabBarRedDot`:`hideTabBarRedDot`}:fail ${r.err}`};let i=[...e.redDots];i[r.index]=n;let a=[...e.badges];return n&&(a[r.index]=``),{state:{...e,redDots:i,badges:a},ok:!0,errMsg:`${n?`showTabBarRedDot`:`hideTabBarRedDot`}:ok`}}function D(e,t){let n=e.config?.list??[],r=t.index,i=Number(r);return n.length?r==null||!Number.isInteger(i)||i<0||i>=n.length?{index:-1,err:`invalid index ${r}`}:{index:i,err:null}:{index:-1,err:`tabBar not configured`}}function O(e){if(typeof e!=`string`)return null;let t=e.trim();return!t||t.length>64?null:/[<>"';{}()\\]/.test(t)?/^(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s/-]+\)$/i.test(t)?t:null:t}function k(e){return e?e.replace(/^\/+/,``):``}function A(e){let[t,n]=(typeof e==`string`?e:``).split(`?`),r={};if(n)for(let e of n.split(`&`)){if(!e)continue;let t=e.indexOf(`=`),n=t>=0?e.slice(0,t):e,i=t>=0?e.slice(t+1):``;n&&(r[decodeURIComponent(n)]=decodeURIComponent(i))}return{pagePath:k(t),query:r}}function j(e){let t={};return e.isTab&&(t[e.pagePath]=[e]),{stack:[e],tabStacks:t,currentTabPath:e.isTab?e.pagePath:null}}function M(e){return e.currentTabPath?{...e.tabStacks,[e.currentTabPath]:[...e.stack]}:e.tabStacks}function N(e,t){let n=e.stack[e.stack.length-1],r=[...e.stack,t];return{next:{...e,stack:r,tabStacks:e.currentTabPath?{...e.tabStacks,[e.currentTabPath]:r}:e.tabStacks},effects:n?[{kind:`lifecycle`,bridgeId:n.bridgeId,event:`pageHide`}]:[]}}function P(e,t){if(e.stack.length<=1)return{error:`no page to back`};let n=Math.min(Math.max(1,Number.isFinite(t)?t:1),e.stack.length-1),r=e.stack.slice(e.stack.length-n),i=e.stack.slice(0,e.stack.length-n),a=i[i.length-1],o={...e,stack:i,tabStacks:e.currentTabPath?{...e.tabStacks,[e.currentTabPath]:i}:e.tabStacks,currentTabPath:a.isTab?a.pagePath:e.currentTabPath},s=[];for(let e of r)s.push({kind:`lifecycle`,bridgeId:e.bridgeId,event:`pageUnload`}),s.push({kind:`closePage`,bridgeId:e.bridgeId});return s.push({kind:`lifecycle`,bridgeId:a.bridgeId,event:`pageShow`}),{next:o,effects:s}}function F(e,t){let n=e.stack[e.stack.length-1],r=[...e.stack.slice(0,e.stack.length-1),t],i={...e,stack:r,tabStacks:e.currentTabPath?{...e.tabStacks,[e.currentTabPath]:r}:e.tabStacks},a=[];return n&&(a.push({kind:`lifecycle`,bridgeId:n.bridgeId,event:`pageUnload`}),a.push({kind:`closePage`,bridgeId:n.bridgeId})),{next:i,effects:a}}function I(e,t){let n=new Set;for(let t of e.stack)n.add(t.bridgeId);for(let t of Object.values(e.tabStacks))for(let e of t)n.add(e.bridgeId);n.delete(t.bridgeId);let r=t.isTab?{[t.pagePath]:[t]}:{},i={stack:[t],tabStacks:r,currentTabPath:t.isTab?t.pagePath:null},a=[];for(let e of n)a.push({kind:`lifecycle`,bridgeId:e,event:`pageUnload`}),a.push({kind:`closePage`,bridgeId:e});return{next:i,effects:a}}function L(e,t,n){let r=e.stack[e.stack.length-1],i=M(e),a,o=i[t];if(o&&o.length>0)a=o;else if(n)a=[n];else throw Error(`[page-stack] switchTab to ${t} requires either a cached substack or a freshly-opened entry`);let s={...e,stack:a,tabStacks:{...i,[t]:a},currentTabPath:t},c=a[a.length-1],l=[];return r&&r.bridgeId!==c.bridgeId&&l.push({kind:`lifecycle`,bridgeId:r.bridgeId,event:`pageHide`}),n||l.push({kind:`lifecycle`,bridgeId:c.bridgeId,event:`pageShow`}),{next:s,effects:l}}function R(e){let t=new Map,n=e.stack[e.stack.length-1]?.bridgeId,r=e=>{t.has(e.bridgeId)||t.set(e.bridgeId,{entry:e,visible:e.bridgeId===n})};for(let t of Object.values(e.tabStacks))for(let e of t)r(e);for(let t of e.stack)r(t);return Array.from(t.values())}function z(e,t){let n=e.navigationBarBackgroundColor??`#ffffff`,r=e.navigationBarTextStyle??`black`,i=e.navigationStyle??`default`;return d({title:e.navigationBarTitleText??t,backgroundColor:n,textStyle:r,style:i,homeButtonVisible:e.homeButton===!0})}function B(e,t,n){switch(t){case`setNavigationBarTitle`:return{...e,title:typeof n.title==`string`?n.title:e.title};case`setNavigationBarColor`:return H(e,n);case`showNavigationBarLoading`:return{...e,loading:!0};case`hideNavigationBarLoading`:return{...e,loading:!1};case`hideHomeButton`:return{...e,homeButtonVisible:!1};default:return e}}var V=[`linear`,`easeIn`,`easeOut`,`easeInOut`];function H(e,t){let n=typeof t.frontColor==`string`?t.frontColor.toLowerCase():void 0,r=n===`#ffffff`?`white`:n===`#000000`?`black`:e.textStyle,i=typeof t.backgroundColor==`string`?t.backgroundColor:e.backgroundColor,a=(()=>{let e=t.animation;if(!e||typeof e!=`object`)return;let n=e,r=typeof n.duration==`number`&&Number.isFinite(n.duration)?Math.max(0,n.duration):0,i=typeof n.timingFunc==`string`?n.timingFunc:`linear`;return{durationMs:r,timingFunc:V.includes(i)?i:`linear`}})();return{...e,textStyle:r,backgroundColor:i,colorAnimation:a}}function U(e,t,n){let r=e=>e.bridgeId===t?{...e,navBar:n(e.navBar)}:e,i=e.stack.map(r),a={};for(let[t,n]of Object.entries(e.tabStacks))a[t]=n.map(r);return{...e,stack:i,tabStacks:a}}var W=44,G=24,K=44;function q({miniApp:e,bridgeId:t,platform:i=`ios`}){let[a,c]=(0,r.useState)(()=>e.getInitialDevice());(0,r.useEffect)(()=>e.onSimulatorEvent(n.DEVICE_CHANGE,c),[e]);let l=a?.safeAreaInsets.top??(i===`ios`?W:G),d=a?.safeAreaInsets.bottom??0,f=a?.notchType??`none`,h=(0,r.useMemo)(()=>e.getRenderPreloadUrl(),[e]),g=(0,r.useMemo)(()=>e.getTabBarConfig(),[e]),_=(0,r.useMemo)(()=>({bridgeId:t,pagePath:k(e.pagePath),query:{...e.query},isTab:!!e.getTabBarConfig()?.list.some(t=>k(t.pagePath)===k(e.pagePath)),windowConfig:e.rootWindowConfig??{},navBar:z(e.rootWindowConfig??{},e.appId)}),[e,t]),[{shell:y,tabBar:x},S]=(0,r.useState)(()=>({shell:j(_),tabBar:v(g)})),C=(0,r.useRef)({shell:y,tabBar:x});(0,r.useEffect)(()=>{C.current={shell:y,tabBar:x}},[y,x]);let w=(0,r.useCallback)(t=>{for(let n of t)n.kind===`lifecycle`?e.notifyLifecycle(n.bridgeId,n.event):n.kind===`closePage`&&e.closePage(n.bridgeId)},[e]);(0,r.useEffect)(()=>e.onSimulatorEvent(n.NAV_BAR,e=>{S(t=>({...t,shell:U(t.shell,e.bridgeId,t=>B(t,e.name,e.params))}))}),[e]),(0,r.useEffect)(()=>e.onSimulatorEvent(n.TAB_ACTION,t=>{let n=b(C.current.tabBar,{kind:`apply`,name:t.name,params:t.params});S(e=>({...e,tabBar:n.state})),e.notifyNavCallback({ok:n.ok,errMsg:n.errMsg,callbacks:t.callbacks})}),[e]);let T=(0,r.useCallback)(async t=>{let n=(n,r)=>e.notifyNavCallback({ok:n,errMsg:r,callbacks:t.callbacks});try{switch(t.name){case`navigateTo`:await J(e,C,S,w,t,n);break;case`navigateBack`:Y(C,S,w,t,n);break;case`redirectTo`:await X(e,C,S,w,t,n);break;case`reLaunch`:await Z(e,C,S,w,t,n);break;case`switchTab`:await Q(e,C,S,w,t,n);break}}catch(e){n(!1,`${t.name}:fail ${e instanceof Error?e.message:String(e)}`)}},[e,w]);(0,r.useEffect)(()=>e.onSimulatorEvent(n.NAV_ACTION,e=>{T(e)}),[T,e]),(0,r.useEffect)(()=>e.onSimulatorEvent(n.API_CALL,t=>{o(e,t.name,t.params,n=>{e.notifyApiResponse({requestId:t.requestId,ok:n.ok,result:n.result,errMsg:n.errMsg,keep:n.keep})})}),[e]);let E=(0,r.useCallback)(()=>{if(C.current.shell.stack.length<=1)return;let t=C.current.shell.stack;T({appSessionId:e.appSessionId??``,bridgeId:t[t.length-1].bridgeId,name:`navigateBack`,params:{delta:1},callbacks:{}})},[e,T]),D=(0,r.useCallback)(t=>{let n=C.current.shell;t===n.currentTabPath&&n.stack.length===1||T({appSessionId:e.appSessionId??``,bridgeId:n.stack[n.stack.length-1].bridgeId,name:`switchTab`,params:{url:`/${t}`},callbacks:{}})},[e,T]),O=y.stack[y.stack.length-1],A=R(y);return(0,r.useEffect)(()=>{e.notifyActivePage(O.bridgeId)},[e,O.bridgeId]),(0,r.useEffect)(()=>{e.notifyPageStack(y.stack.map(e=>({pagePath:e.pagePath,query:e.query})))},[e,y.stack]),(0,s.jsx)(`main`,{className:`device-shell-root`,children:(0,s.jsxs)(`section`,{className:`device-shell`,"aria-label":`Dimina simulator`,style:a?{width:a.screenWidth,height:a.screenHeight}:void 0,children:[(0,s.jsx)(p,{height:l,notchType:f,textStyle:O.navBar.textStyle}),(0,s.jsx)(u,{state:O.navBar,stackDepth:y.stack.length,platform:i,statusBarHeight:l,navBarHeight:K,onBack:E}),(0,s.jsx)(`div`,{className:`device-shell__viewport`,children:A.map(({entry:t,visible:n})=>(0,s.jsx)(`webview`,{className:`device-shell__webview`,src:e.createRenderHostUrl(t.bridgeId,t.pagePath,t.isTab),preload:h,allowpopups:`true`,style:{display:n?`flex`:`none`,zIndex:n?100:1}},t.bridgeId))}),O.isTab&&(0,s.jsx)(m,{state:x,currentPath:y.currentTabPath,resourceBaseUrl:e.resourceBaseUrl,appId:e.appId,onSwitch:D,bottomInset:d}),d>0&&(0,s.jsx)(`div`,{className:`device-shell__home-indicator`,style:{height:d},"aria-hidden":`true`})]})})}async function J(e,t,n,r,i,a){let{pagePath:o,query:s}=A(i.params.url);if(!o){a(!1,`navigateTo:fail invalid url`);return}if(e.getTabBarConfig()?.list.some(e=>k(e.pagePath)===o)){a(!1,`navigateTo:fail can not navigateTo a tabbar page`);return}let c=await e.openPage(o,s),l={bridgeId:c.bridgeId,pagePath:c.pagePath,query:s,isTab:c.isTab,windowConfig:c.windowConfig,navBar:z(c.windowConfig,e.appId)},{next:u,effects:d}=N(t.current.shell,l);n(e=>({...e,shell:u})),r(d),a(!0,`navigateTo:ok`)}function Y(e,t,n,r,i){let a=r.params.delta,o=Number.isFinite(Number(a))?Number(a):1,s=P(e.current.shell,o);if(`error`in s){i(!1,`navigateBack:fail ${s.error}`);return}t(e=>({...e,shell:s.next})),n(s.effects),i(!0,`navigateBack:ok`)}async function X(e,t,n,r,i,a){let{pagePath:o,query:s}=A(i.params.url);if(!o){a(!1,`redirectTo:fail invalid url`);return}if(e.getTabBarConfig()?.list.some(e=>k(e.pagePath)===o)){a(!1,`redirectTo:fail can not redirectTo a tabbar page`);return}let c=await e.openPage(o,s),l={bridgeId:c.bridgeId,pagePath:c.pagePath,query:s,isTab:c.isTab,windowConfig:c.windowConfig,navBar:z(c.windowConfig,e.appId)},{next:u,effects:d}=F(t.current.shell,l);n(e=>({...e,shell:u})),r(d),a(!0,`redirectTo:ok`)}async function Z(e,t,n,r,i,a){let{pagePath:o,query:s}=A(i.params.url);if(!o){a(!1,`reLaunch:fail invalid url`);return}let c=await e.openPage(o,s),l={bridgeId:c.bridgeId,pagePath:c.pagePath,query:s,isTab:c.isTab,windowConfig:c.windowConfig,navBar:z(c.windowConfig,e.appId)},{next:u,effects:d}=I(t.current.shell,l);n(e=>({...e,shell:u})),r(d),a(!0,`reLaunch:ok`)}async function Q(e,t,n,r,i,a){let{pagePath:o}=A(i.params.url);if(!o){a(!1,`switchTab:fail invalid url`);return}if(!e.getTabBarConfig()?.list.some(e=>k(e.pagePath)===o)){a(!1,`switchTab:fail not a tabBar page: ${o}`);return}let s=t.current.shell.tabStacks[o],c=null;if(!s||s.length===0){let t=await e.openPage(o,{});c={bridgeId:t.bridgeId,pagePath:t.pagePath,query:{},isTab:!0,windowConfig:t.windowConfig,navBar:z(t.windowConfig,e.appId)}}let{next:l,effects:u}=L(t.current.shell,o,c);n(e=>({...e,shell:l})),r(u),a(!0,`switchTab:ok`)}export{q as DeviceShell};
2
+ //# sourceMappingURL=device-shell-CPCnCp1L.js.map