@dxos/plugin-deck 0.8.2-staging.7ac8446 → 0.8.2

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 (155) 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-SEYECDHI.mjs → check-app-scheme-7AXGR6UT.mjs} +2 -3
  4. package/dist/lib/browser/check-app-scheme-7AXGR6UT.mjs.map +7 -0
  5. package/dist/lib/browser/{state-7TN26M42.mjs → chunk-FX44YX3G.mjs} +11 -8
  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 +8 -8
  18. package/dist/lib/browser/index.mjs.map +2 -2
  19. package/dist/lib/browser/{intent-resolver-UDYKO2QW.mjs → intent-resolver-3GAC57UA.mjs} +135 -92
  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-XLXN2VEW.mjs → react-root-ISFFOJZX.mjs} +7 -7
  23. package/dist/lib/browser/{react-surface-WNGMZL7I.mjs → react-surface-A63RQB5N.mjs} +7 -7
  24. package/dist/lib/browser/{settings-HMDGSBGO.mjs → settings-X7GDEXU3.mjs} +6 -6
  25. package/dist/lib/browser/settings-X7GDEXU3.mjs.map +7 -0
  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-SC6QEN7R.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-ODG4B6NX.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 +18 -8
  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 +8 -183
  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 +12 -7
  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/Dialog.d.ts +3 -0
  53. package/dist/types/src/components/DeckLayout/Dialog.d.ts.map +1 -0
  54. package/dist/types/src/components/DeckLayout/Popover.d.ts +5 -0
  55. package/dist/types/src/components/DeckLayout/Popover.d.ts.map +1 -0
  56. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  57. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  58. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
  59. package/dist/types/src/components/Plank/Plank.d.ts +18 -5
  60. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
  61. package/dist/types/src/components/Plank/Plank.stories.d.ts +3 -3
  62. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -1
  63. package/dist/types/src/components/Plank/PlankControls.d.ts +3 -2
  64. package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
  65. package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -1
  66. package/dist/types/src/components/Plank/PlankHeading.d.ts +3 -2
  67. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
  68. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
  69. package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +1 -1
  70. package/dist/types/src/components/Sidebar/SidebarButton.d.ts +2 -1
  71. package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -1
  72. package/dist/types/src/hooks/index.d.ts +5 -1
  73. package/dist/types/src/hooks/index.d.ts.map +1 -1
  74. package/dist/types/src/hooks/useBreakpoints.d.ts.map +1 -0
  75. package/dist/types/src/hooks/useCompanions.d.ts.map +1 -0
  76. package/dist/types/src/hooks/useDeckCompanions.d.ts +13 -0
  77. package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -0
  78. package/dist/types/src/hooks/useHoistStatusbar.d.ts +3 -0
  79. package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +1 -0
  80. package/dist/types/src/hooks/useNodeActionExpander.d.ts.map +1 -1
  81. package/dist/types/src/index.d.ts +1 -1
  82. package/dist/types/src/index.d.ts.map +1 -1
  83. package/dist/types/src/layout.d.ts.map +1 -1
  84. package/dist/types/src/translations.d.ts +2 -1
  85. package/dist/types/src/translations.d.ts.map +1 -1
  86. package/dist/types/src/types.d.ts +108 -104
  87. package/dist/types/src/types.d.ts.map +1 -1
  88. package/dist/types/src/util/index.d.ts +1 -4
  89. package/dist/types/src/util/index.d.ts.map +1 -1
  90. package/dist/types/src/util/layoutAppliesTopbar.d.ts +2 -1
  91. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -1
  92. package/dist/types/src/util/overscroll.d.ts.map +1 -1
  93. package/dist/types/src/util/set-active.d.ts.map +1 -1
  94. package/dist/types/tsconfig.tsbuildinfo +1 -1
  95. package/package.json +39 -30
  96. package/src/capabilities/app-graph-builder.ts +120 -92
  97. package/src/capabilities/check-app-scheme.ts +3 -7
  98. package/src/capabilities/index.ts +3 -2
  99. package/src/capabilities/intent-resolver.ts +181 -135
  100. package/src/capabilities/settings.ts +4 -4
  101. package/src/capabilities/state.ts +7 -4
  102. package/src/capabilities/tools.ts +15 -12
  103. package/src/capabilities/url-handler.ts +4 -4
  104. package/src/components/DeckLayout/ContentEmpty.tsx +9 -4
  105. package/src/components/DeckLayout/DeckLayout.tsx +123 -188
  106. package/src/components/DeckLayout/Dialog.tsx +36 -0
  107. package/src/components/DeckLayout/Popover.tsx +104 -0
  108. package/src/components/Plank/Plank.stories.tsx +20 -8
  109. package/src/components/Plank/Plank.tsx +105 -69
  110. package/src/components/Plank/PlankControls.tsx +53 -57
  111. package/src/components/Plank/PlankError.tsx +2 -6
  112. package/src/components/Plank/PlankHeading.tsx +31 -12
  113. package/src/components/Sidebar/ComplementarySidebar.tsx +36 -57
  114. package/src/components/Sidebar/Sidebar.tsx +7 -4
  115. package/src/components/Sidebar/SidebarButton.tsx +26 -7
  116. package/src/components/fragments.ts +1 -1
  117. package/src/hooks/index.ts +5 -1
  118. package/src/{util → hooks}/useCompanions.ts +3 -3
  119. package/src/hooks/useDeckCompanions.ts +33 -0
  120. package/src/{util → hooks}/useHoistStatusbar.ts +9 -4
  121. package/src/hooks/useNodeActionExpander.ts +3 -8
  122. package/src/index.ts +1 -1
  123. package/src/translations.ts +2 -1
  124. package/src/types.ts +77 -71
  125. package/src/util/index.ts +1 -4
  126. package/src/util/layoutAppliesTopbar.ts +8 -2
  127. package/dist/lib/browser/app-graph-builder-VYZ4IWI3.mjs.map +0 -7
  128. package/dist/lib/browser/check-app-scheme-SEYECDHI.mjs.map +0 -7
  129. package/dist/lib/browser/chunk-6ZSOFCPP.mjs +0 -117
  130. package/dist/lib/browser/chunk-6ZSOFCPP.mjs.map +0 -7
  131. package/dist/lib/browser/chunk-B4LOJUWW.mjs +0 -24
  132. package/dist/lib/browser/chunk-B4LOJUWW.mjs.map +0 -7
  133. package/dist/lib/browser/chunk-FJBMNSUC.mjs +0 -1289
  134. package/dist/lib/browser/chunk-FJBMNSUC.mjs.map +0 -7
  135. package/dist/lib/browser/chunk-FLOVGNYB.mjs +0 -81
  136. package/dist/lib/browser/chunk-FLOVGNYB.mjs.map +0 -7
  137. package/dist/lib/browser/chunk-RJNCG4ND.mjs +0 -154
  138. package/dist/lib/browser/chunk-RJNCG4ND.mjs.map +0 -7
  139. package/dist/lib/browser/intent-resolver-UDYKO2QW.mjs.map +0 -7
  140. package/dist/lib/browser/settings-HMDGSBGO.mjs.map +0 -7
  141. package/dist/lib/browser/state-7TN26M42.mjs.map +0 -7
  142. package/dist/lib/browser/tools-SC6QEN7R.mjs.map +0 -7
  143. package/dist/lib/browser/url-handler-ODG4B6NX.mjs.map +0 -7
  144. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +0 -5
  145. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +0 -1
  146. package/dist/types/src/util/useBreakpoints.d.ts.map +0 -1
  147. package/dist/types/src/util/useCompanions.d.ts.map +0 -1
  148. package/dist/types/src/util/useHoistStatusbar.d.ts +0 -2
  149. package/dist/types/src/util/useHoistStatusbar.d.ts.map +0 -1
  150. package/src/components/DeckLayout/Fullscreen.tsx +0 -31
  151. /package/dist/lib/browser/{react-root-XLXN2VEW.mjs.map → react-root-ISFFOJZX.mjs.map} +0 -0
  152. /package/dist/lib/browser/{react-surface-WNGMZL7I.mjs.map → react-surface-A63RQB5N.mjs.map} +0 -0
  153. /package/dist/types/src/{util → hooks}/useBreakpoints.d.ts +0 -0
  154. /package/dist/types/src/{util → hooks}/useCompanions.d.ts +0 -0
  155. /package/src/{util → hooks}/useBreakpoints.ts +0 -0
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { batch } from '@preact/signals-core';
6
- import { 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
- import { isReactiveObject } from '@dxos/live-object';
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,49 +100,56 @@ 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
- layout.popoverContent = subject ? { component: subject, props: options.props } : null;
125
- layout.popoverAnchorId = options.anchorId;
126
+ layout.popoverContent =
127
+ typeof subject === 'string' ? { component: subject, props: options.props } : subject ? { subject } : null;
126
128
  layout.popoverSide = options.side;
129
+ if (options.variant === 'virtual') {
130
+ layout.popoverAnchor = options.anchor;
131
+ } else {
132
+ layout.popoverAnchorId = options.anchorId;
133
+ }
127
134
  },
