@dxos/app-framework 0.7.4 → 0.7.5-main.499c70c

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 (128) hide show
  1. package/dist/lib/browser/chunk-QG25ZU2N.mjs +320 -0
  2. package/dist/lib/browser/chunk-QG25ZU2N.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-SPDTXTOV.mjs +163 -0
  4. package/dist/lib/browser/chunk-SPDTXTOV.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-653Y45CL.mjs → chunk-WBOXEHBE.mjs} +12 -2
  6. package/dist/lib/browser/chunk-WBOXEHBE.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +224 -109
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/{plugin-intent-LU4KL2RO.mjs → plugin-intent-T7Y3MJ5C.mjs} +14 -4
  11. package/dist/lib/browser/{plugin-settings-OM3G2QFY.mjs → plugin-settings-5U2L2NRU.mjs} +6 -2
  12. package/dist/lib/browser/{plugin-surface-LECZMKSQ.mjs → plugin-surface-OKPF3EQI.mjs} +4 -4
  13. package/dist/lib/node/{chunk-SOVLKUWI.cjs → chunk-BW3RNEVI.cjs} +51 -102
  14. package/dist/lib/node/chunk-BW3RNEVI.cjs.map +7 -0
  15. package/dist/lib/node/{chunk-JZ2JVKRY.cjs → chunk-FCMHRU3M.cjs} +17 -5
  16. package/dist/lib/node/chunk-FCMHRU3M.cjs.map +7 -0
  17. package/dist/lib/node/chunk-VWHAALIN.cjs +344 -0
  18. package/dist/lib/node/chunk-VWHAALIN.cjs.map +7 -0
  19. package/dist/lib/node/index.cjs +232 -114
  20. package/dist/lib/node/index.cjs.map +4 -4
  21. package/dist/lib/node/meta.json +1 -1
  22. package/dist/lib/node/plugin-intent-F3TQZIUR.cjs +53 -0
  23. package/dist/lib/node/plugin-intent-F3TQZIUR.cjs.map +7 -0
  24. package/dist/lib/node/{plugin-settings-OZ6IKAE5.cjs → plugin-settings-W6UHMH5M.cjs} +12 -8
  25. package/dist/lib/node/plugin-settings-W6UHMH5M.cjs.map +7 -0
  26. package/dist/lib/node/{plugin-surface-YWDRXQTD.cjs → plugin-surface-CCSIONYW.cjs} +15 -15
  27. package/dist/lib/node/plugin-surface-CCSIONYW.cjs.map +7 -0
  28. package/dist/lib/node-esm/{chunk-YNU7FTGJ.mjs → chunk-3T5UIJY3.mjs} +12 -2
  29. package/dist/lib/node-esm/chunk-3T5UIJY3.mjs.map +7 -0
  30. package/dist/lib/node-esm/chunk-4GX7V5ZE.mjs +164 -0
  31. package/dist/lib/node-esm/chunk-4GX7V5ZE.mjs.map +7 -0
  32. package/dist/lib/node-esm/chunk-CFOUYXQ6.mjs +321 -0
  33. package/dist/lib/node-esm/chunk-CFOUYXQ6.mjs.map +7 -0
  34. package/dist/lib/node-esm/index.mjs +224 -109
  35. package/dist/lib/node-esm/index.mjs.map +4 -4
  36. package/dist/lib/node-esm/meta.json +1 -1
  37. package/dist/lib/node-esm/{plugin-intent-V7ER24Y6.mjs → plugin-intent-W2HQC6LC.mjs} +14 -4
  38. package/dist/lib/node-esm/{plugin-settings-37UVWF2V.mjs → plugin-settings-H5RHNFVC.mjs} +6 -2
  39. package/dist/lib/node-esm/{plugin-surface-TEU42XQN.mjs → plugin-surface-V3YET3UL.mjs} +4 -4
  40. package/dist/types/src/plugins/common/layout.d.ts +145 -171
  41. package/dist/types/src/plugins/common/layout.d.ts.map +1 -1
  42. package/dist/types/src/plugins/common/navigation.d.ts +77 -30
  43. package/dist/types/src/plugins/common/navigation.d.ts.map +1 -1
  44. package/dist/types/src/plugins/plugin-host/HostPlugin.d.ts +2 -7
  45. package/dist/types/src/plugins/plugin-host/HostPlugin.d.ts.map +1 -1
  46. package/dist/types/src/plugins/plugin-host/index.d.ts +2 -0
  47. package/dist/types/src/plugins/plugin-host/index.d.ts.map +1 -1
  48. package/dist/types/src/plugins/plugin-host/plugin.d.ts +7 -1
  49. package/dist/types/src/plugins/plugin-host/plugin.d.ts.map +1 -1
  50. package/dist/types/src/plugins/plugin-intent/IntentContext.d.ts +7 -20
  51. package/dist/types/src/plugins/plugin-intent/IntentContext.d.ts.map +1 -1
  52. package/dist/types/src/plugins/plugin-intent/IntentPlugin.d.ts.map +1 -1
  53. package/dist/types/src/plugins/plugin-intent/index.d.ts +1 -0
  54. package/dist/types/src/plugins/plugin-intent/index.d.ts.map +1 -1
  55. package/dist/types/src/plugins/plugin-intent/intent-dispatcher.d.ts +107 -0
  56. package/dist/types/src/plugins/plugin-intent/intent-dispatcher.d.ts.map +1 -0
  57. package/dist/types/src/plugins/plugin-intent/intent-dispatcher.test.d.ts +2 -0
  58. package/dist/types/src/plugins/plugin-intent/intent-dispatcher.test.d.ts.map +1 -0
  59. package/dist/types/src/plugins/plugin-intent/intent.d.ts +65 -58
  60. package/dist/types/src/plugins/plugin-intent/intent.d.ts.map +1 -1
  61. package/dist/types/src/plugins/plugin-intent/meta.d.ts +1 -0
  62. package/dist/types/src/plugins/plugin-intent/meta.d.ts.map +1 -1
  63. package/dist/types/src/plugins/plugin-intent/provides.d.ts +6 -10
  64. package/dist/types/src/plugins/plugin-intent/provides.d.ts.map +1 -1
  65. package/dist/types/src/plugins/plugin-settings/provides.d.ts +15 -2
  66. package/dist/types/src/plugins/plugin-settings/provides.d.ts.map +1 -1
  67. package/dist/types/src/plugins/plugin-surface/Surface.d.ts +2 -57
  68. package/dist/types/src/plugins/plugin-surface/Surface.d.ts.map +1 -1
  69. package/dist/types/src/plugins/plugin-surface/SurfaceContext.d.ts +85 -0
  70. package/dist/types/src/plugins/plugin-surface/SurfaceContext.d.ts.map +1 -0
  71. package/dist/types/src/plugins/plugin-surface/SurfacePlugin.d.ts.map +1 -1
  72. package/dist/types/src/plugins/plugin-surface/index.d.ts +1 -1
  73. package/dist/types/src/plugins/plugin-surface/index.d.ts.map +1 -1
  74. package/dist/types/src/plugins/plugin-surface/provides.d.ts +5 -4
  75. package/dist/types/src/plugins/plugin-surface/provides.d.ts.map +1 -1
  76. package/dist/types/tsconfig.tsbuildinfo +1 -0
  77. package/package.json +14 -12
  78. package/src/plugins/common/layout.ts +125 -107
  79. package/src/plugins/common/navigation.ts +59 -30
  80. package/src/plugins/plugin-host/HostPlugin.tsx +2 -10
  81. package/src/plugins/plugin-host/PluginContainer.tsx +1 -1
  82. package/src/plugins/plugin-host/index.ts +4 -0
  83. package/src/plugins/plugin-host/plugin.ts +8 -1
  84. package/src/plugins/plugin-intent/IntentContext.tsx +13 -36
  85. package/src/plugins/plugin-intent/IntentPlugin.tsx +44 -120
  86. package/src/plugins/plugin-intent/index.ts +1 -0
  87. package/src/plugins/plugin-intent/intent-dispatcher.test.ts +279 -0
  88. package/src/plugins/plugin-intent/intent-dispatcher.ts +285 -0
  89. package/src/plugins/plugin-intent/intent.ts +126 -65
  90. package/src/plugins/plugin-intent/meta.ts +3 -1
  91. package/src/plugins/plugin-intent/provides.ts +8 -20
  92. package/src/plugins/plugin-settings/provides.ts +10 -5
  93. package/src/plugins/plugin-surface/Surface.tsx +25 -158
  94. package/src/plugins/plugin-surface/SurfaceContext.ts +112 -0
  95. package/src/plugins/plugin-surface/SurfacePlugin.tsx +19 -7
  96. package/src/plugins/plugin-surface/index.ts +1 -1
  97. package/src/plugins/plugin-surface/provides.ts +8 -7
  98. package/tsconfig.json +38 -1
  99. package/dist/lib/browser/chunk-653Y45CL.mjs.map +0 -7
  100. package/dist/lib/browser/chunk-FRXJ25VI.mjs +0 -214
  101. package/dist/lib/browser/chunk-FRXJ25VI.mjs.map +0 -7
  102. package/dist/lib/browser/chunk-YXM35XRE.mjs +0 -213
  103. package/dist/lib/browser/chunk-YXM35XRE.mjs.map +0 -7
  104. package/dist/lib/node/chunk-JZ2JVKRY.cjs.map +0 -7
  105. package/dist/lib/node/chunk-QSVP5HOW.cjs +0 -238
  106. package/dist/lib/node/chunk-QSVP5HOW.cjs.map +0 -7
  107. package/dist/lib/node/chunk-SOVLKUWI.cjs.map +0 -7
  108. package/dist/lib/node/plugin-intent-FVFR2LKB.cjs +0 -43
  109. package/dist/lib/node/plugin-intent-FVFR2LKB.cjs.map +0 -7
  110. package/dist/lib/node/plugin-settings-OZ6IKAE5.cjs.map +0 -7
  111. package/dist/lib/node/plugin-surface-YWDRXQTD.cjs.map +0 -7
  112. package/dist/lib/node-esm/chunk-2R4GVK7O.mjs +0 -215
  113. package/dist/lib/node-esm/chunk-2R4GVK7O.mjs.map +0 -7
  114. package/dist/lib/node-esm/chunk-YFMFQBB4.mjs +0 -214
  115. package/dist/lib/node-esm/chunk-YFMFQBB4.mjs.map +0 -7
  116. package/dist/lib/node-esm/chunk-YNU7FTGJ.mjs.map +0 -7
  117. package/dist/types/src/plugins/plugin-intent/helpers.d.ts +0 -6
  118. package/dist/types/src/plugins/plugin-intent/helpers.d.ts.map +0 -1
  119. package/dist/types/src/plugins/plugin-surface/SurfaceRootContext.d.ts +0 -39
  120. package/dist/types/src/plugins/plugin-surface/SurfaceRootContext.d.ts.map +0 -1
  121. package/src/plugins/plugin-intent/helpers.ts +0 -11
  122. package/src/plugins/plugin-surface/SurfaceRootContext.tsx +0 -60
  123. /package/dist/lib/browser/{plugin-intent-LU4KL2RO.mjs.map → plugin-intent-T7Y3MJ5C.mjs.map} +0 -0
  124. /package/dist/lib/browser/{plugin-settings-OM3G2QFY.mjs.map → plugin-settings-5U2L2NRU.mjs.map} +0 -0
  125. /package/dist/lib/browser/{plugin-surface-LECZMKSQ.mjs.map → plugin-surface-OKPF3EQI.mjs.map} +0 -0
  126. /package/dist/lib/node-esm/{plugin-intent-V7ER24Y6.mjs.map → plugin-intent-W2HQC6LC.mjs.map} +0 -0
  127. /package/dist/lib/node-esm/{plugin-settings-37UVWF2V.mjs.map → plugin-settings-H5RHNFVC.mjs.map} +0 -0
  128. /package/dist/lib/node-esm/{plugin-surface-TEU42XQN.mjs.map → plugin-surface-V3YET3UL.mjs.map} +0 -0
