@dxos/app-framework 0.8.4-main.e098934 → 0.8.4-main.ead640a

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 (157) hide show
  1. package/.storybook/main.mts +11 -0
  2. package/.storybook/preview.mts +8 -0
  3. package/dist/lib/browser/{app-graph-builder-AFFC6VB2.mjs → app-graph-builder-LG4RG2LM.mjs} +30 -29
  4. package/dist/lib/browser/app-graph-builder-LG4RG2LM.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-OZY7HV2A.mjs → chunk-2GRQ4QXA.mjs} +96 -93
  6. package/dist/lib/browser/chunk-2GRQ4QXA.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-T6M7JB7M.mjs → chunk-FRUTKCPG.mjs} +16 -20
  8. package/dist/lib/browser/chunk-FRUTKCPG.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-ORWHM7CO.mjs → chunk-SCPE4ZO2.mjs} +11 -8
  10. package/dist/lib/browser/chunk-SCPE4ZO2.mjs.map +7 -0
  11. package/dist/lib/browser/index.mjs +14 -28
  12. package/dist/lib/browser/index.mjs.map +3 -3
  13. package/dist/lib/browser/{intent-dispatcher-QG7UPGQX.mjs → intent-dispatcher-6SHA5B3N.mjs} +2 -2
  14. package/dist/lib/browser/{intent-resolver-4S4PSTM5.mjs → intent-resolver-UZZ4OANZ.mjs} +7 -7
  15. package/dist/lib/browser/intent-resolver-UZZ4OANZ.mjs.map +7 -0
  16. package/dist/lib/browser/meta.json +1 -1
  17. package/dist/lib/browser/{store-6E33KLGK.mjs → store-ACBEYK4B.mjs} +4 -4
  18. package/dist/lib/browser/{store-6E33KLGK.mjs.map → store-ACBEYK4B.mjs.map} +1 -1
  19. package/dist/lib/browser/testing/index.mjs +11 -14
  20. package/dist/lib/browser/testing/index.mjs.map +3 -3
  21. package/dist/lib/browser/worker.mjs +1 -9
  22. package/dist/lib/node-esm/{app-graph-builder-S4OAULX5.mjs → app-graph-builder-FMHVHPWA.mjs} +30 -29
  23. package/dist/lib/node-esm/app-graph-builder-FMHVHPWA.mjs.map +7 -0
  24. package/dist/lib/node-esm/{chunk-HJFU7QOR.mjs → chunk-CXT6CYPE.mjs} +16 -20
  25. package/dist/lib/node-esm/chunk-CXT6CYPE.mjs.map +7 -0
  26. package/dist/lib/node-esm/{chunk-F63ZRXMK.mjs → chunk-KSPOOYT3.mjs} +96 -93
  27. package/dist/lib/node-esm/chunk-KSPOOYT3.mjs.map +7 -0
  28. package/dist/lib/node-esm/{chunk-UMZQERLE.mjs → chunk-ZX63QUGE.mjs} +11 -8
  29. package/dist/lib/node-esm/chunk-ZX63QUGE.mjs.map +7 -0
  30. package/dist/lib/node-esm/index.mjs +14 -28
  31. package/dist/lib/node-esm/index.mjs.map +3 -3
  32. package/dist/lib/node-esm/{intent-dispatcher-NXBGPJOX.mjs → intent-dispatcher-SIYQ5ZIU.mjs} +2 -2
  33. package/dist/lib/node-esm/{intent-resolver-2ZKXI5ET.mjs → intent-resolver-7FYJMXAG.mjs} +7 -7
  34. package/dist/lib/node-esm/intent-resolver-7FYJMXAG.mjs.map +7 -0
  35. package/dist/lib/node-esm/meta.json +1 -1
  36. package/dist/lib/node-esm/{store-QQUTQHHT.mjs → store-6OBLTVXC.mjs} +4 -4
  37. package/dist/lib/node-esm/{store-QQUTQHHT.mjs.map → store-6OBLTVXC.mjs.map} +1 -1
  38. package/dist/lib/node-esm/testing/index.mjs +11 -14
  39. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  40. package/dist/lib/node-esm/worker.mjs +1 -9
  41. package/dist/types/src/common/capabilities.d.ts +14 -4
  42. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  43. package/dist/types/src/common/collaboration.d.ts +1 -1
  44. package/dist/types/src/common/collaboration.d.ts.map +1 -1
  45. package/dist/types/src/common/file.d.ts +1 -1
  46. package/dist/types/src/common/file.d.ts.map +1 -1
  47. package/dist/types/src/common/layout.d.ts +1 -3
  48. package/dist/types/src/common/layout.d.ts.map +1 -1
  49. package/dist/types/src/common/surface.d.ts +7 -13
  50. package/dist/types/src/common/surface.d.ts.map +1 -1
  51. package/dist/types/src/common/translations.d.ts +1 -1
  52. package/dist/types/src/common/translations.d.ts.map +1 -1
  53. package/dist/types/src/components/App.stories.d.ts +0 -1
  54. package/dist/types/src/components/App.stories.d.ts.map +1 -1
  55. package/dist/types/src/components/useApp.d.ts.map +1 -1
  56. package/dist/types/src/core/capabilities.d.ts +1 -1
  57. package/dist/types/src/core/capabilities.d.ts.map +1 -1
  58. package/dist/types/src/core/manager.d.ts.map +1 -1
  59. package/dist/types/src/core/plugin.d.ts +4 -1
  60. package/dist/types/src/core/plugin.d.ts.map +1 -1
  61. package/dist/types/src/playground/debug/plugin.d.ts +1 -1
  62. package/dist/types/src/playground/debug/plugin.d.ts.map +1 -1
  63. package/dist/types/src/playground/generator/generator.d.ts +1 -1
  64. package/dist/types/src/playground/generator/generator.d.ts.map +1 -1
  65. package/dist/types/src/playground/generator/plugin.d.ts +1 -1
  66. package/dist/types/src/playground/generator/plugin.d.ts.map +1 -1
  67. package/dist/types/src/playground/layout/plugin.d.ts +1 -1
  68. package/dist/types/src/playground/layout/plugin.d.ts.map +1 -1
  69. package/dist/types/src/playground/logger/plugin.d.ts +1 -1
  70. package/dist/types/src/playground/logger/plugin.d.ts.map +1 -1
  71. package/dist/types/src/playground/logger/schema.d.ts +1 -1
  72. package/dist/types/src/playground/logger/schema.d.ts.map +1 -1
  73. package/dist/types/src/playground/playground.stories.d.ts +0 -1
  74. package/dist/types/src/playground/playground.stories.d.ts.map +1 -1
  75. package/dist/types/src/plugin-intent/IntentPlugin.d.ts +1 -1
  76. package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +1 -1
  77. package/dist/types/src/plugin-intent/actions.d.ts +5 -7
  78. package/dist/types/src/plugin-intent/actions.d.ts.map +1 -1
  79. package/dist/types/src/plugin-intent/errors.d.ts.map +1 -1
  80. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts +4 -4
  81. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +1 -1
  82. package/dist/types/src/plugin-intent/intent.d.ts +1 -1
  83. package/dist/types/src/plugin-intent/intent.d.ts.map +1 -1
  84. package/dist/types/src/plugin-intent/meta.d.ts +3 -0
  85. package/dist/types/src/plugin-intent/meta.d.ts.map +1 -0
  86. package/dist/types/src/plugin-settings/SettingsPlugin.d.ts +1 -1
  87. package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +1 -1
  88. package/dist/types/src/plugin-settings/actions.d.ts +5 -7
  89. package/dist/types/src/plugin-settings/actions.d.ts.map +1 -1
  90. package/dist/types/src/plugin-settings/app-graph-builder.d.ts.map +1 -1
  91. package/dist/types/src/plugin-settings/meta.d.ts +3 -0
  92. package/dist/types/src/plugin-settings/meta.d.ts.map +1 -0
  93. package/dist/types/src/plugin-settings/translations.d.ts +2 -1
  94. package/dist/types/src/plugin-settings/translations.d.ts.map +1 -1
  95. package/dist/types/src/react/ErrorBoundary.d.ts +2 -2
  96. package/dist/types/src/react/ErrorBoundary.d.ts.map +1 -1
  97. package/dist/types/src/react/Surface.d.ts +2 -2
  98. package/dist/types/src/react/Surface.d.ts.map +1 -1
  99. package/dist/types/src/react/Surface.stories.d.ts +0 -1
  100. package/dist/types/src/react/Surface.stories.d.ts.map +1 -1
  101. package/dist/types/src/testing/withPluginManager.d.ts +2 -4
  102. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  103. package/dist/types/tsconfig.tsbuildinfo +1 -1
  104. package/moon.yml +4 -0
  105. package/package.json +31 -30
  106. package/src/common/capabilities.ts +22 -4
  107. package/src/common/collaboration.ts +1 -1
  108. package/src/common/file.ts +1 -1
  109. package/src/common/layout.ts +3 -4
  110. package/src/common/surface.ts +15 -18
  111. package/src/common/translations.ts +1 -1
  112. package/src/components/App.stories.tsx +1 -3
  113. package/src/components/useApp.tsx +9 -11
  114. package/src/core/capabilities.test.ts +1 -1
  115. package/src/core/capabilities.ts +1 -1
  116. package/src/core/manager.test.ts +19 -19
  117. package/src/core/manager.ts +12 -5
  118. package/src/core/plugin.ts +8 -2
  119. package/src/playground/debug/plugin.ts +7 -8
  120. package/src/playground/generator/generator.ts +2 -2
  121. package/src/playground/generator/plugin.ts +12 -13
  122. package/src/playground/layout/plugin.ts +9 -8
  123. package/src/playground/logger/plugin.ts +27 -23
  124. package/src/playground/logger/schema.ts +1 -1
  125. package/src/playground/playground.stories.tsx +5 -6
  126. package/src/plugin-intent/IntentPlugin.ts +12 -13
  127. package/src/plugin-intent/actions.ts +4 -6
  128. package/src/plugin-intent/errors.ts +2 -1
  129. package/src/plugin-intent/intent-dispatcher.test.ts +10 -3
  130. package/src/plugin-intent/intent-dispatcher.ts +8 -6
  131. package/src/plugin-intent/intent.ts +1 -1
  132. package/src/plugin-intent/meta.ts +10 -0
  133. package/src/plugin-settings/SettingsPlugin.ts +25 -27
  134. package/src/plugin-settings/actions.ts +9 -13
  135. package/src/plugin-settings/app-graph-builder.ts +17 -15
  136. package/src/plugin-settings/intent-resolver.ts +2 -2
  137. package/src/plugin-settings/meta.ts +10 -0
  138. package/src/plugin-settings/translations.ts +3 -3
  139. package/src/react/ErrorBoundary.tsx +10 -8
  140. package/src/react/Surface.stories.tsx +2 -5
  141. package/src/react/Surface.tsx +11 -3
  142. package/src/testing/withPluginManager.stories.tsx +1 -1
  143. package/src/testing/withPluginManager.tsx +13 -17
  144. package/tsconfig.json +1 -1
  145. package/vitest.config.ts +8 -6
  146. package/dist/lib/browser/app-graph-builder-AFFC6VB2.mjs.map +0 -7
  147. package/dist/lib/browser/chunk-ORWHM7CO.mjs.map +0 -7
  148. package/dist/lib/browser/chunk-OZY7HV2A.mjs.map +0 -7
  149. package/dist/lib/browser/chunk-T6M7JB7M.mjs.map +0 -7
  150. package/dist/lib/browser/intent-resolver-4S4PSTM5.mjs.map +0 -7
  151. package/dist/lib/node-esm/app-graph-builder-S4OAULX5.mjs.map +0 -7
  152. package/dist/lib/node-esm/chunk-F63ZRXMK.mjs.map +0 -7
  153. package/dist/lib/node-esm/chunk-HJFU7QOR.mjs.map +0 -7
  154. package/dist/lib/node-esm/chunk-UMZQERLE.mjs.map +0 -7
  155. package/dist/lib/node-esm/intent-resolver-2ZKXI5ET.mjs.map +0 -7
  156. /package/dist/lib/browser/{intent-dispatcher-QG7UPGQX.mjs.map → intent-dispatcher-6SHA5B3N.mjs.map} +0 -0
  157. /package/dist/lib/node-esm/{intent-dispatcher-NXBGPJOX.mjs.map → intent-dispatcher-SIYQ5ZIU.mjs.map} +0 -0
