@dxos/app-framework 0.3.11-main.47875f3 → 0.3.11-main.47fbf79

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 (34) hide show
  1. package/dist/lib/browser/{chunk-QYIBDGYI.mjs → chunk-7MQ2DVDI.mjs} +2 -3
  2. package/dist/lib/browser/{chunk-QYIBDGYI.mjs.map → chunk-7MQ2DVDI.mjs.map} +2 -2
  3. package/dist/lib/browser/index.mjs +14 -13
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/{plugin-YLDDUID4.mjs → plugin-PGPBA2JO.mjs} +36 -13
  7. package/dist/lib/browser/plugin-PGPBA2JO.mjs.map +7 -0
  8. package/dist/lib/node/{chunk-YWRBMGDT.cjs → chunk-FETX4EGX.cjs} +5 -6
  9. package/dist/lib/node/{chunk-YWRBMGDT.cjs.map → chunk-FETX4EGX.cjs.map} +2 -2
  10. package/dist/lib/node/index.cjs +21 -20
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/{plugin-NP3IH5Q3.cjs → plugin-HLGHD6N5.cjs} +43 -19
  14. package/dist/lib/node/plugin-HLGHD6N5.cjs.map +7 -0
  15. package/dist/types/src/plugins/IntentPlugin/intent.d.ts +33 -2
  16. package/dist/types/src/plugins/IntentPlugin/intent.d.ts.map +1 -1
  17. package/dist/types/src/plugins/IntentPlugin/plugin.d.ts.map +1 -1
  18. package/dist/types/src/plugins/PluginHost/plugin.d.ts +1 -1
  19. package/dist/types/src/plugins/PluginHost/plugin.d.ts.map +1 -1
  20. package/dist/types/src/plugins/SurfacePlugin/Surface.d.ts +4 -5
  21. package/dist/types/src/plugins/SurfacePlugin/Surface.d.ts.map +1 -1
  22. package/dist/types/src/plugins/common/layout.d.ts +15 -14
  23. package/dist/types/src/plugins/common/layout.d.ts.map +1 -1
  24. package/package.json +8 -7
  25. package/src/plugins/IntentPlugin/IntentContext.tsx +1 -1
  26. package/src/plugins/IntentPlugin/intent.ts +38 -2
  27. package/src/plugins/IntentPlugin/plugin.tsx +38 -9
  28. package/src/plugins/PluginHost/plugin.ts +1 -1
  29. package/src/plugins/SurfacePlugin/Surface.tsx +15 -21
  30. package/src/plugins/common/layout.ts +15 -14
  31. package/src/vite-env.d.ts +5 -0
  32. package/tsconfig.json +3 -0
  33. package/dist/lib/browser/plugin-YLDDUID4.mjs.map +0 -7
  34. package/dist/lib/node/plugin-NP3IH5Q3.cjs.map +0 -7
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/plugins/IntentPlugin/plugin.tsx"],
4
+ "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { deepSignal } from 'deepsignal/react';\nimport React from 'react';\n\nimport { log } from '@dxos/log';\n\nimport { type IntentContext, IntentProvider } from './IntentContext';\nimport type { Intent, IntentResolver, IntentResult } from './intent';\nimport IntentMeta from './meta';\nimport { type IntentPluginProvides, type IntentResolverProvides, parseIntentResolverPlugin } from './provides';\nimport type { PluginDefinition } from '../PluginHost';\nimport { filterPlugins, findPlugin } from '../helpers';\n\nconst EXECUTION_LIMIT = 1000;\n\n/**\n * Allows plugins to register intent handlers and routes sent intents to the appropriate plugin.\n * Inspired by https://developer.android.com/reference/android/content/Intent.\n */\nconst IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {\n const state = deepSignal<IntentContext>({ dispatch: async () => ({}), registerResolver: () => () => {} });\n\n const dynamicResolvers = new Set<{ plugin: string; resolver: IntentResolver }>();\n\n return {\n meta: IntentMeta,\n ready: async (plugins) => {\n // Dispatch intent to associated plugin.\n const dispatch = async (intent: Intent) => {\n if (intent.plugin) {\n for (const entry of dynamicResolvers) {\n if (entry.plugin === intent.plugin) {\n const result = await entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n }\n\n const plugin = findPlugin<IntentResolverProvides>(plugins, intent.plugin);\n const result = plugin?.provides.intent.resolver(intent, plugins);\n return result;\n }\n\n for (const entry of dynamicResolvers) {\n const result = await entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // Return resolved value from first plugin that handles the intent.\n for (const plugin of filterPlugins(plugins, parseIntentResolverPlugin)) {\n const result = await plugin.provides.intent.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // https://vitejs.dev/guide/env-and-mode#env-variables\n // TODO(wittjosiah): How to handle this more generically?\n if (import.meta?.env?.DEV) {\n log.warn('No plugin found to handle intent', intent);\n }\n };\n\n // Sequentially dispatch array of invents.\n const dispatchChain = async (intentOrArray: Intent | Intent[], depth = 0) => {\n if (depth > EXECUTION_LIMIT) {\n return {\n error: new Error(\n `Intent execution limit exceeded (${EXECUTION_LIMIT} iterations). This is likely due to an infinite loop within intent resolvers.`,\n ),\n };\n }\n\n let result: IntentResult | undefined;\n for (const intent of Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray]) {\n const data = intent.data ? { result: result?.data, ...intent.data } : result?.data;\n result = (await dispatch({ ...intent, data })) ?? undefined;\n\n if (result?.error) {\n break;\n }\n\n result?.intents?.forEach((intents) => {\n void dispatchChain(intents, depth + 1);\n });\n }\n\n return result;\n };\n\n state.dispatch = dispatchChain;\n state.registerResolver = (plugin, resolver) => {\n const entry = { plugin, resolver };\n dynamicResolvers.add(entry);\n return () => dynamicResolvers.delete(entry);\n };\n },\n provides: {\n intent: state,\n context: ({ children }) => <IntentProvider value={state}>{children}</IntentProvider>,\n },\n };\n};\n\nexport default IntentPlugin;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,mBAA2B;AAC3B,IAAAA,gBAAkB;AAElB,iBAAoB;;;AASpB,IAAMC,kBAAkB;AAMxB,IAAMC,eAAe,MAAA;AACnB,QAAMC,YAAQC,yBAA0B;IAAEC,UAAU,aAAa,CAAC;IAAIC,kBAAkB,MAAM,MAAA;IAAO;EAAE,CAAA;AAEvG,QAAMC,mBAAmB,oBAAIC,IAAAA;AAE7B,SAAO;IACLC,MAAMC;IACNC,OAAO,OAAOC,YAAAA;AAEZ,YAAMP,WAAW,OAAOQ,WAAAA;AACtB,YAAIA,OAAOC,QAAQ;AACjB,qBAAWC,SAASR,kBAAkB;AACpC,gBAAIQ,MAAMD,WAAWD,OAAOC,QAAQ;AAClC,oBAAME,UAAS,MAAMD,MAAME,SAASJ,QAAQD,OAAAA;AAC5C,kBAAII,SAAQ;AACV,uBAAOA;cACT;YACF;UACF;AAEA,gBAAMF,aAASI,kCAAmCN,SAASC,OAAOC,MAAM;AACxE,gBAAME,SAASF,QAAQK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AACxD,iBAAOI;QACT;AAEA,mBAAWD,SAASR,kBAAkB;AACpC,gBAAMS,SAAS,MAAMD,MAAME,SAASJ,QAAQD,OAAAA;AAC5C,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAGA,mBAAWF,cAAUM,qCAAcR,SAASS,+CAAAA,GAA4B;AACtE,gBAAML,SAAS,MAAMF,OAAOK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AAC7D,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAIA,YAAI,aAAaM,KAAKC,KAAK;AACzBC,yBAAIC,KAAK,oCAAoCZ,QAAAA;;;;;;QAC/C;MACF;AAGA,YAAMa,gBAAgB,OAAOC,eAAkCC,QAAQ,MAAC;AACtE,YAAIA,QAAQ3B,iBAAiB;AAC3B,iBAAO;YACL4B,OAAO,IAAIC,MACT,oCAAoC7B,eAAAA,+EAA8F;UAEtI;QACF;AAEA,YAAIe;AACJ,mBAAWH,UAAUkB,MAAMC,QAAQL,aAAAA,IAAiBA,gBAAgB;UAACA;WAAgB;AACnF,gBAAMM,OAAOpB,OAAOoB,OAAO;YAAEjB,QAAQA,QAAQiB;YAAM,GAAGpB,OAAOoB;UAAK,IAAIjB,QAAQiB;AAC9EjB,mBAAU,MAAMX,SAAS;YAAE,GAAGQ;YAAQoB;UAAK,CAAA,KAAOC;AAElD,cAAIlB,QAAQa,OAAO;AACjB;UACF;AAEAb,kBAAQmB,SAASC,QAAQ,CAACD,YAAAA;AACxB,iBAAKT,cAAcS,SAASP,QAAQ,CAAA;UACtC,CAAA;QACF;AAEA,eAAOZ;MACT;AAEAb,YAAME,WAAWqB;AACjBvB,YAAMG,mBAAmB,CAACQ,QAAQG,aAAAA;AAChC,cAAMF,QAAQ;UAAED;UAAQG;QAAS;AACjCV,yBAAiB8B,IAAItB,KAAAA;AACrB,eAAO,MAAMR,iBAAiB+B,OAAOvB,KAAAA;MACvC;IACF;IACAI,UAAU;MACRN,QAAQV;MACRoC,SAAS,CAAC,EAAEC,SAAQ,MAAO,8BAAAC,QAAA,cAACC,sCAAAA;QAAeC,OAAOxC;SAAQqC,QAAAA;IAC5D;EACF;AACF;AAEA,IAAA,iBAAetC;",
6
+ "names": ["import_react", "EXECUTION_LIMIT", "IntentPlugin", "state", "deepSignal", "dispatch", "registerResolver", "dynamicResolvers", "Set", "meta", "IntentMeta", "ready", "plugins", "intent", "plugin", "entry", "result", "resolver", "findPlugin", "provides", "filterPlugins", "parseIntentResolverPlugin", "env", "DEV", "log", "warn", "dispatchChain", "intentOrArray", "depth", "error", "Error", "Array", "isArray", "data", "undefined", "intents", "forEach", "add", "delete", "context", "children", "React", "IntentProvider", "value"]
7
+ }
@@ -1,4 +1,11 @@
1
+ import type { MaybePromise } from '@dxos/util';
1
2
  import type { Plugin } from '../PluginHost';
