@dxos/app-framework 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.mts +1 -1
- package/.storybook/preview.mts +2 -2
- package/CHANGELOG.md +120 -0
- package/TASKS.md +11 -0
- package/dist/lib/chunk-SurfaceManager.mjs +960 -0
- package/dist/lib/chunk-SurfaceManager.mjs.map +1 -0
- package/dist/lib/chunk-capability.mjs +15 -0
- package/dist/lib/chunk-capability.mjs.map +1 -0
- package/dist/lib/{browser/chunk-FJ4765WW.mjs → chunk-context.mjs} +4 -5
- package/dist/lib/chunk-context.mjs.map +1 -0
- package/dist/lib/chunk-history-tracker.mjs +160 -0
- package/dist/lib/chunk-history-tracker.mjs.map +1 -0
- package/dist/lib/chunk-plugin-manager.mjs +1390 -0
- package/dist/lib/chunk-plugin-manager.mjs.map +1 -0
- package/dist/lib/chunk-plugin-manifest.mjs +125 -0
- package/dist/lib/chunk-plugin-manifest.mjs.map +1 -0
- package/dist/lib/chunk-process-manager-capability.mjs +126 -0
- package/dist/lib/chunk-process-manager-capability.mjs.map +1 -0
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/chunk-url-loader.mjs +347 -0
- package/dist/lib/chunk-url-loader.mjs.map +1 -0
- package/dist/lib/cli.mjs +78 -0
- package/dist/lib/cli.mjs.map +1 -0
- package/dist/lib/common/activation-events.mjs +35 -0
- package/dist/lib/common/activation-events.mjs.map +1 -0
- package/dist/lib/common/capabilities.mjs +184 -0
- package/dist/lib/common/capabilities.mjs.map +1 -0
- package/dist/lib/config.mjs +2 -0
- package/dist/lib/core/activation-event.mjs +55 -0
- package/dist/lib/core/activation-event.mjs.map +1 -0
- package/dist/lib/core/capability.mjs +189 -0
- package/dist/lib/core/capability.mjs.map +1 -0
- package/dist/lib/core/plugin-manager.mjs +2 -0
- package/dist/lib/core/plugin.mjs +340 -0
- package/dist/lib/core/plugin.mjs.map +1 -0
- package/dist/lib/core/url-loader.mjs +2 -0
- package/dist/lib/index.mjs +134 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/testing/react.mjs +70 -0
- package/dist/lib/testing/react.mjs.map +1 -0
- package/dist/lib/testing.mjs +399 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/ui.mjs +636 -0
- package/dist/lib/ui.mjs.map +1 -0
- package/dist/lib/vite-plugin.mjs +986 -0
- package/dist/lib/vite-plugin.mjs.map +1 -0
- package/dist/plugin/node-esm/index.mjs +4 -3
- package/dist/plugin/node-esm/index.mjs.map +3 -3
- package/dist/plugin/node-esm/meta.json +1 -1
- package/dist/types/src/common/Role.d.ts +31 -0
- package/dist/types/src/common/Role.d.ts.map +1 -0
- package/dist/types/src/common/Role.test.d.ts +2 -0
- package/dist/types/src/common/Role.test.d.ts.map +1 -0
- package/dist/types/src/common/capabilities.d.ts +11 -2
- package/dist/types/src/common/capabilities.d.ts.map +1 -1
- package/dist/types/src/common/index.d.ts +1 -0
- package/dist/types/src/common/index.d.ts.map +1 -1
- package/dist/types/src/core/capability-manager.d.ts +1 -1
- package/dist/types/src/core/capability-manager.d.ts.map +1 -1
- package/dist/types/src/core/capability.d.ts +38 -5
- package/dist/types/src/core/capability.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manager.d.ts +1 -1
- package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
- package/dist/types/src/core/plugin.d.ts +14 -1
- package/dist/types/src/core/plugin.d.ts.map +1 -1
- package/dist/types/src/core/registry.d.ts +1 -1
- package/dist/types/src/core/registry.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/history/capability.d.ts +1 -1
- package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts +1 -1
- package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -1
- package/dist/types/src/testing/StorybookErrorFallback.d.ts +15 -0
- package/dist/types/src/testing/StorybookErrorFallback.d.ts.map +1 -0
- package/dist/types/src/testing/harness.d.ts +1 -1
- package/dist/types/src/testing/harness.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/react.d.ts +4 -4
- package/dist/types/src/testing/react.d.ts.map +1 -1
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
- package/dist/types/src/testing/withPluginManager.stories.d.ts +6 -0
- package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
- package/dist/types/src/testing/withSurfaceDebug.d.ts +10 -0
- package/dist/types/src/testing/withSurfaceDebug.d.ts.map +1 -0
- package/dist/types/src/ui/components/App/App.d.ts +0 -32
- package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
- package/dist/types/src/ui/components/App/index.d.ts +1 -0
- package/dist/types/src/ui/components/App/index.d.ts.map +1 -1
- package/dist/types/src/ui/components/App/loader.d.ts +35 -0
- package/dist/types/src/ui/components/App/loader.d.ts.map +1 -0
- package/dist/types/src/ui/components/HomeSection/HomeSection.d.ts +26 -0
- package/dist/types/src/ui/components/HomeSection/HomeSection.d.ts.map +1 -0
- package/dist/types/src/ui/components/HomeSection/HomeSection.stories.d.ts +15 -0
- package/dist/types/src/ui/components/HomeSection/HomeSection.stories.d.ts.map +1 -0
- package/dist/types/src/ui/components/HomeSection/index.d.ts +2 -0
- package/dist/types/src/ui/components/HomeSection/index.d.ts.map +1 -0
- package/dist/types/src/ui/components/NamePopover/NamePopover.d.ts +14 -0
- package/dist/types/src/ui/components/NamePopover/NamePopover.d.ts.map +1 -0
- package/dist/types/src/ui/components/NamePopover/index.d.ts +2 -0
- package/dist/types/src/ui/components/NamePopover/index.d.ts.map +1 -0
- package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +21 -9
- package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts +4 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.test.d.ts +2 -0
- package/dist/types/src/ui/components/Surface/SurfaceComponent.test.d.ts.map +1 -0
- package/dist/types/src/ui/components/Surface/SurfaceDebug.d.ts +40 -0
- package/dist/types/src/ui/components/Surface/SurfaceDebug.d.ts.map +1 -0
- package/dist/types/src/ui/components/Surface/SurfaceManager.d.ts +23 -0
- package/dist/types/src/ui/components/Surface/SurfaceManager.d.ts.map +1 -0
- package/dist/types/src/ui/components/Surface/SurfaceManagerContext.d.ts +11 -0
- package/dist/types/src/ui/components/Surface/SurfaceManagerContext.d.ts.map +1 -0
- package/dist/types/src/ui/components/Surface/SurfaceMetrics.d.ts +72 -0
- package/dist/types/src/ui/components/Surface/SurfaceMetrics.d.ts.map +1 -0
- package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/index.d.ts +20 -25
- package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/types.d.ts +56 -52
- package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
- package/dist/types/src/ui/components/index.d.ts +2 -0
- package/dist/types/src/ui/components/index.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/moon.yml +25 -18
- package/package.json +58 -73
- package/src/common/Role.test.ts +41 -0
- package/src/common/Role.ts +54 -0
- package/src/common/capabilities.ts +17 -2
- package/src/common/index.ts +1 -0
- package/src/core/capability-manager.test.ts +1 -1
- package/src/core/capability-manager.ts +1 -1
- package/src/core/capability.ts +45 -6
- package/src/core/plugin-manager.test.ts +38 -3
- package/src/core/plugin-manager.ts +18 -1
- package/src/core/plugin.ts +16 -1
- package/src/core/registry.ts +1 -1
- package/src/plugin-process-manager/process-manager-capability.ts +25 -2
- package/src/testing/StorybookErrorFallback.tsx +40 -0
- package/src/testing/harness.ts +1 -1
- package/src/testing/index.ts +2 -0
- package/src/testing/react.test.tsx +2 -1
- package/src/testing/react.tsx +14 -8
- package/src/testing/withPluginManager.stories.tsx +16 -1
- package/src/testing/withPluginManager.tsx +13 -2
- package/src/testing/withSurfaceDebug.tsx +22 -0
- package/src/ui/components/App/App.tsx +7 -35
- package/src/ui/components/App/index.ts +1 -0
- package/src/ui/components/App/loader.ts +39 -0
- package/src/ui/components/HomeSection/HomeSection.stories.tsx +43 -0
- package/src/ui/components/HomeSection/HomeSection.tsx +69 -0
- package/src/ui/components/HomeSection/index.ts +5 -0
- package/src/ui/components/NamePopover/NamePopover.tsx +70 -0
- package/src/ui/components/NamePopover/index.ts +5 -0
- package/src/ui/components/Surface/DESIGN.md +136 -0
- package/src/ui/components/Surface/SurfaceComponent.stories.tsx +70 -41
- package/src/ui/components/Surface/SurfaceComponent.test.tsx +403 -0
- package/src/ui/components/Surface/SurfaceComponent.tsx +209 -189
- package/src/ui/components/Surface/SurfaceDebug.tsx +435 -0
- package/src/ui/components/Surface/SurfaceManager.ts +95 -0
- package/src/ui/components/Surface/SurfaceManagerContext.ts +23 -0
- package/src/ui/components/Surface/SurfaceMetrics.ts +202 -0
- package/src/ui/components/Surface/SurfaceProfilerContext.tsx +12 -2
- package/src/ui/components/Surface/index.ts +24 -22
- package/src/ui/components/Surface/types.test.ts +47 -54
- package/src/ui/components/Surface/types.ts +66 -91
- package/src/ui/components/index.ts +2 -0
- package/src/ui/hooks/useApp.tsx +7 -3
- package/src/vite-plugin/boot-loader/loader-app/boot-loader.css +1 -1
- package/tsconfig.json +3 -0
- package/vite.config.ts +26 -0
- package/dist/lib/browser/capability-S44TE2H7.mjs +0 -35
- package/dist/lib/browser/capability-S44TE2H7.mjs.map +0 -7
- package/dist/lib/browser/chunk-3NQLTQCP.mjs +0 -47
- package/dist/lib/browser/chunk-3NQLTQCP.mjs.map +0 -7
- package/dist/lib/browser/chunk-53HJHMS5.mjs +0 -7
- package/dist/lib/browser/chunk-53HJHMS5.mjs.map +0 -7
- package/dist/lib/browser/chunk-FJ4765WW.mjs.map +0 -7
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/browser/chunk-KRJ4KK2W.mjs +0 -505
- package/dist/lib/browser/chunk-KRJ4KK2W.mjs.map +0 -7
- package/dist/lib/browser/chunk-LUH4Y5F2.mjs +0 -430
- package/dist/lib/browser/chunk-LUH4Y5F2.mjs.map +0 -7
- package/dist/lib/browser/chunk-NKVRSMFN.mjs +0 -95
- package/dist/lib/browser/chunk-NKVRSMFN.mjs.map +0 -7
- package/dist/lib/browser/chunk-OO53M5UK.mjs +0 -260
- package/dist/lib/browser/chunk-OO53M5UK.mjs.map +0 -7
- package/dist/lib/browser/chunk-OPTS3ZSN.mjs +0 -83
- package/dist/lib/browser/chunk-OPTS3ZSN.mjs.map +0 -7
- package/dist/lib/browser/chunk-Q2GLJTVV.mjs +0 -12
- package/dist/lib/browser/chunk-Q2GLJTVV.mjs.map +0 -7
- package/dist/lib/browser/chunk-SYXIYT6T.mjs +0 -143
- package/dist/lib/browser/chunk-SYXIYT6T.mjs.map +0 -7
- package/dist/lib/browser/chunk-U5MESPM5.mjs +0 -603
- package/dist/lib/browser/chunk-U5MESPM5.mjs.map +0 -7
- package/dist/lib/browser/chunk-UGYCLOXE.mjs +0 -1471
- package/dist/lib/browser/chunk-UGYCLOXE.mjs.map +0 -7
- package/dist/lib/browser/chunk-VF37YTXJ.mjs +0 -28
- package/dist/lib/browser/chunk-VF37YTXJ.mjs.map +0 -7
- package/dist/lib/browser/chunk-YUVQIOTB.mjs +0 -60
- package/dist/lib/browser/chunk-YUVQIOTB.mjs.map +0 -7
- package/dist/lib/browser/cli/index.mjs +0 -74
- package/dist/lib/browser/cli/index.mjs.map +0 -7
- package/dist/lib/browser/common/activation-events.mjs +0 -20
- package/dist/lib/browser/common/activation-events.mjs.map +0 -7
- package/dist/lib/browser/common/capabilities.mjs +0 -56
- package/dist/lib/browser/common/capabilities.mjs.map +0 -7
- package/dist/lib/browser/config/index.mjs +0 -8
- package/dist/lib/browser/config/index.mjs.map +0 -7
- package/dist/lib/browser/core/activation-event.mjs +0 -20
- package/dist/lib/browser/core/activation-event.mjs.map +0 -7
- package/dist/lib/browser/core/capability.mjs +0 -32
- package/dist/lib/browser/core/capability.mjs.map +0 -7
- package/dist/lib/browser/core/plugin-manager.mjs +0 -19
- package/dist/lib/browser/core/plugin-manager.mjs.map +0 -7
- package/dist/lib/browser/core/plugin.mjs +0 -51
- package/dist/lib/browser/core/plugin.mjs.map +0 -7
- package/dist/lib/browser/core/url-loader.mjs +0 -24
- package/dist/lib/browser/core/url-loader.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -98
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/process-manager-capability-QK4BMKMN.mjs +0 -132
- package/dist/lib/browser/process-manager-capability-QK4BMKMN.mjs.map +0 -7
- package/dist/lib/browser/testing/index.mjs +0 -359
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/browser/testing/react.mjs +0 -78
- package/dist/lib/browser/testing/react.mjs.map +0 -7
- package/dist/lib/browser/ui/index.mjs +0 -60
- package/dist/lib/browser/ui/index.mjs.map +0 -7
- package/dist/lib/node-esm/capability-3YRF77LV.mjs +0 -36
- package/dist/lib/node-esm/capability-3YRF77LV.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2UPUZXGU.mjs +0 -9
- package/dist/lib/node-esm/chunk-2UPUZXGU.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-37Z53PXZ.mjs +0 -10
- package/dist/lib/node-esm/chunk-37Z53PXZ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-4SE7SX7N.mjs +0 -261
- package/dist/lib/node-esm/chunk-4SE7SX7N.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-BMW6P6QA.mjs +0 -48
- package/dist/lib/node-esm/chunk-BMW6P6QA.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-COHOWGGJ.mjs +0 -144
- package/dist/lib/node-esm/chunk-COHOWGGJ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-DRT75WGD.mjs +0 -604
- package/dist/lib/node-esm/chunk-DRT75WGD.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FKMVORZN.mjs +0 -431
- package/dist/lib/node-esm/chunk-FKMVORZN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FPW45EZH.mjs +0 -14
- package/dist/lib/node-esm/chunk-FPW45EZH.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-GLH4LGHZ.mjs +0 -84
- package/dist/lib/node-esm/chunk-GLH4LGHZ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-IL7O7IRX.mjs +0 -1472
- package/dist/lib/node-esm/chunk-IL7O7IRX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ND2HETHD.mjs +0 -506
- package/dist/lib/node-esm/chunk-ND2HETHD.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-TLKYZKPW.mjs +0 -29
- package/dist/lib/node-esm/chunk-TLKYZKPW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-VCPGX7WN.mjs +0 -62
- package/dist/lib/node-esm/chunk-VCPGX7WN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XSZMX53L.mjs +0 -96
- package/dist/lib/node-esm/chunk-XSZMX53L.mjs.map +0 -7
- package/dist/lib/node-esm/cli/index.mjs +0 -75
- package/dist/lib/node-esm/cli/index.mjs.map +0 -7
- package/dist/lib/node-esm/common/activation-events.mjs +0 -21
- package/dist/lib/node-esm/common/activation-events.mjs.map +0 -7
- package/dist/lib/node-esm/common/capabilities.mjs +0 -57
- package/dist/lib/node-esm/common/capabilities.mjs.map +0 -7
- package/dist/lib/node-esm/config/index.mjs +0 -9
- package/dist/lib/node-esm/config/index.mjs.map +0 -7
- package/dist/lib/node-esm/core/activation-event.mjs +0 -21
- package/dist/lib/node-esm/core/activation-event.mjs.map +0 -7
- package/dist/lib/node-esm/core/capability.mjs +0 -33
- package/dist/lib/node-esm/core/capability.mjs.map +0 -7
- package/dist/lib/node-esm/core/plugin-manager.mjs +0 -20
- package/dist/lib/node-esm/core/plugin-manager.mjs.map +0 -7
- package/dist/lib/node-esm/core/plugin.mjs +0 -52
- package/dist/lib/node-esm/core/plugin.mjs.map +0 -7
- package/dist/lib/node-esm/core/url-loader.mjs +0 -25
- package/dist/lib/node-esm/core/url-loader.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -99
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/process-manager-capability-5S7WZ2JR.mjs +0 -133
- package/dist/lib/node-esm/process-manager-capability-5S7WZ2JR.mjs.map +0 -7
- package/dist/lib/node-esm/testing/index.mjs +0 -360
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/testing/react.mjs +0 -79
- package/dist/lib/node-esm/testing/react.mjs.map +0 -7
- package/dist/lib/node-esm/ui/index.mjs +0 -61
- package/dist/lib/node-esm/ui/index.mjs.map +0 -7
- package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts +0 -11
- package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +0 -1
- package/src/ui/components/Surface/SurfaceInfo.tsx +0 -106
- package/vitest.config.ts +0 -14
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { useAtomValue } from '@effect-atom/atom-react';
|
|
5
|
+
import { RegistryContext, useAtomValue } from '@effect-atom/atom-react';
|
|
6
6
|
import React, {
|
|
7
7
|
type FC,
|
|
8
8
|
Fragment,
|
|
9
9
|
type NamedExoticComponent,
|
|
10
10
|
Profiler,
|
|
11
|
-
type RefAttributes,
|
|
12
11
|
Suspense,
|
|
13
|
-
forwardRef,
|
|
14
12
|
memo,
|
|
13
|
+
useCallback,
|
|
14
|
+
useContext,
|
|
15
15
|
useEffect,
|
|
16
16
|
useMemo,
|
|
17
17
|
useRef,
|
|
@@ -20,22 +20,15 @@ import React, {
|
|
|
20
20
|
import { log } from '@dxos/log';
|
|
21
21
|
import { ErrorBoundary } from '@dxos/react-error-boundary';
|
|
22
22
|
import { useDefaultValue } from '@dxos/react-hooks';
|
|
23
|
-
import { Position } from '@dxos/util';
|
|
24
23
|
|
|
25
|
-
import { Capabilities } from '../../../common';
|
|
26
|
-
import { type CapabilityManager } from '../../../core';
|
|
24
|
+
import { Capabilities, Role } from '../../../common';
|
|
27
25
|
import { usePluginManager } from '../PluginManager';
|
|
28
26
|
import { SurfaceContext } from './context';
|
|
29
|
-
import {
|
|
27
|
+
import { DebugSurface, isSurfaceDebugEnabled, isSurfaceWrapperEnabled } from './SurfaceDebug';
|
|
28
|
+
import { useSurfaceManager } from './SurfaceManagerContext';
|
|
29
|
+
import { nextDataChurn, surfaceMetrics } from './SurfaceMetrics';
|
|
30
30
|
import { useSurfaceProfilerCallback } from './SurfaceProfilerContext';
|
|
31
|
-
import {
|
|
32
|
-
type Definition,
|
|
33
|
-
type Props,
|
|
34
|
-
type RoleToken,
|
|
35
|
-
type TokenData,
|
|
36
|
-
type TypedProps,
|
|
37
|
-
type WebComponentDefinition,
|
|
38
|
-
} from './types';
|
|
31
|
+
import { type Definition, type Props, type TypedProps, type WebComponentDefinition } from './types';
|
|
39
32
|
|
|
40
33
|
const DEBUG = import.meta.env?.VITE_DEBUG;
|
|
41
34
|
|
|
@@ -54,67 +47,58 @@ type WebComponentWrapperProps = {
|
|
|
54
47
|
[key: string]: any;
|
|
55
48
|
};
|
|
56
49
|
|
|
57
|
-
const WebComponentWrapper = memo(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const propsRef = useRef({ id, role, data, limit, ...rest });
|
|
50
|
+
const WebComponentWrapper = memo(({ id, role, data, limit, definition, ...rest }: WebComponentWrapperProps) => {
|
|
51
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
52
|
+
const elementRef = useRef<HTMLElement | null>(null);
|
|
53
|
+
const propsRef = useRef({ id, role, data, limit, ...rest });
|
|
62
54
|
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
// Update props ref on every render.
|
|
56
|
+
propsRef.current = { id, role, data, limit, ...rest };
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
58
|
+
// Create element only once.
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!containerRef.current || elementRef.current) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
71
63
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
// Create the Web Component.
|
|
65
|
+
const element = document.createElement(definition.tagName);
|
|
66
|
+
elementRef.current = element;
|
|
75
67
|
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
// Set initial properties on the Web Component.
|
|
69
|
+
Object.assign(element, propsRef.current);
|
|
78
70
|
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
// Append to container.
|
|
72
|
+
containerRef.current.appendChild(element);
|
|
81
73
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
forwardedRef.current = element;
|
|
74
|
+
// Cleanup on unmount to prevent memory leaks.
|
|
75
|
+
return () => {
|
|
76
|
+
if (elementRef.current && containerRef.current?.contains(elementRef.current)) {
|
|
77
|
+
containerRef.current.removeChild(elementRef.current);
|
|
87
78
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
79
|
+
elementRef.current = null;
|
|
80
|
+
};
|
|
81
|
+
}, [definition.tagName]);
|
|
82
|
+
|
|
83
|
+
const previousPropsRef = useRef<Record<string, any>>({});
|
|
84
|
+
|
|
85
|
+
// Keep all props (including those in `rest`) in sync on the existing element,
|
|
86
|
+
// removing any that disappeared since the last render so no stale config lingers.
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
const element = elementRef.current;
|
|
89
|
+
if (element) {
|
|
90
|
+
for (const key of Object.keys(previousPropsRef.current)) {
|
|
91
|
+
if (!(key in propsRef.current)) {
|
|
92
|
+
Reflect.deleteProperty(element, key);
|
|
98
93
|
}
|
|
99
|
-
elementRef.current = null;
|
|
100
|
-
};
|
|
101
|
-
}, [definition.tagName, forwardedRef]);
|
|
102
|
-
|
|
103
|
-
// Update props on existing element without recreating it
|
|
104
|
-
// This runs on every render to ensure all props (including those in `rest`) are kept up to date
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
const element = elementRef.current;
|
|
107
|
-
if (!element) {
|
|
108
|
-
return;
|
|
109
94
|
}
|
|
110
|
-
|
|
111
|
-
// Update properties on the existing Web Component
|
|
112
95
|
Object.assign(element, propsRef.current);
|
|
113
|
-
|
|
96
|
+
previousPropsRef.current = propsRef.current;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
114
99
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
);
|
|
100
|
+
return <div ref={containerRef} />;
|
|
101
|
+
});
|
|
118
102
|
|
|
119
103
|
WebComponentWrapper.displayName = 'WebComponentWrapper';
|
|
120
104
|
|
|
@@ -133,124 +117,142 @@ type SurfaceContextProviderProps = {
|
|
|
133
117
|
*/
|
|
134
118
|
// TODO(burdon): Allow DebugPlugin to provide different fallback using react-ui ErrorFallback.
|
|
135
119
|
const SurfaceContextProvider = memo(
|
|
136
|
-
|
|
137
|
-
({ id, role, data
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
ref={forwardedRef}
|
|
154
|
-
{...rest}
|
|
155
|
-
/>
|
|
156
|
-
</SurfaceContext.Provider>
|
|
157
|
-
</ErrorBoundary>
|
|
158
|
-
);
|
|
159
|
-
}
|
|
120
|
+
({ id, role, data, limit, fallback = ErrorFallback, definition, ...rest }: SurfaceContextProviderProps) => {
|
|
121
|
+
const contextValue = useMemo(() => ({ id, role, data }), [id, role, data]);
|
|
122
|
+
const onProfilerRender = useSurfaceProfilerCallback();
|
|
123
|
+
const profilerId = `surface/${id}/${role}`;
|
|
124
|
+
// Count error-boundary trips against this surface (dev only).
|
|
125
|
+
const onError = isSurfaceDebugEnabled() ? () => surfaceMetrics.recordError(id, role) : undefined;
|
|
126
|
+
|
|
127
|
+
// Handle Web Component surfaces.
|
|
128
|
+
if (definition.kind === 'web-component') {
|
|
129
|
+
return (
|
|
130
|
+
<ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>
|
|
131
|
+
<SurfaceContext.Provider value={contextValue}>
|
|
132
|
+
<WebComponentWrapper id={id} role={role} data={data} limit={limit} definition={definition} {...rest} />
|
|
133
|
+
</SurfaceContext.Provider>
|
|
134
|
+
</ErrorBoundary>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
160
137
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
</Profiler>
|
|
176
|
-
) : (
|
|
177
|
-
<Component id={id} role={role} data={data} limit={limit} {...rest} />
|
|
178
|
-
)}
|
|
179
|
-
</SurfaceInfo>
|
|
180
|
-
</SurfaceContext.Provider>
|
|
181
|
-
</div>
|
|
182
|
-
</ErrorBoundary>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
138
|
+
// Handle React component surfaces.
|
|
139
|
+
const Component = definition.component;
|
|
140
|
+
// `props` lets a definition register a plain container and map the surface props onto its own,
|
|
141
|
+
// instead of wrapping it in an adapter component (see `TypedReactDefinition.props`).
|
|
142
|
+
const surfaceProps = { id, role, data, limit, ...rest };
|
|
143
|
+
const component = <Component {...(definition.props?.(surfaceProps) ?? surfaceProps)} />;
|
|
144
|
+
const profiled =
|
|
145
|
+
onProfilerRender && !profilerId.includes('org.dxos.plugin.debug') ? (
|
|
146
|
+
<Profiler id={profilerId} onRender={onProfilerRender}>
|
|
147
|
+
{component}
|
|
148
|
+
</Profiler>
|
|
149
|
+
) : (
|
|
150
|
+
component
|
|
151
|
+
);
|
|
185
152
|
|
|
153
|
+
// Dev builds wrap every surface in `<dx-surface>` for DOM inspection / `window.__DX__`; the
|
|
154
|
+
// `__DX_DEBUG__` flag separately gates the visual highlight overlay (see SurfaceDebug).
|
|
155
|
+
if (isSurfaceWrapperEnabled()) {
|
|
186
156
|
return (
|
|
187
|
-
<ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback}>
|
|
188
|
-
<
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
</SurfaceContext.Provider>
|
|
192
|
-
</div>
|
|
157
|
+
<ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>
|
|
158
|
+
<SurfaceContext.Provider value={contextValue}>
|
|
159
|
+
<DebugSurface info={contextValue}>{profiled}</DebugSurface>
|
|
160
|
+
</SurfaceContext.Provider>
|
|
193
161
|
</ErrorBoundary>
|
|
194
162
|
);
|
|
195
|
-
}
|
|
196
|
-
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Production renders the matched component directly: a surface adds no wrapper element of its own.
|
|
166
|
+
return (
|
|
167
|
+
<ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback} onError={onError}>
|
|
168
|
+
<SurfaceContext.Provider value={contextValue}>{profiled}</SurfaceContext.Provider>
|
|
169
|
+
</ErrorBoundary>
|
|
170
|
+
);
|
|
171
|
+
},
|
|
197
172
|
);
|
|
198
173
|
|
|
199
174
|
SurfaceContextProvider.displayName = 'SurfaceContextProvider';
|
|
200
175
|
|
|
201
176
|
/**
|
|
202
177
|
* A surface is a named region of the screen that can be populated by plugins.
|
|
203
|
-
* The `type` prop is a {@link
|
|
178
|
+
* The `type` prop is a {@link Role.Role} that defines which region and its
|
|
204
179
|
* associated data contract.
|
|
180
|
+
*
|
|
181
|
+
* A surface is a boundary that may resolve to zero, one, or many components, so
|
|
182
|
+
* it intentionally accepts no `ref`; consumers needing an element should own one
|
|
183
|
+
* inside their contributed component.
|
|
205
184
|
*/
|
|
206
|
-
// TODO(burdon): Remove `ref` since relying on this would be error prone.
|
|
207
185
|
export const SurfaceComponent = memo(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
186
|
+
({
|
|
187
|
+
id: _id,
|
|
188
|
+
type,
|
|
189
|
+
data: dataProp,
|
|
190
|
+
limit,
|
|
191
|
+
placeholder = DEFAULT_PLACEHOLDER,
|
|
192
|
+
...rest
|
|
193
|
+
}: TypedProps<Role.Role<any>>) => {
|
|
194
|
+
const data = useDefaultValue(dataProp, () => ({}));
|
|
195
|
+
const surfaceManager = useSurfaceManager();
|
|
196
|
+
// Subscribe only to this role's contributions: contributing/removing a surface for a
|
|
197
|
+
// different role keeps this bucket referentially stable, so the atom does not re-render us.
|
|
198
|
+
const effectiveRole = type?.role ?? '';
|
|
199
|
+
const roleCandidates = useAtomValue(surfaceManager.candidatesAtom(effectiveRole));
|
|
200
|
+
|
|
201
|
+
// NOTE: The data guard runs per render so the surface re-dispatches on reactive data changes.
|
|
202
|
+
const definitions = matchCandidates(roleCandidates, effectiveRole, data);
|
|
203
|
+
// `limit != null` (not truthiness) so an explicit `limit={0}` renders nothing.
|
|
204
|
+
const candidates = limit != null ? definitions.slice(0, limit) : definitions;
|
|
205
|
+
const truncated = limit != null && definitions.length > limit;
|
|
206
|
+
|
|
207
|
+
// Dev metrics: track dispatch count, candidate count, and `data` instability (see SurfaceMetrics).
|
|
208
|
+
const churnRef = useRef<{ data: unknown; churn: number }>({ data: undefined, churn: 0 });
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
if (!isSurfaceDebugEnabled() || effectiveRole === '') {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const previous = churnRef.current;
|
|
214
|
+
const churn = previous.data === undefined ? 0 : nextDataChurn(previous.data, data, previous.churn);
|
|
215
|
+
churnRef.current = { data, churn };
|
|
216
|
+
for (const definition of candidates) {
|
|
217
|
+
surfaceMetrics.recordDispatch(definition.id, effectiveRole, {
|
|
218
|
+
candidates: candidates.length,
|
|
219
|
+
truncated,
|
|
220
|
+
dataChurn: churn,
|
|
221
|
+
});
|
|
224
222
|
}
|
|
223
|
+
});
|
|
225
224
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (DEBUG && candidates.length === 0) {
|
|
230
|
-
log.warn('no candidates for surface', { role: effectiveRole, data });
|
|
231
|
-
return null;
|
|
225
|
+
if (type?.role == null) {
|
|
226
|
+
if (DEBUG) {
|
|
227
|
+
log.warn('Surface is missing required `type` prop', { id: _id });
|
|
232
228
|
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
233
231
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
232
|
+
if (DEBUG && candidates.length === 0) {
|
|
233
|
+
log.warn('no candidates for surface', { role: effectiveRole, data });
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<Suspense fallback={placeholder}>
|
|
239
|
+
{candidates.map((definition) => (
|
|
240
|
+
<SurfaceContextProvider
|
|
241
|
+
key={definition.id}
|
|
242
|
+
id={definition.id}
|
|
243
|
+
role={effectiveRole}
|
|
244
|
+
data={data}
|
|
245
|
+
limit={limit}
|
|
246
|
+
definition={definition}
|
|
247
|
+
{...rest}
|
|
248
|
+
/>
|
|
249
|
+
))}
|
|
250
|
+
</Suspense>
|
|
251
|
+
);
|
|
252
|
+
},
|
|
253
|
+
// The generic call signature is reattached here because `memo` erases it from the inferred type.
|
|
254
|
+
) as (<TToken extends Role.Role<any>>(props: TypedProps<TToken>) => React.ReactNode) &
|
|
255
|
+
NamedExoticComponent<TypedProps<Role.Role<any>>>;
|
|
254
256
|
|
|
255
257
|
SurfaceComponent.displayName = 'Surface';
|
|
256
258
|
|
|
@@ -259,18 +261,23 @@ const ErrorFallback = ({ error }: { error: Error }) => {
|
|
|
259
261
|
const { message } = error instanceof Error ? error : { message: String(error) };
|
|
260
262
|
return (
|
|
261
263
|
<div role='alert' data-testid='error-boundary-fallback'>
|
|
262
|
-
<h1 className='flex
|
|
264
|
+
<h1 className='flex p-2 text-sm text-info-text'>{message}</h1>
|
|
263
265
|
</div>
|
|
264
266
|
);
|
|
265
267
|
};
|
|
266
268
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
269
|
+
/**
|
|
270
|
+
* Filters the pre-indexed candidates for a role through their data guards.
|
|
271
|
+
*/
|
|
272
|
+
const matchCandidates = (
|
|
273
|
+
definitions: ReadonlyArray<Definition> | undefined,
|
|
274
|
+
role: string,
|
|
275
|
+
data: Props['data'],
|
|
276
|
+
): Definition[] => {
|
|
277
|
+
if (!definitions) {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
return definitions.filter(({ filter }) => (filter ? filter(data ?? {}, role) : true));
|
|
274
281
|
};
|
|
275
282
|
|
|
276
283
|
/**
|
|
@@ -291,24 +298,37 @@ export const useSurfaces = () => {
|
|
|
291
298
|
};
|
|
292
299
|
|
|
293
300
|
/**
|
|
294
|
-
*
|
|
301
|
+
* Reports whether a contributed surface matches the given role & data, without mounting it.
|
|
295
302
|
*
|
|
296
303
|
* Typed: pass a `type` role token and `data` is constrained to the token's
|
|
297
304
|
* declared contract (e.g. `AppSurface.Section` requires `attendableId`).
|
|
298
305
|
*/
|
|
299
|
-
|
|
300
|
-
capabilityManager: CapabilityManager.CapabilityManager,
|
|
301
|
-
args: { type: TToken; data?: TokenData<TToken> },
|
|
302
|
-
): boolean {
|
|
303
|
-
const effectiveRole = args.type?.role;
|
|
304
|
-
if (effectiveRole == null) {
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
306
|
+
type IsSurfaceAvailable = <TToken extends Role.Role<any>>(args: { type: TToken; data?: Role.Data<TToken> }) => boolean;
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
/**
|
|
309
|
+
* @returns a stable function that checks whether a contributed surface is available for a
|
|
310
|
+
* role & data. The surface manager is captured via context, so the returned function carries
|
|
311
|
+
* no dependency of its own and is safe to store and invoke later — e.g. from inside another
|
|
312
|
+
* callback such as a render-prop or event handler — since it calls no hooks itself.
|
|
313
|
+
*/
|
|
314
|
+
export const useIsSurfaceAvailable = (): IsSurfaceAvailable => {
|
|
315
|
+
const surfaceManager = useSurfaceManager();
|
|
316
|
+
const registry = useContext(RegistryContext);
|
|
317
|
+
return useCallback<IsSurfaceAvailable>(
|
|
318
|
+
(args: { type: Role.Role<any>; data?: Props['data'] }) => {
|
|
319
|
+
const effectiveRole = args.type?.role;
|
|
320
|
+
if (effectiveRole == null) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const candidates = matchCandidates(
|
|
325
|
+
registry.get(surfaceManager.candidatesAtom(effectiveRole)),
|
|
326
|
+
effectiveRole,
|
|
327
|
+
args.data,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return candidates.length > 0;
|
|
331
|
+
},
|
|
332
|
+
[surfaceManager, registry],
|
|
333
|
+
);
|
|
334
|
+
};
|