@@ -7,11 +7,12 @@ import { defineModule, definePlugin, lazy } from '../../core';
7
7
 
8
8
  const Layout = lazy(() => import('./Layout'));
9
9
 
10
- export const LayoutPlugin = () =>
11
- definePlugin({ id: 'dxos.org/test/layout', name: 'Layout' }, [
12
- defineModule({
13
- id: 'dxos.org/test/layout/root',
14
- activatesOn: Events.Startup,
15
- activate: Layout,
16
- }),
17
- ]);
10
+ const meta = { id: 'dxos.org/test/layout', name: 'Layout' };
11
+
12
+ export const LayoutPlugin = definePlugin(meta, () => [
13
+ defineModule({
14
+ id: 'dxos.org/test/layout/root',
15
+ activatesOn: Events.Startup,
16
+ activate: Layout,
17
+ }),
18
+ ]);
@@ -12,26 +12,30 @@ import { Log } from './schema';
12
12
 
13
13
  const Toolbar = lazy(() => import('./Toolbar'));
14
14
 
15
- export const LoggerPlugin = () =>
16
- definePlugin({ id: 'dxos.org/test/logger', name: 'Logger' }, [
17
- defineModule({
18
- id: 'dxos.org/test/logger/intents',
19
- activatesOn: Events.SetupIntentResolver,
20
- activate: () => [
21
- contributes(
22
- Capabilities.IntentResolver,
23
- createResolver({
24
- intent: Log,
25
- resolve: ({ message }) => {
26
- log.info(message);
27
- },
28
- }),
29
- ),
30
- ],
31
- }),
32
- defineModule({
33
- id: 'dxos.org/test/logger/surfaces',
34
- activatesOn: Events.Startup,
35
- activate: Toolbar,
36
- }),
37
- ]);
15
+ const meta = {
16
+ id: 'dxos.org/test/logger',
17
+ name: 'Logger',
18
+ };
19
+
20
+ export const LoggerPlugin = definePlugin(meta, () => [
21
+ defineModule({
22
+ id: 'dxos.org/test/logger/intents',
23
+ activatesOn: Events.SetupIntentResolver,
24
+ activate: () => [
25
+ contributes(
26
+ Capabilities.IntentResolver,
27
+ createResolver({
28
+ intent: Log,
29
+ resolve: ({ message }) => {
30
+ log.info(message);
31
+ },
32
+ }),
33
+ ),
34
+ ],
35
+ }),
36
+ defineModule({
37
+ id: 'dxos.org/test/logger/surfaces',
38
+ activatesOn: Events.Startup,
39
+ activate: Toolbar,
40
+ }),
41
+ ]);
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
 