3
+ export type IntentData<T extends Record<string, any> = Record<string, any>> = T & {
4
+ /**
5
+ * The data from the result of the previous intent.
6
+ */
7
+ result?: any;
8
+ };
2
9
  /**
3
10
  * An intent is an abstract description of an operation to be performed.
4
11
  * Intents allow actions to be performed across plugins.
@@ -17,7 +24,25 @@ export type Intent = {
17
24
  /**
18
25
  * Any data needed to perform the desired action.
19
26
  */
27
+ data?: IntentData;
28
+ };
29
+ export type IntentResult = {
30
+ /**
31
+ * The output of the action that was performed.
32
+ *
33
+ * If the intent is apart of a chain of intents, the data will be passed to the next intent.
34
+ */
20
35
  data?: any;
36
+ /**
37
+ * An error that occurred while performing the action.
38
+ *
39
+ * If the intent is apart of a chain of intents and an error occurs, the chain will be aborted.
40
+ */
41
+ error?: Error;
42
+ /**
43
+ * Other intent chains to be triggered.
44
+ */
45
+ intents?: Intent[][];
21
46
  };
22
47
  /**
23
48
  * Trigger one or more intents to be sent.
@@ -25,6 +50,12 @@ export type Intent = {
25
50
  *
26
51
  * @returns The result of the last intent.
27
52
  */