128
135
  }),
129
136
  createResolver({
130
137
  intent: LayoutAction.UpdateLayout,
131
- // 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)`
132
139
  // but the filter is not being applied correctly.
133
- filter: (data): data is S.Schema.Type<typeof LayoutAction.AddToast.fields.input> =>
134
- 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),
135
142
  resolve: ({ subject }) => {
136
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
143
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
137
144
  layout.toasts.push(subject);
138
145
  },
139
146
  }),
140
147
  createResolver({
141
148
  intent: LayoutAction.UpdateLayout,
142
- // 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)`
143
150
  // but the filter is not being applied correctly.
144
- filter: (data): data is S.Schema.Type<typeof LayoutAction.SetLayoutMode.fields.input> => {
145
- 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)) {
146
153
  return false;
147
154
  }
148
155
 
@@ -153,7 +160,7 @@ export default (context: PluginsContext) =>
153
160
  return true;
154
161
  },
155
162
  resolve: ({ subject, options }) => {
156
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
163
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
157
164
 
158
165
  const setMode = (mode: LayoutMode) => {
159
166
  const deck = state.deck;
@@ -174,10 +181,8 @@ export default (context: PluginsContext) =>
174
181
  deck.initialized = true;
175
182
  }
176
183
 
177
- if (mode === 'fullscreen' && !deck.fullscreen) {
178
- deck.fullscreen = true;
179
- } else if (mode !== 'fullscreen' && deck.fullscreen) {
180
- deck.fullscreen = false;
184
+ if (mode === 'solo--fullscreen') {
185
+ deck.fullscreen = !deck.fullscreen;
181
186
  }
182
187
  };
183
188
 
@@ -199,10 +204,11 @@ export default (context: PluginsContext) =>
199
204
  }),
