@dxos/app-framework 0.14.0 → 0.16.0

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 (129) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/dist/lib/App.mjs +1 -2
  3. package/dist/lib/Hooks.mjs +1 -2
  4. package/dist/lib/chunk-App.mjs +11 -0
  5. package/dist/lib/chunk-App.mjs.map +1 -0
  6. package/dist/lib/chunk-Cli.mjs +8 -0
  7. package/dist/lib/chunk-Cli.mjs.map +1 -0
  8. package/dist/lib/chunk-Devtools.mjs +112 -0
  9. package/dist/lib/chunk-Devtools.mjs.map +1 -0
  10. package/dist/lib/chunk-Hooks2.mjs +35 -0
  11. package/dist/lib/chunk-Hooks2.mjs.map +1 -0
  12. package/dist/lib/chunk-PluginManagerProvider.mjs +12 -0
  13. package/dist/lib/chunk-PluginManagerProvider.mjs.map +1 -0
  14. package/dist/lib/chunk-ProcessManagerPlugin.mjs +3 -3
  15. package/dist/lib/chunk-ProcessManagerPlugin.mjs.map +1 -1
  16. package/dist/lib/chunk-Role.mjs +19 -0
  17. package/dist/lib/chunk-Role.mjs.map +1 -0
  18. package/dist/lib/chunk-SharedPackages.mjs +11 -0
  19. package/dist/lib/chunk-SharedPackages.mjs.map +1 -0
  20. package/dist/lib/chunk-Surface.mjs.map +1 -1
  21. package/dist/lib/chunk-Surface2.mjs +37 -0
  22. package/dist/lib/chunk-Surface2.mjs.map +1 -0
  23. package/dist/lib/chunk-SurfaceManager.mjs +3 -3
  24. package/dist/lib/chunk-SurfaceManager.mjs.map +1 -1
  25. package/dist/lib/chunk-activation-event.mjs +86 -0
  26. package/dist/lib/chunk-activation-event.mjs.map +1 -0
  27. package/dist/lib/chunk-activation-events.mjs +66 -0
  28. package/dist/lib/chunk-activation-events.mjs.map +1 -0
  29. package/dist/lib/chunk-capabilities.mjs +202 -0
  30. package/dist/lib/chunk-capabilities.mjs.map +1 -0
  31. package/dist/lib/chunk-capability-manager.mjs +203 -0
  32. package/dist/lib/chunk-capability-manager.mjs.map +1 -0
  33. package/dist/lib/chunk-capability2.mjs +2 -2
  34. package/dist/lib/chunk-capability2.mjs.map +1 -1
  35. package/dist/lib/chunk-history-tracker.mjs +1 -1
  36. package/dist/lib/chunk-hooks.mjs +5 -5
  37. package/dist/lib/chunk-hooks.mjs.map +1 -1
  38. package/dist/lib/chunk-plugin-asset-cache.mjs +28 -0
  39. package/dist/lib/chunk-plugin-asset-cache.mjs.map +1 -0
  40. package/dist/lib/chunk-plugin-manager.mjs +68 -35
  41. package/dist/lib/chunk-plugin-manager.mjs.map +1 -1
  42. package/dist/lib/chunk-plugin-manager2.mjs +22 -0
  43. package/dist/lib/chunk-plugin-manager2.mjs.map +1 -0
  44. package/dist/lib/chunk-plugin-manifest.mjs +126 -0
  45. package/dist/lib/chunk-plugin-manifest.mjs.map +1 -0
  46. package/dist/lib/chunk-plugin.mjs +354 -0
  47. package/dist/lib/chunk-plugin.mjs.map +1 -0
  48. package/dist/lib/chunk-process-manager-capability.mjs +11 -10
  49. package/dist/lib/chunk-process-manager-capability.mjs.map +1 -1
  50. package/dist/lib/chunk-registry.mjs.map +1 -1
  51. package/dist/lib/chunk-undo-mapping.mjs +47 -0
  52. package/dist/lib/{plugin-process-manager/history/undo-mapping.mjs.map → chunk-undo-mapping.mjs.map} +1 -1
  53. package/dist/lib/chunk-undo-registry.mjs +22 -0
  54. package/dist/lib/{plugin-process-manager/history/undo-registry.mjs.map → chunk-undo-registry.mjs.map} +1 -1
  55. package/dist/lib/chunk-url-loader.mjs +345 -0
  56. package/dist/lib/chunk-url-loader.mjs.map +1 -0
  57. package/dist/lib/cli.mjs +3 -3
  58. package/dist/lib/cli.mjs.map +1 -1
  59. package/dist/lib/common/Role.mjs +1 -19
  60. package/dist/lib/common/activation-events.mjs +1 -57
  61. package/dist/lib/common/capabilities.mjs +1 -202
  62. package/dist/lib/config.mjs +1 -2
  63. package/dist/lib/core/activation-event.mjs +1 -86
  64. package/dist/lib/core/capability-manager.mjs +1 -203
  65. package/dist/lib/core/capability.mjs +1 -2
  66. package/dist/lib/core/plugin-asset-cache.mjs +1 -28
  67. package/dist/lib/core/plugin-manager.mjs +2 -19
  68. package/dist/lib/core/plugin-manifest.mjs +1 -126
  69. package/dist/lib/core/plugin.mjs +1 -354
  70. package/dist/lib/core/registry.mjs +1 -2
  71. package/dist/lib/core/url-loader.mjs +1 -345
  72. package/dist/lib/index.mjs +19 -19
  73. package/dist/lib/ns/App.mjs +2 -11
  74. package/dist/lib/ns/Cli.mjs +2 -8
  75. package/dist/lib/ns/Devtools.mjs +1 -112
  76. package/dist/lib/ns/Hooks.mjs +2 -35
  77. package/dist/lib/ns/PluginManagerProvider.mjs +2 -12
  78. package/dist/lib/ns/SharedPackages.mjs +2 -11
  79. package/dist/lib/ns/Surface.mjs +2 -35
  80. package/dist/lib/ns/Translations.mjs +1 -2
  81. package/dist/lib/plugin-cli.mjs +2 -4
  82. package/dist/lib/plugin-cli.mjs.map +1 -1
  83. package/dist/lib/plugin-process-manager/ProcessManagerPlugin.mjs +1 -2
  84. package/dist/lib/plugin-process-manager/history/history-tracker.mjs +1 -2
  85. package/dist/lib/plugin-process-manager/history/undo-mapping.mjs +1 -47
  86. package/dist/lib/plugin-process-manager/history/undo-registry.mjs +1 -22
  87. package/dist/lib/testing/react.mjs +1 -1
  88. package/dist/lib/testing/react.mjs.map +1 -1
  89. package/dist/lib/testing.mjs +5 -5
  90. package/dist/lib/testing.mjs.map +1 -1
  91. package/dist/lib/vite-plugin.mjs +3 -3
  92. package/dist/lib/vite-plugin.mjs.map +1 -1
  93. package/dist/types/src/common/activation-events.d.ts +8 -0
  94. package/dist/types/src/common/activation-events.d.ts.map +1 -1
  95. package/dist/types/src/core/plugin-manager/manager-state.d.ts.map +1 -1
  96. package/dist/types/src/core/plugin-manager/module-loader.d.ts +0 -10
  97. package/dist/types/src/core/plugin-manager/module-loader.d.ts.map +1 -1
  98. package/dist/types/src/core/plugin-manager/plugin-manager.d.ts.map +1 -1
  99. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -1
  100. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  101. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  102. package/dist/types/tsconfig.tsbuildinfo +1 -1
  103. package/package.json +32 -32
  104. package/src/common/activation-events.ts +9 -0
  105. package/src/core/plugin-manager/manager-state.ts +14 -12
  106. package/src/core/plugin-manager/module-loader.ts +43 -7
  107. package/src/core/plugin-manager/plugin-manager.ts +2 -1
  108. package/src/plugin-process-manager/process-manager-capability.test.ts +66 -1
  109. package/src/plugin-process-manager/process-manager-capability.ts +22 -2
  110. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +9 -7
  111. package/src/ui/hooks/useApp.tsx +2 -1
  112. package/src/vite-plugin/boot-loader/loader-app/boot-loader.css +19 -1
  113. package/dist/lib/common/Role.mjs.map +0 -1
  114. package/dist/lib/common/activation-events.mjs.map +0 -1
  115. package/dist/lib/common/capabilities.mjs.map +0 -1
  116. package/dist/lib/core/activation-event.mjs.map +0 -1
  117. package/dist/lib/core/capability-manager.mjs.map +0 -1
  118. package/dist/lib/core/plugin-asset-cache.mjs.map +0 -1
  119. package/dist/lib/core/plugin-manager.mjs.map +0 -1
  120. package/dist/lib/core/plugin-manifest.mjs.map +0 -1
  121. package/dist/lib/core/plugin.mjs.map +0 -1
  122. package/dist/lib/core/url-loader.mjs.map +0 -1
  123. package/dist/lib/ns/App.mjs.map +0 -1
  124. package/dist/lib/ns/Cli.mjs.map +0 -1
  125. package/dist/lib/ns/Devtools.mjs.map +0 -1
  126. package/dist/lib/ns/Hooks.mjs.map +0 -1
  127. package/dist/lib/ns/PluginManagerProvider.mjs.map +0 -1
  128. package/dist/lib/ns/SharedPackages.mjs.map +0 -1
  129. package/dist/lib/ns/Surface.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-SurfaceManager.mjs","names":[],"sources":["../../src/ui/components/Surface/context.ts","../../src/ui/components/Surface/SurfaceMetrics.ts","../../src/ui/components/Surface/SurfaceDebug.tsx","../../src/ui/components/Surface/SurfaceManagerContext.ts","../../src/ui/components/Surface/SurfaceProfilerContext.tsx","../../src/ui/components/Surface/SurfaceComponent.tsx","../../src/ui/components/Surface/SurfaceManager.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Context, createContext } from 'react';\n\nexport type SurfaceContext = {\n id?: string;\n /** The resolved role NSID (e.g. `org.dxos.role.article`). */\n role: string;\n data?: Record<string, any>;\n};\n\nexport const SurfaceContext: Context<SurfaceContext | undefined> = createContext<SurfaceContext | undefined>(undefined);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { useSyncExternalStore } from 'react';\n\nimport { shallowEqual } from '@dxos/util';\n\n/**\n * Per-surface dev metrics, complementing the render-timing data collected by the\n * React Profiler ({@link SurfaceProfilerStats}). Captures dispatch-level signals\n * the Profiler cannot see: how many candidates matched, whether the consumer's\n * `data` prop is referentially unstable (the most common Surface footgun), error\n * boundary trips, and mount churn.\n *\n * Keyed by the same `surface/<id>/<role>` identifier the Profiler uses, so the\n * two can be joined in the devtools panel.\n */\nexport type SurfaceMetric = {\n /** `surface/<id>/<role>`. */\n id: string;\n /** Surface definition id. */\n surfaceId: string;\n /** Resolved role NSID. */\n role: string;\n /** Number of times the dispatcher resolved candidates for this surface. */\n dispatches: number;\n /** Candidates matched on the last dispatch. */\n candidates: number;\n /** `true` when more candidates matched than `limit` rendered. */\n truncated: boolean;\n /** `true` when the caller's `data` prop identity churns across renders without changing value. */\n dataUnstable: boolean;\n /** Consecutive renders where the caller's `data` identity changed but value did not. */\n dataChurn: number;\n /** Error boundary trips. */\n errors: number;\n /** Mounts of the matched component. */\n mounts: number;\n /** Unmounts of the matched component. */\n unmounts: number;\n};\n\n/** Consecutive unstable renders before `dataUnstable` is flagged. */\nconst UNSTABLE_THRESHOLD = 3;\n\nexport const surfaceMetricKey = (surfaceId: string, role: string): string => `surface/${surfaceId}/${role}`;\n\n/**\n * Singleton store of surface dev metrics. A module singleton (rather than a React\n * context) so the debug overlay — which renders in its own root outside the app's\n * provider tree — can read the same data as the in-app devtools panel.\n */\nclass SurfaceMetricsStore {\n #metrics = new Map<string, SurfaceMetric>();\n #snapshot: SurfaceMetric[] = [];\n #listeners = new Set<() => void>();\n #pendingNotify = false;\n\n #entry(surfaceId: string, role: string): SurfaceMetric {\n const id = surfaceMetricKey(surfaceId, role);\n let metric = this.#metrics.get(id);\n if (!metric) {\n metric = {\n id,\n surfaceId,\n role,\n dispatches: 0,\n candidates: 0,\n truncated: false,\n dataUnstable: false,\n dataChurn: 0,\n errors: 0,\n mounts: 0,\n unmounts: 0,\n };\n this.#metrics.set(id, metric);\n }\n return metric;\n }\n\n /**\n * Records a dispatch. `previousData` is the consumer's prior `data` reference;\n * the store derives churn/instability from the identity-vs-value comparison.\n */\n recordDispatch(\n surfaceId: string,\n role: string,\n args: { candidates: number; truncated: boolean; dataChurn: number },\n ): void {\n const metric = this.#entry(surfaceId, role);\n metric.dispatches += 1;\n metric.candidates = args.candidates;\n metric.truncated = args.truncated;\n metric.dataChurn = args.dataChurn;\n metric.dataUnstable = args.dataChurn >= UNSTABLE_THRESHOLD;\n this.#schedule();\n }\n\n recordError(surfaceId: string, role: string): void {\n this.#entry(surfaceId, role).errors += 1;\n this.#schedule();\n }\n\n recordMount(surfaceId: string, role: string): void {\n this.#entry(surfaceId, role).mounts += 1;\n this.#schedule();\n }\n\n recordUnmount(surfaceId: string, role: string): void {\n this.#entry(surfaceId, role).unmounts += 1;\n this.#schedule();\n }\n\n clear(): void {\n this.#metrics.clear();\n this.#snapshot = [];\n this.#notify();\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n };\n\n getSnapshot = (): SurfaceMetric[] => this.#snapshot;\n\n /** Defers notification to the next frame to coalesce bursts of records. */\n #schedule(): void {\n if (this.#pendingNotify || typeof requestAnimationFrame === 'undefined') {\n if (typeof requestAnimationFrame === 'undefined') {\n this.#notify();\n }\n return;\n }\n this.#pendingNotify = true;\n requestAnimationFrame(() => {\n this.#pendingNotify = false;\n this.#notify();\n });\n }\n\n #notify(): void {\n this.#snapshot = [...this.#metrics.values()];\n for (const listener of this.#listeners) {\n listener();\n }\n }\n}\n\nexport const surfaceMetrics = new SurfaceMetricsStore();\n\n/**\n * Updates the running churn count for the caller's raw `data` prop.\n *\n * @returns the new churn count (0 when `data` changed value or is unchanged).\n */\nexport const nextDataChurn = (previous: unknown, next: unknown, churn: number): number => {\n if (Object.is(previous, next)) {\n return churn;\n }\n // New reference: churn only when the value is structurally the same (unstable identity).\n return shallowEqual(previous, next) ? churn + 1 : 0;\n};\n\n/**\n * Subscribes to surface dev metrics, sorted with the most concerning first\n * (unstable data, then errors, then dispatch count).\n */\nexport const useSurfaceMetrics = (): SurfaceMetric[] => {\n const metrics = useSyncExternalStore(\n surfaceMetrics.subscribe,\n surfaceMetrics.getSnapshot,\n surfaceMetrics.getSnapshot,\n );\n return [...metrics].sort(\n (a, b) => Number(b.dataUnstable) - Number(a.dataUnstable) || b.errors - a.errors || b.dispatches - a.dispatches,\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React, {\n type PropsWithChildren,\n type ReactNode,\n createElement,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { createRoot } from 'react-dom/client';\n\nimport { addEventListener, combine } from '@dxos/async';\nimport { mx } from '@dxos/ui-theme';\n\nimport { type SurfaceContext } from './context.ts';\nimport { surfaceMetrics } from './SurfaceMetrics.ts';\n\ndeclare global {\n interface Window {\n __DX_DEBUG__?: boolean;\n __DX__?: { surfaces: (component?: string) => HTMLElement[]; mounted: () => MountedSurface[] };\n }\n\n // oxlint-disable-next-line no-var\n var __DX_SURFACE_DEBUG__: DebugState | undefined;\n}\n\nconst DEBUG_FLAG = '__DX_DEBUG__';\n\n/**\n * Custom element tag wrapping each React surface in debug builds.\n */\nexport const DX_SURFACE_TAG = 'dx-surface';\n\n/**\n * Installs the `window.__DX__` DevTools helper. `__DX__.surfaces(component?)` returns the mounted\n * `<dx-surface>` elements (optionally filtered by `data-component`) — a console shortcut for the\n * otherwise-verbose `document.querySelectorAll('dx-surface[data-component=\"…\"]')`.\n */\nconst ensureDebugApi = (): void => {\n if (typeof window === 'undefined') {\n return;\n }\n\n window.__DX__ = {\n ...window.__DX__,\n surfaces: (component?: string) =>\n Array.from(\n document.querySelectorAll<HTMLElement>(\n component ? `${DX_SURFACE_TAG}[data-component=\"${component}\"]` : DX_SURFACE_TAG,\n ),\n ),\n // The registry's view, as the Stats companion samples it — compare with `surfaces()` when a\n // mounted surface is missing from the list.\n mounted: () => getMountedSurfaces(),\n };\n};\n\n/**\n * Whether surface debugging is enabled, via the `VITE_DEBUG` build flag or the\n * `__DX_DEBUG__` runtime global. The runtime flag is toggleable without a\n * rebuild and takes effect on the next render.\n */\nexport const isSurfaceDebugEnabled = (): boolean =>\n Boolean(import.meta.env?.VITE_DEBUG) || (typeof window !== 'undefined' && DEBUG_FLAG in window);\n\n/**\n * Whether the `<dx-surface>` wrapper is rendered. Enabled in any development build (or when\n * `VITE_DEBUG` is set) so the wrapper's `data-*` attributes and `window.__DX__` helpers are available\n * for DOM inspection independent of the runtime highlight flag. Production renders no wrapper.\n */\nexport const isSurfaceWrapperEnabled = (): boolean =>\n Boolean(import.meta.env?.DEV) || Boolean(import.meta.env?.VITE_DEBUG);\n\n// Notifies the overlay when the runtime highlight flag toggles: the overlay is a separate React root,\n// so it does not re-render on a surface render and needs an explicit signal to redraw.\nconst subscribeFlag = (listener: () => void): (() => void) => {\n state.flagListeners.add(listener);\n return () => {\n state.flagListeners.delete(listener);\n };\n};\n\n/**\n * Toggles the `__DX_DEBUG__` runtime flag, which gates the visual highlight overlay (not the\n * `<dx-surface>` wrapper). The overlay redraws immediately; surfaces pick it up on their next render.\n */\nexport const setSurfaceDebug = (enabled: boolean): void => {\n if (typeof window === 'undefined') {\n return;\n }\n\n if (enabled) {\n window[DEBUG_FLAG] = true;\n } else {\n delete window[DEBUG_FLAG];\n }\n for (const listener of state.flagListeners) {\n listener();\n }\n};\n\n// The role picked in the Surfaces card: the overlay draws its surfaces bold and the card shows their\n// data. Kept here, beside the flag, so the overlay (its own React root) can subscribe to it.\nconst subscribeSelection = (listener: () => void): (() => void) => {\n state.selectionListeners.add(listener);\n return () => {\n state.selectionListeners.delete(listener);\n };\n};\n\n/** The selected role NSID, if any. */\nexport const getSelectedSurfaceRole = (): string | undefined => state.selectedRole;\n\n/** Selects a role's surfaces (`undefined` clears the selection). */\nexport const setSelectedSurfaceRole = (role: string | undefined): void => {\n state.selectedRole = role;\n for (const listener of state.selectionListeners) {\n listener();\n }\n};\n\n/** Subscribes to the selected role. */\nexport const useSelectedSurfaceRole = (): string | undefined =>\n useSyncExternalStore(subscribeSelection, getSelectedSurfaceRole, getSelectedSurfaceRole);\n\nlet elementRegistered = false;\n\nconst ensureSurfaceElement = (): void => {\n if (elementRegistered || typeof customElements === 'undefined') {\n return;\n }\n\n elementRegistered = true;\n if (!customElements.get(DX_SURFACE_TAG)) {\n // Defined lazily (not at module scope) so importing this module never evaluates\n // `extends HTMLElement` in non-DOM environments (node tests / SSR).\n // Zero-behavior element; `display: contents` keeps the debug wrapper layout-neutral\n // so the debug DOM matches production (which renders no wrapper).\n class DxSurfaceElement extends HTMLElement {\n connectedCallback(): void {\n this.style.display = 'contents';\n }\n }\n customElements.define(DX_SURFACE_TAG, DxSurfaceElement);\n }\n};\n\ntype InfoRef = { readonly current: SurfaceContext };\n\ntype DebugEntry = {\n key: number;\n element: HTMLElement;\n infoRef: InfoRef;\n};\n\n/**\n * Singleton registry of mounted debug surfaces. Centralizes overlay rendering so\n * a single set of observers/listeners serves every surface, instead of each\n * surface mounting its own portal and observers.\n */\nclass SurfaceDebugManager {\n #entries = new Set<DebugEntry>();\n #snapshot: DebugEntry[] = [];\n #listeners = new Set<() => void>();\n #nextKey = 0;\n\n register(element: HTMLElement, infoRef: InfoRef): () => void {\n const entry: DebugEntry = { key: this.#nextKey++, element, infoRef };\n this.#entries.add(entry);\n this.#update();\n return () => {\n this.#entries.delete(entry);\n this.#update();\n };\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n };\n\n getSnapshot = (): DebugEntry[] => this.#snapshot;\n\n #update(): void {\n this.#snapshot = [...this.#entries];\n for (const listener of this.#listeners) {\n listener();\n }\n }\n}\n\ntype DebugState = {\n manager: SurfaceDebugManager;\n flagListeners: Set<() => void>;\n selectionListeners: Set<() => void>;\n selectedRole?: string;\n overlayMounted: boolean;\n};\n\n// Held on the global so it survives HMR: a re-evaluated module would otherwise start an empty\n// registry (listing only surfaces mounted afterwards) while the overlay root already in the\n// document kept reading the stale module's flag and selection.\nconst state: DebugState = (globalThis.__DX_SURFACE_DEBUG__ ??= {\n manager: new SurfaceDebugManager(),\n flagListeners: new Set(),\n selectionListeners: new Set(),\n overlayMounted: false,\n});\n\n/** A surface currently mounted in the document, as registered by its `<dx-surface>` wrapper. */\nexport type MountedSurface = {\n id?: string;\n role: string;\n data?: Record<string, any>;\n /** Ids of the surfaces this one is rendered inside, innermost first (anonymous ones skipped). */\n ancestors: string[];\n};\n\nconst enclosingSurfaceIds = (element: HTMLElement): string[] => {\n const ids: string[] = [];\n let node = element.parentElement?.closest<HTMLElement>(DX_SURFACE_TAG);\n while (node) {\n if (node.dataset.id) {\n ids.push(node.dataset.id);\n }\n node = node.parentElement?.closest<HTMLElement>(DX_SURFACE_TAG);\n }\n return ids;\n};\n\nconst toMounted = ({ element, infoRef }: DebugEntry): MountedSurface => ({\n id: infoRef.current.id,\n role: infoRef.current.role,\n data: infoRef.current.data,\n ancestors: enclosingSurfaceIds(element),\n});\n\n/**\n * The surfaces mounted right now, without subscribing. Populated only while the wrapper is rendered\n * (dev builds, or under a profiler provider).\n */\nexport const getMountedSurfaces = (): MountedSurface[] => state.manager.getSnapshot().map(toMounted);\n\n/** The mounted surfaces, re-read on every mount or unmount (never on a render, so a profiled caller cannot loop). */\nexport const useMountedSurfaces = (): MountedSurface[] => {\n const entries = useSyncExternalStore(state.manager.subscribe, state.manager.getSnapshot, state.manager.getSnapshot);\n return useMemo(() => entries.map(toMounted), [entries]);\n};\n\nconst ensureOverlay = (): void => {\n if (state.overlayMounted || typeof document === 'undefined') {\n return;\n }\n state.overlayMounted = true;\n const root = document.createElement('div');\n root.id = 'dx-surface-overlay';\n document.body.appendChild(root);\n createRoot(root).render(<SurfaceDebugOverlay />);\n};\n\n/**\n * Measures a `display: contents` element (which has no box of its own) by unioning the border boxes\n * of its rendered children. Deliberately uses each child's own `getBoundingClientRect` rather than a\n * Range over all contents: a Range unions every descendant rect, so content overflowing an inner\n * `overflow: auto` scroll container inflates the width past the surface's visible bounds. A direct\n * child's box is clipped to its own border box, so the highlight stays tight to what is on screen.\n */\nconst measureContents = (element: HTMLElement): DOMRect | null => {\n if (typeof document === 'undefined' || typeof getComputedStyle !== 'function') {\n return null;\n }\n\n const boxes: DOMRect[] = [];\n const collect = (node: Element): void => {\n for (const child of node.children) {\n // A nested `display: contents` child also has no box; descend to the elements that do.\n if (getComputedStyle(child).display === 'contents') {\n collect(child);\n } else {\n const rect = child.getBoundingClientRect();\n if (rect.width > 0 || rect.height > 0) {\n boxes.push(rect);\n }\n }\n }\n };\n collect(element);\n\n if (boxes.length === 0) {\n return null;\n }\n\n let left = Infinity;\n let top = Infinity;\n let right = -Infinity;\n let bottom = -Infinity;\n for (const box of boxes) {\n left = Math.min(left, box.left);\n top = Math.min(top, box.top);\n right = Math.max(right, box.right);\n bottom = Math.max(bottom, box.bottom);\n }\n return new DOMRect(left, top, right - left, bottom - top);\n};\n\nconst EMPTY_RECTS: ReadonlyMap<number, DOMRect> = new Map();\n\n/**\n * Single overlay that draws boundary highlights for every registered surface.\n * One ResizeObserver plus one scroll/resize listener serve all surfaces.\n */\nconst SurfaceDebugOverlay = (): ReactNode => {\n const all = useSyncExternalStore(state.manager.subscribe, state.manager.getSnapshot, state.manager.getSnapshot);\n // The `__DX_DEBUG__` flag gates only this visual overlay; the `<dx-surface>` wrappers stay mounted.\n const enabled = useSyncExternalStore(subscribeFlag, isSurfaceDebugEnabled, isSurfaceDebugEnabled);\n const selected = useSelectedSurfaceRole();\n // The selection shows even with the flag off, so picking a role in the Surfaces card always points at it.\n // Memoized: the measuring effect keys on this array, so a fresh one per render would loop it.\n const entries = useMemo(\n () => (enabled ? all : all.filter((entry) => entry.infoRef.current.role === selected)),\n [all, enabled, selected],\n );\n const [rects, setRects] = useState<ReadonlyMap<number, DOMRect>>(EMPTY_RECTS);\n\n useLayoutEffect(() => {\n // Draw nothing (and install no listeners) while there is nothing to show.\n if (entries.length === 0) {\n setRects(EMPTY_RECTS);\n return;\n }\n\n const measure = () => {\n const next = new Map<number, DOMRect>();\n for (const entry of entries) {\n const rect = measureContents(entry.element);\n if (rect) {\n next.set(entry.key, rect);\n }\n }\n setRects(next);\n };\n\n // Guard for environments without ResizeObserver (SSR / tests); scroll/resize still drive measurement.\n const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : undefined;\n for (const entry of entries) {\n // The element itself has no box (display: contents); observe its parent.\n if (observer && entry.element.parentElement) {\n observer.observe(entry.element.parentElement);\n }\n }\n measure();\n\n return combine(addEventListener(window, 'scroll', measure, true), addEventListener(window, 'resize', measure), () =>\n observer?.disconnect(),\n );\n }, [entries]);\n\n return createPortal(\n <>\n {entries.map((entry) => {\n const rect = rects.get(entry.key);\n return rect ? (\n <SurfaceHighlight key={entry.key} rect={rect} selected={entry.infoRef.current.role === selected} />\n ) : null;\n })}\n </>,\n document.body,\n );\n};\n\n/** A passive outline over one surface, bold for the selected role; selection happens in the Surfaces card. */\nconst SurfaceHighlight = ({ rect, selected }: { rect: DOMRect; selected: boolean }): ReactNode => (\n <div\n className={mx(\n 'z-40 fixed pointer-events-none border',\n selected ? 'border-2 border-error-text bg-error-text/10' : 'border-info-text',\n )}\n style={{ top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n />\n);\n\n/**\n * Recovers the rendered surface component's name from the React fiber. Surfaces are registered as\n * anonymous `component: (props) => <Real .../>` wrappers, so the static component carries no useful\n * name; walking the fiber's child chain to the first component with a display name (as DevTools does)\n * finds the real one. The wrapper's own inferred name is the property key ('component'), so skip it.\n */\nconst renderedComponentName = (element: HTMLElement): string | undefined => {\n // React fiber internals are untyped; the fiber hangs off a `__reactFiber$<id>` own-property and its\n // `child` chain is the rendered subtree.\n const node = element as any;\n const fiberKey = Object.keys(node).find((key) => key.startsWith('__reactFiber$'));\n let fiber = fiberKey ? node[fiberKey]?.child : undefined;\n while (fiber) {\n const { type } = fiber;\n if (typeof type === 'function') {\n const name: string | undefined = type.displayName ?? type.name;\n if (name && name !== 'component') {\n return name;\n }\n }\n fiber = fiber.child;\n }\n return undefined;\n};\n\n/**\n * Debug wrapper that mounts each React surface inside a `<dx-surface>` element\n * and registers it with the centralized overlay. The element is named and\n * inspectable in DevTools and queryable (`document.querySelectorAll('dx-surface')`).\n */\nexport const DebugSurface = ({ info, children }: PropsWithChildren<{ info: SurfaceContext }>): ReactNode => {\n // The manager reads the latest info via this ref without re-registering on data change.\n const infoRef = useRef(info);\n infoRef.current = info;\n const elementRef = useRef<HTMLElement>(null);\n\n useEffect(() => {\n ensureSurfaceElement();\n ensureOverlay();\n ensureDebugApi();\n const { id, role } = infoRef.current;\n if (id) {\n surfaceMetrics.recordMount(id, role);\n }\n const element = elementRef.current;\n const unregister = element ? state.manager.register(element, infoRef) : undefined;\n // Names the rendered component (`data-component`) once mounted — read from the fiber because the\n // surface wrapper is anonymous, so no static name is available at render time.\n const component = element ? renderedComponentName(element) : undefined;\n if (element && component) {\n element.setAttribute('data-component', component);\n }\n\n return () => {\n if (id) {\n surfaceMetrics.recordUnmount(id, role);\n }\n unregister?.();\n };\n }, []);\n\n return createElement(\n DX_SURFACE_TAG,\n {\n 'className': 'contents',\n 'data-id': info.id,\n 'data-role': info.role,\n 'ref': elementRef,\n },\n children,\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Context, createContext, useContext } from 'react';\n\nimport { raise } from '@dxos/debug';\n\nimport { type SurfaceManager } from './SurfaceManager.ts';\n\nconst SurfaceManagerContext: Context<SurfaceManager | undefined> = createContext<SurfaceManager | undefined>(undefined);\n\n/**\n * Get the surface manager. Throws when rendered outside a {@link SurfaceManagerProvider};\n * surfaces already hard-require the plugin-manager context, so there is no optional variant.\n */\nexport const useSurfaceManager = (): SurfaceManager =>\n useContext(SurfaceManagerContext) ?? raise(new Error('Missing SurfaceManagerContext'));\n\n/**\n * Context provider for a surface manager.\n */\nexport const SurfaceManagerProvider = SurfaceManagerContext.Provider;\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React, {\n type Context,\n type ProfilerOnRenderCallback,\n type PropsWithChildren,\n createContext,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nconst MAX_ENTRIES = 500;\n\n/**\n * Single profiler record captured from React Profiler onRender callback.\n */\nexport type SurfaceProfilerEntry = {\n id: string;\n phase: 'mount' | 'update' | 'nested-update';\n actualDuration: number;\n baseDuration: number;\n startTime: number;\n commitTime: number;\n timestamp: number;\n};\n\n/**\n * Aggregated stats for a single profiled surface.\n */\nexport type SurfaceProfilerStats = {\n id: string;\n mountCount: number;\n updateCount: number;\n totalRenders: number;\n avgActualDuration: number;\n maxActualDuration: number;\n avgBaseDuration: number;\n lastActualDuration: number;\n lastCommitTime: number;\n};\n\n/**\n * Store that collects profiler entries and notifies subscribers.\n */\nclass SurfaceProfilerStore {\n private _entries: SurfaceProfilerEntry[] = [];\n // Cumulative per-surface stats: the entries buffer is a window, so a surface that rendered before\n // its last MAX_ENTRIES renders would otherwise have no timings at all.\n private _stats = new Map<string, SurfaceProfilerStats>();\n private _listeners = new Set<() => void>();\n private _snapshot: readonly SurfaceProfilerEntry[] = [];\n private _pendingNotify = false;\n\n /**\n * Records an entry and schedules a deferred notification to avoid re-render loops.\n * `_snapshot` is rebuilt only when the deferred notification actually fires\n * ({@link _notifySync}) — not here — so `getSnapshot` stays referentially stable for any\n * synchronous re-invocation React makes within the same commit (e.g. the tearing check a\n * profiled subscriber's own `useSyncExternalStore` runs right after this Profiler's\n * `onRender` callback). Rebuilding it synchronously here would change what `getSnapshot`\n * returns before listeners are told, which React reads as a torn store and forces an\n * immediate re-render — and if the re-rendering component is itself profiled, that\n * re-render re-triggers `record`, looping forever.\n */\n record(entry: SurfaceProfilerEntry) {\n accumulate(this._stats, entry);\n this._entries.push(entry);\n if (this._entries.length > MAX_ENTRIES) {\n this._entries = this._entries.slice(-MAX_ENTRIES);\n }\n this._scheduleNotify();\n }\n\n clear() {\n this._entries = [];\n this._stats = new Map();\n this._snapshot = [];\n this._notifySync();\n }\n\n /** Cumulative stats for every surface that rendered since the last clear, slowest maximum first. */\n getStats(): SurfaceProfilerStats[] {\n return [...this._stats.values()].map((stats) => ({ ...stats })).sort(byMaxDuration);\n }\n\n subscribe = (listener: () => void) => {\n this._listeners.add(listener);\n return () => {\n this._listeners.delete(listener);\n };\n };\n\n getSnapshot = (): readonly SurfaceProfilerEntry[] => {\n return this._snapshot;\n };\n\n /**\n * Defers notification to the next animation frame to break the\n * Profiler onRender → record → notify → re-render → onRender loop.\n */\n private _scheduleNotify() {\n if (!this._pendingNotify) {\n this._pendingNotify = true;\n requestAnimationFrame(() => {\n this._pendingNotify = false;\n this._notifySync();\n });\n }\n }\n\n private _notifySync() {\n this._snapshot = [...this._entries];\n for (const listener of this._listeners) {\n listener();\n }\n }\n}\n\ntype SurfaceProfilerContextValue = {\n store: SurfaceProfilerStore;\n};\n\nconst SurfaceProfilerContext: Context<SurfaceProfilerContextValue | undefined> = createContext<\n SurfaceProfilerContextValue | undefined\n>(undefined);\n\n/**\n * Provider that collects React Profiler data from Surface components.\n */\nexport const SurfaceProfilerProvider = ({ children }: PropsWithChildren) => {\n const storeRef = useRef<SurfaceProfilerStore>(null);\n if (!storeRef.current) {\n storeRef.current = new SurfaceProfilerStore();\n }\n return (\n <SurfaceProfilerContext.Provider value={{ store: storeRef.current }}>{children}</SurfaceProfilerContext.Provider>\n );\n};\n\n/**\n * Returns a stable onRender callback for use with React Profiler.\n */\nexport const useSurfaceProfilerCallback = (): ProfilerOnRenderCallback | undefined => {\n const store = useContext(SurfaceProfilerContext)?.store;\n return useMemo<ProfilerOnRenderCallback | undefined>(() => {\n if (!store) {\n return undefined;\n }\n return (id, phase, actualDuration, baseDuration, startTime, commitTime) => {\n store.record({\n id,\n phase,\n actualDuration,\n baseDuration,\n startTime,\n commitTime,\n timestamp: Date.now(),\n });\n };\n }, [store]);\n};\n\n/**\n * Returns all profiler entries reactively.\n */\nexport const useSurfaceProfilerEntries = (): readonly SurfaceProfilerEntry[] => {\n const context = useContext(SurfaceProfilerContext);\n return useSyncExternalStore(context?.store.subscribe ?? noop, context?.store.getSnapshot ?? emptySnapshot);\n};\n\n/**\n * Reads the cumulative per-surface stats without subscribing: a profiled component that subscribed\n * would record an entry on every re-render the notification caused, and loop. Sample it on a\n * schedule instead.\n */\nexport const useSurfaceProfilerSnapshot = (): (() => SurfaceProfilerStats[]) => {\n const store = useContext(SurfaceProfilerContext)?.store;\n return useMemo(() => (store ? () => store.getStats() : emptyStats), [store]);\n};\n\n/**\n * Returns stats aggregated over the recent-entries window, grouped by surface id.\n */\nexport const useSurfaceProfilerStats = (): SurfaceProfilerStats[] => {\n const entries = useSurfaceProfilerEntries();\n return aggregateSurfaceProfilerStats(entries);\n};\n\n/** Aggregates raw entries into per-surface stats, slowest maximum first. */\nexport const aggregateSurfaceProfilerStats = (entries: readonly SurfaceProfilerEntry[]): SurfaceProfilerStats[] => {\n const statsMap = new Map<string, SurfaceProfilerStats>();\n for (const entry of entries) {\n accumulate(statsMap, entry);\n }\n return [...statsMap.values()].sort(byMaxDuration);\n};\n\nconst byMaxDuration = (a: SurfaceProfilerStats, b: SurfaceProfilerStats) => b.maxActualDuration - a.maxActualDuration;\n\n/** Folds one entry into the running stats of its surface. */\nconst accumulate = (statsMap: Map<string, SurfaceProfilerStats>, entry: SurfaceProfilerEntry) => {\n let stats = statsMap.get(entry.id);\n if (!stats) {\n stats = {\n id: entry.id,\n mountCount: 0,\n updateCount: 0,\n totalRenders: 0,\n avgActualDuration: 0,\n maxActualDuration: 0,\n avgBaseDuration: 0,\n lastActualDuration: 0,\n lastCommitTime: 0,\n };\n statsMap.set(entry.id, stats);\n }\n\n if (entry.phase === 'mount') {\n stats.mountCount++;\n } else {\n stats.updateCount++;\n }\n stats.totalRenders++;\n stats.avgActualDuration =\n (stats.avgActualDuration * (stats.totalRenders - 1) + entry.actualDuration) / stats.totalRenders;\n stats.avgBaseDuration = (stats.avgBaseDuration * (stats.totalRenders - 1) + entry.baseDuration) / stats.totalRenders;\n stats.maxActualDuration = Math.max(stats.maxActualDuration, entry.actualDuration);\n stats.lastActualDuration = entry.actualDuration;\n stats.lastCommitTime = entry.commitTime;\n};\n\nconst emptyStats = (): SurfaceProfilerStats[] => [];\n\n/**\n * Clears all collected profiler entries.\n */\nexport const useSurfaceProfilerClear = (): (() => void) | undefined => {\n const store = useContext(SurfaceProfilerContext)?.store;\n return useMemo(() => (store ? () => store.clear() : undefined), [store]);\n};\n\nconst noop = () => () => {};\nconst EMPTY_SNAPSHOT: readonly SurfaceProfilerEntry[] = [];\nconst emptySnapshot = () => EMPTY_SNAPSHOT;\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport { RegistryContext } from '@effect/atom-react/RegistryContext';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport React, {\n type FC,\n Fragment,\n type NamedExoticComponent,\n Profiler,\n Suspense,\n memo,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n} from 'react';\n\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { log } from '@dxos/log';\nimport { ErrorBoundary } from '@dxos/react-error-boundary';\nimport { useStable } from '@dxos/react-hooks';\nimport { shallowEqual } from '@dxos/util';\nimport * as Position from '@dxos/util/Position';\n\nimport { ActivationEvents, Capabilities, Role } from '../../../common/index.ts';\nimport { type PluginManager } from '../../../core/index.ts';\nimport { useOptionalPluginManager, usePluginManager } from '../PluginManager/index.ts';\nimport { SurfaceContext } from './context.ts';\nimport { DebugSurface, isSurfaceDebugEnabled, isSurfaceWrapperEnabled } from './SurfaceDebug.tsx';\nimport { type SurfaceManager } from './SurfaceManager.ts';\nimport { useSurfaceManager } from './SurfaceManagerContext.ts';\nimport { nextDataChurn, surfaceMetrics } from './SurfaceMetrics.ts';\nimport { useSurfaceProfilerCallback } from './SurfaceProfilerContext.tsx';\nimport { type Definition, type Props, type TypedProps, type WebComponentDefinition } from './types.ts';\n\nconst DEBUG = import.meta.env?.VITE_DEBUG;\n\nconst DEFAULT_PLACEHOLDER = <Fragment />;\n\nconst EMPTY_DATA: Record<string, any> = Object.freeze({});\n\n/**\n * Fires the role's surface demand event so modules gated on it load (see the `roles` option of\n * the surface module maker). Safe to call repeatedly: the surface manager claims the first\n * demand per role, which keeps mount cost flat where Surface instances are numerous (every card\n * in a grid mounts one) instead of re-entering the scheduler.\n */\nconst requestSurfaces = (\n surfaceManager: SurfaceManager,\n manager: PluginManager.PluginManager | undefined,\n role: string,\n): void => {\n if (!manager || !surfaceManager.requestRole(role)) {\n return;\n }\n EffectEx.runDetached(\n manager\n .activate(ActivationEvents.SurfacesRequested(role))\n .pipe(\n Effect.onExit((exit) =>\n Exit.isFailure(exit) ? Effect.sync(() => surfaceManager.releaseRole(role)) : Effect.void,\n ),\n ),\n );\n};\n\n/**\n * Wrapper component for rendering Web Component surfaces.\n * Handles creation, prop setting, and cleanup of Web Components.\n */\ntype WebComponentWrapperProps = {\n id?: string;\n role: string;\n data?: Record<string, any>;\n limit?: number;\n definition: WebComponentDefinition;\n [key: string]: any;\n};\n\nconst WebComponentWrapper = memo(({ id, role, data, limit, definition, ...rest }: WebComponentWrapperProps) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const elementRef = useRef<HTMLElement | null>(null);\n const propsRef = useRef({ id, role, data, limit, ...rest });\n\n // Update props ref on every render.\n propsRef.current = { id, role, data, limit, ...rest };\n\n // Create element only once.\n useEffect(() => {\n if (!containerRef.current || elementRef.current) {\n return;\n }\n\n // Create the Web Component.\n const element = document.createElement(definition.tagName);\n elementRef.current = element;\n\n // Set initial properties on the Web Component.\n Object.assign(element, propsRef.current);\n\n // Append to container.\n containerRef.current.appendChild(element);\n\n // Cleanup on unmount to prevent memory leaks.\n return () => {\n if (elementRef.current && containerRef.current?.contains(elementRef.current)) {\n containerRef.current.removeChild(elementRef.current);\n }\n elementRef.current = null;\n };\n }, [definition.tagName]);\n\n const previousPropsRef = useRef<Record<string, any>>({});\n\n // Keep all props (including those in `rest`) in sync on the existing element,\n // removing any that disappeared since the last render so no stale config lingers.\n useEffect(() => {\n const element = elementRef.current;\n if (element) {\n for (const key of Object.keys(previousPropsRef.current)) {\n if (!(key in propsRef.current)) {\n Reflect.deleteProperty(element, key);\n }\n }\n Object.assign(element, propsRef.current);\n previousPropsRef.current = propsRef.current;\n }\n });\n\n return <div ref={containerRef} />;\n});\n\nWebComponentWrapper.displayName = 'WebComponentWrapper';\n\ntype SurfaceContextProviderProps = {\n id: string;\n role: string;\n data?: Record<string, any>;\n limit?: number;\n fallback?: FC<{ error: Error; data?: any }>;\n definition: Definition;\n [key: string]: any;\n};\n\n/**\n * Wrapper component that provides context for a surface.\n */\n// TODO(burdon): Allow DebugPlugin to provide different fallback using react-ui ErrorFallback.\nconst SurfaceContextProvider = memo(\n ({ id, role, data, limit, fallback = ErrorFallback, definition, ...rest }: SurfaceContextProviderProps) => {\n const contextValue = useMemo(() => ({ id, role, data }), [id, role, data]);\n const onProfilerRender = useSurfaceProfilerCallback();\n const profilerId = `surface/${id}/${role}`;\n // Count error-boundary trips against this surface (dev only).\n const onError = isSurfaceDebugEnabled() ? () => surfaceMetrics.recordError(id, role) : undefined;\n\n // Handle Web Component surfaces.\n if (definition.kind === 'web-component') {\n return (\n <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>\n <SurfaceContext.Provider value={contextValue}>\n <WebComponentWrapper id={id} role={role} data={data} limit={limit} definition={definition} {...rest} />\n </SurfaceContext.Provider>\n </ErrorBoundary>\n );\n }\n\n // Handle React component surfaces.\n const Component = definition.component;\n // `props` lets a definition register a plain container and map the surface props onto its own,\n // instead of wrapping it in an adapter component (see `TypedReactDefinition.props`).\n const surfaceProps = { id, role, data, limit, ...rest };\n const component = <Component {...(definition.props?.(surfaceProps) ?? surfaceProps)} />;\n const profiled =\n onProfilerRender && !profilerId.includes('org.dxos.plugin.debug') ? (\n <Profiler id={profilerId} onRender={onProfilerRender}>\n {component}\n </Profiler>\n ) : (\n component\n );\n\n // Dev builds wrap every surface in `<dx-surface>` for DOM inspection / `window.__DX__`; the\n // `__DX_DEBUG__` flag separately gates the visual highlight overlay (see SurfaceDebug).\n // A profiler provider (the devtools plugin) needs the wrapper in production too: its Surfaces card\n // and highlight overlay read the mount registry the wrapper populates.\n if (isSurfaceWrapperEnabled() || onProfilerRender) {\n return (\n <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>\n <SurfaceContext.Provider value={contextValue}>\n <DebugSurface info={contextValue}>{profiled}</DebugSurface>\n </SurfaceContext.Provider>\n </ErrorBoundary>\n );\n }\n\n // Production renders the matched component directly: a surface adds no wrapper element of its own.\n return (\n <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>\n <SurfaceContext.Provider value={contextValue}>{profiled}</SurfaceContext.Provider>\n </ErrorBoundary>\n );\n },\n);\n\nSurfaceContextProvider.displayName = 'SurfaceContextProvider';\n\n/**\n * A surface is a named region of the screen that can be populated by plugins.\n * The `type` prop is a {@link Role.Role} that defines which region and its\n * associated data contract.\n *\n * A surface is a boundary that may resolve to zero, one, or many components, so\n * it intentionally accepts no `ref`; consumers needing an element should own one\n * inside their contributed component.\n */\nexport const SurfaceComponent = memo(\n ({ id: _id, type, data, limit, placeholder = DEFAULT_PLACEHOLDER, ...rest }: TypedProps<Role.Role<any>>) => {\n // Shallow is the right depth: an ECHO object is a singleton proxy whose identity survives\n // mutation, and a surface subtree stays fresh through its own subscriptions.\n const stableData = useStable(data ?? EMPTY_DATA, shallowEqual);\n const surfaceManager = useSurfaceManager();\n // Subscribe only to this role's contributions: contributing/removing a surface for a\n // different role keeps this bucket referentially stable, so the atom does not re-render us.\n const effectiveRole = type?.role ?? '';\n const roleCandidates = useAtomValue(surfaceManager.candidatesAtom(effectiveRole));\n // True while a module gated on this role is still activating, so a surface specific to `data`\n // may still be coming (see `holdFallbacks`).\n const rolePending = useAtomValue(surfaceManager.pendingAtom(effectiveRole));\n\n // Rendering a surface for a role is the demand signal for role-gated modules: their\n // contributions land in the candidates atom and re-render this surface.\n const pluginManager = useOptionalPluginManager();\n useEffect(() => {\n requestSurfaces(surfaceManager, pluginManager, effectiveRole);\n }, [surfaceManager, pluginManager, effectiveRole]);\n\n // NOTE: The data guard runs per render so the surface re-dispatches on reactive data changes.\n const matched = matchCandidates(roleCandidates, effectiveRole, stableData);\n const definitions = holdFallbacks(matched, rolePending);\n // `limit != null` (not truthiness) so an explicit `limit={0}` renders nothing.\n const candidates = limit != null ? definitions.slice(0, limit) : definitions;\n const truncated = limit != null && definitions.length > limit;\n\n // Dev metrics: track dispatch count, candidate count, and `data` instability (see SurfaceMetrics).\n const churnRef = useRef<{ data: unknown; churn: number }>({ data: undefined, churn: 0 });\n useEffect(() => {\n if (!isSurfaceDebugEnabled() || effectiveRole === '') {\n return;\n }\n const previous = churnRef.current;\n const churn = previous.data === undefined ? 0 : nextDataChurn(previous.data, data, previous.churn);\n churnRef.current = { data, churn };\n for (const definition of candidates) {\n surfaceMetrics.recordDispatch(definition.id, effectiveRole, {\n candidates: candidates.length,\n truncated,\n dataChurn: churn,\n });\n }\n });\n\n if (type?.role == null) {\n if (DEBUG) {\n log.warn('Surface is missing required `type` prop', { id: _id });\n }\n return null;\n }\n\n // An explicit `limit={0}` means render nothing — including while a role is still activating,\n // where the placeholder below would otherwise reintroduce output the caller opted out of.\n if (limit === 0) {\n return null;\n }\n\n if (candidates.length === 0) {\n // A held fallback is not a miss: the role's own module is still loading, and rendering\n // nothing here (rather than `null`) keeps the plank's placeholder up until it lands.\n if (rolePending) {\n return placeholder;\n }\n if (DEBUG) {\n log.warn('no candidates for surface', { role: effectiveRole, data: stableData });\n }\n return null;\n }\n\n return (\n <Suspense fallback={placeholder}>\n {candidates.map((definition) => (\n <SurfaceContextProvider\n key={definition.id}\n id={definition.id}\n role={effectiveRole}\n data={stableData}\n limit={limit}\n definition={definition}\n {...rest}\n />\n ))}\n </Suspense>\n );\n },\n // The generic call signature is reattached here because `memo` erases it from the inferred type.\n) as (<TToken extends Role.Role<any>>(props: TypedProps<TToken>) => React.ReactNode) &\n NamedExoticComponent<TypedProps<Role.Role<any>>>;\n\nSurfaceComponent.displayName = 'Surface';\n\n// TODO(burdon): Make user facing, with telemetry.\nconst ErrorFallback = ({ error }: { error: Error }) => {\n const { message } = error instanceof Error ? error : { message: String(error) };\n return (\n <div role='alert' data-testid='error-boundary-fallback'>\n <h1 className='flex p-2 text-sm text-info-text'>{message}</h1>\n </div>\n );\n};\n\n/**\n * Withholds catch-all matches while the role's own modules are still activating.\n *\n * A module gated on a role's demand event is absent from the first render that requests it, so an\n * eager `Position.last` catch-all (plugin-space's record article matches any ECHO object) claims the\n * slot and is replaced a second later when the specific module's chunk lands — a flash of unrelated\n * UI, not a slower load. Holding ONLY fallbacks is the conservative half of the fix: a surface that\n * already has a specific match renders immediately, so this can never delay a plank that has real\n * content to show.\n */\nconst holdFallbacks = (definitions: Definition[], pending: boolean): Definition[] =>\n pending ? definitions.filter((definition) => definition.position !== Position.last) : definitions;\n\n/**\n * Filters the pre-indexed candidates for a role through their data guards.\n */\nconst matchCandidates = (\n definitions: ReadonlyArray<Definition> | undefined,\n role: string,\n data: Props['data'],\n): Definition[] => {\n if (!definitions) {\n return [];\n }\n return definitions.filter(({ filter }) => (filter ? filter(data ?? {}, role) : true));\n};\n\n/**\n * @internal\n */\nexport const useSurfaces = () => {\n const manager = usePluginManager();\n const surfacesByModule = useAtomValue(manager.capabilities.atomByModule(Capabilities.ReactSurface));\n return useMemo(() => {\n const result: Definition[] = [];\n for (const [_moduleId, surfaces] of Object.entries(surfacesByModule)) {\n for (const def of surfaces.flat()) {\n result.push(def);\n }\n }\n return result;\n }, [surfacesByModule]);\n};\n\n/**\n * Reports whether a contributed surface matches the given role & data, without mounting it.\n *\n * Typed: pass a `type` role token and `data` is constrained to the token's\n * declared contract (e.g. `AppSurface.Section` requires `attendableId`).\n */\ntype IsSurfaceAvailable = <TToken extends Role.Role<any>>(args: { type: TToken; data?: Role.Data<TToken> }) => boolean;\n\n/**\n * @returns a stable function that checks whether a contributed surface is available for a\n * role & data. The surface manager is captured via context, so the returned function carries\n * no dependency of its own and is safe to store and invoke later — e.g. from inside another\n * callback such as a render-prop or event handler — since it calls no hooks itself.\n */\nexport const useIsSurfaceAvailable = (): IsSurfaceAvailable => {\n const surfaceManager = useSurfaceManager();\n const pluginManager = useOptionalPluginManager();\n const registry = useContext(RegistryContext);\n return useCallback<IsSurfaceAvailable>(\n (args: { type: Role.Role<any>; data?: Props['data'] }) => {\n const effectiveRole = args.type?.role;\n if (effectiveRole == null) {\n return false;\n }\n\n const candidates = matchCandidates(\n registry.get(surfaceManager.candidatesAtom(effectiveRole)),\n effectiveRole,\n args.data,\n );\n\n if (candidates.length === 0) {\n // A miss may mean the role's modules are gated and not yet loaded: fire the demand\n // event so a later check (or a mounted Surface) sees the loaded contributions.\n requestSurfaces(surfaceManager, pluginManager, effectiveRole);\n }\n\n return candidates.length > 0;\n },\n [surfaceManager, pluginManager, registry],\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Atom from 'effect/reactivity/Atom';\n\nimport { DXN } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport * as Position from '@dxos/util/Position';\n\nimport { ActivationEvents, Capabilities } from '../../../common/index.ts';\nimport { ActivationEvent, type CapabilityManager, type PluginManager } from '../../../core/index.ts';\nimport { type Definition } from './types.ts';\n\nconst EMPTY_CANDIDATES: ReadonlyArray<Definition> = [];\n\n/**\n * Groups definitions by role with each bucket pre-sorted by {@link Position}, so\n * dispatch avoids a full scan and re-sort on every render. Pure helper — callers\n * are responsible for filtering out invalid definitions before calling this.\n */\nexport const indexByRole = (definitions: Definition[]): Map<string, Definition[]> => {\n const index = new Map<string, Definition[]>();\n for (const definition of definitions) {\n const roles = Array.isArray(definition.role) ? definition.role : [definition.role];\n for (const role of roles) {\n let bucket = index.get(role);\n if (!bucket) {\n bucket = [];\n index.set(role, bucket);\n }\n bucket.push(definition);\n }\n }\n for (const bucket of index.values()) {\n bucket.sort(Position.compare);\n }\n return index;\n};\n\n/** Whether an activation spec fires on `key` (directly, or as a member of a one-of/all-of). */\nconst activatesOn = (events: ActivationEvent.Events, key: string): boolean => {\n const list = ActivationEvent.isOneOf(events) || ActivationEvent.isAllOf(events) ? events.events : [events];\n return list.some((event) => String(ActivationEvent.eventKey(event)) === key);\n};\n\n/** Definitions are stable objects, so a bucket is unchanged when it holds the same ones in order. */\nconst sameCandidates = (left: ReadonlyArray<Definition>, right: ReadonlyArray<Definition>): boolean =>\n left.length === right.length && left.every((definition, index) => definition === right[index]);\n\n/**\n * Owns the per-manager surface memoization: one derived index atom plus a per-role\n * family of candidate atoms. A single instance is provided via\n * {@link SurfaceManagerProvider}, so instance identity does the per-manager keying\n * (replacing module-level WeakMaps) and `Atom.family` does the per-role keying.\n * Atom lifecycle is tied to the provider rather than module-global state.\n */\nexport class SurfaceManager {\n readonly #capabilities: CapabilityManager.CapabilityManager;\n readonly #plugins: PluginManager.PluginManager;\n\n // Role index (each bucket position-sorted); rebuilt once per contribution change.\n readonly #index = Atom.make((get) => {\n const definitions = get(this.#capabilities.atom(Capabilities.ReactSurface)).flat();\n return indexByRole(this.#dropInvalid(definitions));\n }).pipe(Atom.keepAlive);\n\n // Per-role candidate atoms. The atom carries the equality, so a contribution to a different role\n // recomputes this bucket to an equal value and is dropped — that role's subscribers never\n // re-render. (v4 removed `Data.array`, which used to supply that equality structurally.)\n readonly #candidates = Atom.family<string, Atom.Atom<ReadonlyArray<Definition>>>((role) =>\n Atom.make((get) => {\n const bucket = get(this.#index).get(role);\n return bucket ? [...bucket] : EMPTY_CANDIDATES;\n }).pipe(Atom.withEquality(sameCandidates), Atom.keepAlive),\n );\n\n // Per-role activation-in-flight atoms; see `pendingAtom`. `modules` carries only enabled,\n // non-failed plugins' modules (a plugin that fails — including by exceeding the module timeout —\n // is excluded and auto-disabled), so a role cannot stay pending forever.\n readonly #pending = Atom.family<string, Atom.Atom<boolean>>((role) =>\n Atom.make((get) => {\n if (role === '') {\n return false;\n }\n const key = String(ActivationEvent.eventKey(ActivationEvents.SurfacesRequested(role)));\n const active = new Set(get(this.#plugins.active));\n return get(this.#plugins.modules).some(\n (module) => !active.has(module.id) && activatesOn(module.activation.activatesOn, key),\n );\n }).pipe(Atom.keepAlive),\n );\n\n // Ids already reported as invalid on this manager, so a persistently-malformed\n // contribution warns once rather than on every index rebuild.\n #warnedInvalidIds = new Set<string>();\n\n // Roles whose surface demand event has already been dispatched, so re-renders and\n // repeated availability checks do not re-activate the role's modules.\n #requestedRoles = new Set<string>();\n\n constructor(capabilities: CapabilityManager.CapabilityManager, plugins: PluginManager.PluginManager) {\n this.#capabilities = capabilities;\n this.#plugins = plugins;\n }\n\n /** Derived atom yielding the (position-sorted) candidates for a single role. */\n candidatesAtom(role: string): Atom.Atom<ReadonlyArray<Definition>> {\n return this.#candidates(role);\n }\n\n /**\n * Derived atom: true while a module gated on this role's demand event has yet to activate — i.e.\n * a surface specific to the rendered data may still be coming. Boolean-valued, so subscribers\n * re-render only when it flips, not on every activation.\n */\n pendingAtom(role: string): Atom.Atom<boolean> {\n return this.#pending(role);\n }\n\n /**\n * Claims the first surface demand for a role, returning `true` only to that first caller so\n * it can fire the activation event. Subsequent calls — re-renders, repeated availability\n * checks — return `false`.\n */\n requestRole(role: string): boolean {\n if (role === '' || this.#requestedRoles.has(role)) {\n return false;\n }\n this.#requestedRoles.add(role);\n return true;\n }\n\n /**\n * Returns a claimed role to the pool. The claim is taken before the dispatch is known to have\n * happened, so a dispatch that fails or is interrupted (shutdown, a failed activation) must give\n * it back — nothing else re-requests the role, and `useIsSurfaceAvailable`'s own retry is\n * claimed away too, leaving every surface in that role permanently empty.\n */\n releaseRole(role: string): void {\n this.#requestedRoles.delete(role);\n }\n\n /** Drops definitions with an invalid local id, warning once per id. */\n #dropInvalid(definitions: Definition[]): Definition[] {\n return definitions.filter((definition) => {\n if (DXN.isValidPath(definition.id)) {\n return true;\n }\n if (!this.#warnedInvalidIds.has(definition.id)) {\n this.#warnedInvalidIds.add(definition.id);\n log.warn(\n 'dropping surface with invalid id; the final segment must be camelCase — letters and digits, starting with a letter',\n {\n id: definition.id,\n },\n );\n }\n return false;\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chunk-SurfaceManager.mjs","names":["_Fragment","ActivationEvents.SurfacesRequested","ErrorFallback","ActivationEvent.isOneOf","ActivationEvent.isAllOf","ActivationEvent.eventKey","Capabilities.ReactSurface","ActivationEvents.SurfacesRequested"],"sources":["../../src/ui/components/Surface/context.ts","../../src/ui/components/Surface/SurfaceMetrics.ts","../../src/ui/components/Surface/SurfaceDebug.tsx","../../src/ui/components/Surface/SurfaceManagerContext.ts","../../src/ui/components/Surface/SurfaceProfilerContext.tsx","../../src/ui/components/Surface/SurfaceComponent.tsx","../../src/ui/components/Surface/SurfaceManager.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Context, createContext } from 'react';\n\nexport type SurfaceContext = {\n id?: string;\n /** The resolved role NSID (e.g. `org.dxos.role.article`). */\n role: string;\n data?: Record<string, any>;\n};\n\nexport const SurfaceContext: Context<SurfaceContext | undefined> = createContext<SurfaceContext | undefined>(undefined);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { useSyncExternalStore } from 'react';\n\nimport { shallowEqual } from '@dxos/util';\n\n/**\n * Per-surface dev metrics, complementing the render-timing data collected by the\n * React Profiler ({@link SurfaceProfilerStats}). Captures dispatch-level signals\n * the Profiler cannot see: how many candidates matched, whether the consumer's\n * `data` prop is referentially unstable (the most common Surface footgun), error\n * boundary trips, and mount churn.\n *\n * Keyed by the same `surface/<id>/<role>` identifier the Profiler uses, so the\n * two can be joined in the devtools panel.\n */\nexport type SurfaceMetric = {\n /** `surface/<id>/<role>`. */\n id: string;\n /** Surface definition id. */\n surfaceId: string;\n /** Resolved role NSID. */\n role: string;\n /** Number of times the dispatcher resolved candidates for this surface. */\n dispatches: number;\n /** Candidates matched on the last dispatch. */\n candidates: number;\n /** `true` when more candidates matched than `limit` rendered. */\n truncated: boolean;\n /** `true` when the caller's `data` prop identity churns across renders without changing value. */\n dataUnstable: boolean;\n /** Consecutive renders where the caller's `data` identity changed but value did not. */\n dataChurn: number;\n /** Error boundary trips. */\n errors: number;\n /** Mounts of the matched component. */\n mounts: number;\n /** Unmounts of the matched component. */\n unmounts: number;\n};\n\n/** Consecutive unstable renders before `dataUnstable` is flagged. */\nconst UNSTABLE_THRESHOLD = 3;\n\nexport const surfaceMetricKey = (surfaceId: string, role: string): string => `surface/${surfaceId}/${role}`;\n\n/**\n * Singleton store of surface dev metrics. A module singleton (rather than a React\n * context) so the debug overlay — which renders in its own root outside the app's\n * provider tree — can read the same data as the in-app devtools panel.\n */\nclass SurfaceMetricsStore {\n #metrics = new Map<string, SurfaceMetric>();\n #snapshot: SurfaceMetric[] = [];\n #listeners = new Set<() => void>();\n #pendingNotify = false;\n\n #entry(surfaceId: string, role: string): SurfaceMetric {\n const id = surfaceMetricKey(surfaceId, role);\n let metric = this.#metrics.get(id);\n if (!metric) {\n metric = {\n id,\n surfaceId,\n role,\n dispatches: 0,\n candidates: 0,\n truncated: false,\n dataUnstable: false,\n dataChurn: 0,\n errors: 0,\n mounts: 0,\n unmounts: 0,\n };\n this.#metrics.set(id, metric);\n }\n return metric;\n }\n\n /**\n * Records a dispatch. `previousData` is the consumer's prior `data` reference;\n * the store derives churn/instability from the identity-vs-value comparison.\n */\n recordDispatch(\n surfaceId: string,\n role: string,\n args: { candidates: number; truncated: boolean; dataChurn: number },\n ): void {\n const metric = this.#entry(surfaceId, role);\n metric.dispatches += 1;\n metric.candidates = args.candidates;\n metric.truncated = args.truncated;\n metric.dataChurn = args.dataChurn;\n metric.dataUnstable = args.dataChurn >= UNSTABLE_THRESHOLD;\n this.#schedule();\n }\n\n recordError(surfaceId: string, role: string): void {\n this.#entry(surfaceId, role).errors += 1;\n this.#schedule();\n }\n\n recordMount(surfaceId: string, role: string): void {\n this.#entry(surfaceId, role).mounts += 1;\n this.#schedule();\n }\n\n recordUnmount(surfaceId: string, role: string): void {\n this.#entry(surfaceId, role).unmounts += 1;\n this.#schedule();\n }\n\n clear(): void {\n this.#metrics.clear();\n this.#snapshot = [];\n this.#notify();\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n };\n\n getSnapshot = (): SurfaceMetric[] => this.#snapshot;\n\n /** Defers notification to the next frame to coalesce bursts of records. */\n #schedule(): void {\n if (this.#pendingNotify || typeof requestAnimationFrame === 'undefined') {\n if (typeof requestAnimationFrame === 'undefined') {\n this.#notify();\n }\n return;\n }\n this.#pendingNotify = true;\n requestAnimationFrame(() => {\n this.#pendingNotify = false;\n this.#notify();\n });\n }\n\n #notify(): void {\n this.#snapshot = [...this.#metrics.values()];\n for (const listener of this.#listeners) {\n listener();\n }\n }\n}\n\nexport const surfaceMetrics = new SurfaceMetricsStore();\n\n/**\n * Updates the running churn count for the caller's raw `data` prop.\n *\n * @returns the new churn count (0 when `data` changed value or is unchanged).\n */\nexport const nextDataChurn = (previous: unknown, next: unknown, churn: number): number => {\n if (Object.is(previous, next)) {\n return churn;\n }\n // New reference: churn only when the value is structurally the same (unstable identity).\n return shallowEqual(previous, next) ? churn + 1 : 0;\n};\n\n/**\n * Subscribes to surface dev metrics, sorted with the most concerning first\n * (unstable data, then errors, then dispatch count).\n */\nexport const useSurfaceMetrics = (): SurfaceMetric[] => {\n const metrics = useSyncExternalStore(\n surfaceMetrics.subscribe,\n surfaceMetrics.getSnapshot,\n surfaceMetrics.getSnapshot,\n );\n return [...metrics].sort(\n (a, b) => Number(b.dataUnstable) - Number(a.dataUnstable) || b.errors - a.errors || b.dispatches - a.dispatches,\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React, {\n type PropsWithChildren,\n type ReactNode,\n createElement,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { createRoot } from 'react-dom/client';\n\nimport { addEventListener, combine } from '@dxos/async';\nimport { mx } from '@dxos/ui-theme';\n\nimport { type SurfaceContext } from './context.ts';\nimport { surfaceMetrics } from './SurfaceMetrics.ts';\n\ndeclare global {\n interface Window {\n __DX_DEBUG__?: boolean;\n __DX__?: { surfaces: (component?: string) => HTMLElement[]; mounted: () => MountedSurface[] };\n }\n\n // oxlint-disable-next-line no-var\n var __DX_SURFACE_DEBUG__: DebugState | undefined;\n}\n\nconst DEBUG_FLAG = '__DX_DEBUG__';\n\n/**\n * Custom element tag wrapping each React surface in debug builds.\n */\nexport const DX_SURFACE_TAG = 'dx-surface';\n\n/**\n * Installs the `window.__DX__` DevTools helper. `__DX__.surfaces(component?)` returns the mounted\n * `<dx-surface>` elements (optionally filtered by `data-component`) — a console shortcut for the\n * otherwise-verbose `document.querySelectorAll('dx-surface[data-component=\"…\"]')`.\n */\nconst ensureDebugApi = (): void => {\n if (typeof window === 'undefined') {\n return;\n }\n\n window.__DX__ = {\n ...window.__DX__,\n surfaces: (component?: string) =>\n Array.from(\n document.querySelectorAll<HTMLElement>(\n component ? `${DX_SURFACE_TAG}[data-component=\"${component}\"]` : DX_SURFACE_TAG,\n ),\n ),\n // The registry's view, as the Stats companion samples it — compare with `surfaces()` when a\n // mounted surface is missing from the list.\n mounted: () => getMountedSurfaces(),\n };\n};\n\n/**\n * Whether surface debugging is enabled, via the `VITE_DEBUG` build flag or the\n * `__DX_DEBUG__` runtime global. The runtime flag is toggleable without a\n * rebuild and takes effect on the next render.\n */\nexport const isSurfaceDebugEnabled = (): boolean =>\n Boolean(import.meta.env?.VITE_DEBUG) || (typeof window !== 'undefined' && DEBUG_FLAG in window);\n\n/**\n * Whether the `<dx-surface>` wrapper is rendered. Enabled in any development build (or when\n * `VITE_DEBUG` is set) so the wrapper's `data-*` attributes and `window.__DX__` helpers are available\n * for DOM inspection independent of the runtime highlight flag. Production renders no wrapper.\n */\nexport const isSurfaceWrapperEnabled = (): boolean =>\n Boolean(import.meta.env?.DEV) || Boolean(import.meta.env?.VITE_DEBUG);\n\n// Notifies the overlay when the runtime highlight flag toggles: the overlay is a separate React root,\n// so it does not re-render on a surface render and needs an explicit signal to redraw.\nconst subscribeFlag = (listener: () => void): (() => void) => {\n state.flagListeners.add(listener);\n return () => {\n state.flagListeners.delete(listener);\n };\n};\n\n/**\n * Toggles the `__DX_DEBUG__` runtime flag, which gates the visual highlight overlay (not the\n * `<dx-surface>` wrapper). The overlay redraws immediately; surfaces pick it up on their next render.\n */\nexport const setSurfaceDebug = (enabled: boolean): void => {\n if (typeof window === 'undefined') {\n return;\n }\n\n if (enabled) {\n window[DEBUG_FLAG] = true;\n } else {\n delete window[DEBUG_FLAG];\n }\n for (const listener of state.flagListeners) {\n listener();\n }\n};\n\n// The role picked in the Surfaces card: the overlay draws its surfaces bold and the card shows their\n// data. Kept here, beside the flag, so the overlay (its own React root) can subscribe to it.\nconst subscribeSelection = (listener: () => void): (() => void) => {\n state.selectionListeners.add(listener);\n return () => {\n state.selectionListeners.delete(listener);\n };\n};\n\n/** The selected role NSID, if any. */\nexport const getSelectedSurfaceRole = (): string | undefined => state.selectedRole;\n\n/** Selects a role's surfaces (`undefined` clears the selection). */\nexport const setSelectedSurfaceRole = (role: string | undefined): void => {\n state.selectedRole = role;\n for (const listener of state.selectionListeners) {\n listener();\n }\n};\n\n/** Subscribes to the selected role. */\nexport const useSelectedSurfaceRole = (): string | undefined =>\n useSyncExternalStore(subscribeSelection, getSelectedSurfaceRole, getSelectedSurfaceRole);\n\nlet elementRegistered = false;\n\nconst ensureSurfaceElement = (): void => {\n if (elementRegistered || typeof customElements === 'undefined') {\n return;\n }\n\n elementRegistered = true;\n if (!customElements.get(DX_SURFACE_TAG)) {\n // Defined lazily (not at module scope) so importing this module never evaluates\n // `extends HTMLElement` in non-DOM environments (node tests / SSR).\n // Zero-behavior element; `display: contents` keeps the debug wrapper layout-neutral\n // so the debug DOM matches production (which renders no wrapper).\n class DxSurfaceElement extends HTMLElement {\n connectedCallback(): void {\n this.style.display = 'contents';\n }\n }\n customElements.define(DX_SURFACE_TAG, DxSurfaceElement);\n }\n};\n\ntype InfoRef = { readonly current: SurfaceContext };\n\ntype DebugEntry = {\n key: number;\n element: HTMLElement;\n infoRef: InfoRef;\n};\n\n/**\n * Singleton registry of mounted debug surfaces. Centralizes overlay rendering so\n * a single set of observers/listeners serves every surface, instead of each\n * surface mounting its own portal and observers.\n */\nclass SurfaceDebugManager {\n #entries = new Set<DebugEntry>();\n #snapshot: DebugEntry[] = [];\n #listeners = new Set<() => void>();\n #nextKey = 0;\n\n register(element: HTMLElement, infoRef: InfoRef): () => void {\n const entry: DebugEntry = { key: this.#nextKey++, element, infoRef };\n this.#entries.add(entry);\n this.#update();\n return () => {\n this.#entries.delete(entry);\n this.#update();\n };\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n };\n\n getSnapshot = (): DebugEntry[] => this.#snapshot;\n\n #update(): void {\n this.#snapshot = [...this.#entries];\n for (const listener of this.#listeners) {\n listener();\n }\n }\n}\n\ntype DebugState = {\n manager: SurfaceDebugManager;\n flagListeners: Set<() => void>;\n selectionListeners: Set<() => void>;\n selectedRole?: string;\n overlayMounted: boolean;\n};\n\n// Held on the global so it survives HMR: a re-evaluated module would otherwise start an empty\n// registry (listing only surfaces mounted afterwards) while the overlay root already in the\n// document kept reading the stale module's flag and selection.\nconst state: DebugState = (globalThis.__DX_SURFACE_DEBUG__ ??= {\n manager: new SurfaceDebugManager(),\n flagListeners: new Set(),\n selectionListeners: new Set(),\n overlayMounted: false,\n});\n\n/** A surface currently mounted in the document, as registered by its `<dx-surface>` wrapper. */\nexport type MountedSurface = {\n id?: string;\n role: string;\n data?: Record<string, any>;\n /** Ids of the surfaces this one is rendered inside, innermost first (anonymous ones skipped). */\n ancestors: string[];\n};\n\nconst enclosingSurfaceIds = (element: HTMLElement): string[] => {\n const ids: string[] = [];\n let node = element.parentElement?.closest<HTMLElement>(DX_SURFACE_TAG);\n while (node) {\n if (node.dataset.id) {\n ids.push(node.dataset.id);\n }\n node = node.parentElement?.closest<HTMLElement>(DX_SURFACE_TAG);\n }\n return ids;\n};\n\nconst toMounted = ({ element, infoRef }: DebugEntry): MountedSurface => ({\n id: infoRef.current.id,\n role: infoRef.current.role,\n data: infoRef.current.data,\n ancestors: enclosingSurfaceIds(element),\n});\n\n/**\n * The surfaces mounted right now, without subscribing. Populated only while the wrapper is rendered\n * (dev builds, or under a profiler provider).\n */\nexport const getMountedSurfaces = (): MountedSurface[] => state.manager.getSnapshot().map(toMounted);\n\n/** The mounted surfaces, re-read on every mount or unmount (never on a render, so a profiled caller cannot loop). */\nexport const useMountedSurfaces = (): MountedSurface[] => {\n const entries = useSyncExternalStore(state.manager.subscribe, state.manager.getSnapshot, state.manager.getSnapshot);\n return useMemo(() => entries.map(toMounted), [entries]);\n};\n\nconst ensureOverlay = (): void => {\n if (state.overlayMounted || typeof document === 'undefined') {\n return;\n }\n state.overlayMounted = true;\n const root = document.createElement('div');\n root.id = 'dx-surface-overlay';\n document.body.appendChild(root);\n createRoot(root).render(<SurfaceDebugOverlay />);\n};\n\n/**\n * Measures a `display: contents` element (which has no box of its own) by unioning the border boxes\n * of its rendered children. Deliberately uses each child's own `getBoundingClientRect` rather than a\n * Range over all contents: a Range unions every descendant rect, so content overflowing an inner\n * `overflow: auto` scroll container inflates the width past the surface's visible bounds. A direct\n * child's box is clipped to its own border box, so the highlight stays tight to what is on screen.\n */\nconst measureContents = (element: HTMLElement): DOMRect | null => {\n if (typeof document === 'undefined' || typeof getComputedStyle !== 'function') {\n return null;\n }\n\n const boxes: DOMRect[] = [];\n const collect = (node: Element): void => {\n for (const child of node.children) {\n // A nested `display: contents` child also has no box; descend to the elements that do.\n if (getComputedStyle(child).display === 'contents') {\n collect(child);\n } else {\n const rect = child.getBoundingClientRect();\n if (rect.width > 0 || rect.height > 0) {\n boxes.push(rect);\n }\n }\n }\n };\n collect(element);\n\n if (boxes.length === 0) {\n return null;\n }\n\n let left = Infinity;\n let top = Infinity;\n let right = -Infinity;\n let bottom = -Infinity;\n for (const box of boxes) {\n left = Math.min(left, box.left);\n top = Math.min(top, box.top);\n right = Math.max(right, box.right);\n bottom = Math.max(bottom, box.bottom);\n }\n return new DOMRect(left, top, right - left, bottom - top);\n};\n\nconst EMPTY_RECTS: ReadonlyMap<number, DOMRect> = new Map();\n\n/**\n * Single overlay that draws boundary highlights for every registered surface.\n * One ResizeObserver plus one scroll/resize listener serve all surfaces.\n */\nconst SurfaceDebugOverlay = (): ReactNode => {\n const all = useSyncExternalStore(state.manager.subscribe, state.manager.getSnapshot, state.manager.getSnapshot);\n // The `__DX_DEBUG__` flag gates only this visual overlay; the `<dx-surface>` wrappers stay mounted.\n const enabled = useSyncExternalStore(subscribeFlag, isSurfaceDebugEnabled, isSurfaceDebugEnabled);\n const selected = useSelectedSurfaceRole();\n // The selection shows even with the flag off, so picking a role in the Surfaces card always points at it.\n // Memoized: the measuring effect keys on this array, so a fresh one per render would loop it.\n const entries = useMemo(\n () => (enabled ? all : all.filter((entry) => entry.infoRef.current.role === selected)),\n [all, enabled, selected],\n );\n const [rects, setRects] = useState<ReadonlyMap<number, DOMRect>>(EMPTY_RECTS);\n\n useLayoutEffect(() => {\n // Draw nothing (and install no listeners) while there is nothing to show.\n if (entries.length === 0) {\n setRects(EMPTY_RECTS);\n return;\n }\n\n const measure = () => {\n const next = new Map<number, DOMRect>();\n for (const entry of entries) {\n const rect = measureContents(entry.element);\n if (rect) {\n next.set(entry.key, rect);\n }\n }\n setRects(next);\n };\n\n // Guard for environments without ResizeObserver (SSR / tests); scroll/resize still drive measurement.\n const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : undefined;\n for (const entry of entries) {\n // The element itself has no box (display: contents); observe its parent.\n if (observer && entry.element.parentElement) {\n observer.observe(entry.element.parentElement);\n }\n }\n measure();\n\n return combine(addEventListener(window, 'scroll', measure, true), addEventListener(window, 'resize', measure), () =>\n observer?.disconnect(),\n );\n }, [entries]);\n\n return createPortal(\n <>\n {entries.map((entry) => {\n const rect = rects.get(entry.key);\n return rect ? (\n <SurfaceHighlight key={entry.key} rect={rect} selected={entry.infoRef.current.role === selected} />\n ) : null;\n })}\n </>,\n document.body,\n );\n};\n\n/** A passive outline over one surface, bold for the selected role; selection happens in the Surfaces card. */\nconst SurfaceHighlight = ({ rect, selected }: { rect: DOMRect; selected: boolean }): ReactNode => (\n <div\n className={mx(\n 'z-40 fixed pointer-events-none border',\n selected ? 'border-2 border-error-text bg-error-text/10' : 'border-info-text',\n )}\n style={{ top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n />\n);\n\n/**\n * Recovers the rendered surface component's name from the React fiber. Surfaces are registered as\n * anonymous `component: (props) => <Real .../>` wrappers, so the static component carries no useful\n * name; walking the fiber's child chain to the first component with a display name (as DevTools does)\n * finds the real one. The wrapper's own inferred name is the property key ('component'), so skip it.\n */\nconst renderedComponentName = (element: HTMLElement): string | undefined => {\n // React fiber internals are untyped; the fiber hangs off a `__reactFiber$<id>` own-property and its\n // `child` chain is the rendered subtree.\n const node = element as any;\n const fiberKey = Object.keys(node).find((key) => key.startsWith('__reactFiber$'));\n let fiber = fiberKey ? node[fiberKey]?.child : undefined;\n while (fiber) {\n const { type } = fiber;\n if (typeof type === 'function') {\n const name: string | undefined = type.displayName ?? type.name;\n if (name && name !== 'component') {\n return name;\n }\n }\n fiber = fiber.child;\n }\n return undefined;\n};\n\n/**\n * Debug wrapper that mounts each React surface inside a `<dx-surface>` element\n * and registers it with the centralized overlay. The element is named and\n * inspectable in DevTools and queryable (`document.querySelectorAll('dx-surface')`).\n */\nexport const DebugSurface = ({ info, children }: PropsWithChildren<{ info: SurfaceContext }>): ReactNode => {\n // The manager reads the latest info via this ref without re-registering on data change.\n const infoRef = useRef(info);\n infoRef.current = info;\n const elementRef = useRef<HTMLElement>(null);\n\n useEffect(() => {\n ensureSurfaceElement();\n ensureOverlay();\n ensureDebugApi();\n const { id, role } = infoRef.current;\n if (id) {\n surfaceMetrics.recordMount(id, role);\n }\n const element = elementRef.current;\n const unregister = element ? state.manager.register(element, infoRef) : undefined;\n // Names the rendered component (`data-component`) once mounted — read from the fiber because the\n // surface wrapper is anonymous, so no static name is available at render time.\n const component = element ? renderedComponentName(element) : undefined;\n if (element && component) {\n element.setAttribute('data-component', component);\n }\n\n return () => {\n if (id) {\n surfaceMetrics.recordUnmount(id, role);\n }\n unregister?.();\n };\n }, []);\n\n return createElement(\n DX_SURFACE_TAG,\n {\n 'className': 'contents',\n 'data-id': info.id,\n 'data-role': info.role,\n 'ref': elementRef,\n },\n children,\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Context, createContext, useContext } from 'react';\n\nimport { raise } from '@dxos/debug';\n\nimport { type SurfaceManager } from './SurfaceManager.ts';\n\nconst SurfaceManagerContext: Context<SurfaceManager | undefined> = createContext<SurfaceManager | undefined>(undefined);\n\n/**\n * Get the surface manager. Throws when rendered outside a {@link SurfaceManagerProvider};\n * surfaces already hard-require the plugin-manager context, so there is no optional variant.\n */\nexport const useSurfaceManager = (): SurfaceManager =>\n useContext(SurfaceManagerContext) ?? raise(new Error('Missing SurfaceManagerContext'));\n\n/**\n * Context provider for a surface manager.\n */\nexport const SurfaceManagerProvider = SurfaceManagerContext.Provider;\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React, {\n type Context,\n type ProfilerOnRenderCallback,\n type PropsWithChildren,\n createContext,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nconst MAX_ENTRIES = 500;\n\n/**\n * Single profiler record captured from React Profiler onRender callback.\n */\nexport type SurfaceProfilerEntry = {\n id: string;\n phase: 'mount' | 'update' | 'nested-update';\n actualDuration: number;\n baseDuration: number;\n startTime: number;\n commitTime: number;\n timestamp: number;\n};\n\n/**\n * Aggregated stats for a single profiled surface.\n */\nexport type SurfaceProfilerStats = {\n id: string;\n mountCount: number;\n updateCount: number;\n totalRenders: number;\n avgActualDuration: number;\n maxActualDuration: number;\n avgBaseDuration: number;\n lastActualDuration: number;\n lastCommitTime: number;\n};\n\n/**\n * Store that collects profiler entries and notifies subscribers.\n */\nclass SurfaceProfilerStore {\n private _entries: SurfaceProfilerEntry[] = [];\n // Cumulative per-surface stats: the entries buffer is a window, so a surface that rendered before\n // its last MAX_ENTRIES renders would otherwise have no timings at all.\n private _stats = new Map<string, SurfaceProfilerStats>();\n private _listeners = new Set<() => void>();\n private _snapshot: readonly SurfaceProfilerEntry[] = [];\n private _pendingNotify = false;\n\n /**\n * Records an entry and schedules a deferred notification to avoid re-render loops.\n * `_snapshot` is rebuilt only when the deferred notification actually fires\n * ({@link _notifySync}) — not here — so `getSnapshot` stays referentially stable for any\n * synchronous re-invocation React makes within the same commit (e.g. the tearing check a\n * profiled subscriber's own `useSyncExternalStore` runs right after this Profiler's\n * `onRender` callback). Rebuilding it synchronously here would change what `getSnapshot`\n * returns before listeners are told, which React reads as a torn store and forces an\n * immediate re-render — and if the re-rendering component is itself profiled, that\n * re-render re-triggers `record`, looping forever.\n */\n record(entry: SurfaceProfilerEntry) {\n accumulate(this._stats, entry);\n this._entries.push(entry);\n if (this._entries.length > MAX_ENTRIES) {\n this._entries = this._entries.slice(-MAX_ENTRIES);\n }\n this._scheduleNotify();\n }\n\n clear() {\n this._entries = [];\n this._stats = new Map();\n this._snapshot = [];\n this._notifySync();\n }\n\n /** Cumulative stats for every surface that rendered since the last clear, slowest maximum first. */\n getStats(): SurfaceProfilerStats[] {\n return [...this._stats.values()].map((stats) => ({ ...stats })).sort(byMaxDuration);\n }\n\n subscribe = (listener: () => void) => {\n this._listeners.add(listener);\n return () => {\n this._listeners.delete(listener);\n };\n };\n\n getSnapshot = (): readonly SurfaceProfilerEntry[] => {\n return this._snapshot;\n };\n\n /**\n * Defers notification to the next animation frame to break the\n * Profiler onRender → record → notify → re-render → onRender loop.\n */\n private _scheduleNotify() {\n if (!this._pendingNotify) {\n this._pendingNotify = true;\n requestAnimationFrame(() => {\n this._pendingNotify = false;\n this._notifySync();\n });\n }\n }\n\n private _notifySync() {\n this._snapshot = [...this._entries];\n for (const listener of this._listeners) {\n listener();\n }\n }\n}\n\ntype SurfaceProfilerContextValue = {\n store: SurfaceProfilerStore;\n};\n\nconst SurfaceProfilerContext: Context<SurfaceProfilerContextValue | undefined> = createContext<\n SurfaceProfilerContextValue | undefined\n>(undefined);\n\n/**\n * Provider that collects React Profiler data from Surface components.\n */\nexport const SurfaceProfilerProvider = ({ children }: PropsWithChildren) => {\n const storeRef = useRef<SurfaceProfilerStore>(null);\n if (!storeRef.current) {\n storeRef.current = new SurfaceProfilerStore();\n }\n return (\n <SurfaceProfilerContext.Provider value={{ store: storeRef.current }}>{children}</SurfaceProfilerContext.Provider>\n );\n};\n\n/**\n * Returns a stable onRender callback for use with React Profiler.\n */\nexport const useSurfaceProfilerCallback = (): ProfilerOnRenderCallback | undefined => {\n const store = useContext(SurfaceProfilerContext)?.store;\n return useMemo<ProfilerOnRenderCallback | undefined>(() => {\n if (!store) {\n return undefined;\n }\n return (id, phase, actualDuration, baseDuration, startTime, commitTime) => {\n store.record({\n id,\n phase,\n actualDuration,\n baseDuration,\n startTime,\n commitTime,\n timestamp: Date.now(),\n });\n };\n }, [store]);\n};\n\n/**\n * Returns all profiler entries reactively.\n */\nexport const useSurfaceProfilerEntries = (): readonly SurfaceProfilerEntry[] => {\n const context = useContext(SurfaceProfilerContext);\n return useSyncExternalStore(context?.store.subscribe ?? noop, context?.store.getSnapshot ?? emptySnapshot);\n};\n\n/**\n * Reads the cumulative per-surface stats without subscribing: a profiled component that subscribed\n * would record an entry on every re-render the notification caused, and loop. Sample it on a\n * schedule instead.\n */\nexport const useSurfaceProfilerSnapshot = (): (() => SurfaceProfilerStats[]) => {\n const store = useContext(SurfaceProfilerContext)?.store;\n return useMemo(() => (store ? () => store.getStats() : emptyStats), [store]);\n};\n\n/**\n * Returns stats aggregated over the recent-entries window, grouped by surface id.\n */\nexport const useSurfaceProfilerStats = (): SurfaceProfilerStats[] => {\n const entries = useSurfaceProfilerEntries();\n return aggregateSurfaceProfilerStats(entries);\n};\n\n/** Aggregates raw entries into per-surface stats, slowest maximum first. */\nexport const aggregateSurfaceProfilerStats = (entries: readonly SurfaceProfilerEntry[]): SurfaceProfilerStats[] => {\n const statsMap = new Map<string, SurfaceProfilerStats>();\n for (const entry of entries) {\n accumulate(statsMap, entry);\n }\n return [...statsMap.values()].sort(byMaxDuration);\n};\n\nconst byMaxDuration = (a: SurfaceProfilerStats, b: SurfaceProfilerStats) => b.maxActualDuration - a.maxActualDuration;\n\n/** Folds one entry into the running stats of its surface. */\nconst accumulate = (statsMap: Map<string, SurfaceProfilerStats>, entry: SurfaceProfilerEntry) => {\n let stats = statsMap.get(entry.id);\n if (!stats) {\n stats = {\n id: entry.id,\n mountCount: 0,\n updateCount: 0,\n totalRenders: 0,\n avgActualDuration: 0,\n maxActualDuration: 0,\n avgBaseDuration: 0,\n lastActualDuration: 0,\n lastCommitTime: 0,\n };\n statsMap.set(entry.id, stats);\n }\n\n if (entry.phase === 'mount') {\n stats.mountCount++;\n } else {\n stats.updateCount++;\n }\n stats.totalRenders++;\n stats.avgActualDuration =\n (stats.avgActualDuration * (stats.totalRenders - 1) + entry.actualDuration) / stats.totalRenders;\n stats.avgBaseDuration = (stats.avgBaseDuration * (stats.totalRenders - 1) + entry.baseDuration) / stats.totalRenders;\n stats.maxActualDuration = Math.max(stats.maxActualDuration, entry.actualDuration);\n stats.lastActualDuration = entry.actualDuration;\n stats.lastCommitTime = entry.commitTime;\n};\n\nconst emptyStats = (): SurfaceProfilerStats[] => [];\n\n/**\n * Clears all collected profiler entries.\n */\nexport const useSurfaceProfilerClear = (): (() => void) | undefined => {\n const store = useContext(SurfaceProfilerContext)?.store;\n return useMemo(() => (store ? () => store.clear() : undefined), [store]);\n};\n\nconst noop = () => () => {};\nconst EMPTY_SNAPSHOT: readonly SurfaceProfilerEntry[] = [];\nconst emptySnapshot = () => EMPTY_SNAPSHOT;\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport { RegistryContext } from '@effect/atom-react/RegistryContext';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport React, {\n type FC,\n Fragment,\n type NamedExoticComponent,\n Profiler,\n Suspense,\n memo,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n} from 'react';\n\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { log } from '@dxos/log';\nimport { ErrorBoundary } from '@dxos/react-error-boundary';\nimport { useStable } from '@dxos/react-hooks';\nimport { shallowEqual } from '@dxos/util';\nimport * as Position from '@dxos/util/Position';\n\nimport { ActivationEvents, Capabilities, Role } from '../../../common/index.ts';\nimport { type PluginManager } from '../../../core/index.ts';\nimport { useOptionalPluginManager, usePluginManager } from '../PluginManager/index.ts';\nimport { SurfaceContext } from './context.ts';\nimport { DebugSurface, isSurfaceDebugEnabled, isSurfaceWrapperEnabled } from './SurfaceDebug.tsx';\nimport { type SurfaceManager } from './SurfaceManager.ts';\nimport { useSurfaceManager } from './SurfaceManagerContext.ts';\nimport { nextDataChurn, surfaceMetrics } from './SurfaceMetrics.ts';\nimport { useSurfaceProfilerCallback } from './SurfaceProfilerContext.tsx';\nimport { type Definition, type Props, type TypedProps, type WebComponentDefinition } from './types.ts';\n\nconst DEBUG = import.meta.env?.VITE_DEBUG;\n\nconst DEFAULT_PLACEHOLDER = <Fragment />;\n\nconst EMPTY_DATA: Record<string, any> = Object.freeze({});\n\n/**\n * Fires the role's surface demand event so modules gated on it load (see the `roles` option of\n * the surface module maker). Safe to call repeatedly: the surface manager claims the first\n * demand per role, which keeps mount cost flat where Surface instances are numerous (every card\n * in a grid mounts one) instead of re-entering the scheduler.\n */\nconst requestSurfaces = (\n surfaceManager: SurfaceManager,\n manager: PluginManager.PluginManager | undefined,\n role: string,\n): void => {\n if (!manager || !surfaceManager.requestRole(role)) {\n return;\n }\n EffectEx.runDetached(\n manager\n .activate(ActivationEvents.SurfacesRequested(role))\n .pipe(\n Effect.onExit((exit) =>\n Exit.isFailure(exit) ? Effect.sync(() => surfaceManager.releaseRole(role)) : Effect.void,\n ),\n ),\n );\n};\n\n/**\n * Wrapper component for rendering Web Component surfaces.\n * Handles creation, prop setting, and cleanup of Web Components.\n */\ntype WebComponentWrapperProps = {\n id?: string;\n role: string;\n data?: Record<string, any>;\n limit?: number;\n definition: WebComponentDefinition;\n [key: string]: any;\n};\n\nconst WebComponentWrapper = memo(({ id, role, data, limit, definition, ...rest }: WebComponentWrapperProps) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const elementRef = useRef<HTMLElement | null>(null);\n const propsRef = useRef({ id, role, data, limit, ...rest });\n\n // Update props ref on every render.\n propsRef.current = { id, role, data, limit, ...rest };\n\n // Create element only once.\n useEffect(() => {\n if (!containerRef.current || elementRef.current) {\n return;\n }\n\n // Create the Web Component.\n const element = document.createElement(definition.tagName);\n elementRef.current = element;\n\n // Set initial properties on the Web Component.\n Object.assign(element, propsRef.current);\n\n // Append to container.\n containerRef.current.appendChild(element);\n\n // Cleanup on unmount to prevent memory leaks.\n return () => {\n if (elementRef.current && containerRef.current?.contains(elementRef.current)) {\n containerRef.current.removeChild(elementRef.current);\n }\n elementRef.current = null;\n };\n }, [definition.tagName]);\n\n const previousPropsRef = useRef<Record<string, any>>({});\n\n // Keep all props (including those in `rest`) in sync on the existing element,\n // removing any that disappeared since the last render so no stale config lingers.\n useEffect(() => {\n const element = elementRef.current;\n if (element) {\n for (const key of Object.keys(previousPropsRef.current)) {\n if (!(key in propsRef.current)) {\n Reflect.deleteProperty(element, key);\n }\n }\n Object.assign(element, propsRef.current);\n previousPropsRef.current = propsRef.current;\n }\n });\n\n return <div ref={containerRef} />;\n});\n\nWebComponentWrapper.displayName = 'WebComponentWrapper';\n\ntype SurfaceContextProviderProps = {\n id: string;\n role: string;\n data?: Record<string, any>;\n limit?: number;\n fallback?: FC<{ error: Error; data?: any }>;\n definition: Definition;\n [key: string]: any;\n};\n\n/**\n * Wrapper component that provides context for a surface.\n */\n// TODO(burdon): Allow DebugPlugin to provide different fallback using react-ui ErrorFallback.\nconst SurfaceContextProvider = memo(\n ({ id, role, data, limit, fallback = ErrorFallback, definition, ...rest }: SurfaceContextProviderProps) => {\n const contextValue = useMemo(() => ({ id, role, data }), [id, role, data]);\n const onProfilerRender = useSurfaceProfilerCallback();\n const profilerId = `surface/${id}/${role}`;\n // Count error-boundary trips against this surface (dev only).\n const onError = isSurfaceDebugEnabled() ? () => surfaceMetrics.recordError(id, role) : undefined;\n\n // Handle Web Component surfaces.\n if (definition.kind === 'web-component') {\n return (\n <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>\n <SurfaceContext.Provider value={contextValue}>\n <WebComponentWrapper id={id} role={role} data={data} limit={limit} definition={definition} {...rest} />\n </SurfaceContext.Provider>\n </ErrorBoundary>\n );\n }\n\n // Handle React component surfaces.\n const Component = definition.component;\n // `props` lets a definition register a plain container and map the surface props onto its own,\n // instead of wrapping it in an adapter component (see `TypedReactDefinition.props`).\n const surfaceProps = { id, role, data, limit, ...rest };\n const component = <Component {...(definition.props?.(surfaceProps) ?? surfaceProps)} />;\n const profiled =\n onProfilerRender && !profilerId.includes('org.dxos.plugin.debug') ? (\n <Profiler id={profilerId} onRender={onProfilerRender}>\n {component}\n </Profiler>\n ) : (\n component\n );\n\n // Dev builds wrap every surface in `<dx-surface>` for DOM inspection / `window.__DX__`; the\n // `__DX_DEBUG__` flag separately gates the visual highlight overlay (see SurfaceDebug).\n // A profiler provider (the devtools plugin) needs the wrapper in production too: its Surfaces card\n // and highlight overlay read the mount registry the wrapper populates.\n if (isSurfaceWrapperEnabled() || onProfilerRender) {\n return (\n <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>\n <SurfaceContext.Provider value={contextValue}>\n <DebugSurface info={contextValue}>{profiled}</DebugSurface>\n </SurfaceContext.Provider>\n </ErrorBoundary>\n );\n }\n\n // Production renders the matched component directly: a surface adds no wrapper element of its own.\n return (\n <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>\n <SurfaceContext.Provider value={contextValue}>{profiled}</SurfaceContext.Provider>\n </ErrorBoundary>\n );\n },\n);\n\nSurfaceContextProvider.displayName = 'SurfaceContextProvider';\n\n/**\n * A surface is a named region of the screen that can be populated by plugins.\n * The `type` prop is a {@link Role.Role} that defines which region and its\n * associated data contract.\n *\n * A surface is a boundary that may resolve to zero, one, or many components, so\n * it intentionally accepts no `ref`; consumers needing an element should own one\n * inside their contributed component.\n */\nexport const SurfaceComponent = memo(\n ({ id: _id, type, data, limit, placeholder = DEFAULT_PLACEHOLDER, ...rest }: TypedProps<Role.Role<any>>) => {\n // Shallow is the right depth: an ECHO object is a singleton proxy whose identity survives\n // mutation, and a surface subtree stays fresh through its own subscriptions.\n const stableData = useStable(data ?? EMPTY_DATA, shallowEqual);\n const surfaceManager = useSurfaceManager();\n // Subscribe only to this role's contributions: contributing/removing a surface for a\n // different role keeps this bucket referentially stable, so the atom does not re-render us.\n const effectiveRole = type?.role ?? '';\n const roleCandidates = useAtomValue(surfaceManager.candidatesAtom(effectiveRole));\n // True while a module gated on this role is still activating, so a surface specific to `data`\n // may still be coming (see `holdFallbacks`).\n const rolePending = useAtomValue(surfaceManager.pendingAtom(effectiveRole));\n\n // Rendering a surface for a role is the demand signal for role-gated modules: their\n // contributions land in the candidates atom and re-render this surface.\n const pluginManager = useOptionalPluginManager();\n useEffect(() => {\n requestSurfaces(surfaceManager, pluginManager, effectiveRole);\n }, [surfaceManager, pluginManager, effectiveRole]);\n\n // NOTE: The data guard runs per render so the surface re-dispatches on reactive data changes.\n const matched = matchCandidates(roleCandidates, effectiveRole, stableData);\n const definitions = holdFallbacks(matched, rolePending);\n // `limit != null` (not truthiness) so an explicit `limit={0}` renders nothing.\n const candidates = limit != null ? definitions.slice(0, limit) : definitions;\n const truncated = limit != null && definitions.length > limit;\n\n // Dev metrics: track dispatch count, candidate count, and `data` instability (see SurfaceMetrics).\n const churnRef = useRef<{ data: unknown; churn: number }>({ data: undefined, churn: 0 });\n useEffect(() => {\n if (!isSurfaceDebugEnabled() || effectiveRole === '') {\n return;\n }\n const previous = churnRef.current;\n const churn = previous.data === undefined ? 0 : nextDataChurn(previous.data, data, previous.churn);\n churnRef.current = { data, churn };\n for (const definition of candidates) {\n surfaceMetrics.recordDispatch(definition.id, effectiveRole, {\n candidates: candidates.length,\n truncated,\n dataChurn: churn,\n });\n }\n });\n\n if (type?.role == null) {\n if (DEBUG) {\n log.warn('Surface is missing required `type` prop', { id: _id });\n }\n return null;\n }\n\n // An explicit `limit={0}` means render nothing — including while a role is still activating,\n // where the placeholder below would otherwise reintroduce output the caller opted out of.\n if (limit === 0) {\n return null;\n }\n\n if (candidates.length === 0) {\n // A held fallback is not a miss: the role's own module is still loading, and rendering\n // nothing here (rather than `null`) keeps the plank's placeholder up until it lands.\n if (rolePending) {\n return placeholder;\n }\n if (DEBUG) {\n log.warn('no candidates for surface', { role: effectiveRole, data: stableData });\n }\n return null;\n }\n\n return (\n <Suspense fallback={placeholder}>\n {candidates.map((definition) => (\n <SurfaceContextProvider\n key={definition.id}\n id={definition.id}\n role={effectiveRole}\n data={stableData}\n limit={limit}\n definition={definition}\n {...rest}\n />\n ))}\n </Suspense>\n );\n },\n // The generic call signature is reattached here because `memo` erases it from the inferred type.\n) as (<TToken extends Role.Role<any>>(props: TypedProps<TToken>) => React.ReactNode) &\n NamedExoticComponent<TypedProps<Role.Role<any>>>;\n\nSurfaceComponent.displayName = 'Surface';\n\n// TODO(burdon): Make user facing, with telemetry.\nconst ErrorFallback = ({ error }: { error: Error }) => {\n const { message } = error instanceof Error ? error : { message: String(error) };\n return (\n <div role='alert' data-testid='error-boundary-fallback'>\n <h1 className='flex p-2 text-sm text-info-text'>{message}</h1>\n </div>\n );\n};\n\n/**\n * Withholds catch-all matches while the role's own modules are still activating.\n *\n * A module gated on a role's demand event is absent from the first render that requests it, so an\n * eager `Position.last` catch-all (plugin-space's record article matches any ECHO object) claims the\n * slot and is replaced a second later when the specific module's chunk lands — a flash of unrelated\n * UI, not a slower load. Holding ONLY fallbacks is the conservative half of the fix: a surface that\n * already has a specific match renders immediately, so this can never delay a plank that has real\n * content to show.\n */\nconst holdFallbacks = (definitions: Definition[], pending: boolean): Definition[] =>\n pending ? definitions.filter((definition) => definition.position !== Position.last) : definitions;\n\n/**\n * Filters the pre-indexed candidates for a role through their data guards.\n */\nconst matchCandidates = (\n definitions: ReadonlyArray<Definition> | undefined,\n role: string,\n data: Props['data'],\n): Definition[] => {\n if (!definitions) {\n return [];\n }\n return definitions.filter(({ filter }) => (filter ? filter(data ?? {}, role) : true));\n};\n\n/**\n * @internal\n */\nexport const useSurfaces = () => {\n const manager = usePluginManager();\n const surfacesByModule = useAtomValue(manager.capabilities.atomByModule(Capabilities.ReactSurface));\n return useMemo(() => {\n const result: Definition[] = [];\n for (const [_moduleId, surfaces] of Object.entries(surfacesByModule)) {\n for (const def of surfaces.flat()) {\n result.push(def);\n }\n }\n return result;\n }, [surfacesByModule]);\n};\n\n/**\n * Reports whether a contributed surface matches the given role & data, without mounting it.\n *\n * Typed: pass a `type` role token and `data` is constrained to the token's\n * declared contract (e.g. `AppSurface.Section` requires `attendableId`).\n */\ntype IsSurfaceAvailable = <TToken extends Role.Role<any>>(args: { type: TToken; data?: Role.Data<TToken> }) => boolean;\n\n/**\n * @returns a stable function that checks whether a contributed surface is available for a\n * role & data. The surface manager is captured via context, so the returned function carries\n * no dependency of its own and is safe to store and invoke later — e.g. from inside another\n * callback such as a render-prop or event handler — since it calls no hooks itself.\n */\nexport const useIsSurfaceAvailable = (): IsSurfaceAvailable => {\n const surfaceManager = useSurfaceManager();\n const pluginManager = useOptionalPluginManager();\n const registry = useContext(RegistryContext);\n return useCallback<IsSurfaceAvailable>(\n (args: { type: Role.Role<any>; data?: Props['data'] }) => {\n const effectiveRole = args.type?.role;\n if (effectiveRole == null) {\n return false;\n }\n\n const candidates = matchCandidates(\n registry.get(surfaceManager.candidatesAtom(effectiveRole)),\n effectiveRole,\n args.data,\n );\n\n if (candidates.length === 0) {\n // A miss may mean the role's modules are gated and not yet loaded: fire the demand\n // event so a later check (or a mounted Surface) sees the loaded contributions.\n requestSurfaces(surfaceManager, pluginManager, effectiveRole);\n }\n\n return candidates.length > 0;\n },\n [surfaceManager, pluginManager, registry],\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Atom from 'effect/reactivity/Atom';\n\nimport { DXN } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport * as Position from '@dxos/util/Position';\n\nimport { ActivationEvents, Capabilities } from '../../../common/index.ts';\nimport { ActivationEvent, type CapabilityManager, type PluginManager } from '../../../core/index.ts';\nimport { type Definition } from './types.ts';\n\nconst EMPTY_CANDIDATES: ReadonlyArray<Definition> = [];\n\n/**\n * Groups definitions by role with each bucket pre-sorted by {@link Position}, so\n * dispatch avoids a full scan and re-sort on every render. Pure helper — callers\n * are responsible for filtering out invalid definitions before calling this.\n */\nexport const indexByRole = (definitions: Definition[]): Map<string, Definition[]> => {\n const index = new Map<string, Definition[]>();\n for (const definition of definitions) {\n const roles = Array.isArray(definition.role) ? definition.role : [definition.role];\n for (const role of roles) {\n let bucket = index.get(role);\n if (!bucket) {\n bucket = [];\n index.set(role, bucket);\n }\n bucket.push(definition);\n }\n }\n for (const bucket of index.values()) {\n bucket.sort(Position.compare);\n }\n return index;\n};\n\n/** Whether an activation spec fires on `key` (directly, or as a member of a one-of/all-of). */\nconst activatesOn = (events: ActivationEvent.Events, key: string): boolean => {\n const list = ActivationEvent.isOneOf(events) || ActivationEvent.isAllOf(events) ? events.events : [events];\n return list.some((event) => String(ActivationEvent.eventKey(event)) === key);\n};\n\n/** Definitions are stable objects, so a bucket is unchanged when it holds the same ones in order. */\nconst sameCandidates = (left: ReadonlyArray<Definition>, right: ReadonlyArray<Definition>): boolean =>\n left.length === right.length && left.every((definition, index) => definition === right[index]);\n\n/**\n * Owns the per-manager surface memoization: one derived index atom plus a per-role\n * family of candidate atoms. A single instance is provided via\n * {@link SurfaceManagerProvider}, so instance identity does the per-manager keying\n * (replacing module-level WeakMaps) and `Atom.family` does the per-role keying.\n * Atom lifecycle is tied to the provider rather than module-global state.\n */\nexport class SurfaceManager {\n readonly #capabilities: CapabilityManager.CapabilityManager;\n readonly #plugins: PluginManager.PluginManager;\n\n // Role index (each bucket position-sorted); rebuilt once per contribution change.\n readonly #index = Atom.make((get) => {\n const definitions = get(this.#capabilities.atom(Capabilities.ReactSurface)).flat();\n return indexByRole(this.#dropInvalid(definitions));\n }).pipe(Atom.keepAlive);\n\n // Per-role candidate atoms. The atom carries the equality, so a contribution to a different role\n // recomputes this bucket to an equal value and is dropped — that role's subscribers never\n // re-render. (v4 removed `Data.array`, which used to supply that equality structurally.)\n readonly #candidates = Atom.family<string, Atom.Atom<ReadonlyArray<Definition>>>((role) =>\n Atom.make((get) => {\n const bucket = get(this.#index).get(role);\n return bucket ? [...bucket] : EMPTY_CANDIDATES;\n }).pipe(Atom.withEquality(sameCandidates), Atom.keepAlive),\n );\n\n // Per-role activation-in-flight atoms; see `pendingAtom`. `modules` carries only enabled,\n // non-failed plugins' modules (a plugin that fails — including by exceeding the module timeout —\n // is excluded and auto-disabled), so a role cannot stay pending forever.\n readonly #pending = Atom.family<string, Atom.Atom<boolean>>((role) =>\n Atom.make((get) => {\n if (role === '') {\n return false;\n }\n const key = String(ActivationEvent.eventKey(ActivationEvents.SurfacesRequested(role)));\n const active = new Set(get(this.#plugins.active));\n return get(this.#plugins.modules).some(\n (module) => !active.has(module.id) && activatesOn(module.activation.activatesOn, key),\n );\n }).pipe(Atom.keepAlive),\n );\n\n // Ids already reported as invalid on this manager, so a persistently-malformed\n // contribution warns once rather than on every index rebuild.\n #warnedInvalidIds = new Set<string>();\n\n // Roles whose surface demand event has already been dispatched, so re-renders and\n // repeated availability checks do not re-activate the role's modules.\n #requestedRoles = new Set<string>();\n\n constructor(capabilities: CapabilityManager.CapabilityManager, plugins: PluginManager.PluginManager) {\n this.#capabilities = capabilities;\n this.#plugins = plugins;\n }\n\n /** Derived atom yielding the (position-sorted) candidates for a single role. */\n candidatesAtom(role: string): Atom.Atom<ReadonlyArray<Definition>> {\n return this.#candidates(role);\n }\n\n /**\n * Derived atom: true while a module gated on this role's demand event has yet to activate — i.e.\n * a surface specific to the rendered data may still be coming. Boolean-valued, so subscribers\n * re-render only when it flips, not on every activation.\n */\n pendingAtom(role: string): Atom.Atom<boolean> {\n return this.#pending(role);\n }\n\n /**\n * Claims the first surface demand for a role, returning `true` only to that first caller so\n * it can fire the activation event. Subsequent calls — re-renders, repeated availability\n * checks — return `false`.\n */\n requestRole(role: string): boolean {\n if (role === '' || this.#requestedRoles.has(role)) {\n return false;\n }\n this.#requestedRoles.add(role);\n return true;\n }\n\n /**\n * Returns a claimed role to the pool. The claim is taken before the dispatch is known to have\n * happened, so a dispatch that fails or is interrupted (shutdown, a failed activation) must give\n * it back — nothing else re-requests the role, and `useIsSurfaceAvailable`'s own retry is\n * claimed away too, leaving every surface in that role permanently empty.\n */\n releaseRole(role: string): void {\n this.#requestedRoles.delete(role);\n }\n\n /** Drops definitions with an invalid local id, warning once per id. */\n #dropInvalid(definitions: Definition[]): Definition[] {\n return definitions.filter((definition) => {\n if (DXN.isValidPath(definition.id)) {\n return true;\n }\n if (!this.#warnedInvalidIds.has(definition.id)) {\n this.#warnedInvalidIds.add(definition.id);\n log.warn(\n 'dropping surface with invalid id; the final segment must be camelCase — letters and digits, starting with a letter',\n {\n id: definition.id,\n },\n );\n }\n return false;\n });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import { compositeKey } from "@dxos/util";
3
+ import { DXN } from "@dxos/keys";
4
+ //#region src/core/activation-event.ts
5
+ var activation_event_exports = /* @__PURE__ */ __exportAll({
6
+ Idle: () => Idle,
7
+ Startup: () => Startup,
8
+ allOf: () => allOf,
9
+ eventKey: () => eventKey,
10
+ getEvents: () => getEvents,
11
+ isAllOf: () => isAllOf,
12
+ isOneOf: () => isOneOf,
13
+ make: () => make,
14
+ oneOf: () => oneOf,
15
+ pluginStart: () => pluginStart
16
+ });
17
+ /**
18
+ * Helper to define an activation event from an NSID.
19
+ * Static NSID strings are validated at compile time via {@link DXN.Name}.
20
+ */
21
+ var make = (nsid, specifier) => ({
22
+ id: DXN.make(nsid),
23
+ specifier
24
+ });
25
+ /**
26
+ * Helper to create an activation event key.
27
+ */
28
+ var eventKey = (event) => event.specifier ? compositeKey(event.id, event.specifier) : event.id;
29
+ /**
30
+ * Helper to create an activation event that triggers when any of the given events are activated.
31
+ */
32
+ var oneOf = (...events) => ({
33
+ type: "one-of",
34
+ events
35
+ });
36
+ /**
37
+ * Helper to create an activation event that triggers when all of the given events are activated.
38
+ */
39
+ var allOf = (...events) => ({
40
+ type: "all-of",
41
+ events
42
+ });
43
+ /**
44
+ * Helper to check if an activation event is a one-of event.
45
+ */
46
+ var isOneOf = (events) => "type" in events && events.type === "one-of";
47
+ /**
48
+ * Helper to check if an activation event is an all-of event.
49
+ */
50
+ var isAllOf = (events) => "type" in events && events.type === "all-of";
51
+ /**
52
+ * Helper to get the events from an activation event.
53
+ */
54
+ var getEvents = (events) => "type" in events ? events.events : [events];
55
+ /**
56
+ * Fired when the app is started.
57
+ * Defined in core (rather than the `common` well-known events, which re-export it) because
58
+ * the plugin manager's `start()` delegates `activate(Startup)` and publishes the
59
+ * startup-complete message on this key.
60
+ * @deprecated As an `activatesOn` target — declare `provides`/`requires` instead; the
61
+ * dependency pass replaces startup-event wiring. External callers keep using
62
+ * `PluginManager.activate(Startup)` (it delegates to `start()`).
63
+ */
64
+ var Startup = make("org.dxos.app-framework.event.startup");
65
+ /**
66
+ * Fired once by the host at main-thread idle after the app is interactive; see the `common`
67
+ * well-known events for the authoring-facing documentation.
68
+ *
69
+ * Defined here rather than only in `common` because omitting `activatesOn` normalizes to this
70
+ * event, and `Plugin.normalizeActivation` lives in core — which cannot import `common` without
71
+ * closing the `common -> core` cycle.
72
+ */
73
+ var Idle = make("org.dxos.app-framework.event.idle");
74
+ /**
75
+ * A plugin's feature-start event, by convention `<pluginKey>.event.start`. A plugin's
76
+ * off-critical-path modules declare `activatesOn` on its own start event (conventionally
77
+ * exported as `<Name>Events.Start` from the plugin's types); cross-plugin contributions (a
78
+ * skill, a markdown extension) declare the CONSUMING plugin's start event. Deriving the id
79
+ * from the key alone lets fire sites (enable, idle, demand signals) activate a feature
80
+ * without importing its package.
81
+ */
82
+ var pluginStart = (pluginKey) => make(`${pluginKey}.event.start`);
83
+ //#endregion
84
+ export { eventKey as a, isOneOf as c, pluginStart as d, allOf as i, make as l, Startup as n, getEvents as o, activation_event_exports as r, isAllOf as s, Idle as t, oneOf as u };
85
+
86
+ //# sourceMappingURL=chunk-activation-event.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-activation-event.mjs","names":[],"sources":["../../src/core/activation-event.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { DXN } from '@dxos/keys';\nimport { compositeKey } from '@dxos/util';\n\n/**\n * A unique DXN identifier representing an event.\n *\n * @example dxn:org.dxos.plugin.example.event.ready\n */\nexport type ActivationEvent = {\n id: DXN.DXN;\n specifier?: string;\n};\n\n/**\n * An activation event that can be a single event, or a combination of events.\n */\nexport type Events =\n | ActivationEvent\n | { type: 'one-of'; events: ActivationEvent[] }\n | { type: 'all-of'; events: ActivationEvent[] };\n\n/**\n * Helper to define an activation event from an NSID.\n * Static NSID strings are validated at compile time via {@link DXN.Name}.\n */\nexport const make: {\n <T extends string>(\n nsid: [DXN.Name<T>] extends [never] ? `Invalid NSID \"${T}\": final segment must be camelCase (no hyphens)` : T,\n specifier?: string,\n ): ActivationEvent;\n} = (nsid: string, specifier?: string): ActivationEvent => ({\n id: DXN.make(nsid),\n specifier,\n});\n\n/**\n * Helper to create an activation event key.\n */\nexport const eventKey = (event: ActivationEvent) =>\n event.specifier ? compositeKey(event.id, event.specifier) : event.id;\n\n/**\n * Helper to create an activation event that triggers when any of the given events are activated.\n */\nexport const oneOf = (...events: ActivationEvent[]) => ({ type: 'one-of' as const, events });\n\n/**\n * Helper to create an activation event that triggers when all of the given events are activated.\n */\nexport const allOf = (...events: ActivationEvent[]) => ({ type: 'all-of' as const, events });\n\n/**\n * Helper to check if an activation event is a one-of event.\n */\nexport const isOneOf = (events: Events): events is { type: 'one-of'; events: ActivationEvent[] } =>\n 'type' in events && events.type === 'one-of';\n\n/**\n * Helper to check if an activation event is an all-of event.\n */\nexport const isAllOf = (events: Events): events is { type: 'all-of'; events: ActivationEvent[] } =>\n 'type' in events && events.type === 'all-of';\n\n/**\n * Helper to get the events from an activation event.\n */\nexport const getEvents = (events: Events) => ('type' in events ? events.events : [events]);\n\n/**\n * Fired when the app is started.\n * Defined in core (rather than the `common` well-known events, which re-export it) because\n * the plugin manager's `start()` delegates `activate(Startup)` and publishes the\n * startup-complete message on this key.\n * @deprecated As an `activatesOn` target — declare `provides`/`requires` instead; the\n * dependency pass replaces startup-event wiring. External callers keep using\n * `PluginManager.activate(Startup)` (it delegates to `start()`).\n */\nexport const Startup = make('org.dxos.app-framework.event.startup');\n\n/**\n * Fired once by the host at main-thread idle after the app is interactive; see the `common`\n * well-known events for the authoring-facing documentation.\n *\n * Defined here rather than only in `common` because omitting `activatesOn` normalizes to this\n * event, and `Plugin.normalizeActivation` lives in core — which cannot import `common` without\n * closing the `common -> core` cycle.\n */\nexport const Idle = make('org.dxos.app-framework.event.idle');\n\n/**\n * A plugin's feature-start event, by convention `<pluginKey>.event.start`. A plugin's\n * off-critical-path modules declare `activatesOn` on its own start event (conventionally\n * exported as `<Name>Events.Start` from the plugin's types); cross-plugin contributions (a\n * skill, a markdown extension) declare the CONSUMING plugin's start event. Deriving the id\n * from the key alone lets fire sites (enable, idle, demand signals) activate a feature\n * without importing its package.\n */\nexport const pluginStart = (pluginKey: string | DXN.DXN) => make(`${pluginKey}.event.start`);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,QAKR,MAAc,eAAyC;CAC1D,IAAI,IAAI,KAAK,IAAI;CACjB;AACF;;;;AAKA,IAAa,YAAY,UACvB,MAAM,YAAY,aAAa,MAAM,IAAI,MAAM,SAAS,IAAI,MAAM;;;;AAKpE,IAAa,SAAS,GAAG,YAA+B;CAAE,MAAM;CAAmB;AAAO;;;;AAK1F,IAAa,SAAS,GAAG,YAA+B;CAAE,MAAM;CAAmB;AAAO;;;;AAK1F,IAAa,WAAW,WACtB,UAAU,UAAU,OAAO,SAAS;;;;AAKtC,IAAa,WAAW,WACtB,UAAU,UAAU,OAAO,SAAS;;;;AAKtC,IAAa,aAAa,WAAoB,UAAU,SAAS,OAAO,SAAS,CAAC,MAAM;;;;;;;;;;AAWxF,IAAa,UAAU,KAAK,sCAAsC;;;;;;;;;AAUlE,IAAa,OAAO,KAAK,mCAAmC;;;;;;;;;AAU5D,IAAa,eAAe,cAAgC,KAAK,GAAG,UAAU,aAAa"}
@@ -0,0 +1,66 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import { l as make, n as Startup$1, t as Idle$1 } from "./chunk-activation-event.mjs";
3
+ //#region src/common/activation-events.ts
4
+ var activation_events_exports = /* @__PURE__ */ __exportAll({
5
+ CommandsRequested: () => CommandsRequested,
6
+ Idle: () => Idle,
7
+ PluginAssetsRequested: () => PluginAssetsRequested,
8
+ SpaceTemplatesRequested: () => SpaceTemplatesRequested,
9
+ Startup: () => Startup,
10
+ SurfacesRequested: () => SurfacesRequested
11
+ });
12
+ /**
13
+ * Fired when the app is started.
14
+ * Defined in core; see {@link ActivationEvent$.Startup}.
15
+ */
16
+ var Startup = Startup$1;
17
+ /**
18
+ * Demand signal for React surfaces, keyed by role NSID. Fired when a `Surface` for the role
19
+ * first renders (and by availability checks that miss), so surface modules gated on their bound
20
+ * roles load exactly when a screen region that can host them appears.
21
+ */
22
+ var SurfacesRequested = (role) => make("org.dxos.app-framework.event.surfacesRequested", role);
23
+ /**
24
+ * Demand signal for CLI commands. Fired by whoever is about to build a command tree — the `dx`
25
+ * binary through {@link createCliApp}, the devtools terminal when its panel mounts — so command
26
+ * modules load when someone can actually run a command.
27
+ *
28
+ * A command tree is read ONCE, so this must be fired and awaited before the read: a command
29
+ * registered afterwards is simply absent, and `dx space list` falls through to the root help
30
+ * rather than failing anywhere near the cause.
31
+ */
32
+ var CommandsRequested = make("org.dxos.app-framework.event.commandsRequested");
33
+ /**
34
+ * Demand signal for space templates. Fired by whoever is about to offer a list of them — the create
35
+ * space dialog, and the debug plugin's space generator when its panel mounts.
36
+ *
37
+ * Template content is bulky, so the modules carrying it are gated here rather than contributing at
38
+ * startup. Like a command tree, the list is read once: fire and await this before reading it.
39
+ */
40
+ var SpaceTemplatesRequested = make("org.dxos.app-framework.event.spaceTemplatesRequested");
41
+ /**
42
+ * Demand signal for plugin assets (each plugin's bundled `PLUGIN.mdl`). Fired by whoever is about to
43
+ * read them — plugin-code's graph builder when a plugin's spec node is resolved.
44
+ *
45
+ * Nothing reads a spec while the app boots, so the modules carrying them are gated here rather than
46
+ * running with every plugin's startup. Fire and await this before a one-shot read.
47
+ */
48
+ var PluginAssetsRequested = make("org.dxos.app-framework.event.pluginAssetsRequested");
49
+ /**
50
+ * Fired once by the host at main-thread idle after the app is interactive.
51
+ *
52
+ * For REGISTRATION contributions only — graph builders, operation handler sets, settings: the
53
+ * app-wide indexes a user can reach from anywhere (the navtree, the operation registry, a
54
+ * settings read) and which therefore no single surface can gate. A feature's own modules ride
55
+ * its plugin start event, fired when its surface renders; putting a feature body here instead
56
+ * is what turned the former `DeferredStartup` into a second startup pass.
57
+ *
58
+ * This is also what omitting `activatesOn` normalizes to, so a module that must run at boot has
59
+ * to say `activatesOn: ActivationEvents.Startup` explicitly. Defined in core; see
60
+ * {@link ActivationEvent$.Idle}.
61
+ */
62
+ var Idle = Idle$1;
63
+ //#endregion
64
+ export { Startup as a, SpaceTemplatesRequested as i, Idle as n, SurfacesRequested as o, PluginAssetsRequested as r, activation_events_exports as s, CommandsRequested as t };
65
+
66
+ //# sourceMappingURL=chunk-activation-events.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-activation-events.mjs","names":["ActivationEvent$.Startup","ActivationEvent$.make","ActivationEvent$.Idle"],"sources":["../../src/common/activation-events.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { ActivationEvent as ActivationEvent$ } from '../core/index.ts';\n\n/**\n * Fired when the app is started.\n * Defined in core; see {@link ActivationEvent$.Startup}.\n */\nexport const Startup = ActivationEvent$.Startup;\n\n/**\n * Demand signal for React surfaces, keyed by role NSID. Fired when a `Surface` for the role\n * first renders (and by availability checks that miss), so surface modules gated on their bound\n * roles load exactly when a screen region that can host them appears.\n */\nexport const SurfacesRequested = (role: string) =>\n ActivationEvent$.make('org.dxos.app-framework.event.surfacesRequested', role);\n\n/**\n * Demand signal for CLI commands. Fired by whoever is about to build a command tree — the `dx`\n * binary through {@link createCliApp}, the devtools terminal when its panel mounts — so command\n * modules load when someone can actually run a command.\n *\n * A command tree is read ONCE, so this must be fired and awaited before the read: a command\n * registered afterwards is simply absent, and `dx space list` falls through to the root help\n * rather than failing anywhere near the cause.\n */\nexport const CommandsRequested = ActivationEvent$.make('org.dxos.app-framework.event.commandsRequested');\n\n/**\n * Demand signal for space templates. Fired by whoever is about to offer a list of them — the create\n * space dialog, and the debug plugin's space generator when its panel mounts.\n *\n * Template content is bulky, so the modules carrying it are gated here rather than contributing at\n * startup. Like a command tree, the list is read once: fire and await this before reading it.\n */\nexport const SpaceTemplatesRequested = ActivationEvent$.make('org.dxos.app-framework.event.spaceTemplatesRequested');\n\n/**\n * Demand signal for plugin assets (each plugin's bundled `PLUGIN.mdl`). Fired by whoever is about to\n * read them — plugin-code's graph builder when a plugin's spec node is resolved.\n *\n * Nothing reads a spec while the app boots, so the modules carrying them are gated here rather than\n * running with every plugin's startup. Fire and await this before a one-shot read.\n */\nexport const PluginAssetsRequested = ActivationEvent$.make('org.dxos.app-framework.event.pluginAssetsRequested');\n\n/**\n * Fired once by the host at main-thread idle after the app is interactive.\n *\n * For REGISTRATION contributions only — graph builders, operation handler sets, settings: the\n * app-wide indexes a user can reach from anywhere (the navtree, the operation registry, a\n * settings read) and which therefore no single surface can gate. A feature's own modules ride\n * its plugin start event, fired when its surface renders; putting a feature body here instead\n * is what turned the former `DeferredStartup` into a second startup pass.\n *\n * This is also what omitting `activatesOn` normalizes to, so a module that must run at boot has\n * to say `activatesOn: ActivationEvents.Startup` explicitly. Defined in core; see\n * {@link ActivationEvent$.Idle}.\n */\nexport const Idle = ActivationEvent$.Idle;\n"],"mappings":";;;;;;;;;;;;;;;AAUA,IAAa,UAAUA;;;;;;AAOvB,IAAa,qBAAqB,SAChCC,KAAsB,kDAAkD,IAAI;;;;;;;;;;AAW9E,IAAa,oBAAoBA,KAAsB,gDAAgD;;;;;;;;AASvG,IAAa,0BAA0BA,KAAsB,sDAAsD;;;;;;;;AASnH,IAAa,wBAAwBA,KAAsB,oDAAoD;;;;;;;;;;;;;;AAe/G,IAAa,OAAOC"}