28
- export type IntentDispatcher = (intent: Intent | Intent[]) => Promise<any>;
29
- export type IntentResolver = (intent: Intent, plugins: Plugin[]) => any;
53
+ export type IntentDispatcher = (intent: Intent | Intent[]) => Promise<IntentResult | void>;
54
+ /**
55
+ * Resolves an intent that was dispatched.
56
+ * If the intent is not handled, nothing should be returned.
57
+ *
58
+ * @returns The result of the intent.
59
+ */
60
+ export type IntentResolver = (intent: Intent, plugins: Plugin[]) => MaybePromise<IntentResult | void>;
30
61
  //# sourceMappingURL=intent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/IntentPlugin/intent.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAE3E,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC"}
1
+ {"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/IntentPlugin/intent.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG;IAChF;;OAEG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC;CACd,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;IAEX;;;;OAIG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/IntentPlugin/plugin.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,oBAAoB,EAA0D,MAAM,YAAY,CAAC;AAC/G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD;;;GAGG;AACH,QAAA,MAAM,YAAY,QAAO,iBAAiB,oBAAoB,CA6D7D,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/IntentPlugin/plugin.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,oBAAoB,EAA0D,MAAM,YAAY,CAAC;AAC/G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAKtD;;;GAGG;AACH,QAAA,MAAM,YAAY,QAAO,iBAAiB,oBAAoB,CAsF7D,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -70,7 +70,7 @@ export type PluginDefinition<TProvides = {}, TInitializeProvides = {}> = Omit<Pl
70
70
  *
71
71
  * @return Capabilities provided by the plugin which are merged with base capabilities.
72
72
  */
73
- initialize?: () => Promise<PluginProvides<TInitializeProvides>>;
73
+ initialize?: () => Promise<PluginProvides<TInitializeProvides> | void>;
74
74
  /**
75
75
  * Called once all plugins have been initialized.
76
76
  * This is the place to do any initialization which requires other plugins to be ready.
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/PluginHost/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,SAAS,IAAI,SAAS,GAAG;IAClD;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC;IAKhC,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC;CAC9B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,MAAM,CAAC,SAAS,GAAG,EAAE,IAAI;IACnC,IAAI,EAAE;QACJ;;;;;;WAMG;QACH,EAAE,EAAE,MAAM,CAAC;QAEX;;;;WAIG;QAEH,OAAO,CAAC,EAAE,MAAM,CAAC;QAEjB;;WAEG;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;QAEd;;WAEG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB;;WAEG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAElB;;WAEG;QACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAEhB;;WAEG;QAEH,aAAa,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;KAC/B,CAAC;IAEF;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,SAAS,GAAG,EAAE,EAAE,mBAAmB,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG;IAClG;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,CAAC;IAEzC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAEhE;;;;;OAKG;IAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,UAAU,SAAU,MAAM,CAAC,MAAM,CAAC;IAlF3C;;;;;;OAMG;QACC,MAAM;IAEV;;;;OAIG;;IAIH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;CA2CiD,CAAC;AAEzD,KAAK,UAAU,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,gBAAgB,CAAA;CAAE,CAAC,CAAC;AAEhF,yBAAiB,MAAM,CAAC;IACf,MAAM,IAAI,yFAKhB,CAAC;CACH"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/PluginHost/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,SAAS,IAAI,SAAS,GAAG;IAClD;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC;IAKhC,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC;CAC9B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,MAAM,CAAC,SAAS,GAAG,EAAE,IAAI;IACnC,IAAI,EAAE;QACJ;;;;;;WAMG;QACH,EAAE,EAAE,MAAM,CAAC;QAEX;;;;WAIG;QAEH,OAAO,CAAC,EAAE,MAAM,CAAC;QAEjB;;WAEG;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;QAEd;;WAEG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QAErB;;WAEG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAElB;;WAEG;QACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAEhB;;WAEG;QAEH,aAAa,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;KAC/B,CAAC;IAEF;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,SAAS,GAAG,EAAE,EAAE,mBAAmB,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG;IAClG;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,CAAC;IAEzC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,GAAG,IAAI,CAAC,CAAC;IAEvE;;;;;OAKG;IAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,UAAU,SAAU,MAAM,CAAC,MAAM,CAAC;IAlF3C;;;;;;OAMG;QACC,MAAM;IAEV;;;;OAIG;;IAIH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;CA2CiD,CAAC;AAEzD,KAAK,UAAU,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,gBAAgB,CAAA;CAAE,CAAC,CAAC;AAEhF,yBAAiB,MAAM,CAAC;IACf,MAAM,IAAI,yFAKhB,CAAC;CACH"}
@@ -1,4 +1,4 @@
1
- import React, { type FC, type PropsWithChildren } from 'react';
1
+ import React, { type ReactNode, type PropsWithChildren } from 'react';
2
2
  import { ErrorBoundary } from './ErrorBoundary';
3
3
  /**
4
4
  * Direction determines how multiple components are laid out.
@@ -28,11 +28,10 @@ export type SurfaceProps = PropsWithChildren<{
28
28
  */
29
29
  fallback?: ErrorBoundary['props']['fallback'];
30
30
  /**
31
- * If specified, this will render if no plugin can offer renderable content for the surface.
32
- *
33
- * @deprecated Provide a component with `disposition: 'fallback'` instead.
31
+ * If specified, the Surface will be wrapped in a suspense boundary.
32
+ * The placeholder component will be rendered while the surface component is loading.
34
33
  */
35
- contentFallback?: FC<{}>;
34
+ placeholder?: ReactNode;
36
35
  /**
37
36
  * If more than one component is resolved, the limit determines how many are rendered.
38
37
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Surface.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/SurfacePlugin/Surface.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAEZ,KAAK,EAAE,EAIP,KAAK,iBAAiB,EAEvB,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,GAAG,eAAe,CAAC;AAEhF,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC;IAC3C;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE/B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC;IAEnE;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC;IAE9C;;;;OAIG;IACH,eAAe,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAEzB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;;OAGG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO,yGAkBnB,CAAC"}
1
+ {"version":3,"file":"Surface.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/SurfacePlugin/Surface.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAEZ,KAAK,SAAS,EAGd,KAAK,iBAAiB,EAGvB,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,GAAG,eAAe,CAAC;AAEhF,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC;IAC3C;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE/B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC;IAEnE;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC;IAE9C;;;OAGG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IAExB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE3B;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;;OAGG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO,yGAiBnB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type IntentData } from '../IntentPlugin';
2
3
  import type { Plugin } from '../PluginHost';
3
4
  /**
4
5
  * Basic state provided by a layout plugin.
@@ -63,28 +64,28 @@ export declare enum LayoutAction {
63
64
  * Expected payload for layout actions.
64
65
  */
65
66
  export declare namespace LayoutAction {
66
- type ToggleFullscreen = {
67
+ type ToggleFullscreen = IntentData<{
67
68
  state?: boolean;
68
- };
69
- type ToggleSidebar = {
69
+ }>;
70
+ type ToggleSidebar = IntentData<{
70
71
  state?: boolean;
71
- };
72
- type ToggleComplementarySidebar = {
72
+ }>;
73
+ type ToggleComplementarySidebar = IntentData<{
73
74
  state?: boolean;
74
- };
75
- type OpenDialog = {
75
+ }>;
76
+ type OpenDialog = IntentData<{
76
77
  component: string;
77
78
  subject: any;
78
- };
79
- type CloseDialog = {};
80
- type OpenPopover = {
79
+ }>;
80
+ type CloseDialog = IntentData<{}>;
81
+ type OpenPopover = IntentData<{
81
82
  anchorId: string;
82
83
  component: string;
83
84
  subject: any;
84
- };
85
- type ClosePopover = {};
86
- type Activate = {
85
+ }>;
86
+ type ClosePopover = IntentData<{}>;
87
+ type Activate = IntentData<{
87
88
  id: string;
88
- };
89
+ }>;
89
90
  }
90
91
  //# sourceMappingURL=layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/common/layout.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAM5C;;GAEG;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBjB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE5C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,WAAY,MAAM,uCAG/C,CAAC;AAQF,oBAAY,YAAY;IACtB,iBAAiB,6CAAuC;IACxD,cAAc,0CAAoC;IAClD,4BAA4B,wDAAkD;IAC9E,WAAW,uCAAiC;IAC5C,YAAY,wCAAkC;IAC9C,YAAY,wCAAkC;IAC9C,aAAa,yCAAmC;IAChD,QAAQ,oCAA8B;IACtC,KAAK,iCAA2B;CACjC;AAED;;GAEG;AACH,yBAAiB,YAAY,CAAC;IAC5B,KAAY,gBAAgB,GAAG;QAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IAEF,KAAY,aAAa,GAAG;QAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IAEF,KAAY,0BAA0B,GAAG;QACvC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IAEF,KAAY,UAAU,GAAG;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,GAAG,CAAC;KACd,CAAC;IAEF,KAAY,WAAW,GAAG,EAAE,CAAC;IAE7B,KAAY,WAAW,GAAG;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,GAAG,CAAC;KACd,CAAC;IAEF,KAAY,YAAY,GAAG,EAAE,CAAC;IAE9B,KAAY,QAAQ,GAAG;QACrB,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC;CACH"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/common/layout.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAM5C;;GAEG;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBjB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAE5C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,WAAY,MAAM,uCAG/C,CAAC;AAQF,oBAAY,YAAY;IACtB,iBAAiB,6CAAuC;IACxD,cAAc,0CAAoC;IAClD,4BAA4B,wDAAkD;IAC9E,WAAW,uCAAiC;IAC5C,YAAY,wCAAkC;IAC9C,YAAY,wCAAkC;IAC9C,aAAa,yCAAmC;IAChD,QAAQ,oCAA8B;IACtC,KAAK,iCAA2B;CACjC;AAED;;GAEG;AACH,yBAAiB,YAAY,CAAC;IAC5B,KAAY,gBAAgB,GAAG,UAAU,CAAC;QACxC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC,CAAC;IAEH,KAAY,aAAa,GAAG,UAAU,CAAC;QACrC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC,CAAC;IAEH,KAAY,0BAA0B,GAAG,UAAU,CAAC;QAClD,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC,CAAC;IAEH,KAAY,UAAU,GAAG,UAAU,CAAC;QAClC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,GAAG,CAAC;KACd,CAAC,CAAC;IAEH,KAAY,WAAW,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAEzC,KAAY,WAAW,GAAG,UAAU,CAAC;QACnC,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,GAAG,CAAC;KACd,CAAC,CAAC;IAEH,KAAY,YAAY,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAE1C,KAAY,QAAQ,GAAG,UAAU,CAAC;QAChC,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC,CAAC;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/app-framework",
3
- "version": "0.3.11-main.47875f3",
3
+ "version": "0.3.11-main.47fbf79",
4
4
  "description": "A framework for building applications from composible plugins.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -16,12 +16,13 @@
16
16
  "@preact/signals-react": "^1.3.6",
17
17
  "deepsignal": "1.4.0-shallow.0",
18
18
  "zod": "^3.22.4",
19
- "@dxos/app-graph": "0.3.11-main.47875f3",
20
- "@dxos/async": "0.3.11-main.47875f3",
21
- "@dxos/debug": "0.3.11-main.47875f3",
22
- "@dxos/invariant": "0.3.11-main.47875f3",
23
- "@dxos/log": "0.3.11-main.47875f3",
24
- "@dxos/local-storage": "0.3.11-main.47875f3"
19
+ "@dxos/app-graph": "0.3.11-main.47fbf79",
20
+ "@dxos/async": "0.3.11-main.47fbf79",
21
+ "@dxos/debug": "0.3.11-main.47fbf79",
22
+ "@dxos/invariant": "0.3.11-main.47fbf79",
23
+ "@dxos/local-storage": "0.3.11-main.47fbf79",
24
+ "@dxos/log": "0.3.11-main.47fbf79",
25
+ "@dxos/util": "0.3.11-main.47fbf79"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@types/react": "^18.0.21",
@@ -15,7 +15,7 @@ export type IntentContext = {
15
15
  };
16
16
 
17
17
  const IntentContext: Context<IntentContext> = createContext<IntentContext>({
18
- dispatch: async () => {},
18
+ dispatch: async () => ({}),
19
19
  registerResolver: () => () => {},
20
20
  });
21
21
 
@@ -2,8 +2,17 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import type { MaybePromise } from '@dxos/util';
6
+
5
7
  import type { Plugin } from '../PluginHost';
6
8
 
9
+ export type IntentData<T extends Record<string, any> = Record<string, any>> = T & {
10
+ /**
11
+ * The data from the result of the previous intent.
12
+ */
13
+ result?: any;
14
+ };
15
+
7
16
  /**
8
17
  * An intent is an abstract description of an operation to be performed.
9
18
  * Intents allow actions to be performed across plugins.
@@ -24,7 +33,28 @@ export type Intent = {
24
33
  /**
25
34
  * Any data needed to perform the desired action.
26
35
  */
36
+ data?: IntentData;
37
+ };
38
+
39
+ export type IntentResult = {
40
+ /**
41
+ * The output of the action that was performed.
42
+ *
43
+ * If the intent is apart of a chain of intents, the data will be passed to the next intent.
44
+ */
27
45
  data?: any;
46
+
47
+ /**
48
+ * An error that occurred while performing the action.
49
+ *
50
+ * If the intent is apart of a chain of intents and an error occurs, the chain will be aborted.
51
+ */
52
+ error?: Error;
53
+
54
+ /**
55
+ * Other intent chains to be triggered.
56
+ */
57
+ intents?: Intent[][];
28
58
  };