200
205
  createResolver({
201
206
  intent: LayoutAction.UpdateLayout,
202
- filter: (data): data is S.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
203
- 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),
204
209
  resolve: ({ subject }) => {
205
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
210
+ const { graph } = context.getCapability(Capabilities.AppGraph);
211
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
206
212
  batch(() => {
207
213
  // TODO(wittjosiah): This is a hack to prevent the previous deck from being set for pinned items.
208
214
  // Ideally this should be worked into the data model in a generic way.
@@ -220,15 +226,24 @@ export default (context: PluginsContext) =>
220
226
  return {
221
227
  intents: [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: first })],
222
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
+ }
223
238
  }
224
239
  },
225
240
  }),
226
241
  createResolver({
227
242
  intent: LayoutAction.UpdateLayout,
228
- filter: (data): data is S.Schema.Type<typeof LayoutAction.RevertWorkspace.fields.input> =>
229
- 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),
230
245
  resolve: () => {
231
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
246
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
232
247
  return {
233
248
  intents: [createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: state.previousDeck })],
234
249
  };
@@ -236,65 +251,78 @@ export default (context: PluginsContext) =>
236
251
  }),
237
252
  createResolver({
238
253
  intent: LayoutAction.UpdateLayout,
239
- filter: (data): data is S.Schema.Type<typeof LayoutAction.Open.fields.input> =>
240
- S.is(LayoutAction.Open.fields.input)(data),
241
- resolve: ({ subject, options }) => {
242
- const { graph } = context.requestCapability(Capabilities.AppGraph);
243
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
244
- const attention = context.requestCapability(AttentionCapabilities.Attention);
245
- const settings = context
246
- .requestCapabilities(Capabilities.SettingsStore)[0]
247
- ?.getStore<DeckSettingsProps>(DECK_PLUGIN)?.value;
254
+ filter: (data): data is Schema.Schema.Type<typeof LayoutAction.Open.fields.input> =>
255
+ Schema.is(LayoutAction.Open.fields.input)(data),
256
+ resolve: ({ subject, options }) =>
257
+ Effect.gen(function* () {
258
+ const { graph } = context.getCapability(Capabilities.AppGraph);
259
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
260
+ const attention = context.getCapability(AttentionCapabilities.Attention);
261
+ const settings = context
262
+ .getCapabilities(Capabilities.SettingsStore)[0]
263
+ ?.getStore<DeckSettingsProps>(DECK_PLUGIN)?.value;
248
264
 
249
- const previouslyOpenIds = new Set<string>(state.deck.solo ? [state.deck.solo] : state.deck.active);
250
- batch(() => {
251
- const next = state.deck.solo
252
- ? (subject as string[]).map((id) => createEntryId(id, options?.variant))
253
- : subject.reduce(
254
- (acc, entryId) =>
255
- openEntry(acc, entryId, {
256
- key: options?.key,
257
- positioning: options?.positioning ?? settings?.newPlankPositioning,
258
- pivotId: options?.pivotId,
259
- variant: options?.variant,
260
- }),
261
- state.deck.active,
262
- );
265
+ if (options?.workspace && state.activeDeck !== options?.workspace) {
266
+ const { dispatch } = context.getCapability(Capabilities.IntentDispatcher);
267
+ yield* dispatch(
268
+ createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: options.workspace }),
269
+ );
270
+ }
263
271
 
264
- return setActive({ next, state, attention });
265
- });
272
+ const previouslyOpenIds = new Set<string>(state.deck.solo ? [state.deck.solo] : state.deck.active);
273
+ batch(() => {
274
+ const next = state.deck.solo
275
+ ? (subject as string[]).map((id) => createEntryId(id, options?.variant))
276
+ : subject.reduce(
277
+ (acc, entryId) =>
278
+ openEntry(acc, entryId, {
279
+ key: options?.key,
280
+ positioning: options?.positioning ?? settings?.newPlankPositioning,
281
+ pivotId: options?.pivotId,
282
+ variant: options?.variant,
283
+ }),
284
+ state.deck.active,
285
+ );
266
286
 
267
- const ids = state.deck.solo ? [state.deck.solo] : state.deck.active;
268
- const newlyOpen = ids.filter((i) => !previouslyOpenIds.has(i));
287
+ return setActive({ next, state, attention });
288
+ });
269
289
 
270
- return {
271
- intents: [
272
- ...(options?.scrollIntoView !== false
273
- ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: newlyOpen[0] ?? subject[0] })]
274
- : []),
275
- createIntent(LayoutAction.Expose, { part: 'navigation', subject: newlyOpen[0] ?? subject[0] }),
276
- ...newlyOpen.map((subjectId) => {
277
- const active = graph?.findNode(subjectId)?.data;
278
- const typename = isReactiveObject(active) ? getTypename(active) : undefined;
279
- return createIntent(ObservabilityAction.SendEvent, {
280
- name: 'navigation.activate',
281
- properties: {
282
- subjectId,
283
- typename,
284
- },
285
- });
286
- }),
287
- ],
288
- };
289
- },
290
+ const ids = state.deck.solo ? [state.deck.solo] : state.deck.active;
291
+ const newlyOpen = ids.filter((i) => !previouslyOpenIds.has(i));
292
+
293
+ return {
294
+ intents: [
295
+ ...(options?.scrollIntoView !== false
296
+ ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: newlyOpen[0] ?? subject[0] })]
297
+ : []),
298
+ createIntent(LayoutAction.Expose, { part: 'navigation', subject: newlyOpen[0] ?? subject[0] }),
299
+ ...newlyOpen.map((subjectId) => {
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
+ });
307
+ return createIntent(ObservabilityAction.SendEvent, {
308
+ name: 'navigation.activate',
309
+ properties: {
310
+ subjectId,
311
+ typename,
312
+ },
313
+ });
314
+ }),
315
+ ],
316
+ };
317
+ }),
290
318
  }),
