@dxos/plugin-deck 0.8.2-main.f11618f → 0.8.2-staging.42af850

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 (142) hide show
  1. package/dist/lib/browser/{app-graph-builder-VYZ4IWI3.mjs → app-graph-builder-M5BT34YG.mjs} +17 -16
  2. package/dist/lib/browser/app-graph-builder-M5BT34YG.mjs.map +7 -0
  3. package/dist/lib/browser/{check-app-scheme-O7JPE4TM.mjs → check-app-scheme-7AXGR6UT.mjs} +2 -2
  4. package/dist/lib/browser/check-app-scheme-7AXGR6UT.mjs.map +7 -0
  5. package/dist/lib/browser/{state-Z6UY2Z3M.mjs → chunk-FX44YX3G.mjs} +7 -5
  6. package/dist/lib/browser/chunk-FX44YX3G.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-JE2ARGEB.mjs +1487 -0
  8. package/dist/lib/browser/chunk-JE2ARGEB.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-XMCG42ID.mjs → chunk-KLN73CM3.mjs} +2 -2
  10. package/dist/lib/browser/{chunk-XMCG42ID.mjs.map → chunk-KLN73CM3.mjs.map} +1 -1
  11. package/dist/lib/browser/chunk-SLQNOATN.mjs +127 -0
  12. package/dist/lib/browser/chunk-SLQNOATN.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-TRFYUEBA.mjs +145 -0
  14. package/dist/lib/browser/chunk-TRFYUEBA.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-YN5OZEGS.mjs +162 -0
  16. package/dist/lib/browser/chunk-YN5OZEGS.mjs.map +7 -0
  17. package/dist/lib/browser/index.mjs +6 -8
  18. package/dist/lib/browser/index.mjs.map +2 -2
  19. package/dist/lib/browser/{intent-resolver-JKWXWUV6.mjs → intent-resolver-3GAC57UA.mjs} +84 -66
  20. package/dist/lib/browser/intent-resolver-3GAC57UA.mjs.map +7 -0
  21. package/dist/lib/browser/meta.json +1 -1
  22. package/dist/lib/browser/{react-root-S6ZAKNZA.mjs → react-root-ISFFOJZX.mjs} +7 -7
  23. package/dist/lib/browser/{react-surface-I7WZBOGM.mjs → react-surface-A63RQB5N.mjs} +7 -7
  24. package/dist/lib/browser/{settings-6NU7CF2B.mjs → settings-X7GDEXU3.mjs} +4 -4
  25. package/dist/lib/browser/{settings-6NU7CF2B.mjs.map → settings-X7GDEXU3.mjs.map} +3 -3
  26. package/dist/lib/browser/state-VJ6E3ADY.mjs +10 -0
  27. package/dist/lib/browser/state-VJ6E3ADY.mjs.map +7 -0
  28. package/dist/lib/browser/{tools-VDVQTJMD.mjs → tools-N57NQ2LH.mjs} +28 -18
  29. package/dist/lib/browser/tools-N57NQ2LH.mjs.map +7 -0
  30. package/dist/lib/browser/types.mjs +1 -1
  31. package/dist/lib/browser/{url-handler-3CARFXQK.mjs → url-handler-BUGI6XRE.mjs} +5 -5
  32. package/dist/lib/browser/url-handler-BUGI6XRE.mjs.map +7 -0
  33. package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -179
  34. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  35. package/dist/types/src/capabilities/capabilities.d.ts +10 -2
  36. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/check-app-scheme.d.ts +2 -2
  38. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -1
  39. package/dist/types/src/capabilities/index.d.ts +6 -181
  40. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
  42. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/state.d.ts +7 -3
  44. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  45. package/dist/types/src/capabilities/tools.d.ts +1 -1
  46. package/dist/types/src/capabilities/tools.d.ts.map +1 -1
  47. package/dist/types/src/capabilities/url-handler.d.ts +2 -2
  48. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
  49. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
  50. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  51. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  52. package/dist/types/src/components/DeckLayout/Popover.d.ts.map +1 -1
  53. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  54. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  55. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
  56. package/dist/types/src/components/Plank/Plank.d.ts +18 -5
  57. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
  58. package/dist/types/src/components/Plank/Plank.stories.d.ts +3 -3
  59. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -1
  60. package/dist/types/src/components/Plank/PlankControls.d.ts +1 -0
  61. package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
  62. package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -1
  63. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
  64. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
  65. package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +1 -1
  66. package/dist/types/src/components/Sidebar/SidebarButton.d.ts +2 -1
  67. package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -1
  68. package/dist/types/src/hooks/index.d.ts +5 -1
  69. package/dist/types/src/hooks/index.d.ts.map +1 -1
  70. package/dist/types/src/hooks/useBreakpoints.d.ts.map +1 -0
  71. package/dist/types/src/hooks/useCompanions.d.ts.map +1 -0
  72. package/dist/types/src/hooks/useDeckCompanions.d.ts +13 -0
  73. package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -0
  74. package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +1 -0
  75. package/dist/types/src/hooks/useNodeActionExpander.d.ts.map +1 -1
  76. package/dist/types/src/index.d.ts +1 -1
  77. package/dist/types/src/index.d.ts.map +1 -1
  78. package/dist/types/src/layout.d.ts.map +1 -1
  79. package/dist/types/src/translations.d.ts +0 -1
  80. package/dist/types/src/translations.d.ts.map +1 -1
  81. package/dist/types/src/types.d.ts +106 -104
  82. package/dist/types/src/types.d.ts.map +1 -1
  83. package/dist/types/src/util/index.d.ts +1 -4
  84. package/dist/types/src/util/index.d.ts.map +1 -1
  85. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -1
  86. package/dist/types/src/util/overscroll.d.ts.map +1 -1
  87. package/dist/types/src/util/set-active.d.ts.map +1 -1
  88. package/dist/types/tsconfig.tsbuildinfo +1 -1
  89. package/package.json +38 -30
  90. package/src/capabilities/app-graph-builder.ts +120 -92
  91. package/src/capabilities/check-app-scheme.ts +3 -5
  92. package/src/capabilities/index.ts +1 -0
  93. package/src/capabilities/intent-resolver.ts +100 -78
  94. package/src/capabilities/settings.ts +2 -2
  95. package/src/capabilities/state.ts +4 -2
  96. package/src/capabilities/tools.ts +15 -12
  97. package/src/capabilities/url-handler.ts +4 -4
  98. package/src/components/DeckLayout/ContentEmpty.tsx +3 -2
  99. package/src/components/DeckLayout/DeckLayout.tsx +12 -10
  100. package/src/components/DeckLayout/Dialog.tsx +2 -2
  101. package/src/components/Plank/Plank.stories.tsx +20 -8
  102. package/src/components/Plank/Plank.tsx +101 -68
  103. package/src/components/Plank/PlankControls.tsx +15 -25
  104. package/src/components/Plank/PlankHeading.tsx +22 -10
  105. package/src/components/Sidebar/ComplementarySidebar.tsx +7 -38
  106. package/src/components/Sidebar/Sidebar.tsx +2 -1
  107. package/src/components/Sidebar/SidebarButton.tsx +26 -7
  108. package/src/components/fragments.ts +1 -1
  109. package/src/hooks/index.ts +5 -1
  110. package/src/{util → hooks}/useCompanions.ts +3 -3
  111. package/src/hooks/useDeckCompanions.ts +33 -0
  112. package/src/hooks/useNodeActionExpander.ts +3 -8
  113. package/src/index.ts +1 -1
  114. package/src/translations.ts +0 -1
  115. package/src/types.ts +74 -71
  116. package/src/util/index.ts +1 -4
  117. package/dist/lib/browser/app-graph-builder-VYZ4IWI3.mjs.map +0 -7
  118. package/dist/lib/browser/check-app-scheme-O7JPE4TM.mjs.map +0 -7
  119. package/dist/lib/browser/chunk-6HJZL3WT.mjs +0 -118
  120. package/dist/lib/browser/chunk-6HJZL3WT.mjs.map +0 -7
  121. package/dist/lib/browser/chunk-FLOVGNYB.mjs +0 -81
  122. package/dist/lib/browser/chunk-FLOVGNYB.mjs.map +0 -7
  123. package/dist/lib/browser/chunk-RBJ6DLAC.mjs +0 -24
  124. package/dist/lib/browser/chunk-RBJ6DLAC.mjs.map +0 -7
  125. package/dist/lib/browser/chunk-RDFJGGGX.mjs +0 -1334
  126. package/dist/lib/browser/chunk-RDFJGGGX.mjs.map +0 -7
  127. package/dist/lib/browser/chunk-ZMJMCN7O.mjs +0 -157
  128. package/dist/lib/browser/chunk-ZMJMCN7O.mjs.map +0 -7
  129. package/dist/lib/browser/intent-resolver-JKWXWUV6.mjs.map +0 -7
  130. package/dist/lib/browser/state-Z6UY2Z3M.mjs.map +0 -7
  131. package/dist/lib/browser/tools-VDVQTJMD.mjs.map +0 -7
  132. package/dist/lib/browser/url-handler-3CARFXQK.mjs.map +0 -7
  133. package/dist/types/src/util/useBreakpoints.d.ts.map +0 -1
  134. package/dist/types/src/util/useCompanions.d.ts.map +0 -1
  135. package/dist/types/src/util/useHoistStatusbar.d.ts.map +0 -1
  136. /package/dist/lib/browser/{react-root-S6ZAKNZA.mjs.map → react-root-ISFFOJZX.mjs.map} +0 -0
  137. /package/dist/lib/browser/{react-surface-I7WZBOGM.mjs.map → react-surface-A63RQB5N.mjs.map} +0 -0
  138. /package/dist/types/src/{util → hooks}/useBreakpoints.d.ts +0 -0
  139. /package/dist/types/src/{util → hooks}/useCompanions.d.ts +0 -0
  140. /package/dist/types/src/{util → hooks}/useHoistStatusbar.d.ts +0 -0
  141. /package/src/{util → hooks}/useBreakpoints.ts +0 -0
  142. /package/src/{util → hooks}/useHoistStatusbar.ts +0 -0
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { batch } from '@preact/signals-core';
6
- import { Effect, pipe } from 'effect';
6
+ import { Schema, Effect, pipe, Option } from 'effect';
7
7
 
