@dxos/app-framework 0.7.5-main.9d2a38b → 0.7.5-main.e94eead

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 (205) hide show
  1. package/dist/lib/browser/app-graph-builder-S4MYSHQA.mjs +137 -0
  2. package/dist/lib/browser/app-graph-builder-S4MYSHQA.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-GNLU3GAU.mjs → chunk-BCMEJONP.mjs} +660 -823
  4. package/dist/lib/browser/chunk-BCMEJONP.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-QS4J6O47.mjs +285 -0
  6. package/dist/lib/browser/chunk-QS4J6O47.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-SRZH2PQ2.mjs +32 -0
  8. package/dist/lib/browser/chunk-SRZH2PQ2.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +57 -74
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/intent-dispatcher-GFBH7T2J.mjs +11 -0
  12. package/dist/lib/browser/intent-dispatcher-GFBH7T2J.mjs.map +7 -0
  13. package/dist/lib/browser/intent-resolver-KAFM7CQH.mjs +39 -0
  14. package/dist/lib/browser/intent-resolver-KAFM7CQH.mjs.map +7 -0
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/store-L3VRR7II.mjs +29 -0
  17. package/dist/lib/browser/store-L3VRR7II.mjs.map +7 -0
  18. package/dist/lib/browser/testing/index.mjs +13 -3
  19. package/dist/lib/browser/testing/index.mjs.map +3 -3
  20. package/dist/lib/browser/worker.mjs +77 -0
  21. package/dist/lib/browser/worker.mjs.map +7 -0
  22. package/dist/lib/node/app-graph-builder-VMHWFCTP.cjs +146 -0
  23. package/dist/lib/node/app-graph-builder-VMHWFCTP.cjs.map +7 -0
  24. package/dist/lib/node/chunk-7Y6KJ3OK.cjs +1466 -0
  25. package/dist/lib/node/chunk-7Y6KJ3OK.cjs.map +7 -0
  26. package/dist/lib/node/chunk-B65NJEIJ.cjs +308 -0
  27. package/dist/lib/node/chunk-B65NJEIJ.cjs.map +7 -0
  28. package/dist/lib/node/chunk-VCIHQZSN.cjs +58 -0
  29. package/dist/lib/node/chunk-VCIHQZSN.cjs.map +7 -0
  30. package/dist/lib/node/index.cjs +106 -118
  31. package/dist/lib/node/index.cjs.map +4 -4
  32. package/dist/lib/node/intent-dispatcher-PRCC4KZT.cjs +32 -0
  33. package/dist/lib/node/intent-dispatcher-PRCC4KZT.cjs.map +7 -0
  34. package/dist/lib/node/intent-resolver-OZDKCHPW.cjs +46 -0
  35. package/dist/lib/node/intent-resolver-OZDKCHPW.cjs.map +7 -0
  36. package/dist/lib/node/meta.json +1 -1
  37. package/dist/lib/node/store-BVUKNVKL.cjs +44 -0
  38. package/dist/lib/node/store-BVUKNVKL.cjs.map +7 -0
  39. package/dist/lib/node/testing/index.cjs +18 -8
  40. package/dist/lib/node/testing/index.cjs.map +3 -3
  41. package/dist/lib/node/worker.cjs +99 -0
  42. package/dist/lib/node/worker.cjs.map +7 -0
  43. package/dist/lib/node-esm/app-graph-builder-XHI5IIXQ.mjs +138 -0
  44. package/dist/lib/node-esm/app-graph-builder-XHI5IIXQ.mjs.map +7 -0
  45. package/dist/lib/node-esm/chunk-CBT75GCX.mjs +34 -0
  46. package/dist/lib/node-esm/chunk-CBT75GCX.mjs.map +7 -0
  47. package/dist/lib/node-esm/chunk-JDAHZRYQ.mjs +286 -0
  48. package/dist/lib/node-esm/chunk-JDAHZRYQ.mjs.map +7 -0
  49. package/dist/lib/node-esm/{chunk-KPMTPXQI.mjs → chunk-TVIR2PHY.mjs} +660 -823
  50. package/dist/lib/node-esm/chunk-TVIR2PHY.mjs.map +7 -0
  51. package/dist/lib/node-esm/index.mjs +57 -74
  52. package/dist/lib/node-esm/index.mjs.map +4 -4
  53. package/dist/lib/node-esm/intent-dispatcher-LGACN32C.mjs +12 -0
  54. package/dist/lib/node-esm/intent-dispatcher-LGACN32C.mjs.map +7 -0
  55. package/dist/lib/node-esm/intent-resolver-RBNG76ZX.mjs +40 -0
  56. package/dist/lib/node-esm/intent-resolver-RBNG76ZX.mjs.map +7 -0
  57. package/dist/lib/node-esm/meta.json +1 -1
  58. package/dist/lib/node-esm/store-PHTOEREN.mjs +30 -0
  59. package/dist/lib/node-esm/store-PHTOEREN.mjs.map +7 -0
  60. package/dist/lib/node-esm/testing/index.mjs +13 -3
  61. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  62. package/dist/lib/node-esm/worker.mjs +78 -0
  63. package/dist/lib/node-esm/worker.mjs.map +7 -0
  64. package/dist/types/src/App.d.ts +2 -2
  65. package/dist/types/src/App.d.ts.map +1 -1
  66. package/dist/types/src/common/capabilities.d.ts +88 -124
  67. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  68. package/dist/types/src/common/events.d.ts +22 -11
  69. package/dist/types/src/common/events.d.ts.map +1 -1
  70. package/dist/types/src/common/file.d.ts +1 -1
  71. package/dist/types/src/common/file.d.ts.map +1 -1
  72. package/dist/types/src/common/graph.d.ts +2 -2
  73. package/dist/types/src/common/graph.d.ts.map +1 -1
  74. package/dist/types/src/common/index.d.ts +0 -1
  75. package/dist/types/src/common/index.d.ts.map +1 -1
  76. package/dist/types/src/common/layout.d.ts +218 -121
  77. package/dist/types/src/common/layout.d.ts.map +1 -1
  78. package/dist/types/src/common/surface.d.ts +3 -3
  79. package/dist/types/src/common/surface.d.ts.map +1 -1
  80. package/dist/types/src/common/translations.d.ts +7 -7
  81. package/dist/types/src/common/translations.d.ts.map +1 -1
  82. package/dist/types/src/core/capabilities.d.ts +6 -2
  83. package/dist/types/src/core/capabilities.d.ts.map +1 -1
  84. package/dist/types/src/core/manager.d.ts +2 -9
  85. package/dist/types/src/core/manager.d.ts.map +1 -1
  86. package/dist/types/src/core/plugin.d.ts +5 -2
  87. package/dist/types/src/core/plugin.d.ts.map +1 -1
  88. package/dist/types/src/playground/debug/Debug.d.ts +2 -3
  89. package/dist/types/src/playground/debug/Debug.d.ts.map +1 -1
  90. package/dist/types/src/playground/debug/plugin.d.ts +1 -1
  91. package/dist/types/src/playground/debug/plugin.d.ts.map +1 -1
  92. package/dist/types/src/playground/generator/Main.d.ts +2 -3
  93. package/dist/types/src/playground/generator/Main.d.ts.map +1 -1
  94. package/dist/types/src/playground/generator/Toolbar.d.ts +2 -3
  95. package/dist/types/src/playground/generator/Toolbar.d.ts.map +1 -1
  96. package/dist/types/src/playground/generator/generator.d.ts +5 -3
  97. package/dist/types/src/playground/generator/generator.d.ts.map +1 -1
  98. package/dist/types/src/playground/generator/plugin.d.ts +1 -1
  99. package/dist/types/src/playground/generator/plugin.d.ts.map +1 -1
  100. package/dist/types/src/playground/layout/Layout.d.ts +2 -2
  101. package/dist/types/src/playground/layout/Layout.d.ts.map +1 -1
  102. package/dist/types/src/playground/layout/plugin.d.ts +1 -1
  103. package/dist/types/src/playground/layout/plugin.d.ts.map +1 -1
  104. package/dist/types/src/playground/logger/Toolbar.d.ts +2 -3
  105. package/dist/types/src/playground/logger/Toolbar.d.ts.map +1 -1
  106. package/dist/types/src/playground/logger/plugin.d.ts +1 -1
  107. package/dist/types/src/playground/logger/plugin.d.ts.map +1 -1
  108. package/dist/types/src/playground/logger/schema.d.ts +1 -1
  109. package/dist/types/src/playground/logger/schema.d.ts.map +1 -1
  110. package/dist/types/src/playground/playground.stories.d.ts +2 -3
  111. package/dist/types/src/playground/playground.stories.d.ts.map +1 -1
  112. package/dist/types/src/plugin-intent/IntentPlugin.d.ts +1 -1
  113. package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +1 -1
  114. package/dist/types/src/plugin-intent/actions.d.ts +1 -1
  115. package/dist/types/src/plugin-intent/actions.d.ts.map +1 -1
  116. package/dist/types/src/plugin-intent/index.d.ts +0 -1
  117. package/dist/types/src/plugin-intent/index.d.ts.map +1 -1
  118. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts +27 -20
  119. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +1 -1
  120. package/dist/types/src/plugin-intent/intent.d.ts +3 -3
  121. package/dist/types/src/plugin-intent/intent.d.ts.map +1 -1
  122. package/dist/types/src/plugin-settings/SettingsPlugin.d.ts +1 -1
  123. package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +1 -1
  124. package/dist/types/src/plugin-settings/actions.d.ts +11 -1
  125. package/dist/types/src/plugin-settings/actions.d.ts.map +1 -1
  126. package/dist/types/src/plugin-settings/app-graph-builder.d.ts +197 -0
  127. package/dist/types/src/plugin-settings/app-graph-builder.d.ts.map +1 -0
  128. package/dist/types/src/plugin-settings/intent-resolver.d.ts +4 -0
  129. package/dist/types/src/plugin-settings/intent-resolver.d.ts.map +1 -0
  130. package/dist/types/src/plugin-settings/store.d.ts +5 -0
  131. package/dist/types/src/plugin-settings/store.d.ts.map +1 -0
  132. package/dist/types/src/plugin-settings/translations.d.ts +11 -0
  133. package/dist/types/src/plugin-settings/translations.d.ts.map +1 -0
  134. package/dist/types/src/react/ErrorBoundary.d.ts +1 -1
  135. package/dist/types/src/{plugin-intent → react}/IntentContext.d.ts +1 -1
  136. package/dist/types/src/react/IntentContext.d.ts.map +1 -0
  137. package/dist/types/src/react/Surface.d.ts.map +1 -1
  138. package/dist/types/src/react/Surface.stories.d.ts +15 -0
  139. package/dist/types/src/react/Surface.stories.d.ts.map +1 -0
  140. package/dist/types/src/react/common.d.ts +13 -0
  141. package/dist/types/src/react/common.d.ts.map +1 -0
  142. package/dist/types/src/react/index.d.ts +2 -0
  143. package/dist/types/src/react/index.d.ts.map +1 -1
  144. package/dist/types/src/react/useCapabilities.d.ts.map +1 -1
  145. package/dist/types/src/react/useIntentResolver.d.ts +3 -0
  146. package/dist/types/src/react/useIntentResolver.d.ts.map +1 -0
  147. package/dist/types/src/testing/withPluginManager.d.ts +6 -4
  148. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  149. package/dist/types/src/worker.d.ts +4 -0
  150. package/dist/types/src/worker.d.ts.map +1 -0
  151. package/package.json +29 -21
  152. package/project.json +4 -3
  153. package/src/App.tsx +17 -15
  154. package/src/common/capabilities.ts +30 -11
  155. package/src/common/events.ts +16 -2
  156. package/src/common/file.ts +1 -1
  157. package/src/common/graph.ts +2 -2
  158. package/src/common/index.ts +0 -1
  159. package/src/common/layout.ts +207 -126
  160. package/src/common/surface.ts +2 -2
  161. package/src/common/translations.ts +7 -8
  162. package/src/core/capabilities.ts +16 -7
  163. package/src/core/manager.test.ts +22 -73
  164. package/src/core/manager.ts +105 -91
  165. package/src/core/plugin.ts +6 -3
  166. package/src/playground/debug/plugin.ts +1 -1
  167. package/src/playground/generator/Toolbar.tsx +11 -11
  168. package/src/playground/generator/generator.ts +25 -0
  169. package/src/playground/generator/plugin.ts +6 -1
  170. package/src/playground/layout/plugin.ts +1 -1
  171. package/src/playground/logger/Toolbar.tsx +2 -1
  172. package/src/playground/logger/plugin.ts +7 -4
  173. package/src/playground/logger/schema.ts +1 -1
  174. package/src/plugin-intent/IntentPlugin.tsx +3 -43
  175. package/src/plugin-intent/actions.ts +1 -1
  176. package/src/plugin-intent/errors.ts +1 -1
  177. package/src/plugin-intent/index.ts +0 -1
  178. package/src/plugin-intent/intent-dispatcher.test.ts +48 -29
  179. package/src/plugin-intent/intent-dispatcher.ts +81 -42
  180. package/src/plugin-intent/intent.ts +5 -5
  181. package/src/plugin-settings/SettingsPlugin.ts +19 -13
  182. package/src/plugin-settings/actions.ts +11 -1
  183. package/src/plugin-settings/app-graph-builder.ts +122 -0
  184. package/src/plugin-settings/intent-resolver.ts +34 -0
  185. package/src/plugin-settings/store.ts +30 -0
  186. package/src/plugin-settings/translations.ts +17 -0
  187. package/src/{plugin-intent → react}/IntentContext.tsx +2 -2
  188. package/src/react/Surface.stories.tsx +96 -0
  189. package/src/react/Surface.tsx +11 -8
  190. package/src/react/common.ts +12 -0
  191. package/src/react/index.ts +2 -0
  192. package/src/react/useCapabilities.ts +1 -0
  193. package/src/react/useIntentResolver.ts +22 -0
  194. package/src/testing/withPluginManager.tsx +28 -4
  195. package/src/worker.ts +11 -0
  196. package/tsconfig.json +7 -13
  197. package/dist/lib/browser/chunk-GNLU3GAU.mjs.map +0 -7
  198. package/dist/lib/node/chunk-FBA4BB3J.cjs +0 -1639
  199. package/dist/lib/node/chunk-FBA4BB3J.cjs.map +0 -7
  200. package/dist/lib/node-esm/chunk-KPMTPXQI.mjs.map +0 -7
  201. package/dist/types/src/common/navigation.d.ts +0 -241
  202. package/dist/types/src/common/navigation.d.ts.map +0 -1
  203. package/dist/types/src/plugin-intent/IntentContext.d.ts.map +0 -1
  204. package/src/common/navigation.ts +0 -199
  205. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