29
59
 
30
60
  /**
@@ -33,6 +63,12 @@ export type Intent = {
33
63
  *
34
64
  * @returns The result of the last intent.
35
65
  */
36
- export type IntentDispatcher = (intent: Intent | Intent[]) => Promise<any>;
66
+ export type IntentDispatcher = (intent: Intent | Intent[]) => Promise<IntentResult | void>;
37
67
 
38
- export type IntentResolver = (intent: Intent, plugins: Plugin[]) => any;
68
+ /**
69
+ * Resolves an intent that was dispatched.
70
+ * If the intent is not handled, nothing should be returned.
71
+ *
72
+ * @returns The result of the intent.
73
+ */
74
+ export type IntentResolver = (intent: Intent, plugins: Plugin[]) => MaybePromise<IntentResult | void>;
@@ -5,19 +5,23 @@
5
5
  import { deepSignal } from 'deepsignal/react';
6
6
  import React from 'react';
7
7
 
8
+ import { log } from '@dxos/log';
9
+
8
10
  import { type IntentContext, IntentProvider } from './IntentContext';
9
- import type { Intent, IntentResolver } from './intent';
11
+ import type { Intent, IntentResolver, IntentResult } from './intent';
10
12
  import IntentMeta from './meta';
11
13
  import { type IntentPluginProvides, type IntentResolverProvides, parseIntentResolverPlugin } from './provides';