@@ -2,107 +2,27 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import React, {
6
- Fragment,
7
- type ForwardedRef,
8
- type PropsWithChildren,
9
- type ReactNode,
10
- Suspense,
11
- createContext,
12
- forwardRef,
13
- isValidElement,
14
- memo,
15
- useContext,
16
- useEffect,
17
- useState,
18
- } from 'react';
5
+ import React, { Suspense, forwardRef, memo, useEffect, useId, useMemo } from 'react';
19
6
 
20
- import { raise } from '@dxos/debug';
21
- import { log } from '@dxos/log';
7
+ import { useDefaultValue } from '@dxos/react-hooks';
22
8
 
23
9
  import { ErrorBoundary } from './ErrorBoundary';
24
- import { type SurfaceComponent, type SurfaceResult, useSurfaceRoot } from './SurfaceRootContext';
25
-
26
- /**
27
- * Direction determines how multiple components are laid out.
28
- */
29
- export type Direction = 'inline' | 'inline-reverse' | 'block' | 'block-reverse';
30
-
31
- /**
32
- * SurfaceProps are the props that are passed to the Surface component.
33
- */
34
- export type SurfaceProps = PropsWithChildren<{
35
- /**
36
- * Optional ID for debugging.
37
- */
38
- id?: string;
39
-
40
- /**
41
- * Role defines how the data should be rendered.
42
- */
43
- role?: string;
44
-
45
- /**
46
- * Names allow nested surfaces to be specified in the parent context, similar to a slot.
47
- * Defaults to the value of `role` if not specified.
48
- */
49
- name?: string;
50
-
51
- /**
52
- * The data to be rendered by the surface.
53
- */
54
- data?: Record<string, unknown>;
55
-
56
- /**
57
- * Configure nested surfaces (indexed by the surface's `name`).
58
- */
59
- surfaces?: Record<string, Pick<SurfaceProps, 'data' | 'surfaces'>>;
60
-
61
- /**
62
- * If specified, the Surface will be wrapped in an error boundary.
63
- * The fallback component will be rendered if an error occurs.
64
- */
65
- fallback?: ErrorBoundary['props']['fallback'];
66
-
67
- /**
68
- * If specified, the Surface will be wrapped in a suspense boundary.
69
- * The placeholder component will be rendered while the surface component is loading.
70
- */
71
- placeholder?: ReactNode;
72
-
73
- /**
74
- * If more than one component is resolved, the limit determines how many are rendered.
75
- */
76
- limit?: number | undefined;
77
-
78
- /**
79
- * If more than one component is resolved, the direction determines how they are laid out.
80
- * NOTE: This is not yet implemented.
81
- */
82
- direction?: Direction;
83
-
84
- /**
85
- * Additional props to pass to the component.
86
- * These props are not used by Surface itself but may be used by components which resolve the surface.
87
- */
88
- [key: string]: unknown;
89
- }>;
90
-
91
- let count = 0;
10
+ import { type SurfaceProps, useSurfaceRoot } from './SurfaceContext';
92
11
 