@@ -2,83 +2,10 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { S } from '@dxos/echo-schema';
5
+ import { Schema as S } from '@effect/schema';
6
6
 
7
7
  import { Label } from '../plugin-intent';
8
8
 
9
- //
10
- // Provides
11
- //
12
-
13
- export const Toast = S.Struct({
14
- id: S.String,
15
- title: S.optional(Label),
16
- description: S.optional(Label),
17
- icon: S.optional(S.String),
18
- duration: S.optional(S.Number),
19
- closeLabel: S.optional(Label),
20
- actionLabel: S.optional(Label),
21
- actionAlt: S.optional(Label),
22
- // TODO(wittjosiah): Make class with customizable method?
23
- onAction: S.optional(S.Any),
24
- });
25
-
26
- export type Toast = S.Schema.Type<typeof Toast>;
27
-
28
- /**
29
- * Basic state provided by a layout plugin.
30
- *
31
- * Layout provides the state of global UI landmarks, such as the sidebar, dialog, and popover.
32
- * Generally only one dialog or popover should be open at a time, a layout plugin should manage this.
33
- * For other landmarks, such as toasts, rendering them in the layout prevents them from unmounting when navigating.
34
- */
35
-
36
- const LayoutMode = S.Union(S.Literal('deck'), S.Literal('solo'), S.Literal('fullscreen'));
37
- export const isLayoutMode = (value: any): value is LayoutMode => S.is(LayoutMode)(value);
38
- export type LayoutMode = S.Schema.Type<typeof LayoutMode>;
39
-
40
- export const Layout = S.mutable(
41
- S.Struct({
42
- layoutMode: LayoutMode,
43
-
44
- sidebarOpen: S.Boolean,
45
- complementarySidebarOpen: S.Boolean,
46
- /**
47
- * @deprecated Data to be passed to the complementary sidebar Surface.
48
- */
49
- complementarySidebarContent: S.optional(S.Any),
50
-
51
- dialogOpen: S.Boolean,
52
- /**
53
- * Data to be passed to the dialog Surface.
54
- */
55
- dialogContent: S.optional(S.Any),
56
- // TODO(wittjosiah): Custom properties?
57
- dialogBlockAlign: S.optional(S.Literal('start', 'center')),
58
- dialogType: S.optional(S.Literal('default', 'alert')),
59
-
60
- popoverOpen: S.Boolean,
61
- /**
62
- * Data to be passed to the popover Surface.
63
- */
64
- popoverContent: S.optional(S.Any),
65
- popoverAnchorId: S.optional(S.String),
66
-
67
- toasts: S.mutable(S.Array(Toast)),
68
-
69
- /**
70
- * The identifier of a component to scroll into view when it is mounted.
71
- */
72
- scrollIntoView: S.optional(S.String),
73
- }),
74
- );
75
-
76
- export type Layout = S.Schema.Type<typeof Layout>;
77
-
78
- //
79
- // Intents
80
- //
81
-
82
9
  export const LAYOUT_PLUGIN = 'dxos.org/plugin/layout';
