@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,11 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { Schema as S } from '@effect/schema';
5
6
  import { Effect, Fiber, pipe } from 'effect';
6
7
  import { describe, expect, test } from 'vitest';
7
8
 
8
- import { S } from '@dxos/echo-schema';
9
-
10
9
  import { chain, createIntent } from './intent';
11
10
  import { type AnyIntentResolver, createDispatcher, createResolver } from './intent-dispatcher';
12
11
 
@@ -108,8 +107,8 @@ describe('Intent dispatcher', () => {
108
107
  const { dispatch } = createDispatcher(() => [computeResolver, toStringResolver, concatResolver]);
109
108
  const intent = pipe(createIntent(Compute, { value: 1 }), chain(ToString, {}), chain(Concat, { plus: '!' }));
110
109
 
111
- expect(intent.first.action).toBe(Compute._tag);
112
- expect(intent.last.action).toBe(Concat._tag);
110
+ expect(intent.first.id).toBe(Compute._tag);
111
+ expect(intent.last.id).toBe(Concat._tag);
113
112
  expect(intent.all.length).toBe(3);
114
113
 
115
114
  const program = Effect.gen(function* () {
@@ -137,7 +136,10 @@ describe('Intent dispatcher', () => {
137
136
  });
138
137
 
139
138
  test('filter resolvers by plugin', async () => {
140
- const otherComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }));
139
+ const otherComputeResolver = createResolver({
140
+ intent: Compute,
141
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
142
+ });
141
143
  const { dispatch } = createDispatcher((module) => (module === 'test' ? [computeResolver] : [otherComputeResolver]));
142
144
  const program = Effect.gen(function* () {
143
145
  const a = yield* dispatch(createIntent(Compute, { value: 1 }));
@@ -153,8 +155,10 @@ describe('Intent dispatcher', () => {
153
155
  });
154
156
 
155
157
  test('filter resolvers by predicate', async () => {
156
- const conditionalComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
158
+ const conditionalComputeResolver = createResolver({
159
+ intent: Compute,
157
160
  filter: (data): data is { value: number } => data?.value > 1,
161
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
158
162
  });
159
163
  const { dispatch } = createDispatcher(() => [conditionalComputeResolver, computeResolver]);
160
164
  const program = Effect.gen(function* () {
@@ -171,8 +175,10 @@ describe('Intent dispatcher', () => {
171
175
  });
172
176
 
173
177
  test('hoist resolvers', async () => {
174
- const hoistedComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
175
- disposition: 'hoist',
178
+ const hoistedComputeResolver = createResolver({
179
+ intent: Compute,
180
+ position: 'hoist',
181
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
176
182
  });
177
183
  const { dispatchPromise } = createDispatcher(() => [computeResolver, hoistedComputeResolver]);
178
184
  const { data } = await dispatchPromise(createIntent(Compute, { value: 1 }));
@@ -180,13 +186,17 @@ describe('Intent dispatcher', () => {
180
186
  });
181
187
 
182
188
  test('fallback resolvers', async () => {
183
- const conditionalComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 2 } }), {
189
+ const conditionalComputeResolver = createResolver({
190
+ intent: Compute,
184
191
  filter: (data): data is { value: number } => data?.value === 1,
192
+ resolve: async (data) => ({ data: { value: data?.value * 2 } }),
185
193
  });
186
- const fallbackComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
187
- disposition: 'fallback',
194
+ const fallbackComputeResolver = createResolver({
195
+ intent: Compute,
196
+ position: 'fallback',
197
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
188
198
  });
189
- const { dispatch } = createDispatcher(() => [fallbackComputeResolver, conditionalComputeResolver]);
199
+ const { dispatch } = createDispatcher(() => [conditionalComputeResolver, fallbackComputeResolver]);
190
200
  const program = Effect.gen(function* () {
191
201
  const a = yield* dispatch(createIntent(Compute, { value: 1 }));
192
202
 
@@ -224,8 +234,9 @@ class ToString extends S.TaggedClass<ToString>()('ToString', {
224
234
  }),
225
235
  }) {}
226
236
 
227
- const toStringResolver = createResolver(ToString, async (data) => {
228
- return { data: { string: data.value.toString() } };
237
+ const toStringResolver = createResolver({
238
+ intent: ToString,
239
+ resolve: async (data) => ({ data: { string: data.value.toString() } }),
229
240
  });
230
241
 
231
242
  class Compute extends S.TaggedClass<Compute>()('Compute', {
@@ -237,16 +248,19 @@ class Compute extends S.TaggedClass<Compute>()('Compute', {
237
248
  }),
238
249
  }) {}
239
250
 
240
- const computeResolver = createResolver(Compute, (data, undo) => {
241
- return Effect.gen(function* () {
242
- if (undo) {
243
- return { data: { value: data.value / 2 } };
244
- }
245
-
246
- yield* Effect.sleep(data.value * 10);
247
- const value = data.value * 2;
248
- return { data: { value }, undoable: { message: 'test', data: { value } } };
249
- });
251
+ const computeResolver = createResolver({
252
+ intent: Compute,
253
+ resolve: (data, undo) => {
254
+ return Effect.gen(function* () {
255
+ if (undo) {
256
+ return { data: { value: data.value / 2 } };
257
+ }
258
+
259
+ yield* Effect.sleep(data.value * 10);
260
+ const value = data.value * 2;
261
+ return { data: { value }, undoable: { message: 'test', data: { value } } };
262
+ });
263
+ },
250
264
  });
251
265
 
252
266
  class Concat extends S.TaggedClass<Concat>()('Concat', {
@@ -259,8 +273,9 @@ class Concat extends S.TaggedClass<Concat>()('Concat', {
259
273
  }),
260
274
  }) {}
261
275
 
262
- const concatResolver = createResolver(Concat, async (data) => {
263
- return { data: { string: data.string + data.plus } };
276
+ const concatResolver = createResolver({
277
+ intent: Concat,
278
+ resolve: async (data) => ({ data: { string: data.string + data.plus } }),
264
279
  });
265
280
 
266
281
  class Add extends S.TaggedClass<Add>()('Add', {
@@ -268,8 +283,9 @@ class Add extends S.TaggedClass<Add>()('Add', {
268
283
  output: S.Number,
269
284
  }) {}
270
285
 
271
- const addResolver = createResolver(Add, async (data) => {
272
- return { data: data[0] + data[1] };
286
+ const addResolver = createResolver({
287
+ intent: Add,
288
+ resolve: async (data) => ({ data: data[0] + data[1] }),
273
289
  });
274
290
 
275
291
  class SideEffect extends S.TaggedClass<SideEffect>()('SideEffect', {
@@ -277,4 +293,7 @@ class SideEffect extends S.TaggedClass<SideEffect>()('SideEffect', {
277
293
  output: S.Void,
278
294
  }) {}
279
295
 
280
- const sideEffectResolver = createResolver(SideEffect, async () => {});
296
+ const sideEffectResolver = createResolver({
297
+ intent: SideEffect,
298
+ resolve: async () => {},
299
+ });
@@ -5,7 +5,9 @@
5
5
  import { Effect, Option, pipe, Ref } from 'effect';
6
6
  import { type Simplify } from 'effect/Types';
7
7
 
8
- import { type MaybePromise, byDisposition, type Disposition } from '@dxos/util';
8
+ import { create } from '@dxos/live-object';
9
+ import { log } from '@dxos/log';
10
+ import { byPosition, type MaybePromise, type Position, type GuardedType } from '@dxos/util';
9
11
 
10
12
  import { IntentAction } from './actions';
11
13
  import { CycleDetectedError, NoResolversError } from './errors';
@@ -21,6 +23,8 @@ import {
21
23
  type IntentSchema,
22
24
  type Label,
23
25
  } from './intent';
26
+ import { Events, Capabilities } from '../common';
27
+ import { contributes, type PluginsContext } from '../core';
24
28
 
25
29
  const EXECUTION_LIMIT = 100;
26
30
  const HISTORY_LIMIT = 100;
@@ -28,13 +32,13 @@ const HISTORY_LIMIT = 100;
28
32
  /**
29
33
  * The return value of an intent effect.
30
34
  */
31
- export type IntentEffectResult<Fields extends IntentParams> = {
35
+ export type IntentEffectResult<Input, Output> = {
32
36
  /**
33
37
  * The output of the action that was performed.
34
38
  *
35
39
  * If the intent is apart of a chain of intents, the data will be passed to the next intent.
36
40
  */
37
- data?: IntentResultData<Fields>;
41
+ data?: Output;
38
42
 
39
43
  /**
40
44
  * If provided, the action will be undoable.
@@ -48,7 +52,7 @@ export type IntentEffectResult<Fields extends IntentParams> = {
48
52
  /**
49
53
  * Will be merged with the original intent data when firing the undo intent.
50
54
  */
51
- data?: Partial<IntentData<Fields>>;
55
+ data?: Partial<Input>;
52
56
  };
53
57
 
54
58
  /**
@@ -66,33 +70,32 @@ export type IntentEffectResult<Fields extends IntentParams> = {
66
70
  intents?: AnyIntentChain[];
67
71
  };
68
72
 
69
- export type AnyIntentEffectResult = IntentEffectResult<any>;
73
+ export type AnyIntentEffectResult = IntentEffectResult<any, any>;
70
74
 
71
75
  /**
72
76
  * The result of an intent dispatcher.
73
77
  */
74
- export type IntentDispatcherResult<Fields extends IntentParams> = Pick<IntentEffectResult<Fields>, 'data' | 'error'>;
78
+ export type IntentDispatcherResult<Input, Output> = Pick<IntentEffectResult<Input, Output>, 'data' | 'error'>;
75
79
 
76
80
  /**
77
81
  * The implementation of an intent effect.
78
82
  */
79
- export type IntentEffectDefinition<Fields extends IntentParams> = (
80
- data: IntentData<Fields>,
83
+ export type IntentEffectDefinition<Input, Output> = (
84
+ data: Input,
81
85
  undo: boolean,
82
- ) => MaybePromise<IntentEffectResult<Fields> | void> | Effect.Effect<IntentEffectResult<Fields> | void>;
86
+ ) => MaybePromise<IntentEffectResult<Input, Output> | void> | Effect.Effect<IntentEffectResult<Input, Output> | void>;
83
87
 
84
88
  /**
85
89
  * Intent resolver to match intents to their effects.
86
90
  */
87
- export type IntentResolver<Tag extends string, Fields extends IntentParams> = Readonly<{
88
- action: Tag;
89
- disposition?: Disposition;
90
- // TODO(wittjosiah): Would be nice to make this a guard for intents with optional data.
91
- filter?: (data: IntentData<Fields>) => boolean;
92
- effect: IntentEffectDefinition<Fields>;
91
+ export type IntentResolver<Tag extends string, Fields extends IntentParams, Data = IntentData<Fields>> = Readonly<{
92
+ intent: IntentSchema<Tag, Fields>;
93
+ position?: Position;
94
+ filter?: (data: IntentData<Fields>) => data is Data;
95
+ resolve: IntentEffectDefinition<GuardedType<IntentResolver<Tag, Fields, Data>['filter']>, IntentResultData<Fields>>;
93
96
  }>;
94
97
 
95
- export type AnyIntentResolver = IntentResolver<any, any>;
98
+ export type AnyIntentResolver = IntentResolver<any, any, any>;
96
99
 
97
100
  /**
98
101
  * Creates an intent resolver to match intents to their effects.
@@ -101,22 +104,16 @@ export type AnyIntentResolver = IntentResolver<any, any>;
101
104
  * @param params.disposition Determines the priority of the resolver when multiple are resolved.
102
105
  * @param params.filter Optional filter to determine if the resolver should be used.
103
106
  */
104
- export const createResolver = <Tag extends string, Fields extends IntentParams>(
105
- schema: IntentSchema<Tag, Fields>,
106
- effect: IntentEffectDefinition<Fields>,
107
- params: Pick<IntentResolver<Tag, Fields>, 'disposition' | 'filter'> = {},
108
- ): IntentResolver<Tag, Fields> => ({
109
- action: schema._tag,
110
- effect,
111
- ...params,
112
- });
107
+ export const createResolver = <Tag extends string, Fields extends IntentParams, Data = IntentData<Fields>>(
108
+ resolver: IntentResolver<Tag, Fields, Data>,
109
+ ) => resolver;
113
110
 
114
111
  /**
115
112
  * Invokes intents and returns the result.
116
113
  */
117
114
  export type PromiseIntentDispatcher = <Fields extends IntentParams>(
118
115
  intent: IntentChain<any, any, any, Fields>,
119
- ) => Promise<Simplify<IntentDispatcherResult<Fields>>>;
116
+ ) => Promise<Simplify<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>>;
120
117
 
121
118
  /**
122
119
  * Creates an effect for intents.
@@ -124,9 +121,15 @@ export type PromiseIntentDispatcher = <Fields extends IntentParams>(
124
121
  export type IntentDispatcher = <Fields extends IntentParams>(
125
122
  intent: IntentChain<any, any, any, Fields>,
126
123
  depth?: number,
127
- ) => Effect.Effect<Simplify<Required<IntentDispatcherResult<Fields>>['data']>, Error>;
128
-
129
- type IntentResult<Tag extends string, Fields extends IntentParams> = IntentEffectResult<Fields> & {
124
+ ) => Effect.Effect<
125
+ Simplify<Required<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>['data']>,
126
+ Error
127
+ >;
128
+
129
+ type IntentResult<Tag extends string, Fields extends IntentParams> = IntentEffectResult<
130
+ IntentData<Fields>,
131
+ IntentResultData<Fields>
132
+ > & {
130
133
  _intent: Intent<Tag, Fields>;
131
134
  };
132
135
 
@@ -135,7 +138,7 @@ export type AnyIntentResult = IntentResult<any, any>;
135
138
  /**
136
139
  * Invokes the most recent undoable intent with undo flags.
137
140
  */
138
- export type PromiseIntentUndo = () => Promise<IntentDispatcherResult<any>>;
141
+ export type PromiseIntentUndo = () => Promise<IntentDispatcherResult<any, any>>;
139
142
 
140
143
  /**
141
144
  * Creates an effect which undoes the last intent.
@@ -158,7 +161,7 @@ export type IntentContext = {
158
161
  /**
159
162
  * Sets of an intent dispatcher.
160
163
  *
161
- * @param resolvers An array of available intent resolvers.
164
+ * @param getResolvers A function that returns an array of available intent resolvers.
162
165
  * @param params.historyLimit The maximum number of intent results to keep in history.
163
166
  * @param params.executionLimit The maximum recursion depth of intent chains.
164
167
  */
@@ -168,24 +171,20 @@ export const createDispatcher = (
168
171
  ): IntentContext => {
169
172
  const historyRef = Effect.runSync(Ref.make<AnyIntentResult[][]>([]));
170
173
 
171
- const handleIntent = (intent: AnyIntent) => {
172
- return Effect.gen(function* () {
174
+ const handleIntent = (intent: AnyIntent) =>
175
+ Effect.gen(function* () {
173
176
  const candidates = getResolvers(intent.module)
174
- .filter((r) => r.action === intent.action)
177
+ .filter((r) => r.intent._tag === intent.id)
175
178
  .filter((r) => !r.filter || r.filter(intent.data))
176
- .toSorted(byDisposition);
179
+ .toSorted(byPosition);
177
180
  if (candidates.length === 0) {
178
- return {
179
- _intent: intent,
180
- error: new NoResolversError(intent.action),
181
- } as AnyIntentResult;
181
+ yield* Effect.fail(new NoResolversError(intent.id));
182
182
  }
183
183
 
184
- const effect = candidates[0].effect(intent.data, intent.undo ?? false);
184
+ const effect = candidates[0].resolve(intent.data, intent.undo ?? false);
185
185
  const result = Effect.isEffect(effect) ? yield* effect : yield* Effect.promise(async () => effect);
186
186
  return { _intent: intent, ...result } as AnyIntentResult;
187
187
  });
188
- };
189
188
 
190
189
  const dispatch: IntentDispatcher = (intentChain, depth = 0) => {
191
190
  return Effect.gen(function* () {
@@ -238,7 +237,10 @@ export const createDispatcher = (
238
237
  const dispatchPromise: PromiseIntentDispatcher = (intentChain) => {
239
238
  return Effect.runPromise(dispatch(intentChain))
240
239
  .then((data) => ({ data }))
241
- .catch((error) => ({ error }));
240
+ .catch((error) => {
241
+ log.catch(error);
242
+ return { error };
243
+ });
242
244
  };
243
245
 
244
246
  const undo: IntentUndo = () => {
@@ -267,3 +269,40 @@ export const createDispatcher = (
267
269
 
268
270
  return { dispatch, dispatchPromise, undo, undoPromise };
269
271
  };
272
+
273
+ const defaultEffect = () => Effect.fail(new Error('Intent runtime not ready'));
274
+ const defaultPromise = () => Effect.runPromise(defaultEffect());
275
+
276
+ export default (context: PluginsContext) => {
277
+ const state = create<IntentContext>({
278
+ dispatch: defaultEffect,
279
+ dispatchPromise: defaultPromise,
280
+ undo: defaultEffect,
281
+ undoPromise: defaultPromise,
282
+ });
283
+
284
+ // TODO(wittjosiah): Make getResolver callback async and allow resolvers to be requested on demand.
285
+ const { dispatch, dispatchPromise, undo, undoPromise } = createDispatcher((module) =>
286
+ context
287
+ .requestCapabilities(Capabilities.IntentResolver, (c, moduleId): c is AnyIntentResolver => {
288
+ return module ? moduleId === module : true;
289
+ })
290
+ .flat(),
291
+ );
292
+
293
+ const manager = context.requestCapability(Capabilities.PluginManager);
294
+ state.dispatch = (intentChain, depth) => {
295
+ return Effect.gen(function* () {
296
+ yield* manager._activate(Events.SetupIntentResolver);
297
+ return yield* dispatch(intentChain, depth);
298
+ });
299
+ };
300
+ state.dispatchPromise = async (intentChain) => {
301
+ await manager.activate(Events.SetupIntentResolver);
302
+ return await dispatchPromise(intentChain);
303
+ };
304
+ state.undo = undo;
305
+ state.undoPromise = undoPromise;
306
+
307
+ return contributes(Capabilities.IntentDispatcher, state);
308
+ };
@@ -2,7 +2,7 @@
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
  export type IntentParams = {
8
8
  readonly input: S.Schema.All;
@@ -25,9 +25,9 @@ export type Intent<Tag extends string, Fields extends IntentParams> = {
25
25
  _schema: IntentSchema<Tag, Fields>;
26
26
 
27
27
  /**
28
- * The action to perform.
28
+ * The id of the intent.
29
29
  */
30
- action: Tag;
30
+ id: Tag;
31
31
 
32
32
  /**
33
33
  * Any data needed to perform the desired action.
@@ -84,7 +84,7 @@ export const createIntent = <Tag extends string, Fields extends IntentParams>(
84
84
  const intent = {
85
85
  ...params,
86
86
  _schema: schema,
87
- action: schema._tag,
87
+ id: schema._tag,
88
88
  data,
89
89
  } satisfies Intent<Tag, Fields>;
90
90
 
@@ -122,7 +122,7 @@ export const chain =
122
122
  const last = {
123
123
  ...params,
124
124
  _schema: schema,
125
- action: schema._tag,
125
+ id: schema._tag,
126
126
  data,
127
127
  } satisfies Intent<NextTag, NextFields>;
128
128
 
@@ -2,12 +2,12 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { RootSettingsStore } from '@dxos/local-storage';
6
-
7
5
  import { SETTINGS_PLUGIN } from './actions';
6
+ import translations from './translations';
8
7
  import { Capabilities, Events } from '../common';
9
- import { contributes, defineModule, definePlugin } from '../core';
8
+ import { contributes, defineModule, definePlugin, lazy } from '../core';
10
9
 
10
+ // TODO(wittjosiah): Add options to exclude some modules.
11
11
  export const SettingsPlugin = () =>
12
12
  definePlugin({ id: SETTINGS_PLUGIN }, [
13
13
  defineModule({
@@ -15,15 +15,21 @@ export const SettingsPlugin = () =>
15
15
  activatesOn: Events.Startup,
16
16
  activatesBefore: [Events.SetupSettings],
17
17
  activatesAfter: [Events.SettingsReady],
18
- activate: (context) => {
19
- const allSettings = context.requestCapabilities(Capabilities.Settings);
20
- const settingsStore = new RootSettingsStore();
21
-
22
- allSettings.forEach((setting) => {
23
- settingsStore.createStore(setting as any);
24
- });
25
-
26
- return contributes(Capabilities.SettingsStore, settingsStore);
27
- },
18
+ activate: lazy(() => import('./store')),
19
+ }),
20
+ defineModule({
21
+ id: `${SETTINGS_PLUGIN}/module/translations`,
22
+ activatesOn: Events.SetupTranslations,
23
+ activate: () => contributes(Capabilities.Translations, translations),
24
+ }),
25
+ defineModule({
26
+ id: `${SETTINGS_PLUGIN}/module/intent-resolver`,
27
+ activatesOn: Events.SetupIntentResolver,
28
+ activate: lazy(() => import('./intent-resolver')),
29
+ }),
30
+ defineModule({
31
+ id: `${SETTINGS_PLUGIN}/module/app-graph-builder`,
32
+ activatesOn: Events.SetupAppGraph,
33
+ activate: lazy(() => import('./app-graph-builder')),
28
34
  }),
29
35
  ]);
@@ -2,10 +2,12 @@
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
  export const SETTINGS_PLUGIN = 'dxos.org/plugin/settings';
8
8
  export const SETTINGS_ACTION = `${SETTINGS_PLUGIN}/action`;
9
+ export const SETTINGS_ID = '!dxos:settings';
10
+ export const SETTINGS_KEY = 'settings';
9
11
 
10
12
  export namespace SettingsAction {
11
13
  export class Open extends S.TaggedClass<Open>()(`${SETTINGS_ACTION}/open`, {
@@ -14,4 +16,12 @@ export namespace SettingsAction {
14
16
  }),
15
17
  output: S.Void,
16
18
  }) {}
19
+
20
+ export class OpenPluginRegistry extends S.TaggedClass<OpenPluginRegistry>()(
21
+ `${SETTINGS_ACTION}/open-plugin-registry`,
22
+ {
23
+ input: S.Void,
24
+ output: S.Void,
25
+ },
26
+ ) {}
17
27
  }
@@ -0,0 +1,122 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { createExtension, type Node } from '@dxos/app-graph';
6
+ import { type SettingsStore, type SettingsValue } from '@dxos/local-storage';
7
+ import { isNonNullable } from '@dxos/util';
8
+
9
+ import { SETTINGS_ID, SETTINGS_KEY, SETTINGS_PLUGIN, SettingsAction } from './actions';
10
+ import { Capabilities } from '../common';
11
+ import { contributes, type PluginMeta, type PluginsContext } from '../core';
12
+ import { createIntent } from '../plugin-intent';
13
+
14
+ export default (context: PluginsContext) =>
15
+ contributes(Capabilities.AppGraphBuilder, [
16
+ createExtension({
17
+ id: `${SETTINGS_PLUGIN}/action`,
18
+ filter: (node): node is Node<null> => node.id === 'root',
19
+ actions: () => [
20
+ {
21
+ id: SETTINGS_PLUGIN,
22
+ data: async () => {
23
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
24
+ await dispatch(createIntent(SettingsAction.Open));
25
+ },
26
+ properties: {
27
+ label: ['open settings label', { ns: SETTINGS_PLUGIN }],
28
+ icon: 'ph--gear--regular',
29
+ keyBinding: {
30
+ macos: 'meta+,',
31
+ windows: 'alt+,',
32
+ },
33
+ },
34
+ },
35
+ ],
36
+ }),
37
+ createExtension({
38
+ id: `${SETTINGS_PLUGIN}/core`,
39
+ filter: (node): node is Node<null> => node.id === 'root',
40
+ connector: () => [
41
+ {
42
+ id: SETTINGS_ID,
43
+ type: SETTINGS_PLUGIN,
44
+ properties: {
45
+ label: ['app settings label', { ns: SETTINGS_PLUGIN }],
46
+ icon: 'ph--gear--regular',
47
+ disposition: 'pin-end',
48
+ position: 'hoist',
49
+ testId: 'treeView.appSettings',
50
+ },
51
+ },
52
+ ],
53
+ }),
54
+ createExtension({
55
+ id: `${SETTINGS_PLUGIN}/core-plugins`,
56
+ filter: (node): node is Node<null> => node.id === SETTINGS_ID,
57
+ connector: () => {
58
+ const manager = context.requestCapability(Capabilities.PluginManager);
59
+ const [settingsStore] = context.requestCapabilities(Capabilities.SettingsStore);
60
+ return [
61
+ ...manager.plugins
62
+ .filter((plugin) => manager.core.includes(plugin.meta.id))
63
+ .map((plugin): [PluginMeta, SettingsStore<SettingsValue>] | null => {
64
+ const settings = settingsStore?.getStore(plugin.meta.id);
65
+ if (!settings) {
66
+ return null;
67
+ }
68
+
69
+ return [plugin.meta, settings];
70
+ })
71
+ .filter(isNonNullable)
72
+ .map(([meta, settings]) => ({
73
+ id: `${SETTINGS_KEY}:${meta.id.replaceAll('/', ':')}`,
74
+ type: 'category',
75
+ data: settings,
76
+ properties: {
77
+ label: meta.name ?? meta.id,
78
+ icon: meta.icon ?? 'ph--circle--regular',
79
+ },
80
+ })),
81
+
82
+ {
83
+ id: `${SETTINGS_KEY}:custom-plugins`,
84
+ type: 'collection',
85
+ properties: {
86
+ label: ['custom plugins label', { ns: SETTINGS_PLUGIN }],
87
+ icon: 'ph--squares-four--regular',
88
+ role: 'branch',
89
+ },
90
+ },
91
+ ];
92
+ },
93
+ }),
94
+ createExtension({
95
+ id: `${SETTINGS_PLUGIN}/custom-plugins`,
96
+ filter: (node): node is Node<null> => node.id === `${SETTINGS_KEY}:custom-plugins`,
97
+ connector: () => {
98
+ const manager = context.requestCapability(Capabilities.PluginManager);
99
+ const [settingsStore] = context.requestCapabilities(Capabilities.SettingsStore);
100
+ return manager.plugins
101
+ .filter((plugin) => !manager.core.includes(plugin.meta.id))
102
+ .map((plugin): [PluginMeta, SettingsStore<SettingsValue>] | null => {
103
+ const settings = settingsStore?.getStore(plugin.meta.id);
104
+ if (!settings) {
105
+ return null;
106
+ }
107
+
108
+ return [plugin.meta, settings];
109
+ })
110
+ .filter(isNonNullable)
111
+ .map(([meta, settings]) => ({
112
+ id: `${SETTINGS_KEY}:${meta.id.replaceAll('/', ':')}`,
113
+ type: 'category',
114
+ data: settings,
115
+ properties: {
116
+ label: meta.name ?? meta.id,
117
+ icon: meta.icon ?? 'ph--circle--regular',
118
+ },
119
+ }));
120
+ },
121
+ }),
122
+ ]);
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { pipe } from 'effect';
6
+
7
+ import { SETTINGS_ID, SETTINGS_KEY, SettingsAction } from './actions';
8
+ import { Capabilities, LayoutAction } from '../common';
9
+ import { contributes } from '../core';
10
+ import { createResolver, createIntent, chain } from '../plugin-intent';
11
+
12
+ export default () =>
13
+ contributes(
14
+ Capabilities.IntentResolver,
15
+ createResolver({
16
+ intent: SettingsAction.Open,
17
+ resolve: ({ plugin }) => {
18
+ const openSettings = createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: SETTINGS_ID });
19
+ return {
20
+ intents: [
21
+ plugin
22
+ ? pipe(
23
+ openSettings,
24
+ chain(LayoutAction.Open, {
25
+ part: 'main',
26
+ subject: [`${SETTINGS_KEY}:${plugin.replaceAll('/', ':')}`],
27
+ }),
28
+ )
29
+ : openSettings,
30
+ ],
31
+ };
32
+ },
33
+ }),
34
+ );