12
14
  import type { PluginDefinition } from '../PluginHost';
13
15
  import { filterPlugins, findPlugin } from '../helpers';
14
16
 
17
+ const EXECUTION_LIMIT = 1000;
18
+
15
19
  /**
16
20
  * Allows plugins to register intent handlers and routes sent intents to the appropriate plugin.
17
21
  * Inspired by https://developer.android.com/reference/android/content/Intent.
18
22
  */
19
23
  const IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {
20
- const state = deepSignal<IntentContext>({ dispatch: async () => {}, registerResolver: () => () => {} });
24
+ const state = deepSignal<IntentContext>({ dispatch: async () => ({}), registerResolver: () => () => {} });
21
25
 
22
26
  const dynamicResolvers = new Set<{ plugin: string; resolver: IntentResolver }>();
23
27
 
@@ -29,7 +33,7 @@ const IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {
29
33
  if (intent.plugin) {
30
34
  for (const entry of dynamicResolvers) {
31
35
  if (entry.plugin === intent.plugin) {
32
- const result = entry.resolver(intent, plugins);
36
+ const result = await entry.resolver(intent, plugins);
33
37
  if (result) {
34
38
  return result;
35
39
  }
@@ -37,11 +41,12 @@ const IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {
37
41
  }
38
42
 
39
43
  const plugin = findPlugin<IntentResolverProvides>(plugins, intent.plugin);
40
- return plugin?.provides.intent.resolver(intent, plugins);
44
+ const result = plugin?.provides.intent.resolver(intent, plugins);
45
+ return result;
41
46
  }
42
47
 
43
48
  for (const entry of dynamicResolvers) {
44
- const result = entry.resolver(intent, plugins);
49
+ const result = await entry.resolver(intent, plugins);
45
50
  if (result) {
46
51
  return result;
47
52
  }
@@ -54,18 +59,42 @@ const IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {
54
59
  return result;
55
60
  }
56
61
  }
62
+
63
+ // https://vitejs.dev/guide/env-and-mode#env-variables
64
+ // TODO(wittjosiah): How to handle this more generically?
65
+ if (import.meta?.env?.DEV) {
66
+ log.warn('No plugin found to handle intent', intent);
67
+ }
57
68
  };
58
69
 
59
70
  // Sequentially dispatch array of invents.
60
- state.dispatch = async (intentOrArray) => {
61
- let result: any = null;
71
+ const dispatchChain = async (intentOrArray: Intent | Intent[], depth = 0) => {
72
+ if (depth > EXECUTION_LIMIT) {
73
+ return {
74
+ error: new Error(
75
+ `Intent execution limit exceeded (${EXECUTION_LIMIT} iterations). This is likely due to an infinite loop within intent resolvers.`,
76
+ ),
77
+ };
78
+ }
79
+
80
+ let result: IntentResult | undefined;
62
81
  for (const intent of Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray]) {
63
- const data = intent.data ? { ...result, ...intent.data } : result;
64
- result = await dispatch({ ...intent, data });
82
+ const data = intent.data ? { result: result?.data, ...intent.data } : result?.data;
83
+ result = (await dispatch({ ...intent, data })) ?? undefined;
84
+
85
+ if (result?.error) {
86
+ break;
87
+ }
88
+
89
+ result?.intents?.forEach((intents) => {
90
+ void dispatchChain(intents, depth + 1);
91
+ });
65
92
  }
93
+
66
94
  return result;
67
95
  };
68
96
 
97
+ state.dispatch = dispatchChain;
69
98
  state.registerResolver = (plugin, resolver) => {
70
99
  const entry = { plugin, resolver };
71
100
  dynamicResolvers.add(entry);
@@ -91,7 +91,7 @@ export type PluginDefinition<TProvides = {}, TInitializeProvides = {}> = Omit<Pl
91
91
  *
92
92
  * @return Capabilities provided by the plugin which are merged with base capabilities.
93
93
  */
94
- initialize?: () => Promise<PluginProvides<TInitializeProvides>>;
94
+ initialize?: () => Promise<PluginProvides<TInitializeProvides> | void>;
95
95
 
96
96
  /**
97
97
  * Called once all plugins have been initialized.
@@ -4,12 +4,12 @@
4
4
 
5
5
  import React, {
6
6
  forwardRef,
7
- type FC,
8
7
  type ReactNode,
9
8
  Fragment,
10
9
  type ForwardedRef,
11
10
  type PropsWithChildren,
12
11
  isValidElement,
12
+ Suspense,
13
13
  } from 'react';
14
14
  import { createContext, useContext } from 'react';
15
15
 
@@ -50,11 +50,10 @@ export type SurfaceProps = PropsWithChildren<{
50
50
  fallback?: ErrorBoundary['props']['fallback'];
51
51
 
52
52
  /**
53
- * If specified, this will render if no plugin can offer renderable content for the surface.
54
- *
55
- * @deprecated Provide a component with `disposition: 'fallback'` instead.
53
+ * If specified, the Surface will be wrapped in a suspense boundary.
54
+ * The placeholder component will be rendered while the surface component is loading.
56
55
  */
57
- contentFallback?: FC<{}>;
56
+ placeholder?: ReactNode;
58
57
 
59
58
  /**
60
59
  * If more than one component is resolved, the limit determines how many are rendered.
@@ -78,21 +77,20 @@ export type SurfaceProps = PropsWithChildren<{
78
77
  * A surface is a named region of the screen that can be populated by plugins.
79
78
  */
80
79
  export const Surface = forwardRef<HTMLElement, SurfaceProps>(
81
- ({ role, name = role, fallback, ...rest }, forwardedRef) => {
80
+ ({ role, name = role, fallback, placeholder, ...rest }, forwardedRef) => {
82
81
  const props = { role, name, fallback, ...rest };
83
82
  const context = useContext(SurfaceContext);
84
83
  const data = props.data ?? ((name && context?.surfaces?.[name]?.data) || {});
85
84
 
86
- return (
87
- <>
88
- {fallback ? (
89
- <ErrorBoundary data={data} fallback={fallback}>
90
- <SurfaceResolver {...props} ref={forwardedRef} />
91
- </ErrorBoundary>
92
- ) : (
93
- <SurfaceResolver {...props} ref={forwardedRef} />
94
- )}
95
- </>
85
+ const resolver = <SurfaceResolver {...props} ref={forwardedRef} />;
86
+ const suspense = placeholder ? <Suspense fallback={placeholder}>{resolver}</Suspense> : resolver;
87
+
88
+ return fallback ? (
89
+ <ErrorBoundary data={data} fallback={fallback}>
90
+ {suspense}
91
+ </ErrorBoundary>
92
+ ) : (
93
+ suspense
96
94
  );
97
95
  },
98
96
  );
@@ -111,11 +109,7 @@ const SurfaceResolver = forwardRef<HTMLElement, SurfaceProps>((props, forwardedR
111
109
  },
112
110
  };
113
111
 
114
- return (
115
- <SurfaceContext.Provider value={currentContext}>
116
- {nodes.length > 0 ? nodes : props.contentFallback ? <props.contentFallback /> : null}
117
- </SurfaceContext.Provider>
118
- );
112
+ return <SurfaceContext.Provider value={currentContext}>{nodes}</SurfaceContext.Provider>;
119
113
  });
120
114
 
121
115
  const resolveNodes = (
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { z } from 'zod';
6
6
 
7
+ import { type IntentData } from '../IntentPlugin';
7
8
  import type { Plugin } from '../PluginHost';
8
9
 
9
10
  //
@@ -72,34 +73,34 @@ export enum LayoutAction {
72
73
  * Expected payload for layout actions.
73
74
  */
74
75
  export namespace LayoutAction {
75
- export type ToggleFullscreen = {
76
+ export type ToggleFullscreen = IntentData<{
76
77
  state?: boolean;
77
- };
78
+ }>;
78
79
 
79
- export type ToggleSidebar = {
80
+ export type ToggleSidebar = IntentData<{
80
81
  state?: boolean;
81
- };
82
+ }>;
82
83
 
83
- export type ToggleComplementarySidebar = {
84
+ export type ToggleComplementarySidebar = IntentData<{
84
85
  state?: boolean;
85
- };
86
+ }>;
86
87
 
87
- export type OpenDialog = {
88
+ export type OpenDialog = IntentData<{
88
89
  component: string;
89
90
  subject: any;
90
- };
91
+ }>;
91
92
 
92
- export type CloseDialog = {};
93
+ export type CloseDialog = IntentData<{}>;
93
94
 
94
- export type OpenPopover = {
95
+ export type OpenPopover = IntentData<{
95
96
  anchorId: string;
96
97
  component: string;
97
98
  subject: any;
98
- };
99
+ }>;
99
100
 
100
- export type ClosePopover = {};
101
+ export type ClosePopover = IntentData<{}>;
101
102
 
102
- export type Activate = {
103
+ export type Activate = IntentData<{
103
104
  id: string;
104
- };
105
+ }>;
105
106
  }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ /// <reference types="vite/client" />
package/tsconfig.json CHANGED
@@ -32,6 +32,9 @@
32
32
  {
33
33
  "path": "../../common/log"
34
34
  },
35
+ {
36
+ "path": "../../common/util"
37
+ },
35
38
  {
36
39
  "path": "../app-graph"
37
40
  }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/IntentPlugin/plugin.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { deepSignal } from 'deepsignal/react';\nimport React from 'react';\n\nimport { type IntentContext, IntentProvider } from './IntentContext';\nimport type { Intent, IntentResolver } from './intent';\nimport IntentMeta from './meta';\nimport { type IntentPluginProvides, type IntentResolverProvides, parseIntentResolverPlugin } from './provides';\nimport type { PluginDefinition } from '../PluginHost';\nimport { filterPlugins, findPlugin } from '../helpers';\n\n/**\n * Allows plugins to register intent handlers and routes sent intents to the appropriate plugin.\n * Inspired by https://developer.android.com/reference/android/content/Intent.\n */\nconst IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {\n const state = deepSignal<IntentContext>({ dispatch: async () => {}, registerResolver: () => () => {} });\n\n const dynamicResolvers = new Set<{ plugin: string; resolver: IntentResolver }>();\n\n return {\n meta: IntentMeta,\n ready: async (plugins) => {\n // Dispatch intent to associated plugin.\n const dispatch = async (intent: Intent) => {\n if (intent.plugin) {\n for (const entry of dynamicResolvers) {\n if (entry.plugin === intent.plugin) {\n const result = entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n }\n\n const plugin = findPlugin<IntentResolverProvides>(plugins, intent.plugin);\n return plugin?.provides.intent.resolver(intent, plugins);\n }\n\n for (const entry of dynamicResolvers) {\n const result = entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // Return resolved value from first plugin that handles the intent.\n for (const plugin of filterPlugins(plugins, parseIntentResolverPlugin)) {\n const result = await plugin.provides.intent.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n };\n\n // Sequentially dispatch array of invents.\n state.dispatch = async (intentOrArray) => {\n let result: any = null;\n for (const intent of Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray]) {\n const data = intent.data ? { ...result, ...intent.data } : result;\n result = await dispatch({ ...intent, data });\n }\n return result;\n };\n\n state.registerResolver = (plugin, resolver) => {\n const entry = { plugin, resolver };\n dynamicResolvers.add(entry);\n return () => dynamicResolvers.delete(entry);\n };\n },\n provides: {\n intent: state,\n context: ({ children }) => <IntentProvider value={state}>{children}</IntentProvider>,\n },\n };\n};\n\nexport default IntentPlugin;\n"],
5
- "mappings": ";;;;;;;;;;;AAIA,SAASA,kBAAkB;AAC3B,OAAOC,WAAW;AAalB,IAAMC,eAAe,MAAA;AACnB,QAAMC,QAAQC,WAA0B;IAAEC,UAAU,YAAA;IAAa;IAAGC,kBAAkB,MAAM,MAAA;IAAO;EAAE,CAAA;AAErG,QAAMC,mBAAmB,oBAAIC,IAAAA;AAE7B,SAAO;IACLC,MAAMC;IACNC,OAAO,OAAOC,YAAAA;AAEZ,YAAMP,WAAW,OAAOQ,WAAAA;AACtB,YAAIA,OAAOC,QAAQ;AACjB,qBAAWC,SAASR,kBAAkB;AACpC,gBAAIQ,MAAMD,WAAWD,OAAOC,QAAQ;AAClC,oBAAME,SAASD,MAAME,SAASJ,QAAQD,OAAAA;AACtC,kBAAII,QAAQ;AACV,uBAAOA;cACT;YACF;UACF;AAEA,gBAAMF,SAASI,WAAmCN,SAASC,OAAOC,MAAM;AACxE,iBAAOA,QAAQK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;QAClD;AAEA,mBAAWG,SAASR,kBAAkB;AACpC,gBAAMS,SAASD,MAAME,SAASJ,QAAQD,OAAAA;AACtC,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAGA,mBAAWF,UAAUM,cAAcR,SAASS,yBAAAA,GAA4B;AACtE,gBAAML,SAAS,MAAMF,OAAOK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AAC7D,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;MACF;AAGAb,YAAME,WAAW,OAAOiB,kBAAAA;AACtB,YAAIN,SAAc;AAClB,mBAAWH,UAAUU,MAAMC,QAAQF,aAAAA,IAAiBA,gBAAgB;UAACA;WAAgB;AACnF,gBAAMG,OAAOZ,OAAOY,OAAO;YAAE,GAAGT;YAAQ,GAAGH,OAAOY;UAAK,IAAIT;AAC3DA,mBAAS,MAAMX,SAAS;YAAE,GAAGQ;YAAQY;UAAK,CAAA;QAC5C;AACA,eAAOT;MACT;AAEAb,YAAMG,mBAAmB,CAACQ,QAAQG,aAAAA;AAChC,cAAMF,QAAQ;UAAED;UAAQG;QAAS;AACjCV,yBAAiBmB,IAAIX,KAAAA;AACrB,eAAO,MAAMR,iBAAiBoB,OAAOZ,KAAAA;MACvC;IACF;IACAI,UAAU;MACRN,QAAQV;MACRyB,SAAS,CAAC,EAAEC,SAAQ,MAAO,sBAAA,cAACC,gBAAAA;QAAeC,OAAO5B;SAAQ0B,QAAAA;IAC5D;EACF;AACF;AAEA,IAAA,iBAAe3B;",
6
- "names": ["deepSignal", "React", "IntentPlugin", "state", "deepSignal", "dispatch", "registerResolver", "dynamicResolvers", "Set", "meta", "IntentMeta", "ready", "plugins", "intent", "plugin", "entry", "result", "resolver", "findPlugin", "provides", "filterPlugins", "parseIntentResolverPlugin", "intentOrArray", "Array", "isArray", "data", "add", "delete", "context", "children", "IntentProvider", "value"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/IntentPlugin/plugin.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { deepSignal } from 'deepsignal/react';\nimport React from 'react';\n\nimport { type IntentContext, IntentProvider } from './IntentContext';\nimport type { Intent, IntentResolver } from './intent';\nimport IntentMeta from './meta';\nimport { type IntentPluginProvides, type IntentResolverProvides, parseIntentResolverPlugin } from './provides';\nimport type { PluginDefinition } from '../PluginHost';\nimport { filterPlugins, findPlugin } from '../helpers';\n\n/**\n * Allows plugins to register intent handlers and routes sent intents to the appropriate plugin.\n * Inspired by https://developer.android.com/reference/android/content/Intent.\n */\nconst IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {\n const state = deepSignal<IntentContext>({ dispatch: async () => {}, registerResolver: () => () => {} });\n\n const dynamicResolvers = new Set<{ plugin: string; resolver: IntentResolver }>();\n\n return {\n meta: IntentMeta,\n ready: async (plugins) => {\n // Dispatch intent to associated plugin.\n const dispatch = async (intent: Intent) => {\n if (intent.plugin) {\n for (const entry of dynamicResolvers) {\n if (entry.plugin === intent.plugin) {\n const result = entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n }\n\n const plugin = findPlugin<IntentResolverProvides>(plugins, intent.plugin);\n return plugin?.provides.intent.resolver(intent, plugins);\n }\n\n for (const entry of dynamicResolvers) {\n const result = entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // Return resolved value from first plugin that handles the intent.\n for (const plugin of filterPlugins(plugins, parseIntentResolverPlugin)) {\n const result = await plugin.provides.intent.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n };\n\n // Sequentially dispatch array of invents.\n state.dispatch = async (intentOrArray) => {\n let result: any = null;\n for (const intent of Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray]) {\n const data = intent.data ? { ...result, ...intent.data } : result;\n result = await dispatch({ ...intent, data });\n }\n return result;\n };\n\n state.registerResolver = (plugin, resolver) => {\n const entry = { plugin, resolver };\n dynamicResolvers.add(entry);\n return () => dynamicResolvers.delete(entry);\n };\n },\n provides: {\n intent: state,\n context: ({ children }) => <IntentProvider value={state}>{children}</IntentProvider>,\n },\n };\n};\n\nexport default IntentPlugin;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,mBAA2B;AAC3B,IAAAA,gBAAkB;AAalB,IAAMC,eAAe,MAAA;AACnB,QAAMC,YAAQC,yBAA0B;IAAEC,UAAU,YAAA;IAAa;IAAGC,kBAAkB,MAAM,MAAA;IAAO;EAAE,CAAA;AAErG,QAAMC,mBAAmB,oBAAIC,IAAAA;AAE7B,SAAO;IACLC,MAAMC;IACNC,OAAO,OAAOC,YAAAA;AAEZ,YAAMP,WAAW,OAAOQ,WAAAA;AACtB,YAAIA,OAAOC,QAAQ;AACjB,qBAAWC,SAASR,kBAAkB;AACpC,gBAAIQ,MAAMD,WAAWD,OAAOC,QAAQ;AAClC,oBAAME,SAASD,MAAME,SAASJ,QAAQD,OAAAA;AACtC,kBAAII,QAAQ;AACV,uBAAOA;cACT;YACF;UACF;AAEA,gBAAMF,aAASI,kCAAmCN,SAASC,OAAOC,MAAM;AACxE,iBAAOA,QAAQK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;QAClD;AAEA,mBAAWG,SAASR,kBAAkB;AACpC,gBAAMS,SAASD,MAAME,SAASJ,QAAQD,OAAAA;AACtC,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAGA,mBAAWF,cAAUM,qCAAcR,SAASS,+CAAAA,GAA4B;AACtE,gBAAML,SAAS,MAAMF,OAAOK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AAC7D,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;MACF;AAGAb,YAAME,WAAW,OAAOiB,kBAAAA;AACtB,YAAIN,SAAc;AAClB,mBAAWH,UAAUU,MAAMC,QAAQF,aAAAA,IAAiBA,gBAAgB;UAACA;WAAgB;AACnF,gBAAMG,OAAOZ,OAAOY,OAAO;YAAE,GAAGT;YAAQ,GAAGH,OAAOY;UAAK,IAAIT;AAC3DA,mBAAS,MAAMX,SAAS;YAAE,GAAGQ;YAAQY;UAAK,CAAA;QAC5C;AACA,eAAOT;MACT;AAEAb,YAAMG,mBAAmB,CAACQ,QAAQG,aAAAA;AAChC,cAAMF,QAAQ;UAAED;UAAQG;QAAS;AACjCV,yBAAiBmB,IAAIX,KAAAA;AACrB,eAAO,MAAMR,iBAAiBoB,OAAOZ,KAAAA;MACvC;IACF;IACAI,UAAU;MACRN,QAAQV;MACRyB,SAAS,CAAC,EAAEC,SAAQ,MAAO,8BAAAC,QAAA,cAACC,sCAAAA;QAAeC,OAAO7B;SAAQ0B,QAAAA;IAC5D;EACF;AACF;AAEA,IAAA,iBAAe3B;",
6
- "names": ["import_react", "IntentPlugin", "state", "deepSignal", "dispatch", "registerResolver", "dynamicResolvers", "Set", "meta", "IntentMeta", "ready", "plugins", "intent", "plugin", "entry", "result", "resolver", "findPlugin", "provides", "filterPlugins", "parseIntentResolverPlugin", "intentOrArray", "Array", "isArray", "data", "add", "delete", "context", "children", "React", "IntentProvider", "value"]
7
- }