291
319
  createResolver({
292
320
  intent: LayoutAction.UpdateLayout,
293
- filter: (data): data is S.Schema.Type<typeof LayoutAction.Close.fields.input> =>
294
- 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),
295
323
  resolve: ({ subject }) => {
296
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
297
- const attention = context.requestCapability(AttentionCapabilities.Attention);
324
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
325
+ const attention = context.getCapability(AttentionCapabilities.Attention);
298
326
  const active = state.deck.solo ? [state.deck.solo] : state.deck.active;
299
327
  const next = subject.reduce((acc, id) => closeEntry(acc, id), active);
300
328
  const toAttend = setActive({ next, state, attention });
@@ -313,11 +341,11 @@ export default (context: PluginsContext) =>
313
341
  }),
314
342
  createResolver({
315
343
  intent: LayoutAction.UpdateLayout,
316
- filter: (data): data is S.Schema.Type<typeof LayoutAction.Set.fields.input> =>
317
- 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),
318
346
  resolve: ({ subject }) => {
319
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
320
- const attention = context.requestCapability(AttentionCapabilities.Attention);
347
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
348
+ const attention = context.getCapability(AttentionCapabilities.Attention);
321
349
  const toAttend = setActive({ next: subject as string[], state, attention });
322
350
  return {
323
351
  intents: toAttend ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: toAttend })] : [],