93
12
  /**
94
13
  * A surface is a named region of the screen that can be populated by plugins.
95
14
  */
96
15
  export const Surface = memo(
97
16
  forwardRef<HTMLElement, SurfaceProps>(
98
- ({ id: _id, role, name = role, fallback, placeholder, ...rest }, forwardedRef) => {
99
- const props = { role, name, fallback, ...rest };
100
- const { debugInfo } = useSurfaceRoot();
17
+ ({ id: _id, role, data: _data, limit, fallback, placeholder, ...rest }, forwardedRef) => {
18
+ const { surfaces, debugInfo } = useSurfaceRoot();
19
+ const data = useDefaultValue(_data, () => ({}));
101
20
 
102
21
  // Track debug info.
103
- const [id] = useState<string>(() => _id ?? `surface-${++count}`);
22
+ const reactId = useId();
23
+ const id = _id ?? reactId;
104
24
  useEffect(() => {
105
- debugInfo?.set(id, { id, created: Date.now(), name, role, renderCount: 0 });
25
+ debugInfo?.set(id, { id, created: Date.now(), role, renderCount: 0 });
106
26
  return () => {
107
27
  debugInfo?.delete(id);
108
28
  };
@@ -112,11 +32,23 @@ export const Surface = memo(
112
32
  debugInfo.get(id)!.renderCount++;
113
33
  }
114
34
 
115
- const context = useContext(SurfaceContext);
116
- const data = props.data ?? ((name && context?.surfaces?.[name]?.data) || {});
35
+ const candidates = useMemo(() => {
36
+ const definitions = Object.values(surfaces)
37
+ .filter((definition) =>
38
+ Array.isArray(definition.role) ? definition.role.includes(role) : definition.role === role,
39
+ )
40
+ .filter(({ filter }) => (filter ? filter(data) : true))
41
+ .toSorted(({ disposition: a = 'static' }, { disposition: b = 'static' }) => {
42
+ return a === b ? 0 : a === 'hoist' || b === 'fallback' ? -1 : b === 'hoist' || a === 'fallback' ? 1 : 0;
43
+ });
44
+ return limit ? definitions.slice(0, limit) : definitions;
45
+ }, [surfaces, role, data, limit]);
117
46
 
118
- const resolver = <SurfaceResolver {...props} id={id} ref={forwardedRef} />;
119
- const suspense = placeholder ? <Suspense fallback={placeholder}>{resolver}</Suspense> : resolver;
47
+ const nodes = candidates.map(({ component: Component, id }) => (
48
+ <Component ref={forwardedRef} key={id} id={id} role={role} data={data} limit={limit} {...rest} />
49
+ ));
50
+
51
+ const suspense = placeholder ? <Suspense fallback={placeholder}>{nodes}</Suspense> : nodes;
120
52
 
121
53
  return fallback ? (
122
54
  <ErrorBoundary data={data} fallback={fallback}>
@@ -128,68 +60,3 @@ export const Surface = memo(
128
60
  },
129
61
  ),
130
62
  );
131
-
132
- const SurfaceContext = createContext<SurfaceProps | undefined>(undefined);
133
-
134
- export const useSurface = (): SurfaceProps =>
135
- useContext(SurfaceContext) ?? raise(new Error('Surface context not found'));
136
-
137
- /**
138
- * Root surface component.
139
- */
140
- const SurfaceResolver = forwardRef<HTMLElement, SurfaceProps>((props, forwardedRef) => {
141
- const { components } = useSurfaceRoot();
142
- const parent = useContext(SurfaceContext);
143
- const nodes = resolveNodes(components, props, parent, forwardedRef);
144
- const currentContext: SurfaceProps = {
145
- ...props,
146
- surfaces: {
147
- ...((props.name && parent?.surfaces?.[props.name]?.surfaces) || {}),
148
- ...props.surfaces,
149
- },
150
- };
151
-
152
- return <SurfaceContext.Provider value={currentContext}>{nodes}</SurfaceContext.Provider>;
153
- });
154
-
155
- /**
156
- * Resolve surface nodes from across all component.
157
- */
158
- const resolveNodes = (
159
- components: Record<string, SurfaceComponent>,
160
- props: SurfaceProps,
161
- context: SurfaceProps | undefined,
162
- forwardedRef: ForwardedRef<HTMLElement>,
163
- ): ReactNode[] => {
164
- const data = {
165
- ...((props.name && context?.surfaces?.[props.name]?.data) || {}),
166
- ...props.data,
167
- };
168
-
169
- const candidates = Object.entries(components)
170
- .map(([key, component]): [string, SurfaceResult] | undefined => {
171
- // TODO(burdon): Avoid variable return types in plugin contract.
172
- const result = component({ ...props, data }, forwardedRef);
173
- if (!result || typeof result !== 'object') {
174
- return undefined;
175
- }
176
-
177
- // Normalize tuple.
178
- if ('node' in result) {
179
- return [key, result];
180
- } else if (isValidElement(result)) {
181
- return [key, { node: result }];
182
- } else {
183
- log.warn('invalid result', { result });
184
- return undefined;
185
- }
186
- })
187
- .filter((result): result is [string, SurfaceResult] => Boolean(result))
188
- .sort(([, { disposition: a = 'default' }], [, { disposition: b = 'default' }]) => {
189
- return a === b ? 0 : a === 'hoist' || b === 'fallback' ? -1 : b === 'hoist' || a === 'fallback' ? 1 : 0;
190
- });
191
-
192
- // TODO(burdon): Does this prematurely process the node?
193
- const nodes = candidates.map(([key, result]) => <Fragment key={key}>{result.node}</Fragment>);
194
- return props.limit ? nodes.slice(0, props.limit) : nodes;
195
- };
@@ -0,0 +1,112 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { createContext, useContext, type JSX, type ForwardedRef, type PropsWithChildren, type ReactNode } from 'react';
6
+
7
+ import { raise } from '@dxos/debug';
8
+ import { type GuardedType, type MakeOptional } from '@dxos/util';
9
+
10
+ import { type ErrorBoundary } from './ErrorBoundary';
11
+
12
+ /**
13
+ * SurfaceProps are the props that are passed to the Surface component.
14
+ */
15
+ export type SurfaceProps<T extends Record<string, any> = Record<string, unknown>> = PropsWithChildren<{
16
+ /**
17
+ * If specified, the Surface will be wrapped in an error boundary.
18
+ * The fallback component will be rendered if an error occurs.
19
+ */
20
+ fallback?: ErrorBoundary['props']['fallback'];
21
+
22
+ /**
23
+ * If specified, the Surface will be wrapped in a suspense boundary.
24
+ * The placeholder component will be rendered while the surface component is loading.
25
+ */
26
+ placeholder?: ReactNode;
27
+ }> &
28
+ MakeOptional<CoreSurfaceProps<T>, 'id' | 'data'> & {
29
+ /**
30
+ * Additional props to pass to the component.
31
+ * These props are not used by Surface itself but may be used by components which resolve the surface.
32
+ */
33
+ [key: string]: unknown;
34
+ };
35
+
36
+ // NOTE: If `[key: string]: unknown` is included in shared types, when re-used other fields become unknown as well.
37
+ type CoreSurfaceProps<T extends Record<string, any> = Record<string, unknown>> = {
38
+ /**
39
+ * ID for debugging.
40
+ */
41
+ id: string;
42
+
43
+ /**
44
+ * Role defines how the data should be rendered.
45
+ */
46
+ role: string;
47
+
48
+ /**
49
+ * The data to be rendered by the surface.
50
+ */
51
+ data: T;
52
+
53
+ /**
54
+ * If more than one component is resolved, the limit determines how many are rendered.
55
+ */
56
+ limit?: number | undefined;
57
+ };
58
+
59
+ type SurfaceComponentProps<T extends Record<string, any> = Record<string, unknown>> = PropsWithChildren<
60
+ CoreSurfaceProps<T> & { [key: string]: unknown }
61
+ >;
62
+
63
+ /**
64
+ * React component used to render a surface once is has matched.
65
+ */
66
+ export type SurfaceComponent<T extends Record<string, any> = Record<string, unknown>> = (
67
+ props: SurfaceComponentProps<T>,
68
+ forwardedRef: ForwardedRef<HTMLElement>,
69
+ ) => JSX.Element | null;
70
+
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
+ /**
81
+ * Definition of when a SurfaceComponent should be rendered.
82
+ */
83
+ export type SurfaceDefinition<T extends Record<string, any> = any> = {
84
+ id: string;
85
+ role: string | string[];
86
+ disposition?: SurfaceDisposition;
87
+ filter?: (data: Record<string, unknown>) => data is T;
88
+ component: SurfaceComponent<GuardedType<SurfaceDefinition<T>['filter']>>;
89
+ };
90
+
91
+ 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;
@@ -6,9 +6,9 @@ import React from 'react';
6
6
 
7
7
  import { create } from '@dxos/live-object';
8
8
 
9
- import { SurfaceProvider, type SurfaceRootContext } from './SurfaceRootContext';
9
+ import { SurfaceProvider, type SurfaceContextValue } from './SurfaceContext';
10
10
  import SurfaceMeta from './meta';
11
- import { parseSurfacePlugin, type SurfacePluginProvides } from './provides';
11
+ import { parseSurfacePlugin, type SurfacePluginProvides, type SurfaceDefinitions } from './provides';
12
12
  import { filterPlugins } from '../helpers';
13
13
  import { type PluginDefinition } from '../plugin-host';
14
14
 
@@ -16,14 +16,15 @@ import { type PluginDefinition } from '../plugin-host';
16
16
  * Provides a registry of surface components.
17
17
  */
18
18
  export const SurfacePlugin = (): PluginDefinition<SurfacePluginProvides> => {
19
- const state = create<SurfaceRootContext>({ components: {}, debugInfo: new Map() });
19
+ const state = create<SurfaceContextValue>({ surfaces: {}, debugInfo: new Map() });
20
20
 
21
21
  return {
22
22
  meta: SurfaceMeta,
23
- ready: async (plugins) => {
24
- state.components = filterPlugins(plugins, parseSurfacePlugin).reduce((acc, plugin) => {
25
- return { ...acc, [plugin.meta.id]: plugin.provides.surface.component };
26
- }, {});
23
+ ready: async (context) => {
24
+ state.surfaces = filterPlugins(context.plugins, parseSurfacePlugin).reduce(
25
+ (acc, plugin) => reduceSurfaces(plugin.provides.surface.definitions(context), acc),
26
+ {},
27
+ );
27
28
  },
28
29
  provides: {
29
30
  surface: state,
@@ -31,3 +32,14 @@ export const SurfacePlugin = (): PluginDefinition<SurfacePluginProvides> => {
31
32
  },
32
33
  };
33
34
  };
35
+
36
+ const reduceSurfaces = (
37
+ definitions: SurfaceDefinitions,
38
+ surfaces: SurfaceContextValue['surfaces'] = {},
39
+ ): SurfaceContextValue['surfaces'] => {
40
+ if (Array.isArray(definitions)) {
41
+ return definitions.reduce((acc, definition) => reduceSurfaces(definition, acc), surfaces);
42
+ }
43
+
44
+ return { ...surfaces, [definitions.id]: definitions };
45
+ };
@@ -9,6 +9,6 @@ export * from './provides';
9
9
 
10
10
  export * from './ErrorBoundary';
11
11
  export * from './Surface';
12
- export * from './SurfaceRootContext';
12
+ export * from './SurfaceContext';
13
13
 
14
14
  export default SurfacePlugin;
@@ -2,25 +2,26 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type SurfaceComponent, type SurfaceRootContext } from './SurfaceRootContext';
6
- import { type Plugin } from '../plugin-host';
5
+ import { type SurfaceDefinition, type SurfaceContextValue } from './SurfaceContext';
6
+ import { type HostContext, type Plugin } from '../plugin-host';
7
+
8
+ export type SurfaceDefinitions = SurfaceDefinition | SurfaceDefinition[] | SurfaceDefinitions[];
7
9
 
8
- // TODO(burdon): Predicate based providers?
9
10
  export type SurfaceProvides = {
10
11
  surface: {
11
12
  /**
12
13
  * Used by the `Surface` resolver to find a component to render.
13
14
  */
14
- component: SurfaceComponent;
15
+ definitions: (context: HostContext) => SurfaceDefinitions;
15
16
  };
16
17
  };
17
18
 
18
19
  export type SurfacePluginProvides = {
19
- surface: SurfaceRootContext;
20
+ surface: SurfaceContextValue;
20
21
  };
21
22
 
22
23
  export const parseRootSurfacePlugin = (plugin?: Plugin) =>
23
- (plugin?.provides as any)?.surface?.components ? (plugin as Plugin<SurfacePluginProvides>) : undefined;
24
+ (plugin?.provides as any)?.surface?.surfaces ? (plugin as Plugin<SurfacePluginProvides>) : undefined;
24
25
 
25
26
  export const parseSurfacePlugin = (plugin?: Plugin) =>
26
- (plugin?.provides as any)?.surface?.component ? (plugin as Plugin<SurfaceProvides>) : undefined;
27
+ (plugin?.provides as any)?.surface?.definitions ? (plugin as Plugin<SurfaceProvides>) : undefined;
package/tsconfig.json CHANGED
@@ -16,7 +16,44 @@
16
16
  "include": [
17
17
  "src"
18
18
  ],
19
- "references": [],
19
+ "references": [
20
+ {
21
+ "path": "../../common/async"
22
+ },
23
+ {
24
+ "path": "../../common/context"
25
+ },
26
+ {
27
+ "path": "../../common/debug"
28
+ },
29
+ {
30
+ "path": "../../common/invariant"
31
+ },
32
+ {
33
+ "path": "../../common/local-storage"
34
+ },
35
+ {
36
+ "path": "../../common/log"
37
+ },
38
+ {
39
+ "path": "../../common/util"
40
+ },
41
+ {
42
+ "path": "../../core/echo/echo-schema"
43
+ },
44
+ {
45
+ "path": "../../core/echo/live-object"
46
+ },
47
+ {
48
+ "path": "../../ui/primitives/react-hooks"
49
+ },
50
+ {
51
+ "path": "../app-graph"
52
+ },
53
+ {
54
+ "path": "../client-protocol"
55
+ }
56
+ ],
20
57
  "ts-node": {
21
58
  "compilerOptions": {
22
59
  "module": "CommonJS"
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/plugin-settings/SettingsPlugin.tsx", "../../../src/plugins/plugin-settings/meta.ts", "../../../src/plugins/plugin-settings/provides.ts", "../../../src/plugins/plugin-settings/index.ts"],
4
- "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { RootSettingsStore } from '@dxos/local-storage';\n\nimport SettingsMeta from './meta';\nimport { type SettingsPluginProvides } from './provides';\nimport { type PluginDefinition } from '../plugin-host';\n\nexport const SettingsPlugin = (): PluginDefinition<SettingsPluginProvides> => {\n // Global settings singleton.\n // TODO(burdon): Make settings storage extensible so that other plugins can decide the persistence of them.\n const settingsStore = new RootSettingsStore();\n\n return {\n meta: SettingsMeta,\n provides: {\n settingsStore,\n },\n };\n};\n", "//\n// Copyright 2023 DXOS.org\n//\n\nconst SettingsMeta = {\n id: 'dxos.org/plugin/settings',\n};\n\nexport default SettingsMeta;\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type SettingsStoreFactory, type SettingsValue } from '@dxos/local-storage';\n\nimport { type Plugin } from '../plugin-host';\n\nexport type SettingsProvides<T extends SettingsValue> = {\n settings: T;\n};\n\nexport type SettingsPluginProvides = {\n settingsStore: SettingsStoreFactory;\n};\n\nexport const parseSettingsPlugin = (plugin: Plugin) => {\n return typeof (plugin.provides as any).settingsStore === 'object'\n ? (plugin as Plugin<SettingsPluginProvides>)\n : undefined;\n};\n\nconst SETTINGS_PLUGIN = 'dxos.org/plugin/settings';\n\nconst SETTINGS_ACTION = `${SETTINGS_PLUGIN}/action`;\n\nexport enum SettingsAction {\n OPEN = `${SETTINGS_ACTION}/open`,\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { SettingsPlugin } from './SettingsPlugin';\n\nexport * from './provides';\n\nexport default SettingsPlugin;\n"],
5
- "mappings": ";AAIA,SAASA,yBAAyB;;;ACAlC,IAAMC,eAAe;EACnBC,IAAI;AACN;AAEA,IAAA,eAAeD;;;ADER,IAAME,iBAAiB,MAAA;AAG5B,QAAMC,gBAAgB,IAAIC,kBAAAA;AAE1B,SAAO;IACLC,MAAMC;IACNC,UAAU;MACRJ;IACF;EACF;AACF;;;AELO,IAAMK,sBAAsB,CAACC,WAAAA;AAClC,SAAO,OAAQA,OAAOC,SAAiBC,kBAAkB,WACpDF,SACDG;AACN;AAEA,IAAMC,kBAAkB;AAExB,IAAMC,kBAAkB,GAAGD,eAAAA;;UAEfE,iBAAAA;4CACH,GAAGD,eAAAA,OAAsB,IAAA;GADtBC,mBAAAA,iBAAAA,CAAAA,EAAAA;;;AClBZ,IAAA,0BAAeC;",
6
- "names": ["RootSettingsStore", "SettingsMeta", "id", "SettingsPlugin", "settingsStore", "RootSettingsStore", "meta", "SettingsMeta", "provides", "parseSettingsPlugin", "plugin", "provides", "settingsStore", "undefined", "SETTINGS_PLUGIN", "SETTINGS_ACTION", "SettingsAction", "SettingsPlugin"]
7
- }
@@ -1,214 +0,0 @@
1
- import {
2
- filterPlugins
3
- } from "./chunk-3E7RY3CE.mjs";
4
-
5
- // packages/sdk/app-framework/src/plugins/plugin-surface/SurfacePlugin.tsx
6
- import React from "react";
7
- import { create } from "@dxos/live-object";
8
-
9
- // packages/sdk/app-framework/src/plugins/plugin-surface/SurfaceRootContext.tsx
10
- import { createContext, useContext } from "react";
11
- import { raise } from "@dxos/debug";
12
- var SurfaceRootContext = /* @__PURE__ */ createContext(void 0);
13
- var useSurfaceRoot = () => useContext(SurfaceRootContext) ?? raise(new Error("Missing SurfaceRootContext"));
14
- var SurfaceProvider = SurfaceRootContext.Provider;
15
-
16
- // packages/sdk/app-framework/src/plugins/plugin-surface/meta.ts
17
- var SurfaceMeta = {
18
- id: "dxos.org/plugin/surface"
19
- };
20
- var meta_default = SurfaceMeta;
21
-
22
- // packages/sdk/app-framework/src/plugins/plugin-surface/provides.ts
23
- var parseRootSurfacePlugin = (plugin) => plugin?.provides?.surface?.components ? plugin : void 0;
24
- var parseSurfacePlugin = (plugin) => plugin?.provides?.surface?.component ? plugin : void 0;
25
-
26
- // packages/sdk/app-framework/src/plugins/plugin-surface/SurfacePlugin.tsx
27
- var SurfacePlugin = () => {
28
- const state = create({
29
- components: {},
30
- debugInfo: /* @__PURE__ */ new Map()
31
- });
32
- return {
33
- meta: meta_default,
34
- ready: async (plugins) => {
35
- state.components = filterPlugins(plugins, parseSurfacePlugin).reduce((acc, plugin) => {
36
- return {
37
- ...acc,
38
- [plugin.meta.id]: plugin.provides.surface.component
39
- };
40
- }, {});
41
- },
42
- provides: {
43
- surface: state,
44
- context: ({ children }) => /* @__PURE__ */ React.createElement(SurfaceProvider, {
45
- value: state
46
- }, children)
47
- }
48
- };
49
- };
50
-
51
- // packages/sdk/app-framework/src/plugins/plugin-surface/helpers.ts
52
- var isObject = (data) => !!data && typeof data === "object";
53
-
54
- // packages/sdk/app-framework/src/plugins/plugin-surface/ErrorBoundary.tsx
55
- import React2, { Component } from "react";
56
- var ErrorBoundary = class extends Component {
57
- constructor(props) {
58
- super(props);
59
- this.state = {
60
- error: void 0
61
- };
62
- }
63
- static getDerivedStateFromError(error) {
64
- return {
65
- error
66
- };
67
- }
68
- componentDidUpdate(prevProps) {
69
- if (prevProps.data !== this.props.data) {
70
- this.resetError();
71
- }
72
- }
73
- render() {
74
- if (this.state.error) {
75
- return /* @__PURE__ */ React2.createElement(this.props.fallback, {
76
- data: this.props.data,
77
- error: this.state.error,
78
- reset: this.resetError
79
- });
80
- }
81
- return this.props.children;
82
- }
83
- resetError() {
84
- this.setState({
85
- error: void 0
86
- });
87
- }
88
- };
89
-
90
- // packages/sdk/app-framework/src/plugins/plugin-surface/Surface.tsx
91
- import React3, { Fragment, Suspense, createContext as createContext2, forwardRef, isValidElement, memo, useContext as useContext2, useEffect, useState } from "react";
92
- import { raise as raise2 } from "@dxos/debug";
93
- import { log } from "@dxos/log";
94
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/app-framework/src/plugins/plugin-surface/Surface.tsx";
95
- var count = 0;
96
- var Surface = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef(({ id: _id, role, name = role, fallback, placeholder, ...rest }, forwardedRef) => {
97
- const props = {
98
- role,
99
- name,
100
- fallback,
101
- ...rest
102
- };
103
- const { debugInfo } = useSurfaceRoot();
104
- const [id] = useState(() => _id ?? `surface-${++count}`);
105
- useEffect(() => {
106
- debugInfo?.set(id, {
107
- id,
108
- created: Date.now(),
109
- name,
110
- role,
111
- renderCount: 0
112
- });
113
- return () => {
114
- debugInfo?.delete(id);
115
- };
116
- }, [
117
- id
118
- ]);
119
- if (debugInfo?.get(id)) {
120
- debugInfo.get(id).renderCount++;
121
- }
122
- const context = useContext2(SurfaceContext);
123
- const data = props.data ?? (name && context?.surfaces?.[name]?.data || {});
124
- const resolver = /* @__PURE__ */ React3.createElement(SurfaceResolver, {
125
- ...props,
126
- id,
127
- ref: forwardedRef
128
- });
129
- const suspense = placeholder ? /* @__PURE__ */ React3.createElement(Suspense, {
130
- fallback: placeholder
131
- }, resolver) : resolver;
132
- return fallback ? /* @__PURE__ */ React3.createElement(ErrorBoundary, {
133
- data,
134
- fallback
135
- }, suspense) : suspense;
136
- }));
137
- var SurfaceContext = /* @__PURE__ */ createContext2(void 0);
138
- var useSurface = () => useContext2(SurfaceContext) ?? raise2(new Error("Surface context not found"));
139
- var SurfaceResolver = /* @__PURE__ */ forwardRef((props, forwardedRef) => {
140
- const { components } = useSurfaceRoot();
141
- const parent = useContext2(SurfaceContext);
142
- const nodes = resolveNodes(components, props, parent, forwardedRef);
143
- const currentContext = {
144
- ...props,
145
- surfaces: {
146
- ...props.name && parent?.surfaces?.[props.name]?.surfaces || {},
147
- ...props.surfaces
148
- }
149
- };
150
- return /* @__PURE__ */ React3.createElement(SurfaceContext.Provider, {
151
- value: currentContext
152
- }, nodes);
153
- });
154
- var resolveNodes = (components, props, context, forwardedRef) => {
155
- const data = {
156
- ...props.name && context?.surfaces?.[props.name]?.data || {},
157
- ...props.data
158
- };
159
- const candidates = Object.entries(components).map(([key, component]) => {
160
- const result = component({
161
- ...props,
162
- data
163
- }, forwardedRef);
164
- if (!result || typeof result !== "object") {
165
- return void 0;
166
- }
167
- if ("node" in result) {
168
- return [
169
- key,
170
- result
171
- ];
172
- } else if (/* @__PURE__ */ isValidElement(result)) {
173
- return [
174
- key,
175
- {
176
- node: result
177
- }
178
- ];
179
- } else {
180
- log.warn("invalid result", {
181
- result
182
- }, {
183
- F: __dxlog_file,
184
- L: 183,
185
- S: void 0,
186
- C: (f, a) => f(...a)
187
- });
188
- return void 0;
189
- }
190
- }).filter((result) => Boolean(result)).sort(([, { disposition: a = "default" }], [, { disposition: b = "default" }]) => {
191
- return a === b ? 0 : a === "hoist" || b === "fallback" ? -1 : b === "hoist" || a === "fallback" ? 1 : 0;
192
- });
193
- const nodes = candidates.map(([key, result]) => /* @__PURE__ */ React3.createElement(Fragment, {
194
- key
195
- }, result.node));
196
- return props.limit ? nodes.slice(0, props.limit) : nodes;
197
- };
198
-
199
- // packages/sdk/app-framework/src/plugins/plugin-surface/index.ts
200
- var plugin_surface_default = SurfacePlugin;
201
-
202
- export {
203
- useSurfaceRoot,
204
- SurfaceProvider,
205
- meta_default,
206
- parseRootSurfacePlugin,
207
- parseSurfacePlugin,
208
- isObject,
209
- ErrorBoundary,
210
- Surface,
211
- useSurface,
212
- plugin_surface_default
213
- };
214
- //# sourceMappingURL=chunk-FRXJ25VI.mjs.map