83
10
  export const LAYOUT_ACTION = `${LAYOUT_PLUGIN}/action`;
84
11
 
@@ -86,71 +13,225 @@ export const LAYOUT_ACTION = `${LAYOUT_PLUGIN}/action`;
86
13
  * Expected payload for layout actions.
87
14
  */
88
15
  export namespace LayoutAction {
89
- export class SetLayout extends S.TaggedClass<SetLayout>()(`${LAYOUT_ACTION}/set-layout`, {
16
+ export const UPDATE_LAYOUT = `${LAYOUT_ACTION}/update-layout`;
17
+
18
+ /**
19
+ * Generic layout action.
20
+ */
21
+ export class UpdateLayout extends S.TaggedClass<UpdateLayout>()(UPDATE_LAYOUT, {
22
+ input: S.Struct({
23
+ part: S.String.annotations({ description: 'The part of the layout to mutate.' }),
24
+ subject: S.optional(S.Any.annotations({ description: 'The subject of the layout update.' })),
25
+ options: S.optional(
26
+ S.Record({ key: S.String, value: S.Any }).annotations({
27
+ description: 'Additional options for the layout action.',
28
+ }),
29
+ ),
30
+ }),
31
+ output: S.Void,
32
+ }) {}
33
+
34
+ //
35
+ // Common layout actions.
36
+ //
37
+
38
+ // NOTE: These are layout actions which are currently in common use.
39
+ // They constrain the generic layout action types to provide additional type safety.
40
+ // However, they all follow the same generic structure and intent id.
41
+ // This allows for plugins to update the layout without depending on a specific layout plugin.
42
+ // The expectation is that other norms other than these will emerge over time.
43
+
44
+ export class SetLayoutMode extends S.TaggedClass<SetLayoutMode>()(UPDATE_LAYOUT, {
90
45
  input: S.Struct({
91
- /**
92
- * Element to set the state of.
93
- */
94
- element: S.Literal('fullscreen', 'sidebar', 'complementary', 'dialog', 'popover', 'toast'),
95
-
96
- /**
97
- * Whether the element is on or off.
98
- *
99
- * If omitted, the element's state will be toggled or set based on other provided data.
100
- * For example, if `component` is provided, the state will be set to `true`.
101
- */
102
- state: S.optional(S.Boolean),
103
-
104
- /**
105
- * Component to render in the dialog or popover.
106
- */
107
- component: S.optional(S.String),
108
-
109
- /**
110
- * Data to be passed to the dialog or popover Surface.
111
- */
112
- subject: S.optional(S.Any),
113
-
114
- /**
115
- * Anchor ID for the popover.
116
- */
117
- anchorId: S.optional(S.String),
118
-
119
- // TODO(wittjosiah): Custom properties?
120
-
121
- /**
122
- * Block alignment for the dialog.
123
- */
124
- dialogBlockAlign: S.optional(S.Literal('start', 'center')),
125
-
126
- /**
127
- * Type of dialog.
128
- */
129
- dialogType: S.optional(S.Literal('default', 'alert')),
46
+ part: S.Literal('mode').annotations({ description: 'Setting the layout mode.' }),
47
+ subject: S.optional(S.String.annotations({ description: 'Item which is the subject of the new layout mode.' })),
48
+ options: S.Union(
49
+ S.Struct({ mode: S.String.annotations({ description: 'The new layout mode.' }) }),
50
+ S.Struct({ revert: S.Boolean.annotations({ description: 'Revert to the previous layout mode.' }) }),
51
+ ),
130
52
  }),
131
53
  output: S.Void,
132
54
  }) {}
133
55
 
134
- // TODO(wittjosiah): Do all these need to be separate actions?
56
+ export class UpdateSidebar extends S.TaggedClass<UpdateSidebar>()(UPDATE_LAYOUT, {
57
+ input: S.Struct({
58
+ part: S.Literal('sidebar').annotations({ description: 'Updating the sidebar.' }),
59
+ subject: S.optional(S.String.annotations({ description: 'URI of the component to display in the sidebar.' })),
60
+ options: S.optional(
61
+ S.Struct({
62
+ state: S.Literal('closed', 'collapsed', 'expanded').annotations({
63
+ description: 'Whether the sidebar is closed, collapsed, or expanded.',
64
+ }),
65
+ }),
66
+ ),
67
+ }),
68
+ output: S.Void,
69
+ }) {}
70
+
71
+ export class UpdateComplementary extends S.TaggedClass<UpdateComplementary>()(UPDATE_LAYOUT, {
72
+ input: S.Struct({
73
+ part: S.Literal('complementary').annotations({ description: 'Updating the complementary sidebar.' }),
74
+ subject: S.optional(
75
+ S.String.annotations({ description: 'URI of the component to display in the complementary area.' }),
76
+ ),
77
+ options: S.optional(
78
+ S.Struct({
79
+ state: S.Literal('closed', 'collapsed', 'expanded').annotations({
80
+ description: 'Whether the complementary sidebar is closed, collapsed, or expanded.',
81
+ }),
82
+ }),
83
+ ),
84
+ }),
85
+ output: S.Void,
86
+ }) {}
135
87
 
136
- export class SetLayoutMode extends S.TaggedClass<SetLayoutMode>()(`${LAYOUT_ACTION}/set-layout-mode`, {
137
- input: S.Union(
138
- S.Struct({
139
- layoutMode: LayoutMode,
88
+ export class UpdateDialog extends S.TaggedClass<UpdateDialog>()(UPDATE_LAYOUT, {
89
+ input: S.Struct({
90
+ part: S.Literal('dialog').annotations({ description: 'Updating the dialog.' }),
91
+ subject: S.optional(S.String.annotations({ description: 'URI of the component to display in the dialog.' })),
92
+ options: S.Struct({
93
+ state: S.optional(S.Boolean.annotations({ description: 'Whether the dialog is open or closed.' })),
94
+ blockAlign: S.optional(
95
+ S.Literal('start', 'center', 'end').annotations({ description: 'The alignment of the dialog.' }),
96
+ ),
97
+ type: S.optional(S.Literal('default', 'alert').annotations({ description: 'The type of dialog.' })),
98
+ props: S.optional(
99
+ S.Record({ key: S.String, value: S.Any }).annotations({
100
+ description: 'Additional props for the dialog.',
101
+ }),
102
+ ),
140
103
  }),
141
- S.Struct({
142
- revert: S.Literal(true),
104
+ }),
105
+ output: S.Void,
106
+ }) {}
107
+
108
+ export class UpdatePopover extends S.TaggedClass<UpdatePopover>()(UPDATE_LAYOUT, {
109
+ input: S.Struct({
110
+ part: S.Literal('popover').annotations({ description: 'Updating the popover.' }),
111
+ subject: S.optional(S.String.annotations({ description: 'URI of the component to display in the popover.' })),
112
+ options: S.Struct({
113
+ anchorId: S.String.annotations({ description: 'The id of the element to anchor the popover to.' }),
114
+ side: S.optional(
115
+ S.Literal('top', 'right', 'bottom', 'left').annotations({ description: 'The side of the anchor.' }),
116
+ ),
117
+ state: S.optional(S.Boolean.annotations({ description: 'Whether the popover is open or closed.' })),
118
+ props: S.optional(
119
+ S.Record({ key: S.String, value: S.Any }).annotations({
120
+ description: 'Additional props for the popover.',
121
+ }),
122
+ ),
143
123
  }),
124
+ }),
125
+ output: S.Void,
126
+ }) {}
127
+
128
+ export const Toast = S.Struct({
129
+ id: S.String.annotations({ description: 'The id of the toast.' }),
130
+ title: S.optional(Label.annotations({ description: 'The title of the toast.' })),
131
+ description: S.optional(Label.annotations({ description: 'The description of the toast.' })),
132
+ icon: S.optional(S.String.annotations({ description: 'The icon of the toast.' })),
133
+ duration: S.optional(S.Number.annotations({ description: 'The duration of the toast.' })),
134
+ closeLabel: S.optional(Label.annotations({ description: 'The label of the close button.' })),
135
+ actionLabel: S.optional(Label.annotations({ description: 'The label of the action button.' })),
136
+ actionAlt: S.optional(Label.annotations({ description: 'The alt text of the action button.' })),
137
+ onAction: S.optional(
138
+ S.Any.annotations({ description: 'The action to perform when the action button is clicked.' }),
144
139
  ),
140
+ });
141
+
142
+ export interface Toast extends Omit<S.Schema.Type<typeof Toast>, 'onAction'> {
143
+ onAction?: () => void;
144
+ }
145
+
146
+ export class AddToast extends S.TaggedClass<AddToast>()(UPDATE_LAYOUT, {
147
+ input: S.Struct({
148
+ part: S.Literal('toast').annotations({ description: 'Adding a toast.' }),
149
+ subject: Toast.annotations({ description: 'The toast to add.' }),
150
+ }),
151
+ output: S.Void,
152
+ }) {}
153
+
154
+ export class SwitchWorkspace extends S.TaggedClass<SwitchWorkspace>()(UPDATE_LAYOUT, {
155
+ input: S.Struct({
156
+ part: S.Literal('workspace').annotations({ description: 'Switching the workspace.' }),
157
+ subject: S.String.annotations({ description: 'The id of the workspace to switch to.' }),
158
+ }),
159
+ output: S.Void,
160
+ }) {}
161
+
162
+ export class RevertWorkspace extends S.TaggedClass<RevertWorkspace>()(UPDATE_LAYOUT, {
163
+ input: S.Struct({
164
+ part: S.Literal('workspace').annotations({ description: 'Switching the workspace.' }),
165
+ options: S.Struct({
166
+ revert: S.Literal(true).annotations({ description: 'Revert to the previous workspace.' }),
167
+ }),
168
+ }),
169
+ output: S.Void,
170
+ }) {}
171
+
172
+ export class Open extends S.TaggedClass<Open>()(UPDATE_LAYOUT, {
173
+ input: S.Struct({
174
+ part: S.Literal('main').annotations({ description: 'Opening an item in the main content area.' }),
175
+ subject: S.Array(S.String.annotations({ description: 'Ids of the items to open.' })),
176
+ options: S.optional(
177
+ S.Struct({
178
+ state: S.optional(S.Literal(true).annotations({ description: 'The items are being added.' })),
179
+ key: S.optional(
180
+ S.String.annotations({ description: 'If provided, will replace item with a matching key (id prefix).' }),
181
+ ),
182
+ scrollIntoView: S.optional(S.Boolean.annotations({ description: 'Scroll the items into view.' })),
183
+ pivotId: S.optional(S.String.annotations({ description: 'The id of the item to place new items next to.' })),
184
+ positioning: S.optional(
185
+ S.Union(
186
+ S.Literal('start').annotations({ description: 'The items are being added before the pivot item.' }),
187
+ S.Literal('end').annotations({ description: 'The items are being added after the pivot item.' }),
188
+ ),
189
+ ),
190
+ }),
191
+ ),
192
+ }),
193
+ output: S.Void,
194
+ }) {}
195
+
196
+ export class Set extends S.TaggedClass<Set>()(UPDATE_LAYOUT, {
197
+ input: S.Struct({
198
+ part: S.Literal('main').annotations({ description: 'Setting items in the main content area.' }),
199
+ subject: S.Array(S.String.annotations({ description: 'Ids of the items to set.' })),
200
+ options: S.Struct({
201
+ override: S.Literal(true).annotations({ description: 'Override the current items in the main content area.' }),
202
+ }),
203
+ }),
204
+ output: S.Void,
205
+ }) {}
206
+
207
+ export class Close extends S.TaggedClass<Close>()(UPDATE_LAYOUT, {
208
+ input: S.Struct({
209
+ part: S.Literal('main').annotations({ description: 'Closing items in the main content area.' }),
210
+ subject: S.Array(S.String.annotations({ description: 'Ids of the items to close.' })),
211
+ options: S.Struct({
212
+ state: S.Literal(false).annotations({ description: 'The items are being removed.' }),
213
+ }),
214
+ }),
215
+ output: S.Void,
216
+ }) {}
217
+
218
+ export class ScrollIntoView extends S.TaggedClass<ScrollIntoView>()(UPDATE_LAYOUT, {
219
+ input: S.Struct({
220
+ part: S.Literal('current').annotations({ description: 'Setting the current item' }),
221
+ subject: S.optional(S.String.annotations({ description: 'The id of the item to set as current.' })),
222
+ options: S.optional(
223
+ S.Record({ key: S.String, value: S.Any }).annotations({
224
+ description: 'Additional options for the scroll into view.',
225
+ }),
226
+ ),
227
+ }),
145
228
  output: S.Void,
146
229
  }) {}
147
230
 
148
- export class ScrollIntoView extends S.TaggedClass<ScrollIntoView>()(`${LAYOUT_ACTION}/scroll-into-view`, {
231
+ export class Expose extends S.TaggedClass<Expose>()(UPDATE_LAYOUT, {
149
232
  input: S.Struct({
150
- id: S.optional(S.String),
151
- // TODO(wittjosiah): Factor out to thread scroll into view action?
152
- cursor: S.optional(S.String),
153
- ref: S.optional(S.String),
233
+ part: S.Literal('navigation').annotations({ description: 'Exposing an item in the navigation area.' }),
234
+ subject: S.String.annotations({ description: 'The id of the item to expose.' }),
154
235
  }),
155
236
  output: S.Void,
156
237
  }) {}
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { type JSX, type ForwardedRef, type PropsWithChildren, type ReactNode } from 'react';
6
6
 
7
- import { type GuardedType, type MakeOptional, type Disposition } from '@dxos/util';
7
+ import { type GuardedType, type MakeOptional, type Position } from '@dxos/util';
8
8
 
9
9
  import { type ErrorBoundary } from '../react';
10
10
 
@@ -73,7 +73,7 @@ export type SurfaceComponent<T extends Record<string, any> = Record<string, unkn
73
73
  export type SurfaceDefinition<T extends Record<string, any> = any> = Readonly<{
74
74
  id: string;
75
75
  role: string | string[];
76
- disposition?: Disposition;
76
+ position?: Position;
77
77
  filter?: (data: Record<string, unknown>) => data is T;
78
78
  component: SurfaceComponent<GuardedType<SurfaceDefinition<T>['filter']>>;
79
79
  }>;
@@ -2,17 +2,16 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { z } from 'zod';
5
+ import { Schema as S } from '@effect/schema';
6
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>;
7
+ export const ResourceKey = S.Union(S.String, S.Record({ key: S.String, value: S.Any }));
8
+ export type ResourceKey = S.Schema.Type<typeof ResourceKey>;
10
9
 
11
- export const ResourceLanguage = z.record(ResourceKey);
12
- export type ResourceLanguage = z.infer<typeof ResourceLanguage>;
10
+ export const ResourceLanguage = S.Record({ key: S.String, value: ResourceKey });
11
+ export type ResourceLanguage = S.Schema.Type<typeof ResourceLanguage>;
13
12
 
14
13
  /**
15
14
  * A resource is a collection of translations for a language.
16
15
  */
17
- export const Resource = z.record(ResourceLanguage);
18
- export type Resource = z.infer<typeof Resource>;
16
+ export const Resource = S.Record({ key: S.String, value: ResourceLanguage });
17
+ export type Resource = S.Schema.Type<typeof Resource>;
@@ -79,17 +79,20 @@ export const contributes = <T>(
79
79
  return { interface: interfaceDef, implementation, deactivate } satisfies Capability<T>;
80
80
  };
81
81
 
82
- type LazyCapability<T, U> = () => Promise<{ default: (props: T) => MaybePromise<Capability<U>> }>;
82
+ type LoadCapability<T, U> = () => Promise<{ default: (props: T) => MaybePromise<Capability<U>> }>;
83
+ type LoadCapabilities<T> = () => Promise<{ default: (props: T) => MaybePromise<AnyCapability[]> }>;
84
+ // TODO(wittjosiah): Not having the array be `any` causes type errors when using the lazy capability.
85
+ type LazyCapability<T, U> = (props?: T) => Promise<() => Promise<Capability<U> | AnyCapability[]>>;
83
86
 
84
87
  /**
85
88
  * Helper to define a lazily loaded implementation of a capability.
86
89
  */
87
90
  export const lazy =
88
- <T, U>(c: LazyCapability<T, U>) =>
89
- (props?: T): Promise<Capability<U>> =>
90
- c().then(({ default: getCapability }) => {
91
- return getCapability(props as T);
92
- });
91
+ <T, U>(c: LoadCapability<T, U> | LoadCapabilities<T>): LazyCapability<T, U> =>
92
+ async (props?: T) => {
93
+ const { default: getCapability } = await c();
94
+ return async () => getCapability(props as T);
95
+ };
93
96
 
94
97
  /**
95
98
  * Context which is passed to plugins, allowing them to interact with each other.
@@ -136,7 +139,11 @@ export class PluginsContext {
136
139
  }
137
140
 
138
141
  current.push(new CapabilityImpl(moduleId, implementation));
139
- log('capability contributed', { id: interfaceDef.identifier, count: untracked(() => current.length) });
142
+ log('capability contributed', {
143
+ id: interfaceDef.identifier,
144
+ moduleId,
145
+ count: untracked(() => current.length),
146
+ });
140
147
  }
141
148
 
142
149
  /**
@@ -152,6 +159,8 @@ export class PluginsContext {
152
159
  if (index !== -1) {
153
160
  current.splice(index, 1);
154
161
  log('capability removed', { id: interfaceDef.identifier, count: untracked(() => current.length) });
162
+ } else {
163
+ log.warn('capability not removed', { id: interfaceDef.identifier });
155
164
  }
156
165
  }
157
166
 
@@ -62,10 +62,10 @@ describe('PluginManager', () => {
62
62
  const Test = definePlugin(testMeta, [Hello]);
63
63
 
64
64
  const manager = new PluginManager({ plugins: [Test], core: [], pluginLoader });
65
- manager.enable(testMeta.id);
65
+ await manager.enable(testMeta.id);
66
66
  expect(manager.enabled).toEqual([Test.meta.id]);
67
67
  expect(manager.modules).toEqual([Hello]);
68
- manager.disable(testMeta.id);
68
+ await manager.disable(testMeta.id);
69
69
  expect(manager.enabled).toEqual([]);
70
70
  expect(manager.modules).toEqual([]);
71
71
  });
@@ -111,7 +111,8 @@ describe('PluginManager', () => {
111
111
  const Fail = defineModule({
112
112
  id: 'dxos.org/test/fail',
113
113
  activatesOn: FailEvent,
114
- activate: async () => raise(new Error('test')),
114
+ // TODO(wittjosiah): Test and catch more failure modes.
115
+ activate: async () => async () => raise(new Error('test')),
115
116
  });
116
117
  plugins = [definePlugin(testMeta, [Hello, Fail])];
117
118
 
@@ -287,7 +288,7 @@ describe('PluginManager', () => {
287
288
  id: 'dxos.org/test/count',
288
289
  activatesOn: Events.Startup,
289
290
  activatesBefore: [CountEvent],
290
- activate: (context) => {
291
+ activate: async (context) => async () => {
291
292
  computeTotal(context);
292
293
  return contributes(Total, state);
293
294
  },
@@ -328,57 +329,18 @@ describe('PluginManager', () => {
328
329
 
329
330
  {
330
331
  await manager.disable(Test.meta.id);
331
- expect(manager.active).toEqual([...Test.modules.map((m) => m.id), Count.meta.id]);
332
- expect(manager.pendingReset).toEqual([CountEvent.id, Events.Startup.id]);
333
-
334
- const totals = manager.context.requestCapabilities(Total);
335
- expect(totals).toHaveLength(1);
336
- expect(totals[0].total).toEqual(6);
337
- }
338
-
339
- {
340
- await manager.reset(CountEvent);
341
- expect(manager.active).toEqual([Count.meta.id]);
342
- expect(manager.pendingReset).toEqual([Events.Startup.id]);
343
-
344
- const totals = manager.context.requestCapabilities(Total);
345
- expect(totals).toHaveLength(1);
346
- expect(totals[0].total).toEqual(6);
347
- }
348
-
349
- {
350
- await manager.reset(Events.Startup);
351
332
  expect(manager.active).toEqual([Count.meta.id]);
352
333
  expect(manager.pendingReset).toEqual([]);
353
334
 
354
335
  const totals = manager.context.requestCapabilities(Total);
355
336
  expect(totals).toHaveLength(1);
356
- expect(totals[0].total).toEqual(0);
337
+ // Total doesn't change because it is not reactive.
338
+ expect(totals[0].total).toEqual(6);
357
339
  }
358
340
 
359
341
  {
360
342
  await manager.enable(Test.meta.id);
361
- expect(manager.active).toEqual([Count.meta.id]);
362
- expect(manager.pendingReset).toEqual([CountEvent.id, Events.Startup.id]);
363
-
364
- const totals = manager.context.requestCapabilities(Total);
365
- expect(totals).toHaveLength(1);
366
- expect(totals[0].total).toEqual(0);
367
- }
368
-
369
- {
370
- await manager.reset(CountEvent);
371
343
  expect(manager.active).toEqual([Count.meta.id, ...Test.modules.map((m) => m.id)]);
372
- expect(manager.pendingReset).toEqual([Events.Startup.id]);
373
-
374
- const totals = manager.context.requestCapabilities(Total);
375
- expect(totals).toHaveLength(1);
376
- expect(totals[0].total).toEqual(0);
377
- }
378
-
379
- {
380
- await manager.reset(Events.Startup);
381
- expect(manager.active).toEqual([...Test.modules.map((m) => m.id), Count.meta.id]);
382
344
  expect(manager.pendingReset).toEqual([]);
383
345
 
384
346
  const totals = manager.context.requestCapabilities(Total);
@@ -464,9 +426,6 @@ describe('PluginManager', () => {
464
426
  using activeUpdates = updateCounter(() => {
465
427
  const _ = manager.active.length;
466
428
  });
467
- using pendingRemovalUpdates = updateCounter(() => {
468
- const _ = manager.pendingRemoval.length;
469
- });
470
429
  using eventsFiredUpdates = updateCounter(() => {
471
430
  const _ = manager.eventsFired.length;
472
431
  });
@@ -477,7 +436,6 @@ describe('PluginManager', () => {
477
436
  expect(enabledUpdates.count).toEqual(0);
478
437
  expect(modulesUpdates.count).toEqual(0);
479
438
  expect(activeUpdates.count).toEqual(0);
480
- expect(pendingRemovalUpdates.count).toEqual(0);
481
439
  expect(eventsFiredUpdates.count).toEqual(0);
482
440
  expect(pendingResetUpdates.count).toEqual(0);
483
441
 
@@ -486,7 +444,6 @@ describe('PluginManager', () => {
486
444
  expect(enabledUpdates.count).toEqual(1);
487
445
  expect(modulesUpdates.count).toEqual(1);
488
446
  expect(activeUpdates.count).toEqual(0);
489
- expect(pendingRemovalUpdates.count).toEqual(0);
490
447
  expect(eventsFiredUpdates.count).toEqual(0);
491
448
  expect(pendingResetUpdates.count).toEqual(0);
492
449
 
@@ -495,7 +452,6 @@ describe('PluginManager', () => {
495
452
  expect(enabledUpdates.count).toEqual(1);
496
453
  expect(modulesUpdates.count).toEqual(1);
497
454
  expect(activeUpdates.count).toEqual(1);
498
- expect(pendingRemovalUpdates.count).toEqual(0);
499
455
  expect(eventsFiredUpdates.count).toEqual(1);
500
456
  expect(pendingResetUpdates.count).toEqual(0);
501
457
 
@@ -503,17 +459,15 @@ describe('PluginManager', () => {
503
459
  expect(pluginUpdates.count).toEqual(2);
504
460
  expect(enabledUpdates.count).toEqual(2);
505
461
  expect(modulesUpdates.count).toEqual(2);
506
- expect(activeUpdates.count).toEqual(1);
507
- expect(pendingRemovalUpdates.count).toEqual(0);
462
+ expect(activeUpdates.count).toEqual(2);
508
463
  expect(eventsFiredUpdates.count).toEqual(1);
509
- expect(pendingResetUpdates.count).toEqual(1);
464
+ expect(pendingResetUpdates.count).toEqual(2);
510
465
 
511
466
  await manager.activate(CountEvent);
512
467
  expect(pluginUpdates.count).toEqual(2);
513
468
  expect(enabledUpdates.count).toEqual(2);
514
469
  expect(modulesUpdates.count).toEqual(2);
515
470
  expect(activeUpdates.count).toEqual(2);
516
- expect(pendingRemovalUpdates.count).toEqual(0);
517
471
  expect(eventsFiredUpdates.count).toEqual(1);
518
472
  expect(pendingResetUpdates.count).toEqual(2);
519
473
 
@@ -521,47 +475,42 @@ describe('PluginManager', () => {
521
475
  expect(pluginUpdates.count).toEqual(3);
522
476
  expect(enabledUpdates.count).toEqual(3);
523
477
  expect(modulesUpdates.count).toEqual(3);
524
- expect(activeUpdates.count).toEqual(2);
525
- expect(pendingRemovalUpdates.count).toEqual(0);
478
+ expect(activeUpdates.count).toEqual(3);
526
479
  expect(eventsFiredUpdates.count).toEqual(1);
527
- expect(pendingResetUpdates.count).toEqual(3);
480
+ expect(pendingResetUpdates.count).toEqual(4);
528
481
 
529
482
  await manager.reset(CountEvent);
530
483
  expect(pluginUpdates.count).toEqual(3);
531
484
  expect(enabledUpdates.count).toEqual(3);
532
485
  expect(modulesUpdates.count).toEqual(3);
533
- // Starts at 2, plus deactivates 2, plus activates 3.
534
- expect(activeUpdates.count).toEqual(7);
535
- expect(pendingRemovalUpdates.count).toEqual(0);
486
+ // Starts at 3, plus deactivates 3, plus activates 3.
487
+ expect(activeUpdates.count).toEqual(9);
536
488
  expect(eventsFiredUpdates.count).toEqual(1);
537
489
  expect(pendingResetUpdates.count).toEqual(4);
538
490
 
539
491
  await manager.disable(One.meta.id);
540
492
  expect(pluginUpdates.count).toEqual(3);
541
493
  expect(enabledUpdates.count).toEqual(4);
542
- expect(modulesUpdates.count).toEqual(3);
543
- expect(activeUpdates.count).toEqual(7);
544
- expect(pendingRemovalUpdates.count).toEqual(1);
494
+ expect(modulesUpdates.count).toEqual(4);
495
+ expect(activeUpdates.count).toEqual(10);
545
496
  expect(eventsFiredUpdates.count).toEqual(1);
546
- expect(pendingResetUpdates.count).toEqual(5);
497
+ expect(pendingResetUpdates.count).toEqual(4);
547
498
 
548
499
  await manager.remove(One.meta.id);
549
500
  expect(pluginUpdates.count).toEqual(4);
550
501
  expect(enabledUpdates.count).toEqual(4);
551
- expect(modulesUpdates.count).toEqual(3);
552
- expect(activeUpdates.count).toEqual(7);
553
- expect(pendingRemovalUpdates.count).toEqual(1);
502
+ expect(modulesUpdates.count).toEqual(4);
503
+ expect(activeUpdates.count).toEqual(10);
554
504
  expect(eventsFiredUpdates.count).toEqual(1);
555
- expect(pendingResetUpdates.count).toEqual(5);
505
+ expect(pendingResetUpdates.count).toEqual(4);
556
506
 
557
507
  await manager.reset(CountEvent);
558
508
  expect(pluginUpdates.count).toEqual(4);
559
509
  expect(enabledUpdates.count).toEqual(4);
560
510
  expect(modulesUpdates.count).toEqual(4);
561
- // Starts at 7, plus deactivates 3, plus activates 2.
562
- expect(activeUpdates.count).toEqual(12);
563
- expect(pendingRemovalUpdates.count).toEqual(2);
511
+ // Starts at 10, plus deactivates 2, plus activates 2.
512
+ expect(activeUpdates.count).toEqual(14);
564
513
  expect(eventsFiredUpdates.count).toEqual(1);
565
- expect(pendingResetUpdates.count).toEqual(6);
514
+ expect(pendingResetUpdates.count).toEqual(4);
566
515
  });
567
516
  });