@@ -326,24 +354,24 @@ export default (context: PluginsContext) =>
326
354
  }),
327
355
  createResolver({
328
356
  intent: LayoutAction.UpdateLayout,
329
- filter: (data): data is S.Schema.Type<typeof LayoutAction.ScrollIntoView.fields.input> =>
330
- 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),
331
359
  resolve: ({ subject }) => {
332
- const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
360
+ const layout = context.getCapability(DeckCapabilities.MutableDeckState);
333
361
  layout.scrollIntoView = subject;
334
362
  },
335
363
  }),
336
364
  createResolver({
337
365
  intent: DeckAction.UpdatePlankSize,
338
366
  resolve: (data) => {
339
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
367
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
340
368
  state.deck.plankSizing[data.id] = data.size;
341
369
  },
342
370
  }),
343
371
  createResolver({
344
372
  intent: DeckAction.ChangeCompanion,
345
373
  resolve: (data) => {
346
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
374
+ const state = context.getCapability(DeckCapabilities.MutableDeckState);
347
375
  // TODO(thure): Reactivity only works when creating a lexically new `activeCompanions`… Are these not proxy objects?
348
376
  if (data.companion === null) {
349
377
  const { [data.primary]: _, ...nextActiveCompanions } = state.deck.activeCompanions ?? {};
@@ -360,9 +388,9 @@ export default (context: PluginsContext) =>
360
388
  createResolver({
361
389
  intent: DeckAction.Adjust,
362
390
  resolve: (adjustment) => {
363
- const state = context.requestCapability(DeckCapabilities.MutableDeckState);
364
- const attention = context.requestCapability(AttentionCapabilities.Attention);
365
- 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);
366
394
 
367
395
  return batch(() => {
368
396
  if (adjustment.type === 'increment-end' || adjustment.type === 'increment-start') {
@@ -374,23 +402,28 @@ export default (context: PluginsContext) =>
374
402
  }
375
403
 
376
404
  if (adjustment.type === 'companion') {
377
- const node = graph.findNode(adjustment.id);
378
- const [companion] = node
379
- ? graph
380
- .nodes(node, { filter: (n): n is Node<any> => n.type === PLANK_COMPANION_TYPE })
381
- .toSorted((a, b) => byPosition(a.properties, b.properties))
382
- : [];
383
- if (companion) {
384
- return {
385
- intents: [
386
- // TODO(wittjosiah): This should remember the previously selected companion.
387
- createIntent(DeckAction.ChangeCompanion, { primary: adjustment.id, companion: companion.id }),
388
- ],
389
- };
390
- }
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
+ );
391
424
  }
392
425
 
393
- if (adjustment.type === 'solo') {
426
+ if (adjustment.type.startsWith('solo')) {
394
427
  const entryId = adjustment.id;
395
428
  if (!state.deck.solo) {
396
429
  // Solo the entry.
@@ -399,21 +432,34 @@ export default (context: PluginsContext) =>
399
432
  createIntent(LayoutAction.SetLayoutMode, {
400
433
  part: 'mode',
401
434
  subject: entryId,
402
- options: { mode: 'solo' },
435
+ options: { mode: adjustment.type },
403
436
  }),
404
437
  ],
405
438
  };
406
439
  } else {
407
- // Un-solo the current entry.
408
- return {
409
- intents: [
410
- // NOTE: The order of these is important.
411
- pipe(
412
- createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { mode: 'deck' } }),
413
- chain(LayoutAction.Open, { part: 'main', subject: [entryId] }),
414
- ),
415
- ],
416
- };
440
+ if (adjustment.type === 'solo--fullscreen') {
441
+ // Toggle fullscreen on the current entry.
442
+ return {
443
+ intents: [
444
+ createIntent(LayoutAction.SetLayoutMode, {
445
+ part: 'mode',
446
+ subject: entryId,
447
+ options: { mode: 'solo--fullscreen' },
448
+ }),
449
+ ],
450
+ };
451
+ } else if (adjustment.type === 'solo') {
452
+ // Un-solo the current entry.
453
+ return {
454
+ intents: [
455
+ // NOTE: The order of these is important.
456
+ pipe(
457
+ createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { mode: 'deck' } }),
458
+ chain(LayoutAction.Open, { part: 'main', subject: [entryId] }),
459
+ ),
460
+ ],
461
+ };
462
+ }
417
463
  }
418
464
  }
419
465
  });