8
8
  import {
9
9
  Capabilities,
@@ -11,16 +11,16 @@ import {
11
11
  contributes,
12
12
  IntentAction,
13
13
  LayoutAction,
14
- type PluginsContext,
14
+ type PluginContext,
15
15
  createIntent,
16
16
  chain,
17
17
  } from '@dxos/app-framework';
18
- import { getTypename, S } from '@dxos/echo-schema';
18
+ import { getTypename } from '@dxos/echo-schema';
19
19
  import { invariant } from '@dxos/invariant';
20
20
  import { isLiveObject } from '@dxos/live-object';
21
21
  import { log } from '@dxos/log';
22
22
  import { AttentionCapabilities } from '@dxos/plugin-attention';
23
- import { type Node } from '@dxos/plugin-graph';
23
+ import { isActionLike } from '@dxos/plugin-graph';
24
24
  import { ObservabilityAction } from '@dxos/plugin-observability/types';
25
25
  import { byPosition, isNonNullable } from '@dxos/util';
26
26
 
@@ -38,13 +38,13 @@ import {
38
38
  } from '../types';
39
39
  import { setActive } from '../util';
40
40
 
41
- export default (context: PluginsContext) =>
41
+ export default (context: PluginContext) =>
42
42
  contributes(Capabilities.IntentResolver, [
43
43
  createResolver({
44
44
  intent: IntentAction.ShowUndo,
45
45
  resolve: (data) => {
46
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
47
- const { undoPromise: undo } = context.requestCapability(Capabilities.IntentDispatcher);
46
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
47
+ const { undoPromise: undo } = context.getCapability(Capabilities.IntentDispatcher);
48
48
 
49
49
  // TODO(wittjosiah): Support undoing further back than the last action.
50
50
  if (layout.currentUndoId) {
@@ -67,12 +67,12 @@ export default (context: PluginsContext) =>
67
67
  }),
68
68
  createResolver({
69
69
  intent: LayoutAction.UpdateLayout,
70
- // TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdateSidebar.fields.input)`
70
+ // TODO(wittjosiah): This should be able to just be `Schema.is(LayoutAction.UpdateSidebar.fields.input)`
71
71
  // but the filter is not being applied correctly.
72
- filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdateSidebar.fields.input> =>
73
- S.is(LayoutAction.UpdateSidebar.fields.input)(data),
72
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.UpdateSidebar.fields.input> =>
73
+ Schema.is(LayoutAction.UpdateSidebar.fields.input)(data),
74
74
  resolve: ({ options }) => {
75
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
75
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
76
76
  const next = options?.state ?? layout.sidebarState;
77
77
  if (next !== layout.sidebarState) {
78
78
  layout.sidebarState = next;
@@ -81,12 +81,12 @@ export default (context: PluginsContext) =>
81
81
  }),
82
82
  createResolver({
83
83
  intent: LayoutAction.UpdateLayout,
84
- // TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdateComplementary.fields.input)`
84
+ // TODO(wittjosiah): This should be able to just be `Schema.is(LayoutAction.UpdateComplementary.fields.input)`
85
85
  // but the filter is not being applied correctly.
86
- filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdateComplementary.fields.input> =>
87
- S.is(LayoutAction.UpdateComplementary.fields.input)(data),
86
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.UpdateComplementary.fields.input> =>
87
+ Schema.is(LayoutAction.UpdateComplementary.fields.input)(data),
88
88
  resolve: ({ subject, options }) => {
89
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
89
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
90
90
 
91
91
  if (layout.complementarySidebarPanel !== subject) {
92
92
  layout.complementarySidebarPanel = subject;
@@ -100,26 +100,28 @@ export default (context: PluginsContext) =>
100
100
  }),
101
101
  createResolver({
102
102
  intent: LayoutAction.UpdateLayout,
103
- // TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdateDialog.fields.input)`
103
+ // TODO(wittjosiah): This should be able to just be `Schema.is(LayoutAction.UpdateDialog.fields.input)`
104
104
  // but the filter is not being applied correctly.
105
- filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdateDialog.fields.input> =>
106
- S.is(LayoutAction.UpdateDialog.fields.input)(data),
105
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.UpdateDialog.fields.input> =>
106
+ Schema.is(LayoutAction.UpdateDialog.fields.input)(data),
107
107
  resolve: ({ subject, options }) => {
108
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
108
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
109
109
  layout.dialogOpen = options.state ?? Boolean(subject);
110
- layout.dialogContent = subject ? { component: subject, props: options.props } : null;
111
- layout.dialogBlockAlign = options.blockAlign ?? 'center';
112
110
  layout.dialogType = options.type ?? 'default';
111
+ layout.dialogBlockAlign = options.blockAlign ?? 'center';
112
+ layout.dialogOverlayClasses = options.overlayClasses;
113
+ layout.dialogOverlayStyle = options.overlayStyle;
114
+ layout.dialogContent = subject ? { component: subject, props: options.props } : null;
113
115
  },
114
116
  }),
115
117
  createResolver({
116
118
  intent: LayoutAction.UpdateLayout,
117
- // TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdatePopover.fields.input)`
119
+ // TODO(wittjosiah): This should be able to just be `Schema.is(LayoutAction.UpdatePopover.fields.input)`
118
120
  // but the filter is not being applied correctly.
119
- filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdatePopover.fields.input> =>
120
- S.is(LayoutAction.UpdatePopover.fields.input)(data),
121
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.UpdatePopover.fields.input> =>
122
+ Schema.is(LayoutAction.UpdatePopover.fields.input)(data),
121
123
  resolve: ({ subject, options }) => {
122
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
124
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
123
125
  layout.popoverOpen = options.state ?? Boolean(subject);
124
126
  layout.popoverContent =
125
127
  typeof subject === 'string' ? { component: subject, props: options.props } : subject ? { subject } : null;
@@ -133,21 +135,21 @@ export default (context: PluginsContext) =>
133
135
  }),
134
136
  createResolver({
135
137
  intent: LayoutAction.UpdateLayout,
136
- // TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.AddToast.fields.input)`
138
+ // TODO(wittjosiah): This should be able to just be `Schema.is(LayoutAction.AddToast.fields.input)`
137
139
  // but the filter is not being applied correctly.
138
- filter: (data): data is S.Schema.Type<typeof LayoutAction.AddToast.fields.input> =>
139
- S.is(LayoutAction.AddToast.fields.input)(data),
140
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.AddToast.fields.input> =>
141
+ Schema.is(LayoutAction.AddToast.fields.input)(data),
140
142
  resolve: ({ subject }) => {
141
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
143
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
142
144
  layout.toasts.push(subject);
143
145
  },
144
146
  }),
145
147
  createResolver({
146
148
  intent: LayoutAction.UpdateLayout,
147
- // TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.SetLayoutMode.fields.input)`
149
+ // TODO(wittjosiah): This should be able to just be `Schema.is(LayoutAction.SetLayoutMode.fields.input)`
148
150
  // but the filter is not being applied correctly.
149
- filter: (data): data is S.Schema.Type<typeof LayoutAction.SetLayoutMode.fields.input> => {
150
- if (!S.is(LayoutAction.SetLayoutMode.fields.input)(data)) {
151
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.SetLayoutMode.fields.input> => {
152
+ if (!Schema.is(LayoutAction.SetLayoutMode.fields.input)(data)) {
151
153
  return false;
152
154
  }
153
155
 
@@ -158,7 +160,7 @@ export default (context: PluginsContext) =>
158
160
  return true;
159
161
  },
160
162
  resolve: ({ subject, options }) => {
161
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
163
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
162
164
 
163
165
  const setMode = (mode: LayoutMode) => {
164
166
  const deck = state.deck;
@@ -202,10 +204,11 @@ export default (context: PluginsContext) =>
202
204
  }),
203
205
  createResolver({
204
206
  intent: LayoutAction.UpdateLayout,
205
- filter: (data): data is S.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
206
- S.is(LayoutAction.SwitchWorkspace.fields.input)(data),
207
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
208
+ Schema.is(LayoutAction.SwitchWorkspace.fields.input)(data),
207
209
  resolve: ({ subject }) => {
208
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
210
+ const { graph } = context.getCapability(Capabilities.AppGraph);
211
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
209
212
  batch(() => {
210
213
  // TODO(wittjosiah): This is a hack to prevent the previous deck from being set for pinned items.
211
214
  // Ideally this should be worked into the data model in a generic way.
@@ -223,15 +226,24 @@ export default (context: PluginsContext) =>
223
226
  return {
224
227
  intents: [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: first })],
225
228
  };
229
+ } else {
230
+ const [item] = graph
231
+ .getConnections(subject)
232
+ .filter((node) => !isActionLike(node) && node.properties.disposition !== 'hidden');
233
+ if (item) {
234
+ return {
235
+ intents: [createIntent(LayoutAction.Open, { part: 'main', subject: [item.id] })],
236
+ };
237
+ }
226
238
  }
227
239
  },
228
240
  }),
229
241
  createResolver({
230
242
  intent: LayoutAction.UpdateLayout,
231
- filter: (data): data is S.Schema.Type<typeof LayoutAction.RevertWorkspace.fields.input> =>
232
- S.is(LayoutAction.RevertWorkspace.fields.input)(data),
243
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.RevertWorkspace.fields.input> =>
244
+ Schema.is(LayoutAction.RevertWorkspace.fields.input)(data),
233
245
  resolve: () => {
234
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
246
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
235
247
  return {
236
248
  intents: [createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: state.previousDeck })],
237
249
  };
@@ -239,19 +251,19 @@ export default (context: PluginsContext) =>
239
251
  }),
240
252
  createResolver({
241
253
  intent: LayoutAction.UpdateLayout,
242
- filter: (data): data is S.Schema.Type<typeof LayoutAction.Open.fields.input> =>
243
- S.is(LayoutAction.Open.fields.input)(data),
254
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.Open.fields.input> =>
255
+ Schema.is(LayoutAction.Open.fields.input)(data),
244
256
  resolve: ({ subject, options }) =>
245
257
  Effect.gen(function* () {
246
- const { graph } = context.requestCapability(Capabilities.AppGraph);
247
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
248
- const attention = context.requestCapability(AttentionCapabilities.Attention);
258
+ const { graph } = context.getCapability(Capabilities.AppGraph);
259
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
260
+ const attention = context.getCapability(AttentionCapabilities.Attention);
249
261
  const settings = context
250
- .requestCapabilities(Capabilities.SettingsStore)[0]
262
+ .getCapabilities(Capabilities.SettingsStore)[0]
251
263
  ?.getStore<DeckSettingsProps>(DECK_PLUGIN)?.value;
252
264
 
253
265
  if (options?.workspace && state.activeDeck !== options?.workspace) {
254
- const { dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
266
+ const { dispatch } = context.getCapability(Capabilities.IntentDispatcher);
255
267
  yield* dispatch(
256
268
  createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: options.workspace }),
257
269
  );
@@ -285,8 +297,13 @@ export default (context: PluginsContext) =>
285
297
  : []),
286
298
  createIntent(LayoutAction.Expose, { part: 'navigation', subject: newlyOpen[0] ?? subject[0] }),
287
299
  ...newlyOpen.map((subjectId) => {
288
- const active = graph?.findNode(subjectId)?.data;
289
- const typename = isLiveObject(active) ? getTypename(active) : undefined;
300
+ const typename = Option.match(graph.getNode(subjectId), {
301
+ onNone: () => undefined,
302
+ onSome: (node) => {
303
+ const active = node.data;
304
+ return isLiveObject(active) ? getTypename(active) : undefined;
305
+ },
306
+ });
290
307
  return createIntent(ObservabilityAction.SendEvent, {
291
308
  name: 'navigation.activate',
292
309
  properties: {
@@ -301,11 +318,11 @@ export default (context: PluginsContext) =>
301
318
  }),
302
319
  createResolver({
303
320
  intent: LayoutAction.UpdateLayout,
304
- filter: (data): data is S.Schema.Type<typeof LayoutAction.Close.fields.input> =>
305
- S.is(LayoutAction.Close.fields.input)(data),
321
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.Close.fields.input> =>
322
+ Schema.is(LayoutAction.Close.fields.input)(data),
306
323
  resolve: ({ subject }) => {
307
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
308
- const attention = context.requestCapability(AttentionCapabilities.Attention);
324
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
325
+ const attention = context.getCapability(AttentionCapabilities.Attention);
309
326
  const active = state.deck.solo ? [state.deck.solo] : state.deck.active;
310
327
  const next = subject.reduce((acc, id) => closeEntry(acc, id), active);
311
328
  const toAttend = setActive({ next, state, attention });
@@ -324,11 +341,11 @@ export default (context: PluginsContext) =>
324
341
  }),
325
342
  createResolver({
326
343
  intent: LayoutAction.UpdateLayout,
327
- filter: (data): data is S.Schema.Type<typeof LayoutAction.Set.fields.input> =>
328
- S.is(LayoutAction.Set.fields.input)(data),
344
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.Set.fields.input> =>
345
+ Schema.is(LayoutAction.Set.fields.input)(data),
329
346
  resolve: ({ subject }) => {
330
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
331
- const attention = context.requestCapability(AttentionCapabilities.Attention);
347
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
348
+ const attention = context.getCapability(AttentionCapabilities.Attention);
332
349
  const toAttend = setActive({ next: subject as string[], state, attention });
333
350
  return {
334
351
  intents: toAttend ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: toAttend })] : [],
@@ -337,24 +354,24 @@ export default (context: PluginsContext) =>
337
354
  }),
338
355
  createResolver({
339
356
  intent: LayoutAction.UpdateLayout,
340
- filter: (data): data is S.Schema.Type<typeof LayoutAction.ScrollIntoView.fields.input> =>
341
- S.is(LayoutAction.ScrollIntoView.fields.input)(data),
357
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.ScrollIntoView.fields.input> =>
358
+ Schema.is(LayoutAction.ScrollIntoView.fields.input)(data),
342
359
  resolve: ({ subject }) => {
343
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
360
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
344
361
  layout.scrollIntoView = subject;
345
362
  },
346
363
  }),
347
364
  createResolver({
348
365
  intent: DeckAction.UpdatePlankSize,
349
366
  resolve: (data) => {
350
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
367
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
351
368
  state.deck.plankSizing[data.id] = data.size;
352
369
  },
353
370
  }),
354
371
  createResolver({
355
372
  intent: DeckAction.ChangeCompanion,
356
373
  resolve: (data) => {
357
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
374
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
358
375
  // TODO(thure): Reactivity only works when creating a lexically new `activeCompanions`… Are these not proxy objects?
359
376
  if (data.companion === null) {
360
377
  const { [data.primary]: _, ...nextActiveCompanions } = state.deck.activeCompanions ?? {};
@@ -371,9 +388,9 @@ export default (context: PluginsContext) =>
371
388
  createResolver({
372
389
  intent: DeckAction.Adjust,
373
390
  resolve: (adjustment) => {
374
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
375
- const attention = context.requestCapability(AttentionCapabilities.Attention);
376
- const { graph } = context.requestCapability(Capabilities.AppGraph);
391
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
392
+ const attention = context.getCapability(AttentionCapabilities.Attention);
393
+ const { graph } = context.getCapability(Capabilities.AppGraph);
377
394
 
378
395
  return batch(() => {
379
396
  if (adjustment.type === 'increment-end' || adjustment.type === 'increment-start') {
@@ -385,20 +402,25 @@ export default (context: PluginsContext) =>
385
402
  }
386
403
 
387
404
  if (adjustment.type === 'companion') {
388
- const node = graph.findNode(adjustment.id);
389
- const [companion] = node
390
- ? graph
391
- .nodes(node, { filter: (n): n is Node<any> => n.type === PLANK_COMPANION_TYPE })
392
- .toSorted((a, b) => byPosition(a.properties, b.properties))
393
- : [];
394
- if (companion) {
395
- return {
396
- intents: [
397
- // TODO(wittjosiah): This should remember the previously selected companion.
398
- createIntent(DeckAction.ChangeCompanion, { primary: adjustment.id, companion: companion.id }),
399
- ],
400
- };
401
- }
405
+ return pipe(
406
+ graph.getNode(adjustment.id),
407
+ Option.map((node) =>
408
+ graph
409
+ .getConnections(node.id)
410
+ .filter((n) => n.type === PLANK_COMPANION_TYPE)
411
+ .toSorted((a, b) => byPosition(a.properties, b.properties)),
412
+ ),
413
+ Option.flatMap((companions) => (companions.length > 0 ? Option.some(companions[0]) : Option.none())),
414
+ Option.match({
415
+ onNone: () => ({}),
416
+ onSome: (companion) => ({
417
+ intents: [
418
+ // TODO(wittjosiah): This should remember the previously selected companion.
419
+ createIntent(DeckAction.ChangeCompanion, { primary: adjustment.id, companion: companion.id }),
420
+ ],
421
+ }),
422
+ }),
423
+ );
402
424
  }
403
425
 
404
426
  if (adjustment.type.startsWith('solo')) {
@@ -11,9 +11,9 @@ import { DeckSettingsSchema, type DeckSettingsProps } from '../types';
11
11
  export default () => {
12
12
  const settings = live<DeckSettingsProps>({
13
13
  showHints: false,
14
- enableDeck: true,
15
- enableNativeRedirect: false,
14
+ enableDeck: false,
16
15
  enableStatusbar: false,
16
+ enableNativeRedirect: false,
17
17
  newPlankPositioning: 'start',
18
18
  overscroll: 'none',
19
19
  });
@@ -10,7 +10,7 @@ import { type SidebarState } from '@dxos/react-ui';
10
10
 
11
11
  import { DeckCapabilities } from './capabilities';
12
12
  import { DECK_PLUGIN } from '../meta';
13
- import { getMode, type DeckState, type DeckPluginState, defaultDeck } from '../types';
13
+ import { getMode, type DeckPluginState, defaultDeck, type DeckState } from '../types';
14
14
 
15
15
  const boolean = /true|false/;
16
16
 
@@ -29,7 +29,7 @@ const migrateSidebarState = () => {
29
29
  });
30
30
  };
31
31
 
32
- export default () => {
32
+ const DeckStateFactory = () => {
33
33
  migrateSidebarState();
34
34
 
35
35
  const state = new LocalStorageStore<DeckPluginState>(DECK_PLUGIN, {
@@ -100,3 +100,5 @@ export default () => {
100
100
  contributes(Capabilities.Layout, layout),
101
101
  ];
102
102
  };
103
+
104
+ export default DeckStateFactory;
@@ -2,6 +2,9 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { Schema } from 'effect';
6
+
7
+ import { createTool, ToolResult } from '@dxos/ai';
5
8
  import {
6
9
  contributes,
7
10
  createIntent,
@@ -9,8 +12,6 @@ import {
9
12
  LayoutAction,
10
13
  type PromiseIntentDispatcher,
11
14
  } from '@dxos/app-framework';
12
- import { defineTool, ToolResult } from '@dxos/artifact';
13
- import { S } from '@dxos/echo-schema';
14
15
  import { invariant } from '@dxos/invariant';
15
16
 
16
17
  import { meta } from '../meta';
@@ -27,7 +28,7 @@ declare global {
27
28
 
28
29
  export default () =>
29
30
  contributes(Capabilities.Tools, [
30
- defineTool(meta.id, {
31
+ createTool(meta.id, {
31
32
  name: 'show',
32
33
  description: `
33
34
  Show an item as a companion to an existing plank. This will make the item appear alongside the primary content.
@@ -35,19 +36,21 @@ export default () =>
35
36
  `,
36
37
  caption: 'Showing item...',
37
38
  // TODO(wittjosiah): Refactor Layout/Navigation/Deck actions so that they can be used directly.
38
- schema: S.Struct({
39
- id: S.String.annotations({
39
+ schema: Schema.Struct({
40
+ id: Schema.String.annotations({
40
41
  description: 'The ID of the item to show.',
41
42
  }),
42
43
  }),
43
44
  execute: async ({ id }, { extensions }) => {
44
- invariant(extensions?.pivotId, 'No pivot ID');
45
- invariant(extensions?.dispatch, 'No intent dispatcher');
45
+ invariant(extensions);
46
+ const { pivotId, dispatch, part } = extensions;
47
+ invariant(pivotId, 'No pivot ID');
48
+ invariant(dispatch, 'No intent dispatcher');
46
49
 
47
- if (extensions.part === 'deck') {
48
- const { data, error } = await extensions.dispatch(
50
+ if (part === 'deck') {
51
+ const { data, error } = await dispatch(
49
52
  createIntent(DeckAction.ChangeCompanion, {
50
- primary: extensions.pivotId,
53
+ primary: pivotId,
51
54
  companion: id,
52
55
  }),
53
56
  );
@@ -57,12 +60,12 @@ export default () =>
57
60
 
58
61
  return ToolResult.Success(data);
59
62
  } else {
60
- const { data, error } = await extensions.dispatch(
63
+ const { data, error } = await dispatch(
61
64
  createIntent(LayoutAction.Open, {
62
65
  subject: [id],
63
66
  part: 'main',
64
67
  options: {
65
- pivotId: extensions.pivotId,
68
+ pivotId,
66
69
  positioning: 'end',
67
70
  },
68
71
  }),
@@ -2,16 +2,16 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, contributes, createIntent, LayoutAction, type PluginsContext } from '@dxos/app-framework';
5
+ import { Capabilities, contributes, createIntent, LayoutAction, type PluginContext } from '@dxos/app-framework';
6
6
  import { scheduledEffect } from '@dxos/echo-signals/core';
7
7
 
8
8
  import { DeckCapabilities } from './capabilities';
9
9
  import { defaultDeck } from '../types';
10
10
 
11
11
  // TODO(wittjosiah): Cleanup the url handling. May justify introducing routing capabilities.
12
- export default async (context: PluginsContext) => {
13
- const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher) ?? {};
14
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
12
+ export default async (context: PluginContext) => {
13
+ const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
14
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
15
15
 
16
16
  const handleNavigation = async () => {
17
17
  const pathname = window.location.pathname;
@@ -7,8 +7,9 @@ import React from 'react';
7
7
  import { Surface, useCapability } from '@dxos/app-framework';
8
8
 
9
9
  import { DeckCapabilities } from '../../capabilities';
10
+ import { useBreakpoints } from '../../hooks';
10
11
  import { getMode } from '../../types';
11
- import { layoutAppliesTopbar, useBreakpoints } from '../../util';
12
+ import { layoutAppliesTopbar } from '../../util';
12
13
  import { ToggleSidebarButton } from '../Sidebar';
13
14
  import { fixedSidebarToggleStyles } from '../fragments';
14
15
 
@@ -20,7 +21,7 @@ export const ContentEmpty = () => {
20
21
  return (
21
22
  <div
22
23
  role='none'
23
- className='grid place-items-center p-8 relative bg-deck'
24
+ className='grid place-items-center p-8 relative bg-deckSurface'
24
25
  data-testid='layoutPlugin.firstRunMessage'
25
26
  >
26
27
  <Surface role='keyshortcuts' />
@@ -26,9 +26,10 @@ import { StatusBar } from './StatusBar';
26
26
  import { Toast } from './Toast';
27
27
  import { Topbar } from './Topbar';
28
28
  import { DeckCapabilities } from '../../capabilities';
29
+ import { useBreakpoints, useHoistStatusbar } from '../../hooks';
29
30
  import { DECK_PLUGIN } from '../../meta';
30
31
  import { type DeckSettingsProps, getMode } from '../../types';
31
- import { calculateOverscroll, layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
32
+ import { calculateOverscroll, layoutAppliesTopbar } from '../../util';
32
33
  import { Plank } from '../Plank';
33
34
  import { ComplementarySidebar, Sidebar, ToggleComplementarySidebarButton, ToggleSidebarButton } from '../Sidebar';
34
35
  import { fixedComplementarySidebarToggleStyles, fixedSidebarToggleStyles } from '../fragments';
@@ -38,7 +39,7 @@ export type DeckLayoutProps = {
38
39
  };
39
40
 
40
41
  const PlankSeparator = ({ order }: { order: number }) =>
41
- order > 0 ? <span role='separator' className='row-span-2 bg-deck is-4' style={{ gridColumn: order }} /> : null;
42
+ order > 0 ? <span role='separator' className='row-span-2 bg-deckSurface is-4' style={{ gridColumn: order }} /> : null;
42
43
 
43
44
  export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
44
45
  const { dispatchPromise: dispatch } = useIntentDispatcher();
@@ -59,7 +60,7 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
59
60
  useEffect(() => {
60
61
  // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
61
62
  const attended = untracked(() => {
62
- const attention = pluginManager.context.requestCapability(AttentionCapabilities.Attention);
63
+ const attention = pluginManager.context.getCapability(AttentionCapabilities.Attention);
63
64
  return attention.current;
64
65
  });
65
66
  const firstId = solo ?? active[0];
@@ -78,7 +79,7 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
78
79
  if (!isNotMobile && getMode(deck) === 'deck') {
79
80
  // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
80
81
  const attended = untracked(() => {
81
- const attention = pluginManager.context.requestCapability(AttentionCapabilities.Attention);
82
+ const attention = pluginManager.context.getCapability(AttentionCapabilities.Attention);
82
83
  return attention.current;
83
84
  });
84
85
 
@@ -91,14 +92,15 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
91
92
  }
92
93
  }, [isNotMobile, deck, dispatch]);
93
94
 
94
- // If deck is disabled in settings, ensure that the layout is in solo mode.
95
+ // When deck is disabled in settings, set to solo mode if the current layout mode is deck.
96
+ // TODO(thure): Applying this as an effect should be avoided over emitting the intent only when the setting changes.
95
97
  useEffect(() => {
96
- if (!settings.enableDeck) {
98
+ if (!settings.enableDeck && layoutMode === 'deck') {
97
99
  void dispatch(
98
100
  createIntent(LayoutAction.SetLayoutMode, { part: 'mode', subject: active[0], options: { mode: 'solo' } }),
99
101
  );
100
102
  }
101
- }, [settings.enableDeck, dispatch, active]);
103
+ }, [settings.enableDeck, dispatch, active, layoutMode]);
102
104
 
103
105
  /**
104
106
  * Clear scroll restoration state if the window is resized
@@ -190,8 +192,8 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
190
192
  {!isEmpty && (
191
193
  <Main.Content
192
194
  bounce
193
- classNames={mainPosition}
194
195
  handlesFocus
196
+ classNames={mainPosition}
195
197
  style={
196
198
  {
197
199
  '--dx-main-sidebarWidth':
@@ -213,7 +215,7 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
213
215
  >
214
216
  <div
215
217
  role='none'
216
- className={!solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
218
+ className={!solo ? 'relative bg-deckSurface overflow-hidden' : 'sr-only'}
217
219
  {...(solo && { inert: '' })}
218
220
  >
219
221
  {!topbar && !fullscreen && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
@@ -247,7 +249,7 @@ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
247
249
  </div>
248
250
  <div
249
251
  role='none'
250
- className={solo ? 'relative bg-deck overflow-hidden' : 'sr-only'}
252
+ className={solo ? 'relative bg-deckSurface overflow-hidden' : 'sr-only'}
251
253
  {...(!solo && { inert: '' })}
252
254
  >
253
255
  {!topbar && !fullscreen && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
@@ -12,7 +12,7 @@ import { PlankContentError } from '../Plank';
12
12
 
13
13
  export const Dialog = () => {
14
14
  const context = useCapability(DeckCapabilities.MutableDeckState);
15
- const { dialogType, dialogBlockAlign, dialogContent, dialogOpen } = context;
15
+ const { dialogOpen, dialogType, dialogBlockAlign, dialogOverlayClasses, dialogOverlayStyle, dialogContent } = context;
16
16
  const Root = dialogType === 'alert' ? AlertDialog.Root : NaturalDialog.Root;
17
17
  const Overlay = dialogType === 'alert' ? AlertDialog.Overlay : NaturalDialog.Overlay;
18
18
 
@@ -27,7 +27,7 @@ export const Dialog = () => {
27
27
  // TODO(burdon): Placeholder creates a suspense boundary; replace with defaults.
28
28
  <Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} placeholder={<div />} />
29
29
  ) : (
30
- <Overlay blockAlign={dialogBlockAlign}>
30
+ <Overlay blockAlign={dialogBlockAlign} classNames={dialogOverlayClasses} style={dialogOverlayStyle}>
31
31
  <Surface role='dialog' data={dialogContent} limit={1} fallback={PlankContentError} />
32
32
  </Overlay>
33
33
  )}