7
7
  export class Log extends Schema.TaggedClass<Log>()('dxos.org/test/logger/log', {
8
8
  input: Schema.Struct({
@@ -2,12 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
10
- import { withLayout, withTheme } from '@dxos/storybook-utils';
8
+ import { withTheme } from '@dxos/react-ui/testing';
11
9
 
12
10
  import { useApp } from '../components';
13
11
  import { IntentPlugin } from '../plugin-intent';
@@ -17,7 +15,8 @@ import { GeneratorPlugin, createNumberPlugin } from './generator';
17
15
  import { LayoutPlugin } from './layout';
18
16
  import { LoggerPlugin } from './logger';
19
17
 
20
- const plugins = [IntentPlugin(), LayoutPlugin(), DebugPlugin(), LoggerPlugin(), GeneratorPlugin()];
18
+ const pluginFactories = [IntentPlugin, LayoutPlugin, DebugPlugin(), LoggerPlugin(), GeneratorPlugin()];
19
+ const plugins = pluginFactories.map((factory) => (typeof factory === 'function' ? factory() : factory));
21
20
 
22
21
  const Placeholder = () => {
23
22
  return <div>Loading...</div>;
@@ -25,7 +24,7 @@ const Placeholder = () => {
25
24
 
26
25
  const DefaultStory = () => {
27
26
  const App = useApp({
28
- pluginLoader: (id) => createNumberPlugin(id),
27
+ pluginLoader: (id) => createNumberPlugin(id)(),
29
28
  plugins,
30
29
  core: plugins.map((plugin) => plugin.meta.id),
31
30
  placeholder: Placeholder,
@@ -37,7 +36,7 @@ const DefaultStory = () => {
37
36
  const meta = {
38
37
  title: 'sdk/app-framework/playground',
39
38
  render: DefaultStory,
40
- decorators: [withTheme, withLayout()],
39
+ decorators: [withTheme],
41
40
  } satisfies Meta<typeof DefaultStory>;
42
41
 
43
42
  export default meta;
@@ -5,17 +5,16 @@
5
5
  import { Events } from '../common';
6
6
  import { defineModule, definePlugin, lazy } from '../core';
7
7
 
8
- import { INTENT_PLUGIN } from './actions';
8
+ import { meta } from './meta';
9
9
 
10
- export const IntentPlugin = () =>
11
- definePlugin({ id: INTENT_PLUGIN, name: 'Intent' }, [
12
- defineModule({
13
- id: `${INTENT_PLUGIN}/module/dispatcher`,
14
- // TODO(wittjosiah): This will mean that startup needs to be reset when intents are added or removed.
15
- // This is fine for now because it's how it worked prior to capabilities api anyways.
16
- // In the future, the intent dispatcher should be able to be reset without resetting the entire app.
17
- activatesOn: Events.Startup,
18
- activatesAfter: [Events.DispatcherReady],
19
- activate: lazy(() => import('./intent-dispatcher')),
20
- }),
21
- ]);
10
+ export const IntentPlugin = definePlugin(meta, () => [
11
+ defineModule({
12
+ id: `${meta.id}/module/dispatcher`,
13
+ // TODO(wittjosiah): This will mean that startup needs to be reset when intents are added or removed.
14
+ // This is fine for now because it's how it worked prior to capabilities api anyways.
15
+ // In the future, the intent dispatcher should be able to be reset without resetting the entire app.
16
+ activatesOn: Events.Startup,
17
+ activatesAfter: [Events.DispatcherReady],
18
+ activate: lazy(() => import('./intent-dispatcher')),
19
+ }),
20
+ ]);
@@ -2,18 +2,16 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
 
7
7
  import { Label } from './intent';
8
-
9
- export const INTENT_PLUGIN = 'dxos.org/plugin/intent';
10
- export const INTENT_ACTION = `${INTENT_PLUGIN}/action`;
8
+ import { meta } from './meta';
11
9
 
12
10
  export namespace IntentAction {
13
11
  /**
14
12
  * Log an intent.
15
13
  */
16
- export class Track extends Schema.TaggedClass<Track>()(`${INTENT_ACTION}/track`, {
14
+ export class Track extends Schema.TaggedClass<Track>()(`${meta.id}/action/track`, {
17
15
  input: Schema.Struct({
18
16
  intents: Schema.Array(Schema.String),
19
17
  error: Schema.optional(Schema.String),
@@ -24,7 +22,7 @@ export namespace IntentAction {
24
22
  /**
25
23
  * Fired after an intent is dispatched if the intent is undoable.
26
24
  */
27
- export class ShowUndo extends Schema.TaggedClass<ShowUndo>()(`${INTENT_ACTION}/show-undo`, {
25
+ export class ShowUndo extends Schema.TaggedClass<ShowUndo>()(`${meta.id}/action/show-undo`, {
28
26
  input: Schema.Struct({
29
27
  message: Label,
30
28
  }),
@@ -24,10 +24,11 @@ export class BaseError extends Error {
24
24
 
25
25
  export class NoResolversError extends BaseError {
26
26
  constructor(action: string) {
27
- super('NO_RESOLVERS', 'No resolvers were found for the action', { action });
27
+ super('NO_RESOLVERS', `No resolvers were found for the action: ${action}`, { action });
28
28
  }
29
29
  }
30
30
 
31
+ // TODO(burdon): Detect loops.
31
32
  export class CycleDetectedError extends BaseError {
32
33
  constructor(context?: Record<string, any>) {
33
34
  super(
@@ -2,7 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Effect, Fiber, Schema, pipe } from 'effect';
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Fiber from 'effect/Fiber';
7
+ import * as Function from 'effect/Function';
8
+ import * as Schema from 'effect/Schema';
6
9
  import { describe, expect, test } from 'vitest';
7
10
 
8
11
  import { chain, createIntent } from './intent';
@@ -104,7 +107,11 @@ describe('Intent dispatcher', () => {
104
107
 
105
108
  test('chain intents', async () => {
106
109
  const { dispatch } = createDispatcher(() => [computeResolver, toStringResolver, concatResolver]);
107
- const intent = pipe(createIntent(Compute, { value: 1 }), chain(ToString, {}), chain(Concat, { plus: '!' }));
110
+ const intent = Function.pipe(
111
+ createIntent(Compute, { value: 1 }),
112
+ chain(ToString, {}),
113
+ chain(Concat, { plus: '!' }),
114
+ );
108
115
 
109
116
  expect(intent.first.id).toBe(Compute._tag);
110
117
  expect(intent.last.id).toBe(Concat._tag);
@@ -120,7 +127,7 @@ describe('Intent dispatcher', () => {
120
127
 
121
128
  test('undo chained intent', async () => {
122
129
  const { dispatch, undo } = createDispatcher(() => [computeResolver, toStringResolver, concatResolver]);
123
- const intent = pipe(createIntent(Compute, { value: 1 }), chain(Compute, {}), chain(Compute, {}));
130
+ const intent = Function.pipe(createIntent(Compute, { value: 1 }), chain(Compute, {}), chain(Compute, {}));
124
131
  const program = Effect.gen(function* () {
125
132
  const a = yield* dispatch(intent);
126
133
 
@@ -2,8 +2,11 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Effect, Option, Ref, pipe } from 'effect';
6
- import { type Simplify } from 'effect/Types';
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Option from 'effect/Option';
8
+ import * as Ref from 'effect/Ref';
9
+ import type * as Types from 'effect/Types';
7
10
 
8
11
  import { live } from '@dxos/live-object';
9
12
  import { log } from '@dxos/log';
@@ -132,7 +135,7 @@ export const createResolver = <Tag extends string, Fields extends IntentParams,
132
135
  */
133
136
  export type PromiseIntentDispatcher = <Fields extends IntentParams>(
134
137
  intent: IntentChain<any, any, any, Fields>,
135
- ) => Promise<Simplify<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>>;
138
+ ) => Promise<Types.Simplify<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>>;
136
139
 
137
140
  /**
138
141
  * Creates an effect for intents.
@@ -141,7 +144,7 @@ export type IntentDispatcher = <Fields extends IntentParams>(
141
144
  intent: IntentChain<any, any, any, Fields>,
142
145
  depth?: number,
143
146
  ) => Effect.Effect<
144
- Simplify<Required<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>['data']>,
147
+ Types.Simplify<Required<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>['data']>,
145
148
  Error
146
149
  >;
147
150
 
@@ -197,7 +200,6 @@ export const createDispatcher = (
197
200
  .filter((resolver) => !resolver.filter || resolver.filter(intent.data))
198
201
  .toSorted(byPosition);
199
202
  if (candidates.length === 0) {
200
- log.info('no resolvers found', { intent: intent.id });
201
203
  return yield* Effect.fail(new NoResolversError(intent.id));
202
204
  }
203
205
 
@@ -253,7 +255,7 @@ export const createDispatcher = (
253
255
 
254
256
  if (result.undoable && isUndoable(results)) {
255
257
  // TODO(wittjosiah): Is there a better way to handle showing undo for chains?
256
- yield* pipe(
258
+ yield* Function.pipe(
257
259
  dispatch(createIntent(IntentAction.ShowUndo, { message: result.undoable.message })),
258
260
  Effect.catchSome((err) =>
259
261
  err instanceof NoResolversError ? Option.some(Effect.succeed(undefined)) : Option.none(),
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
 
7
7
  export type IntentParams = {
8
8
  readonly input: Schema.Schema.All;
@@ -0,0 +1,10 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type PluginMeta } from '@dxos/app-framework';
6
+
7
+ export const meta: PluginMeta = {
8
+ id: 'dxos.org/plugin/intent',
9
+ name: 'Intent',
10
+ };
@@ -5,32 +5,30 @@
5
5
  import { Capabilities, Events } from '../common';
6
6
  import { contributes, defineModule, definePlugin, lazy } from '../core';
7
7
 
8
- import { SETTINGS_PLUGIN } from './actions';
8
+ import { meta } from './meta';
9
9
  import { translations } from './translations';
10
10
 
11
- // TODO(wittjosiah): Add options to exclude some modules.
12
- export const SettingsPlugin = () =>
13
- definePlugin({ id: SETTINGS_PLUGIN, name: 'Settings' }, [
14
- defineModule({
15
- id: `${SETTINGS_PLUGIN}/module/store`,
16
- activatesOn: Events.Startup,
17
- activatesBefore: [Events.SetupSettings],
18
- activatesAfter: [Events.SettingsReady],
19
- activate: lazy(() => import('./store')),
20
- }),
21
- defineModule({
22
- id: `${SETTINGS_PLUGIN}/module/translations`,
23
- activatesOn: Events.SetupTranslations,
24
- activate: () => contributes(Capabilities.Translations, translations),
25
- }),
26
- defineModule({
27
- id: `${SETTINGS_PLUGIN}/module/intent-resolver`,
28
- activatesOn: Events.SetupIntentResolver,
29
- activate: lazy(() => import('./intent-resolver')),
30
- }),
31
- defineModule({
32
- id: `${SETTINGS_PLUGIN}/module/app-graph-builder`,
33
- activatesOn: Events.SetupAppGraph,
34
- activate: lazy(() => import('./app-graph-builder')),
35
- }),
36
- ]);
11
+ export const SettingsPlugin = definePlugin(meta, () => [
12
+ defineModule({
13
+ id: `${meta.id}/module/store`,
14
+ activatesOn: Events.Startup,
15
+ activatesBefore: [Events.SetupSettings],
16
+ activatesAfter: [Events.SettingsReady],
17
+ activate: lazy(() => import('./store')),
18
+ }),
19
+ defineModule({
20
+ id: `${meta.id}/module/translations`,
21
+ activatesOn: Events.SetupTranslations,
22
+ activate: () => contributes(Capabilities.Translations, translations),
23
+ }),
24
+ defineModule({
25
+ id: `${meta.id}/module/intent-resolver`,
26
+ activatesOn: Events.SetupIntentResolver,
27
+ activate: lazy(() => import('./intent-resolver')),
28
+ }),
29
+ defineModule({
30
+ id: `${meta.id}/module/app-graph-builder`,
31
+ activatesOn: Events.SetupAppGraph,
32
+ activate: lazy(() => import('./app-graph-builder')),
33
+ }),
34
+ ]);
@@ -2,28 +2,24 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
 
7
- export const SETTINGS_PLUGIN = 'dxos.org/plugin/settings';
8
- export const SETTINGS_ACTION = `${SETTINGS_PLUGIN}/action`;
9
- // TODO(wittjosiah): This is a hack to prevent the previous deck from being set for pinned items.
10
- // Ideally this should be worked into the data model in a generic way.
7
+ import { meta } from './meta';
8
+
9
+ // TODO(burdon): Document.
11
10
  export const SETTINGS_ID = '!dxos:settings';
12
11
  export const SETTINGS_KEY = 'settings';
13
12
 
14
13
  export namespace SettingsAction {
15
- export class Open extends Schema.TaggedClass<Open>()(`${SETTINGS_ACTION}/open`, {
14
+ export class Open extends Schema.TaggedClass<Open>()(`${meta.id}/open`, {
16
15
  input: Schema.Struct({
17
16
  plugin: Schema.optional(Schema.String),
18
17
  }),
19
18
  output: Schema.Void,
20
19
  }) {}
21
20
 
22
- export class OpenPluginRegistry extends Schema.TaggedClass<OpenPluginRegistry>()(
23
- `${SETTINGS_ACTION}/open-plugin-registry`,
24
- {
25
- input: Schema.Void,
26
- output: Schema.Void,
27
- },
28
- ) {}
21
+ export class OpenPluginRegistry extends Schema.TaggedClass<OpenPluginRegistry>()(`${meta.id}/open-plugin-registry`, {
22
+ input: Schema.Void,
23
+ output: Schema.Void,
24
+ }) {}
29
25
  }
@@ -3,7 +3,8 @@
3
3
  //
4
4
 
5
5
  import { Rx } from '@effect-rx/rx-react';
6
- import { Option, pipe } from 'effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Option from 'effect/Option';
7
8
 
8
9
  import { ROOT_ID, createExtension } from '@dxos/app-graph';
9
10
  import { type SettingsStore, type SettingsValue } from '@dxos/local-storage';
@@ -13,26 +14,27 @@ import { Capabilities } from '../common';
13
14
  import { type PluginContext, type PluginMeta, contributes } from '../core';
14
15
  import { createIntent } from '../plugin-intent';
15
16
 
16
- import { SETTINGS_ID, SETTINGS_KEY, SETTINGS_PLUGIN, SettingsAction } from './actions';
17
+ import { SETTINGS_ID, SETTINGS_KEY, SettingsAction } from './actions';
18
+ import { meta } from './meta';
17
19
 
18
20
  export default (context: PluginContext) =>
19
21
  contributes(Capabilities.AppGraphBuilder, [
20
22
  createExtension({
21
- id: `${SETTINGS_PLUGIN}/action`,
23
+ id: `${meta.id}/action`,
22
24
  actions: (node) =>
23
25
  Rx.make((get) =>
24
- pipe(
26
+ Function.pipe(
25
27
  get(node),
26
28
  Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
27
29
  Option.map(() => [
28
30
  {
29
- id: SETTINGS_PLUGIN,
31
+ id: meta.id,
30
32
  data: async () => {
31
33
  const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
32
34
  await dispatch(createIntent(SettingsAction.Open));
33
35
  },
34
36
  properties: {
35
- label: ['open settings label', { ns: SETTINGS_PLUGIN }],
37
+ label: ['open settings label', { ns: meta.id }],
36
38
  icon: 'ph--gear--regular',
37
39
  disposition: 'menu',
38
40
  keyBinding: {
@@ -47,18 +49,18 @@ export default (context: PluginContext) =>
47
49
  ),
48
50
  }),
49
51
  createExtension({
50
- id: `${SETTINGS_PLUGIN}/core`,
52
+ id: `${meta.id}/core`,
51
53
  connector: (node) =>
52
54
  Rx.make((get) =>
53
- pipe(
55
+ Function.pipe(
54
56
  get(node),
55
57
  Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
56
58
  Option.map(() => [
57
59
  {
58
60
  id: SETTINGS_ID,
59
- type: SETTINGS_PLUGIN,
61
+ type: meta.id,
60
62
  properties: {
61
- label: ['app settings label', { ns: SETTINGS_PLUGIN }],
63
+ label: ['app settings label', { ns: meta.id }],
62
64
  icon: 'ph--gear--regular',
63
65
  disposition: 'pin-end',
64
66
  position: 'hoist',
@@ -71,10 +73,10 @@ export default (context: PluginContext) =>
71
73
  ),
72
74
  }),
73
75
  createExtension({
74
- id: `${SETTINGS_PLUGIN}/core-plugins`,
76
+ id: `${meta.id}/core-plugins`,
75
77
  connector: (node) =>
76
78
  Rx.make((get) =>
77
- pipe(
79
+ Function.pipe(
78
80
  get(node),
79
81
  Option.flatMap((node) => (node.id !== SETTINGS_ID ? Option.none() : Option.some(node))),
80
82
  Option.map(() => {
@@ -106,7 +108,7 @@ export default (context: PluginContext) =>
106
108
  id: `${SETTINGS_KEY}:custom-plugins`,
107
109
  type: 'category',
108
110
  properties: {
109
- label: ['custom plugins label', { ns: SETTINGS_PLUGIN }],
111
+ label: ['custom plugins label', { ns: meta.id }],
110
112
  icon: 'ph--squares-four--regular',
111
113
  role: 'branch',
112
114
  disposition: 'collection',
@@ -119,10 +121,10 @@ export default (context: PluginContext) =>
119
121
  ),
120
122
  }),
121
123
  createExtension({
122
- id: `${SETTINGS_PLUGIN}/custom-plugins`,
124
+ id: `${meta.id}/custom-plugins`,
123
125
  connector: (node) =>
124
126
  Rx.make((get) =>
125
- pipe(
127
+ Function.pipe(
126
128
  get(node),
127
129
  Option.flatMap((node) =>
128
130
  node.id !== `${SETTINGS_KEY}:custom-plugins` ? Option.none() : Option.some(node),
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { pipe } from 'effect';
5
+ import * as Function from 'effect/Function';
6
6
 
7
7
  import { Capabilities, LayoutAction } from '../common';
8
8
  import { contributes } from '../core';
@@ -20,7 +20,7 @@ export default () =>
20
20
  return {
21
21
  intents: [
22
22
  plugin
23
- ? pipe(
23
+ ? Function.pipe(
24
24
  openSettings,
25
25
  chain(LayoutAction.Open, {
26
26
  part: 'main',
@@ -0,0 +1,10 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type PluginMeta } from '@dxos/app-framework';
6
+
7
+ export const meta: PluginMeta = {
8
+ id: 'dxos.org/plugin/settings',
9
+ name: 'Settings',
10
+ };
@@ -1,15 +1,15 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
5
  import { type Resource } from '@dxos/react-ui';
6
6
 
7
- import { SETTINGS_PLUGIN } from './actions';
7
+ import { meta } from './meta';
8
8
 
9
9
  export const translations = [
10
10
  {
11
11
  'en-US': {
12
- [SETTINGS_PLUGIN]: {
12
+ [meta.id]: {
13
13
  'open settings label': 'Open settings',
14
14
  'app settings label': 'Settings',
15
15
  'custom plugins label': 'Plugins',
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React, { Component, type FC, type JSX, type PropsWithChildren, type ReactNode } from 'react';
5
+ import React, { Component, type FC, type PropsWithChildren, type ReactNode } from 'react';
6
6
 
7
7
  type State = {
8
8
  error: Error | undefined;
@@ -32,7 +32,7 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, State> {
32
32
  }
33
33
  }
34
34
 
35
- override render(): string | number | boolean | JSX.Element | Iterable<ReactNode> | null | undefined {
35
+ override render(): ReactNode {
36
36
  if (this.state.error) {
37
37
  const Fallback = this.props.fallback ?? DefaultFallback;
38
38
  return <Fallback data={this.props.data} error={this.state.error} />;
@@ -46,9 +46,11 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, State> {
46
46
  }
47
47
  }
48
48
 
49
- const DefaultFallback: NonNullable<ErrorBoundaryProps['fallback']> = ({ data, error }) => (
50
- <div className='flex flex-col gap-2 overflow-hidden border border-red-500 rounded-sm'>
51
- <pre className='whitespace-pre-wrap font-sm text-xs p-2'>ERROR: {error.message}</pre>
52
- <pre className='whitespace-pre-wrap font-sm text-xs p-2 text-subdued'>{JSON.stringify(data, null, 2)}</pre>
53
- </div>
54
- );
49
+ const DefaultFallback: NonNullable<ErrorBoundaryProps['fallback']> = ({ data, error }) => {
50
+ return (
51
+ <div className='flex flex-col gap-2 overflow-hidden border border-red-500 rounded-sm'>
52
+ <h1 className='p-2'>ERROR: {error.message}</h1>
53
+ <pre className='p-2 overflow-y-auto text-sm text-subdued'>{JSON.stringify(data, null, 2)}</pre>
54
+ </div>
55
+ );
56
+ };
@@ -2,14 +2,12 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useCallback, useState } from 'react';
9
7
 
10
8
  import { faker } from '@dxos/random';
11
9
  import { Button, List, ListItem } from '@dxos/react-ui';
12
- import { withLayout, withTheme } from '@dxos/storybook-utils';
10
+ import { withTheme } from '@dxos/react-ui/testing';
13
11
 
14
12
  import { Capabilities, createSurface } from '../common';
15
13
  import { type PluginManager } from '../core';
@@ -90,8 +88,7 @@ const DefaultStory = (props: { manager: PluginManager }) => {
90
88
  const meta = {
91
89
  title: 'sdk/app-framework/Surface',
92
90
  render: DefaultStory,
93
- // NOTE: Intentionally not using withPluginManager to try to reduce surface area of the story.
94
- decorators: [withTheme, withLayout()],
91
+ decorators: [withTheme],
95
92
  args: {
96
93
  manager: setupPluginManager(),
97
94
  },
@@ -2,7 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { Fragment, Suspense, forwardRef, memo, useMemo } from 'react';
5
+ import React, {
6
+ Fragment,
7
+ type NamedExoticComponent,
8
+ type RefAttributes,
9
+ Suspense,
10
+ forwardRef,
11
+ memo,
12
+ useMemo,
13
+ } from 'react';
6
14
 
7
15
  import { useDefaultValue } from '@dxos/react-hooks';
8
16
  import { byPosition } from '@dxos/util';
@@ -44,8 +52,8 @@ export const isSurfaceAvailable = (context: PluginContext, { role, data }: Pick<
44
52
  /**
45
53
  * A surface is a named region of the screen that can be populated by plugins.
46
54
  */
47
- export const Surface = memo(
48
- forwardRef<HTMLElement, SurfaceProps>(
55
+ export const Surface: NamedExoticComponent<SurfaceProps & RefAttributes<HTMLElement>> = memo(
56
+ forwardRef(
49
57
  ({ id: _id, role, data: _data, limit, fallback, placeholder = DEFAULT_PLACEHOLDER, ...rest }, forwardedRef) => {
50
58
  // TODO(wittjosiah): This will make all surfaces depend on a single signal.
51
59
  // This isn't ideal because it means that any change to the data will cause all surfaces to re-render.