@@ -3,17 +3,17 @@
3
3
  //
4
4
 
5
5
  import { Capabilities, contributes } from '@dxos/app-framework';
6
- import { create } from '@dxos/live-object';
6
+ import { live } from '@dxos/live-object';
7
7
 
8
8
  import { DECK_PLUGIN } from '../meta';
9
9
  import { DeckSettingsSchema, type DeckSettingsProps } from '../types';
10
10
 
11
11
  export default () => {
12
- const settings = create<DeckSettingsProps>({
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
  });
@@ -4,13 +4,13 @@
4
4
 
5
5
  import { Capabilities, contributes } from '@dxos/app-framework';
6
6
  import { invariant } from '@dxos/invariant';
7
- import { create } from '@dxos/live-object';
7
+ import { live } from '@dxos/live-object';
8
8
  import { LocalStorageStore } from '@dxos/local-storage';
9
9
  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, {
@@ -41,6 +41,7 @@ export default () => {
41
41
  dialogBlockAlign: undefined,
42
42
  dialogType: undefined,
43
43
  popoverContent: null,
44
+ popoverAnchor: undefined,
44
45
  popoverAnchorId: undefined,
45
46
  popoverOpen: false,
46
47
  toasts: [],
@@ -67,7 +68,7 @@ export default () => {
67
68
  .prop({ key: 'activeDeck', type: LocalStorageStore.string() })
68
69
  .prop({ key: 'previousDeck', type: LocalStorageStore.string() });
69
70
 
70
- const layout = create<Capabilities.Layout>({
71
+ const layout = live<Capabilities.Layout>({
71
72
  get mode() {
72
73
  return getMode(state.values.deck);
73
74
  },
@@ -99,3 +100,5 @@ export default () => {
99
100
  contributes(Capabilities.Layout, layout),
100
101
  ];
101
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
  }),