@dxos/app-framework 0.7.5-main.9cb18ac → 0.7.5-main.9d2a38b
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/dist/lib/browser/chunk-GNLU3GAU.mjs +1595 -0
- package/dist/lib/browser/chunk-GNLU3GAU.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +112 -618
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +67 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/chunk-FBA4BB3J.cjs +1639 -0
- package/dist/lib/node/chunk-FBA4BB3J.cjs.map +7 -0
- package/dist/lib/node/index.cjs +123 -651
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +91 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-KPMTPXQI.mjs +1597 -0
- package/dist/lib/node-esm/chunk-KPMTPXQI.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +112 -618
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +68 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/App.d.ts +22 -13
- package/dist/types/src/App.d.ts.map +1 -1
- package/dist/types/src/common/capabilities.d.ts +340 -0
- package/dist/types/src/common/capabilities.d.ts.map +1 -0
- package/dist/types/src/common/events.d.ts +41 -0
- package/dist/types/src/common/events.d.ts.map +1 -0
- package/dist/types/src/common/file.d.ts +14 -0
- package/dist/types/src/common/file.d.ts.map +1 -0
- package/dist/types/src/common/graph.d.ts +21 -0
- package/dist/types/src/common/graph.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/index.d.ts +3 -1
- package/dist/types/src/common/index.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/layout.d.ts +0 -11
- package/dist/types/src/common/layout.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/navigation.d.ts +0 -2
- package/dist/types/src/common/navigation.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-surface/SurfaceContext.d.ts → common/surface.d.ts} +12 -29
- package/dist/types/src/common/surface.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/translations.d.ts +0 -11
- package/dist/types/src/common/translations.d.ts.map +1 -0
- package/dist/types/src/core/capabilities.d.ts +90 -0
- package/dist/types/src/core/capabilities.d.ts.map +1 -0
- package/dist/types/src/core/capabilities.test.d.ts +2 -0
- package/dist/types/src/core/capabilities.test.d.ts.map +1 -0
- package/dist/types/src/core/events.d.ts +58 -0
- package/dist/types/src/core/events.d.ts.map +1 -0
- package/dist/types/src/core/index.d.ts +5 -0
- package/dist/types/src/core/index.d.ts.map +1 -0
- package/dist/types/src/core/manager.d.ts +126 -0
- package/dist/types/src/core/manager.d.ts.map +1 -0
- package/dist/types/src/core/manager.test.d.ts +2 -0
- package/dist/types/src/core/manager.test.d.ts.map +1 -0
- package/dist/types/src/core/plugin.d.ts +94 -0
- package/dist/types/src/core/plugin.d.ts.map +1 -0
- package/dist/types/src/helpers.d.ts +10 -0
- package/dist/types/src/helpers.d.ts.map +1 -0
- package/dist/types/src/helpers.test.d.ts +2 -0
- package/dist/types/src/helpers.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/playground/debug/Debug.d.ts +6 -0
- package/dist/types/src/playground/debug/Debug.d.ts.map +1 -0
- package/dist/types/src/playground/debug/index.d.ts +2 -0
- package/dist/types/src/playground/debug/index.d.ts.map +1 -0
- package/dist/types/src/playground/debug/plugin.d.ts +2 -0
- package/dist/types/src/playground/debug/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/generator/Main.d.ts +6 -0
- package/dist/types/src/playground/generator/Main.d.ts.map +1 -0
- package/dist/types/src/playground/generator/Toolbar.d.ts +6 -0
- package/dist/types/src/playground/generator/Toolbar.d.ts.map +1 -0
- package/dist/types/src/playground/generator/generator.d.ts +5 -0
- package/dist/types/src/playground/generator/generator.d.ts.map +1 -0
- package/dist/types/src/playground/generator/index.d.ts +3 -0
- package/dist/types/src/playground/generator/index.d.ts.map +1 -0
- package/dist/types/src/playground/generator/plugin.d.ts +2 -0
- package/dist/types/src/playground/generator/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/layout/Layout.d.ts +8 -0
- package/dist/types/src/playground/layout/Layout.d.ts.map +1 -0
- package/dist/types/src/playground/layout/index.d.ts +2 -0
- package/dist/types/src/playground/layout/index.d.ts.map +1 -0
- package/dist/types/src/playground/layout/plugin.d.ts +2 -0
- package/dist/types/src/playground/layout/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/logger/Toolbar.d.ts +6 -0
- package/dist/types/src/playground/logger/Toolbar.d.ts.map +1 -0
- package/dist/types/src/playground/logger/index.d.ts +2 -0
- package/dist/types/src/playground/logger/index.d.ts.map +1 -0
- package/dist/types/src/playground/logger/plugin.d.ts +2 -0
- package/dist/types/src/playground/logger/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/logger/schema.d.ts +13 -0
- package/dist/types/src/playground/logger/schema.d.ts.map +1 -0
- package/dist/types/src/playground/playground.stories.d.ts +10 -0
- package/dist/types/src/playground/playground.stories.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-intent → plugin-intent}/IntentContext.d.ts +1 -1
- package/dist/types/src/plugin-intent/IntentContext.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/IntentPlugin.d.ts +2 -0
- package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/actions.d.ts +23 -0
- package/dist/types/src/plugin-intent/actions.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/errors.d.ts +16 -0
- package/dist/types/src/plugin-intent/errors.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/index.d.ts +6 -0
- package/dist/types/src/plugin-intent/index.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.d.ts +18 -19
- package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/intent-dispatcher.test.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-intent → plugin-intent}/intent.d.ts +5 -25
- package/dist/types/src/plugin-intent/intent.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/SettingsPlugin.d.ts +2 -0
- package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-settings/provides.d.ts → plugin-settings/actions.d.ts} +1 -10
- package/dist/types/src/plugin-settings/actions.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/index.d.ts +3 -0
- package/dist/types/src/plugin-settings/index.d.ts.map +1 -0
- package/dist/types/src/react/ErrorBoundary.d.ts.map +1 -0
- package/dist/types/src/react/PluginManagerProvider.d.ts +10 -0
- package/dist/types/src/react/PluginManagerProvider.d.ts.map +1 -0
- package/dist/types/src/react/Surface.d.ts +12 -0
- package/dist/types/src/react/Surface.d.ts.map +1 -0
- package/dist/types/src/react/index.d.ts +5 -0
- package/dist/types/src/react/index.d.ts.map +1 -0
- package/dist/types/src/react/useCapabilities.d.ts +13 -0
- package/dist/types/src/react/useCapabilities.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/withPluginManager.d.ts +10 -0
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -0
- package/dist/types/src/testing/withPluginManager.stories.d.ts +5 -0
- package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +28 -15
- package/project.json +2 -1
- package/src/App.tsx +138 -34
- package/src/common/capabilities.ts +82 -0
- package/src/common/events.ts +63 -0
- package/src/common/file.ts +22 -0
- package/src/common/graph.ts +30 -0
- package/src/{plugins/common → common}/index.ts +3 -1
- package/src/{plugins/common → common}/layout.ts +0 -16
- package/src/{plugins/common → common}/navigation.ts +0 -15
- package/src/{plugins/plugin-surface/SurfaceContext.ts → common/surface.ts} +6 -37
- package/src/common/translations.ts +18 -0
- package/src/core/capabilities.test.ts +116 -0
- package/src/core/capabilities.ts +213 -0
- package/src/core/events.ts +58 -0
- package/src/core/index.ts +8 -0
- package/src/core/manager.test.ts +567 -0
- package/src/core/manager.ts +497 -0
- package/src/core/plugin.ts +128 -0
- package/src/helpers.test.ts +97 -0
- package/src/helpers.ts +45 -0
- package/src/index.ts +6 -3
- package/src/playground/debug/Debug.tsx +39 -0
- package/src/playground/debug/index.ts +5 -0
- package/src/playground/debug/plugin.ts +17 -0
- package/src/playground/generator/Main.tsx +71 -0
- package/src/playground/generator/Toolbar.tsx +46 -0
- package/src/playground/generator/generator.ts +23 -0
- package/src/playground/generator/index.ts +6 -0
- package/src/playground/generator/plugin.ts +18 -0
- package/src/playground/layout/Layout.tsx +33 -0
- package/src/playground/layout/index.ts +5 -0
- package/src/playground/layout/plugin.ts +17 -0
- package/src/playground/logger/Toolbar.tsx +28 -0
- package/src/playground/logger/index.ts +5 -0
- package/src/playground/logger/plugin.ts +33 -0
- package/src/playground/logger/schema.ts +12 -0
- package/src/playground/playground.stories.tsx +34 -0
- package/src/{plugins/plugin-intent → plugin-intent}/IntentContext.tsx +12 -4
- package/src/plugin-intent/IntentPlugin.tsx +60 -0
- package/src/plugin-intent/actions.ts +22 -0
- package/src/plugin-intent/errors.ts +39 -0
- package/src/{plugins/plugin-intent → plugin-intent}/index.ts +2 -6
- package/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.test.ts +36 -35
- package/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.ts +49 -65
- package/src/{plugins/plugin-intent → plugin-intent}/intent.ts +5 -21
- package/src/plugin-settings/SettingsPlugin.ts +29 -0
- package/src/plugin-settings/actions.ts +17 -0
- package/src/plugin-settings/index.ts +6 -0
- package/src/react/PluginManagerProvider.ts +22 -0
- package/src/react/Surface.tsx +72 -0
- package/src/react/index.ts +8 -0
- package/src/react/useCapabilities.ts +39 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/withPluginManager.stories.tsx +47 -0
- package/src/testing/withPluginManager.tsx +67 -0
- package/tsconfig.json +13 -1
- package/dist/lib/browser/chunk-3E7RY3CE.mjs +0 -72
- package/dist/lib/browser/chunk-3E7RY3CE.mjs.map +0 -7
- package/dist/lib/browser/chunk-QG25ZU2N.mjs +0 -320
- package/dist/lib/browser/chunk-QG25ZU2N.mjs.map +0 -7
- package/dist/lib/browser/chunk-SPDTXTOV.mjs +0 -163
- package/dist/lib/browser/chunk-SPDTXTOV.mjs.map +0 -7
- package/dist/lib/browser/chunk-WBOXEHBE.mjs +0 -51
- package/dist/lib/browser/chunk-WBOXEHBE.mjs.map +0 -7
- package/dist/lib/browser/plugin-intent-T7Y3MJ5C.mjs +0 -32
- package/dist/lib/browser/plugin-intent-T7Y3MJ5C.mjs.map +0 -7
- package/dist/lib/browser/plugin-settings-5U2L2NRU.mjs +0 -15
- package/dist/lib/browser/plugin-settings-5U2L2NRU.mjs.map +0 -7
- package/dist/lib/browser/plugin-surface-OKPF3EQI.mjs +0 -24
- package/dist/lib/browser/plugin-surface-OKPF3EQI.mjs.map +0 -7
- package/dist/lib/node/chunk-BW3RNEVI.cjs +0 -185
- package/dist/lib/node/chunk-BW3RNEVI.cjs.map +0 -7
- package/dist/lib/node/chunk-FCMHRU3M.cjs +0 -70
- package/dist/lib/node/chunk-FCMHRU3M.cjs.map +0 -7
- package/dist/lib/node/chunk-QBM42OQ6.cjs +0 -97
- package/dist/lib/node/chunk-QBM42OQ6.cjs.map +0 -7
- package/dist/lib/node/chunk-VWHAALIN.cjs +0 -344
- package/dist/lib/node/chunk-VWHAALIN.cjs.map +0 -7
- package/dist/lib/node/plugin-intent-F3TQZIUR.cjs +0 -53
- package/dist/lib/node/plugin-intent-F3TQZIUR.cjs.map +0 -7
- package/dist/lib/node/plugin-settings-W6UHMH5M.cjs +0 -36
- package/dist/lib/node/plugin-settings-W6UHMH5M.cjs.map +0 -7
- package/dist/lib/node/plugin-surface-CCSIONYW.cjs +0 -45
- package/dist/lib/node/plugin-surface-CCSIONYW.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-3T5UIJY3.mjs +0 -53
- package/dist/lib/node-esm/chunk-3T5UIJY3.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-4GX7V5ZE.mjs +0 -164
- package/dist/lib/node-esm/chunk-4GX7V5ZE.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CFOUYXQ6.mjs +0 -321
- package/dist/lib/node-esm/chunk-CFOUYXQ6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-EYCTSFEJ.mjs +0 -74
- package/dist/lib/node-esm/chunk-EYCTSFEJ.mjs.map +0 -7
- package/dist/lib/node-esm/plugin-intent-W2HQC6LC.mjs +0 -33
- package/dist/lib/node-esm/plugin-intent-W2HQC6LC.mjs.map +0 -7
- package/dist/lib/node-esm/plugin-settings-H5RHNFVC.mjs +0 -16
- package/dist/lib/node-esm/plugin-settings-H5RHNFVC.mjs.map +0 -7
- package/dist/lib/node-esm/plugin-surface-V3YET3UL.mjs +0 -25
- package/dist/lib/node-esm/plugin-surface-V3YET3UL.mjs.map +0 -7
- package/dist/types/src/plugins/common/file.d.ts +0 -22
- package/dist/types/src/plugins/common/file.d.ts.map +0 -1
- package/dist/types/src/plugins/common/graph.d.ts +0 -51
- package/dist/types/src/plugins/common/graph.d.ts.map +0 -1
- package/dist/types/src/plugins/common/index.d.ts.map +0 -1
- package/dist/types/src/plugins/common/layout.d.ts.map +0 -1
- package/dist/types/src/plugins/common/metadata.d.ts +0 -16
- package/dist/types/src/plugins/common/metadata.d.ts.map +0 -1
- package/dist/types/src/plugins/common/navigation.d.ts.map +0 -1
- package/dist/types/src/plugins/common/translations.d.ts.map +0 -1
- package/dist/types/src/plugins/helpers.d.ts +0 -41
- package/dist/types/src/plugins/helpers.d.ts.map +0 -1
- package/dist/types/src/plugins/index.d.ts +0 -7
- package/dist/types/src/plugins/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/HostPlugin.d.ts +0 -16
- package/dist/types/src/plugins/plugin-host/HostPlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/PluginContainer.d.ts +0 -14
- package/dist/types/src/plugins/plugin-host/PluginContainer.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/PluginContext.d.ts +0 -47
- package/dist/types/src/plugins/plugin-host/PluginContext.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/index.d.ts +0 -6
- package/dist/types/src/plugins/plugin-host/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/plugin.d.ts +0 -104
- package/dist/types/src/plugins/plugin-host/plugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/plugin.test.d.ts +0 -35
- package/dist/types/src/plugins/plugin-host/plugin.test.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/IntentContext.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/IntentPlugin.d.ts +0 -8
- package/dist/types/src/plugins/plugin-intent/IntentPlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/index.d.ts +0 -7
- package/dist/types/src/plugins/plugin-intent/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/intent-dispatcher.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/intent-dispatcher.test.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/intent.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/meta.d.ts +0 -6
- package/dist/types/src/plugins/plugin-intent/meta.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/provides.d.ts +0 -16
- package/dist/types/src/plugins/plugin-intent/provides.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/SettingsPlugin.d.ts +0 -4
- package/dist/types/src/plugins/plugin-settings/SettingsPlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/index.d.ts +0 -4
- package/dist/types/src/plugins/plugin-settings/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/meta.d.ts +0 -5
- package/dist/types/src/plugins/plugin-settings/meta.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/provides.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/ErrorBoundary.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/Surface.d.ts +0 -7
- package/dist/types/src/plugins/plugin-surface/Surface.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/SurfaceContext.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/SurfacePlugin.d.ts +0 -7
- package/dist/types/src/plugins/plugin-surface/SurfacePlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/helpers.d.ts +0 -21
- package/dist/types/src/plugins/plugin-surface/helpers.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/index.d.ts +0 -8
- package/dist/types/src/plugins/plugin-surface/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/meta.d.ts +0 -5
- package/dist/types/src/plugins/plugin-surface/meta.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/provides.d.ts +0 -17
- package/dist/types/src/plugins/plugin-surface/provides.d.ts.map +0 -1
- package/src/plugins/common/file.ts +0 -36
- package/src/plugins/common/graph.ts +0 -70
- package/src/plugins/common/metadata.ts +0 -29
- package/src/plugins/common/translations.ts +0 -36
- package/src/plugins/helpers.ts +0 -92
- package/src/plugins/index.ts +0 -11
- package/src/plugins/plugin-host/HostPlugin.tsx +0 -88
- package/src/plugins/plugin-host/PluginContainer.tsx +0 -120
- package/src/plugins/plugin-host/PluginContext.tsx +0 -79
- package/src/plugins/plugin-host/index.ts +0 -12
- package/src/plugins/plugin-host/plugin.test.ts +0 -158
- package/src/plugins/plugin-host/plugin.ts +0 -133
- package/src/plugins/plugin-intent/IntentPlugin.tsx +0 -75
- package/src/plugins/plugin-intent/meta.ts +0 -11
- package/src/plugins/plugin-intent/provides.ts +0 -26
- package/src/plugins/plugin-settings/SettingsPlugin.tsx +0 -22
- package/src/plugins/plugin-settings/index.ts +0 -9
- package/src/plugins/plugin-settings/meta.ts +0 -9
- package/src/plugins/plugin-settings/provides.ts +0 -34
- package/src/plugins/plugin-surface/Surface.tsx +0 -62
- package/src/plugins/plugin-surface/SurfacePlugin.tsx +0 -45
- package/src/plugins/plugin-surface/helpers.ts +0 -22
- package/src/plugins/plugin-surface/index.ts +0 -14
- package/src/plugins/plugin-surface/meta.ts +0 -9
- package/src/plugins/plugin-surface/provides.ts +0 -27
- /package/dist/types/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.test.d.ts +0 -0
- /package/dist/types/src/{plugins/plugin-surface → react}/ErrorBoundary.d.ts +0 -0
- /package/src/{plugins/plugin-surface → react}/ErrorBoundary.tsx +0 -0
package/src/App.tsx
CHANGED
|
@@ -1,63 +1,167 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { effect } from '@preact/signals-core';
|
|
6
|
+
import React, { type PropsWithChildren, type ReactNode } from 'react';
|
|
6
7
|
|
|
7
8
|
import { invariant } from '@dxos/invariant';
|
|
9
|
+
import { create } from '@dxos/live-object';
|
|
8
10
|
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
11
|
+
import { Capabilities, Events } from './common';
|
|
12
|
+
import { PluginManager, type PluginManagerOptions, type Plugin } from './core';
|
|
13
|
+
import { topologicalSort } from './helpers';
|
|
14
|
+
import { ErrorBoundary, PluginManagerProvider } from './react';
|
|
15
|
+
|
|
16
|
+
const ENABLED_KEY = 'dxos.org/app-framework/enabled';
|
|
17
|
+
|
|
18
|
+
export type CreateAppOptions = {
|
|
19
|
+
pluginManager?: PluginManager;
|
|
20
|
+
pluginLoader?: PluginManagerOptions['pluginLoader'];
|
|
21
|
+
plugins?: Plugin[];
|
|
22
|
+
core?: string[];
|
|
23
|
+
defaults?: string[];
|
|
24
|
+
placeholder?: ReactNode;
|
|
25
|
+
fallback?: ErrorBoundary['props']['fallback'];
|
|
26
|
+
cacheEnabled?: boolean;
|
|
27
|
+
};
|
|
13
28
|
|
|
14
29
|
/**
|
|
15
30
|
* Expected usage is for this to be the entrypoint of the application.
|
|
16
31
|
* Initializes plugins and renders the root components.
|
|
17
32
|
*
|
|
18
33
|
* @example
|
|
19
|
-
* const
|
|
20
|
-
* const
|
|
21
|
-
*
|
|
22
|
-
* [MyPluginMeta.id]: Plugin.lazy(() => import('./plugins/MyPlugin/plugin')),
|
|
23
|
-
* };
|
|
24
|
-
* const core = [LayoutMeta.id];
|
|
25
|
-
* const default = [MyPluginMeta.id];
|
|
34
|
+
* const plugins = [LayoutPlugin(), MyPlugin()];
|
|
35
|
+
* const core = [LayoutPluginId];
|
|
36
|
+
* const default = [MyPluginId];
|
|
26
37
|
* const fallback = <div>Initializing Plugins...</div>;
|
|
27
|
-
* const App = createApp({
|
|
38
|
+
* const App = createApp({ plugins, core, default, fallback });
|
|
28
39
|
* createRoot(document.getElementById('root')!).render(
|
|
29
40
|
* <StrictMode>
|
|
30
41
|
* <App />
|
|
31
42
|
* </StrictMode>,
|
|
32
43
|
* );
|
|
33
44
|
*
|
|
45
|
+
* @param params.pluginLoader A function which loads new plugins.
|
|
34
46
|
* @param params.plugins All plugins available to the application.
|
|
35
|
-
* @param params.meta All plugin metadata.
|
|
36
47
|
* @param params.core Core plugins which will always be enabled.
|
|
37
48
|
* @param params.defaults Default plugins are enabled by default but can be disabled by the user.
|
|
38
|
-
* @param params.
|
|
49
|
+
* @param params.placeholder Placeholder component to render during startup.
|
|
50
|
+
* @param params.fallback Fallback component to render if an error occurs during startup.
|
|
51
|
+
* @param params.cacheEnabled Whether to cache enabled plugins in localStorage.
|
|
39
52
|
*/
|
|
40
|
-
export const createApp = ({
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
export const createApp = ({
|
|
54
|
+
pluginManager,
|
|
55
|
+
pluginLoader: _pluginLoader,
|
|
56
|
+
plugins = [],
|
|
57
|
+
core = plugins.map(({ meta }) => meta.id),
|
|
58
|
+
defaults = [],
|
|
59
|
+
placeholder = null,
|
|
60
|
+
fallback = DefaultFallback,
|
|
61
|
+
cacheEnabled = false,
|
|
62
|
+
}: CreateAppOptions) => {
|
|
63
|
+
// TODO(wittjosiah): Provide a custom plugin loader which supports loading via url.
|
|
64
|
+
const pluginLoader =
|
|
65
|
+
_pluginLoader ??
|
|
66
|
+
((id: string) => {
|
|
67
|
+
const plugin = plugins.find((plugin) => plugin.meta.id === id);
|
|
68
|
+
invariant(plugin, `Plugin not found: ${id}`);
|
|
69
|
+
return plugin;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const state = create({ ready: false, error: null });
|
|
73
|
+
const cached: string[] = JSON.parse(localStorage.getItem(ENABLED_KEY) ?? '[]');
|
|
74
|
+
const enabled = cacheEnabled && cached.length > 0 ? cached : defaults;
|
|
75
|
+
const manager = pluginManager ?? new PluginManager({ pluginLoader, plugins, core, enabled });
|
|
76
|
+
|
|
77
|
+
manager.activation.on(({ event, state: _state, error }) => {
|
|
78
|
+
if (event === Events.Startup.id) {
|
|
79
|
+
state.ready = _state === 'activated';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (error && !state.ready && !state.error) {
|
|
83
|
+
state.error = error;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
effect(() => {
|
|
88
|
+
cacheEnabled && localStorage.setItem(ENABLED_KEY, JSON.stringify(manager.enabled));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
manager.context.contributeCapability({
|
|
92
|
+
interface: Capabilities.PluginManager,
|
|
93
|
+
implementation: manager,
|
|
94
|
+
module: 'dxos.org/app-framework/plugin-manager',
|
|
51
95
|
});
|
|
52
96
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
invariant(Root);
|
|
97
|
+
setupDevtools(manager);
|
|
98
|
+
|
|
99
|
+
void manager.activate(Events.Startup);
|
|
57
100
|
|
|
58
101
|
return () => (
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
</
|
|
102
|
+
<ErrorBoundary fallback={fallback}>
|
|
103
|
+
<App placeholder={placeholder} manager={manager} state={state} />
|
|
104
|
+
</ErrorBoundary>
|
|
62
105
|
);
|
|
63
106
|
};
|
|
107
|
+
|
|
108
|
+
type AppProps = Required<Pick<CreateAppOptions, 'placeholder'>> & {
|
|
109
|
+
manager: PluginManager;
|
|
110
|
+
state: { ready: boolean; error: unknown };
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const App = ({ placeholder, manager, state }: AppProps) => {
|
|
114
|
+
if (state.error) {
|
|
115
|
+
// This trigger the error boundary to provide UI feedback for the startup error.
|
|
116
|
+
throw state.error;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// TODO(wittjosiah): Consider using Suspense instead?
|
|
120
|
+
if (!state.ready) {
|
|
121
|
+
return <>{placeholder}</>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const reactContexts = manager.context.requestCapabilities(Capabilities.ReactContext);
|
|
125
|
+
const reactRoots = manager.context.requestCapabilities(Capabilities.ReactRoot);
|
|
126
|
+
|
|
127
|
+
const ComposedContext = composeContexts(reactContexts);
|
|
128
|
+
return (
|
|
129
|
+
<PluginManagerProvider value={manager}>
|
|
130
|
+
<ComposedContext>
|
|
131
|
+
{reactRoots.map(({ id, root: Component }) => (
|
|
132
|
+
<Component key={id} />
|
|
133
|
+
))}
|
|
134
|
+
</ComposedContext>
|
|
135
|
+
</PluginManagerProvider>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Default fallback does not use tailwind or theme.
|
|
140
|
+
const DefaultFallback = ({ error }: { error: Error }) => {
|
|
141
|
+
return (
|
|
142
|
+
<div style={{ padding: '1rem' }}>
|
|
143
|
+
{/* TODO(wittjosiah): Link to docs for replacing default. */}
|
|
144
|
+
<h1 style={{ fontSize: '1.2rem', fontWeight: 700, margin: '0.5rem 0' }}>{error.message}</h1>
|
|
145
|
+
<pre>{error.stack}</pre>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const composeContexts = (contexts: Capabilities.ReactContext[]) => {
|
|
151
|
+
if (contexts.length === 0) {
|
|
152
|
+
return ({ children }: PropsWithChildren) => <>{children}</>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return topologicalSort(contexts)
|
|
156
|
+
.map(({ context }) => context)
|
|
157
|
+
.reduce((Acc, Next) => ({ children }) => (
|
|
158
|
+
<Acc>
|
|
159
|
+
<Next>{children}</Next>
|
|
160
|
+
</Acc>
|
|
161
|
+
));
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const setupDevtools = (manager: PluginManager) => {
|
|
165
|
+
(globalThis as any).composer ??= {};
|
|
166
|
+
(globalThis as any).composer.manager = manager;
|
|
167
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type FC, type PropsWithChildren } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type GraphBuilder } from '@dxos/app-graph';
|
|
8
|
+
import { type Space } from '@dxos/client-protocol';
|
|
9
|
+
import { type S } from '@dxos/echo-schema';
|
|
10
|
+
import { type RootSettingsStore } from '@dxos/local-storage';
|
|
11
|
+
import { type DeepReadonly } from '@dxos/util';
|
|
12
|
+
|
|
13
|
+
import { type FileInfo } from './file';
|
|
14
|
+
import { type NodeSerializer } from './graph';
|
|
15
|
+
import { type Layout } from './layout';
|
|
16
|
+
import { type LayoutParts } from './navigation';
|
|
17
|
+
import { type SurfaceDefinition } from './surface';
|
|
18
|
+
import { type Resource } from './translations';
|
|
19
|
+
import { defineCapability, type PluginManager } from '../core';
|
|
20
|
+
import { type AnyIntentResolver, type IntentContext } from '../plugin-intent';
|
|
21
|
+
|
|
22
|
+
export namespace Capabilities {
|
|
23
|
+
export const PluginManager = defineCapability<PluginManager>('dxos.org/app-framework/capability/plugin-manager');
|
|
24
|
+
|
|
25
|
+
export const Null = defineCapability<null>('dxos.org/app-framework/capability/null');
|
|
26
|
+
|
|
27
|
+
export type ReactContext = Readonly<{ id: string; dependsOn?: string[]; context: FC<PropsWithChildren> }>;
|
|
28
|
+
export const ReactContext = defineCapability<ReactContext>('dxos.org/app-framework/capability/react-context');
|
|
29
|
+
|
|
30
|
+
export type ReactRoot = Readonly<{ id: string; root: FC<PropsWithChildren> }>;
|
|
31
|
+
export const ReactRoot = defineCapability<ReactRoot>('dxos.org/app-framework/capability/react-root');
|
|
32
|
+
|
|
33
|
+
export type ReactSurface = SurfaceDefinition | readonly SurfaceDefinition[];
|
|
34
|
+
export const ReactSurface = defineCapability<ReactSurface>('dxos.org/app-framework/common/react-surface');
|
|
35
|
+
|
|
36
|
+
export type IntentResolver = AnyIntentResolver | readonly AnyIntentResolver[];
|
|
37
|
+
export const IntentResolver = defineCapability<IntentResolver>('dxos.org/app-framework/capability/intent-resolver');
|
|
38
|
+
|
|
39
|
+
export const IntentDispatcher = defineCapability<IntentContext>(
|
|
40
|
+
'dxos.org/app-framework/capability/intent-dispatcher',
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export const Layout = defineCapability<Readonly<Layout>>('dxos.org/app-framework/capability/layout');
|
|
44
|
+
export const MutableLayout = defineCapability<Layout>('dxos.org/app-framework/capability/layout');
|
|
45
|
+
|
|
46
|
+
export type MutableLocation = { active: LayoutParts; closed: string[] };
|
|
47
|
+
export type Location = DeepReadonly<MutableLocation>;
|
|
48
|
+
export const Location = defineCapability<Location>('dxos.org/app-framework/capability/location');
|
|
49
|
+
export const MutableLocation = defineCapability<MutableLocation>('dxos.org/app-framework/capability/location');
|
|
50
|
+
|
|
51
|
+
export const Translations = defineCapability<Readonly<Resource[]>>('dxos.org/app-framework/capability/translations');
|
|
52
|
+
|
|
53
|
+
export const AppGraph = defineCapability<Readonly<Pick<GraphBuilder, 'graph' | 'explore'>>>(
|
|
54
|
+
'dxos.org/app-framework/capability/app-graph',
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
export const AppGraphBuilder = defineCapability<Parameters<GraphBuilder['addExtension']>[0]>(
|
|
58
|
+
'dxos.org/app-framework/capability/app-graph-builder',
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export const AppGraphSerializer = defineCapability<NodeSerializer[]>(
|
|
62
|
+
'dxos.org/app-framework/capability/app-graph-serializer',
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
export const SettingsStore = defineCapability<RootSettingsStore>('dxos.org/app-framework/capability/settings-store');
|
|
66
|
+
|
|
67
|
+
// TODO(wittjosiah): The generics caused type inference issues for schemas when contributing settings.
|
|
68
|
+
// export type Settings = Parameters<RootSettingsStore['createStore']>[0];
|
|
69
|
+
// export type Settings<T extends SettingsValue = SettingsValue> = SettingsProps<T>;
|
|
70
|
+
export type Settings = {
|
|
71
|
+
schema: S.Schema.All;
|
|
72
|
+
prefix: string;
|
|
73
|
+
value?: Record<string, any>;
|
|
74
|
+
};
|
|
75
|
+
export const Settings = defineCapability<Settings>('dxos.org/app-framework/capability/settings');
|
|
76
|
+
|
|
77
|
+
export type Metadata = Readonly<{ id: string; metadata: Record<string, any> }>;
|
|
78
|
+
export const Metadata = defineCapability<Metadata>('dxos.org/app-framework/capability/metadata');
|
|
79
|
+
|
|
80
|
+
export type FileUploader = (file: File, space: Space) => Promise<FileInfo | undefined>;
|
|
81
|
+
export const FileUploader = defineCapability<FileUploader>('dxos.org/app-framework/capability/file-uploader');
|
|
82
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capabilities } from './capabilities';
|
|
6
|
+
import { defineEvent } from '../core';
|
|
7
|
+
|
|
8
|
+
export namespace Events {
|
|
9
|
+
/**
|
|
10
|
+
* Fired when the app is started.
|
|
11
|
+
*/
|
|
12
|
+
export const Startup = defineEvent('dxos.org/app-framework/event/startup');
|
|
13
|
+
|
|
14
|
+
//
|
|
15
|
+
// Dependent Events
|
|
16
|
+
//
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fired before the intent dispatcher is activated.
|
|
20
|
+
*/
|
|
21
|
+
export const SetupIntents = defineEvent('dxos.org/app-framework/event/setup-intents');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Fired before the settings store is activated.
|
|
25
|
+
*/
|
|
26
|
+
export const SetupSettings = defineEvent('dxos.org/app-framework/event/setup-settings');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Fired before the graph is created.
|
|
30
|
+
*/
|
|
31
|
+
export const SetupAppGraph = defineEvent('dxos.org/app-framework/event/setup-graph');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Fired before the translations provider is created.
|
|
35
|
+
*/
|
|
36
|
+
export const SetupTranslations = defineEvent('dxos.org/app-framework/event/setup-translations');
|
|
37
|
+
|
|
38
|
+
//
|
|
39
|
+
// Triggered Events
|
|
40
|
+
//
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Fired after the intent dispatcher is ready.
|
|
44
|
+
*/
|
|
45
|
+
export const DispatcherReady = defineEvent('dxos.org/app-framework/event/dispatcher-ready');
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Fired after the settings store is ready.
|
|
49
|
+
*/
|
|
50
|
+
export const SettingsReady = defineEvent('dxos.org/app-framework/event/settings-ready');
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fired when the graph is ready.
|
|
54
|
+
*/
|
|
55
|
+
export const AppGraphReady = defineEvent('dxos.org/app-framework/event/graph-ready');
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fired when plugin state is ready.
|
|
59
|
+
*/
|
|
60
|
+
export const createStateEvent = (specifier: string) => defineEvent('dxos.org/app-framework/event/state', specifier);
|
|
61
|
+
export const LayoutReady = createStateEvent(Capabilities.Layout.identifier);
|
|
62
|
+
export const LocationReady = createStateEvent(Capabilities.Location.identifier);
|
|
63
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { S } from '@dxos/echo-schema';
|
|
6
|
+
|
|
7
|
+
// TODO(burdon): See Accept attribute (uses MIME types).
|
|
8
|
+
// E.g., 'image/*': ['.jpg', '.jpeg', '.png', '.gif'],
|
|
9
|
+
export const defaultFileTypes = {
|
|
10
|
+
images: ['png', 'jpg', 'jpeg', 'gif'],
|
|
11
|
+
media: ['mp3', 'mp4', 'mov', 'avi'],
|
|
12
|
+
text: ['pdf', 'txt', 'md'],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const FileInfoSchema = S.Struct({
|
|
16
|
+
name: S.String,
|
|
17
|
+
type: S.String,
|
|
18
|
+
url: S.optional(S.String),
|
|
19
|
+
cid: S.optional(S.String), // TODO(burdon): Meta key? Or other common properties with other file management system? (e.g., WNFS).
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export type FileInfo = S.Schema.Type<typeof FileInfoSchema>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { Node } from '@dxos/app-graph';
|
|
6
|
+
import { type MaybePromise } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
// TODO(wittjosiah): Factor out.
|
|
9
|
+
export type SerializedNode = {
|
|
10
|
+
name: string;
|
|
11
|
+
data: string;
|
|
12
|
+
type?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// TODO(wittjosiah): Factor out.
|
|
16
|
+
export type NodeSerializer<T = any> = {
|
|
17
|
+
inputType: string;
|
|
18
|
+
outputType: string;
|
|
19
|
+
disposition?: 'hoist' | 'fallback';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Takes a node and serializes it into a format that can be stored.
|
|
23
|
+
*/
|
|
24
|
+
serialize: (node: Node<T>) => MaybePromise<SerializedNode>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Takes a serialized node and deserializes it into the application.
|
|
28
|
+
*/
|
|
29
|
+
deserialize: (data: SerializedNode, ancestors: unknown[]) => MaybePromise<T>;
|
|
30
|
+
};
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './capabilities';
|
|
6
|
+
export * from './events';
|
|
5
7
|
export * from './file';
|
|
6
8
|
export * from './graph';
|
|
7
9
|
export * from './layout';
|
|
8
|
-
export * from './metadata';
|
|
9
10
|
export * from './navigation';
|
|
11
|
+
export * from './surface';
|
|
10
12
|
export * from './translations';
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import { S } from '@dxos/echo-schema';
|
|
6
6
|
|
|
7
|
-
import { type Plugin } from '../plugin-host';
|
|
8
7
|
import { Label } from '../plugin-intent';
|
|
9
8
|
|
|
10
9
|
//
|
|
@@ -76,21 +75,6 @@ export const Layout = S.mutable(
|
|
|
76
75
|
|
|
77
76
|
export type Layout = S.Schema.Type<typeof Layout>;
|
|
78
77
|
|
|
79
|
-
/**
|
|
80
|
-
* Provides for a plugin that can manage the app layout.
|
|
81
|
-
*/
|
|
82
|
-
export type LayoutProvides = {
|
|
83
|
-
layout: Readonly<Layout>;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Type guard for layout plugins.
|
|
88
|
-
*/
|
|
89
|
-
export const parseLayoutPlugin = (plugin: Plugin) => {
|
|
90
|
-
const success = S.is(Layout)((plugin.provides as any).layout);
|
|
91
|
-
return success ? (plugin as Plugin<LayoutProvides>) : undefined;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
78
|
//
|
|
95
79
|
// Intents
|
|
96
80
|
//
|
|
@@ -6,8 +6,6 @@ import { Schema as S } from '@effect/schema';
|
|
|
6
6
|
|
|
7
7
|
import { pick } from '@dxos/util';
|
|
8
8
|
|
|
9
|
-
import { type Plugin } from '../plugin-host';
|
|
10
|
-
|
|
11
9
|
// NOTE(thure): These are chosen from RFC 1738’s `safe` characters: http://www.faqs.org/rfcs/rfc1738.html
|
|
12
10
|
export const SLUG_LIST_SEPARATOR = '+';
|
|
13
11
|
export const SLUG_ENTRY_SEPARATOR = '_';
|
|
@@ -84,19 +82,6 @@ export const isLayoutAdjustment = (value: unknown): value is LayoutAdjustment =>
|
|
|
84
82
|
return S.is(LayoutAdjustmentSchema)(value);
|
|
85
83
|
};
|
|
86
84
|
|
|
87
|
-
export const parseNavigationPlugin = (plugin: Plugin): Plugin<LocationProvides> | undefined => {
|
|
88
|
-
const location = (plugin.provides as any)?.location;
|
|
89
|
-
if (!location) {
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (S.is(LocationProvidesSchema)({ location })) {
|
|
94
|
-
return plugin as Plugin<LocationProvides>;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return undefined;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
85
|
/**
|
|
101
86
|
* Utilities.
|
|
102
87
|
*/
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type JSX, type ForwardedRef, type PropsWithChildren, type ReactNode } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import { type GuardedType, type MakeOptional } from '@dxos/util';
|
|
7
|
+
import { type GuardedType, type MakeOptional, type Disposition } from '@dxos/util';
|
|
9
8
|
|
|
10
|
-
import { type ErrorBoundary } from '
|
|
9
|
+
import { type ErrorBoundary } from '../react';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* SurfaceProps are the props that are passed to the Surface component.
|
|
@@ -68,45 +67,15 @@ export type SurfaceComponent<T extends Record<string, any> = Record<string, unkn
|
|
|
68
67
|
forwardedRef: ForwardedRef<HTMLElement>,
|
|
69
68
|
) => JSX.Element | null;
|
|
70
69
|
|
|
71
|
-
/**
|
|
72
|
-
* Determines the priority of the surface when multiple components are resolved.
|
|
73
|
-
*
|
|
74
|
-
* - `static` - The component is rendered in the order it was resolved.
|
|
75
|
-
* - `hoist` - The component is rendered before `static` components.
|
|
76
|
-
* - `fallback` - The component is rendered after `static` components.
|
|
77
|
-
*/
|
|
78
|
-
export type SurfaceDisposition = 'static' | 'hoist' | 'fallback';
|
|
79
|
-
|
|
80
70
|
/**
|
|
81
71
|
* Definition of when a SurfaceComponent should be rendered.
|
|
82
72
|
*/
|
|
83
|
-
export type SurfaceDefinition<T extends Record<string, any> = any> = {
|
|
73
|
+
export type SurfaceDefinition<T extends Record<string, any> = any> = Readonly<{
|
|
84
74
|
id: string;
|
|
85
75
|
role: string | string[];
|
|
86
|
-
disposition?:
|
|
76
|
+
disposition?: Disposition;
|
|
87
77
|
filter?: (data: Record<string, unknown>) => data is T;
|
|
88
78
|
component: SurfaceComponent<GuardedType<SurfaceDefinition<T>['filter']>>;
|
|
89
|
-
}
|
|
79
|
+
}>;
|
|
90
80
|
|
|
91
81
|
export const createSurface = <T extends Record<string, any> = any>(definition: SurfaceDefinition<T>) => definition;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Surface debug info.
|
|
95
|
-
* NOTE: Short-term measure to track perf issues.
|
|
96
|
-
*/
|
|
97
|
-
export type DebugInfo = {
|
|
98
|
-
id: string;
|
|
99
|
-
created: number;
|
|
100
|
-
renderCount: number;
|
|
101
|
-
} & Pick<SurfaceProps, 'role'>;
|
|
102
|
-
|
|
103
|
-
export type SurfaceContextValue = {
|
|
104
|
-
surfaces: Record<string, SurfaceDefinition>;
|
|
105
|
-
debugInfo?: Map<string, DebugInfo>;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const SurfaceContext = createContext<SurfaceContextValue | undefined>(undefined);
|
|
109
|
-
|
|
110
|
-
export const useSurfaceRoot = () => useContext(SurfaceContext) ?? raise(new Error('Missing SurfaceContext'));
|
|
111
|
-
|
|
112
|
-
export const SurfaceProvider = SurfaceContext.Provider;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
// TODO(burdon): Replace zod with effect.
|
|
8
|
+
export const ResourceKey = z.union([z.string(), z.record(z.any())]);
|
|
9
|
+
export type ResourceKey = z.infer<typeof ResourceKey>;
|
|
10
|
+
|
|
11
|
+
export const ResourceLanguage = z.record(ResourceKey);
|
|
12
|
+
export type ResourceLanguage = z.infer<typeof ResourceLanguage>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A resource is a collection of translations for a language.
|
|
16
|
+
*/
|
|
17
|
+
export const Resource = z.record(ResourceLanguage);
|
|
18
|
+
export type Resource = z.infer<typeof Resource>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { updateCounter } from '@dxos/echo-schema/testing';
|
|
8
|
+
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
9
|
+
|
|
10
|
+
import { defineCapability, PluginsContext } from './capabilities';
|
|
11
|
+
|
|
12
|
+
registerSignalsRuntime();
|
|
13
|
+
|
|
14
|
+
const defaultOptions = {
|
|
15
|
+
activate: () => Promise.resolve(false),
|
|
16
|
+
reset: () => Promise.resolve(false),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe('PluginsContext', () => {
|
|
20
|
+
it('should return empty array if no capabilities are contributed', () => {
|
|
21
|
+
const context = new PluginsContext(defaultOptions);
|
|
22
|
+
const interfaceDef = defineCapability<{ example: string }>('@dxos/app-framework/test/example');
|
|
23
|
+
expect(context.requestCapabilities(interfaceDef)).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should be able to contribute and request capabilities', () => {
|
|
27
|
+
const context = new PluginsContext(defaultOptions);
|
|
28
|
+
const interfaceDef = defineCapability<{ example: string }>('@dxos/app-framework/test/example');
|
|
29
|
+
const implementation = { example: 'identifier' };
|
|
30
|
+
context.contributeCapability({ interface: interfaceDef, implementation, module: 'test' });
|
|
31
|
+
expect(context.requestCapabilities(interfaceDef)).toEqual([implementation]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should be able to remove capabilities', () => {
|
|
35
|
+
const context = new PluginsContext(defaultOptions);
|
|
36
|
+
const interfaceDef = defineCapability<{ example: string }>('@dxos/app-framework/test/example');
|
|
37
|
+
const implementation = { example: 'identifier' };
|
|
38
|
+
context.contributeCapability({ interface: interfaceDef, implementation, module: 'test' });
|
|
39
|
+
expect(context.requestCapabilities(interfaceDef)).toEqual([implementation]);
|
|
40
|
+
context.removeCapability(interfaceDef, implementation);
|
|
41
|
+
expect(context.requestCapabilities(interfaceDef)).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should be able to contribute and request multiple implementations', () => {
|
|
45
|
+
const context = new PluginsContext(defaultOptions);
|
|
46
|
+
const interfaceDef = defineCapability<{ example: string }>('@dxos/app-framework/test/example');
|
|
47
|
+
const implementation1 = { example: 'first' };
|
|
48
|
+
const implementation2 = { example: 'second' };
|
|
49
|
+
context.contributeCapability({ interface: interfaceDef, implementation: implementation1, module: 'test' });
|
|
50
|
+
context.contributeCapability({ interface: interfaceDef, implementation: implementation2, module: 'test' });
|
|
51
|
+
expect(context.requestCapabilities(interfaceDef)).toEqual([implementation1, implementation2]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should be able to request multiple capabilities', () => {
|
|
55
|
+
const context = new PluginsContext(defaultOptions);
|
|
56
|
+
const interfaceDef1 = defineCapability<{ one: number }>('@dxos/app-framework/test/one');
|
|
57
|
+
const interfaceDef2 = defineCapability<{ two: number }>('@dxos/app-framework/test/two');
|
|
58
|
+
const implementation1 = { one: 1 };
|
|
59
|
+
const implementation2 = { two: 2 };
|
|
60
|
+
context.contributeCapability({ interface: interfaceDef1, implementation: implementation1, module: 'test' });
|
|
61
|
+
context.contributeCapability({ interface: interfaceDef2, implementation: implementation2, module: 'test' });
|
|
62
|
+
expect(context.requestCapabilities(interfaceDef1)).toEqual([implementation1]);
|
|
63
|
+
expect(context.requestCapabilities(interfaceDef2)).toEqual([implementation2]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should be reactive', () => {
|
|
67
|
+
const context = new PluginsContext(defaultOptions);
|
|
68
|
+
const interfaceDef = defineCapability<{ example: string }>('@dxos/app-framework/test/example');
|
|
69
|
+
|
|
70
|
+
using updates = updateCounter(() => {
|
|
71
|
+
context.requestCapabilities(interfaceDef);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(updates.count).toEqual(0);
|
|
75
|
+
|
|
76
|
+
const implementation = { example: 'identifier' };
|
|
77
|
+
context.contributeCapability({ interface: interfaceDef, implementation, module: 'test' });
|
|
78
|
+
expect(updates.count).toEqual(1);
|
|
79
|
+
|
|
80
|
+
context.removeCapability(interfaceDef, implementation);
|
|
81
|
+
expect(updates.count).toEqual(2);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should not be reactive to changes within the implementation', () => {
|
|
85
|
+
const context = new PluginsContext(defaultOptions);
|
|
86
|
+
const interfaceDef = defineCapability<{ example: string }>('@dxos/app-framework/test/example');
|
|
87
|
+
|
|
88
|
+
using updates = updateCounter(() => {
|
|
89
|
+
context.requestCapabilities(interfaceDef);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(updates.count).toEqual(0);
|
|
93
|
+
|
|
94
|
+
const implementation = { example: 'identifier' };
|
|
95
|
+
context.contributeCapability({ interface: interfaceDef, implementation, module: 'test' });
|
|
96
|
+
expect(updates.count).toEqual(1);
|
|
97
|
+
|
|
98
|
+
implementation.example = 'updated';
|
|
99
|
+
expect(updates.count).toEqual(1);
|
|
100
|
+
|
|
101
|
+
const capabilities = context.requestCapabilities(interfaceDef);
|
|
102
|
+
expect(capabilities).toEqual([implementation]);
|
|
103
|
+
expect(capabilities[0].example).toEqual('updated');
|
|
104
|
+
expect(updates.count).toEqual(1);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should be able to wait for a capability', async () => {
|
|
108
|
+
const context = new PluginsContext(defaultOptions);
|
|
109
|
+
const interfaceDef = defineCapability<{ example: string }>('@dxos/app-framework/test/example');
|
|
110
|
+
const implementation = { example: 'identifier' };
|
|
111
|
+
const promise = context.waitForCapability(interfaceDef);
|
|
112
|
+
context.contributeCapability({ interface: interfaceDef, implementation, module: 'test' });
|
|
113
|
+
const capability = await promise;
|
|
114
|
+
expect(capability).toEqual(implementation);
|
|
115
|
+
});
|
|
116
|
+
});
|