@dxos/plugin-deck 0.8.1-staging.391c573 → 0.8.1-staging.9eaf14f

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 (176) hide show
  1. package/dist/lib/browser/{app-graph-builder-IYHAGFA3.mjs → app-graph-builder-VYZ4IWI3.mjs} +3 -3
  2. package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs → check-app-scheme-SEYECDHI.mjs} +2 -2
  3. package/dist/lib/browser/{chunk-YCKJNTKG.mjs → chunk-6ZSOFCPP.mjs} +26 -6
  4. package/dist/lib/browser/chunk-6ZSOFCPP.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-B4LOJUWW.mjs +24 -0
  6. package/dist/lib/browser/{chunk-Z23S33X6.mjs → chunk-FJBMNSUC.mjs} +638 -483
  7. package/dist/lib/browser/chunk-FJBMNSUC.mjs.map +7 -0
  8. package/dist/lib/browser/chunk-FLOVGNYB.mjs +81 -0
  9. package/dist/lib/browser/chunk-FLOVGNYB.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-N7TEPFVR.mjs → chunk-NSATFAEE.mjs} +3 -3
  11. package/dist/lib/browser/{chunk-N7TEPFVR.mjs.map → chunk-NSATFAEE.mjs.map} +2 -2
  12. package/dist/lib/browser/{chunk-FYKBOM3C.mjs → chunk-RJNCG4ND.mjs} +66 -40
  13. package/dist/lib/browser/chunk-RJNCG4ND.mjs.map +7 -0
  14. package/dist/lib/browser/{chunk-22AQ5IVX.mjs → chunk-XMCG42ID.mjs} +2 -3
  15. package/dist/lib/browser/chunk-XMCG42ID.mjs.map +7 -0
  16. package/dist/lib/browser/index.mjs +14 -9
  17. package/dist/lib/browser/index.mjs.map +3 -3
  18. package/dist/lib/browser/{intent-resolver-P5BVUQKU.mjs → intent-resolver-UDYKO2QW.mjs} +78 -88
  19. package/dist/lib/browser/intent-resolver-UDYKO2QW.mjs.map +7 -0
  20. package/dist/lib/browser/meta.json +1 -1
  21. package/dist/lib/browser/{react-root-EP4UF3KA.mjs → react-root-XLXN2VEW.mjs} +8 -10
  22. package/dist/lib/browser/react-root-XLXN2VEW.mjs.map +7 -0
  23. package/dist/lib/browser/{react-surface-5B3RLJCD.mjs → react-surface-WNGMZL7I.mjs} +11 -10
  24. package/dist/lib/browser/react-surface-WNGMZL7I.mjs.map +7 -0
  25. package/dist/lib/browser/{settings-X3P2HKQJ.mjs → settings-HMDGSBGO.mjs} +5 -4
  26. package/dist/lib/browser/settings-HMDGSBGO.mjs.map +7 -0
  27. package/dist/lib/browser/{state-2MOTLKVR.mjs → state-7TN26M42.mjs} +7 -11
  28. package/dist/lib/browser/state-7TN26M42.mjs.map +7 -0
  29. package/dist/lib/browser/tools-SC6QEN7R.mjs +78 -0
  30. package/dist/lib/browser/tools-SC6QEN7R.mjs.map +7 -0
  31. package/dist/lib/browser/types.mjs +12 -6
  32. package/dist/lib/browser/{url-handler-MVHTKUYA.mjs → url-handler-ODG4B6NX.mjs} +7 -9
  33. package/dist/lib/browser/url-handler-ODG4B6NX.mjs.map +7 -0
  34. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  35. package/dist/types/src/capabilities/capabilities.d.ts +36 -14
  36. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
  39. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  40. package/dist/types/src/capabilities/settings.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/state.d.ts +18 -6
  42. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/tools.d.ts +1 -0
  44. package/dist/types/src/capabilities/tools.d.ts.map +1 -1
  45. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -1
  46. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  47. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -1
  48. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +1 -4
  49. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  50. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  51. package/dist/types/src/components/DeckLayout/index.d.ts +1 -0
  52. package/dist/types/src/components/DeckLayout/index.d.ts.map +1 -1
  53. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts +6 -0
  54. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -0
  55. package/dist/types/src/components/DeckSettings/index.d.ts +2 -0
  56. package/dist/types/src/components/DeckSettings/index.d.ts.map +1 -0
  57. package/dist/types/src/components/Plank/Plank.d.ts +14 -0
  58. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -0
  59. package/dist/types/src/components/Plank/Plank.stories.d.ts +8 -0
  60. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -0
  61. package/dist/types/src/components/{DeckLayout → Plank}/PlankControls.d.ts +8 -1
  62. package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -0
  63. package/dist/types/src/components/{DeckLayout → Plank}/PlankError.d.ts +2 -2
  64. package/dist/types/src/components/Plank/PlankError.d.ts.map +1 -0
  65. package/dist/types/src/components/Plank/PlankHeading.d.ts +20 -0
  66. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -0
  67. package/dist/types/src/components/Plank/PlankLoading.d.ts.map +1 -0
  68. package/dist/types/src/components/Plank/index.d.ts +6 -0
  69. package/dist/types/src/components/Plank/index.d.ts.map +1 -0
  70. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -0
  71. package/dist/types/src/components/Sidebar/Sidebar.d.ts.map +1 -0
  72. package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -0
  73. package/dist/types/src/components/Sidebar/index.d.ts +4 -0
  74. package/dist/types/src/components/Sidebar/index.d.ts.map +1 -0
  75. package/dist/types/src/components/index.d.ts +1 -1
  76. package/dist/types/src/components/index.d.ts.map +1 -1
  77. package/dist/types/src/events.d.ts +0 -1
  78. package/dist/types/src/events.d.ts.map +1 -1
  79. package/dist/types/src/hooks/index.d.ts +0 -1
  80. package/dist/types/src/hooks/index.d.ts.map +1 -1
  81. package/dist/types/src/index.d.ts +1 -0
  82. package/dist/types/src/index.d.ts.map +1 -1
  83. package/dist/types/src/layout.d.ts +7 -1
  84. package/dist/types/src/layout.d.ts.map +1 -1
  85. package/dist/types/src/meta.d.ts +2 -5
  86. package/dist/types/src/meta.d.ts.map +1 -1
  87. package/dist/types/src/translations.d.ts +4 -0
  88. package/dist/types/src/translations.d.ts.map +1 -1
  89. package/dist/types/src/types.d.ts +50 -48
  90. package/dist/types/src/types.d.ts.map +1 -1
  91. package/dist/types/src/util/index.d.ts +1 -0
  92. package/dist/types/src/util/index.d.ts.map +1 -1
  93. package/dist/types/src/util/set-active.d.ts +2 -2
  94. package/dist/types/src/util/set-active.d.ts.map +1 -1
  95. package/dist/types/src/util/useCompanions.d.ts +8 -0
  96. package/dist/types/src/util/useCompanions.d.ts.map +1 -0
  97. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  98. package/package.json +28 -29
  99. package/src/DeckPlugin.ts +0 -1
  100. package/src/capabilities/capabilities.ts +3 -4
  101. package/src/capabilities/intent-resolver.ts +63 -9
  102. package/src/capabilities/react-root.tsx +1 -9
  103. package/src/capabilities/react-surface.tsx +3 -4
  104. package/src/capabilities/settings.ts +7 -2
  105. package/src/capabilities/state.ts +4 -11
  106. package/src/capabilities/tools.ts +34 -22
  107. package/src/capabilities/url-handler.ts +2 -8
  108. package/src/components/DeckLayout/ActiveNode.tsx +2 -1
  109. package/src/components/DeckLayout/Banner.tsx +5 -3
  110. package/src/components/DeckLayout/ContentEmpty.tsx +1 -1
  111. package/src/components/DeckLayout/DeckLayout.tsx +58 -24
  112. package/src/components/DeckLayout/Fullscreen.tsx +1 -1
  113. package/src/components/DeckLayout/Toast.tsx +1 -1
  114. package/src/components/DeckLayout/index.ts +2 -0
  115. package/src/components/{LayoutSettings.tsx → DeckSettings/DeckSettings.tsx} +15 -10
  116. package/src/components/DeckSettings/index.ts +5 -0
  117. package/src/components/Plank/Plank.stories.tsx +43 -0
  118. package/src/components/Plank/Plank.tsx +230 -0
  119. package/src/components/{DeckLayout → Plank}/PlankControls.tsx +73 -27
  120. package/src/components/{DeckLayout → Plank}/PlankError.tsx +3 -3
  121. package/src/components/Plank/PlankHeading.tsx +207 -0
  122. package/src/components/Plank/index.ts +9 -0
  123. package/src/components/{DeckLayout → Sidebar}/ComplementarySidebar.tsx +65 -81
  124. package/src/components/Sidebar/index.ts +7 -0
  125. package/src/components/index.ts +1 -1
  126. package/src/events.ts +0 -1
  127. package/src/hooks/index.ts +0 -1
  128. package/src/index.ts +1 -0
  129. package/src/layout.ts +19 -2
  130. package/src/meta.ts +4 -4
  131. package/src/translations.ts +4 -0
  132. package/src/types.ts +81 -79
  133. package/src/util/index.ts +1 -0
  134. package/src/util/set-active.ts +2 -2
  135. package/src/util/useCompanions.ts +18 -0
  136. package/src/util/useHoistStatusbar.ts +2 -2
  137. package/dist/lib/browser/chunk-22AQ5IVX.mjs.map +0 -7
  138. package/dist/lib/browser/chunk-FYKBOM3C.mjs.map +0 -7
  139. package/dist/lib/browser/chunk-IZ5RPJ6T.mjs +0 -24
  140. package/dist/lib/browser/chunk-YCKJNTKG.mjs.map +0 -7
  141. package/dist/lib/browser/chunk-Z23S33X6.mjs.map +0 -7
  142. package/dist/lib/browser/intent-resolver-P5BVUQKU.mjs.map +0 -7
  143. package/dist/lib/browser/react-root-EP4UF3KA.mjs.map +0 -7
  144. package/dist/lib/browser/react-surface-5B3RLJCD.mjs.map +0 -7
  145. package/dist/lib/browser/settings-X3P2HKQJ.mjs.map +0 -7
  146. package/dist/lib/browser/state-2MOTLKVR.mjs.map +0 -7
  147. package/dist/lib/browser/tools-64LXGLYR.mjs +0 -59
  148. package/dist/lib/browser/tools-64LXGLYR.mjs.map +0 -7
  149. package/dist/lib/browser/url-handler-MVHTKUYA.mjs.map +0 -7
  150. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +0 -1
  151. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +0 -15
  152. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +0 -1
  153. package/dist/types/src/components/DeckLayout/Plank.d.ts +0 -13
  154. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +0 -1
  155. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +0 -1
  156. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +0 -1
  157. package/dist/types/src/components/DeckLayout/PlankLoading.d.ts.map +0 -1
  158. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +0 -1
  159. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +0 -1
  160. package/dist/types/src/components/LayoutSettings.d.ts +0 -6
  161. package/dist/types/src/components/LayoutSettings.d.ts.map +0 -1
  162. package/dist/types/src/hooks/useNode.d.ts +0 -11
  163. package/dist/types/src/hooks/useNode.d.ts.map +0 -1
  164. package/src/components/DeckLayout/NodePlankHeading.tsx +0 -148
  165. package/src/components/DeckLayout/Plank.tsx +0 -149
  166. package/src/hooks/useNode.ts +0 -46
  167. /package/dist/lib/browser/{app-graph-builder-IYHAGFA3.mjs.map → app-graph-builder-VYZ4IWI3.mjs.map} +0 -0
  168. /package/dist/lib/browser/{check-app-scheme-S3EYUPMF.mjs.map → check-app-scheme-SEYECDHI.mjs.map} +0 -0
  169. /package/dist/lib/browser/{chunk-IZ5RPJ6T.mjs.map → chunk-B4LOJUWW.mjs.map} +0 -0
  170. /package/dist/types/src/components/{DeckLayout → Plank}/PlankLoading.d.ts +0 -0
  171. /package/dist/types/src/components/{DeckLayout → Sidebar}/ComplementarySidebar.d.ts +0 -0
  172. /package/dist/types/src/components/{DeckLayout → Sidebar}/Sidebar.d.ts +0 -0
  173. /package/dist/types/src/components/{DeckLayout → Sidebar}/SidebarButton.d.ts +0 -0
  174. /package/src/components/{DeckLayout → Plank}/PlankLoading.tsx +0 -0
  175. /package/src/components/{DeckLayout → Sidebar}/Sidebar.tsx +0 -0
  176. /package/src/components/{DeckLayout → Sidebar}/SidebarButton.tsx +0 -0
@@ -16,16 +16,26 @@ import {
16
16
  chain,
17
17
  } from '@dxos/app-framework';
18
18
  import { getTypename, S } from '@dxos/echo-schema';
19
+ import { invariant } from '@dxos/invariant';
19
20
  import { isReactiveObject } from '@dxos/live-object';
20
21
  import { log } from '@dxos/log';
21
22
  import { AttentionCapabilities } from '@dxos/plugin-attention';
23
+ import { type Node } from '@dxos/plugin-graph';
22
24
  import { ObservabilityAction } from '@dxos/plugin-observability/types';
23
- import { isNonNullable } from '@dxos/util';
25
+ import { byPosition, isNonNullable } from '@dxos/util';
24
26
 
25
27
  import { DeckCapabilities } from './capabilities';
26
- import { closeEntry, incrementPlank, openEntry } from '../layout';
28
+ import { closeEntry, createEntryId, incrementPlank, openEntry } from '../layout';
27
29
  import { DECK_PLUGIN } from '../meta';
28
- import { DeckAction, type LayoutMode, type DeckSettingsProps, isLayoutMode, getMode } from '../types';
30
+ import {
31
+ DeckAction,
32
+ type LayoutMode,
33
+ type DeckSettingsProps,
34
+ isLayoutMode,
35
+ getMode,
36
+ defaultDeck,
37
+ PLANK_COMPANION_TYPE,
38
+ } from '../types';
29
39
  import { setActive } from '../util';
30
40
 
31
41
  export default (context: PluginsContext) =>
@@ -201,7 +211,7 @@ export default (context: PluginsContext) =>
201
211
  }
202
212
  state.activeDeck = subject;
203
213
  if (!state.decks[subject]) {
204
- state.decks[subject] = { initialized: false, active: [], inactive: [], fullscreen: false, plankSizing: {} };
214
+ state.decks[subject] = { ...defaultDeck };
205
215
  }
206
216
  });
207
217
 
@@ -239,13 +249,14 @@ export default (context: PluginsContext) =>
239
249
  const previouslyOpenIds = new Set<string>(state.deck.solo ? [state.deck.solo] : state.deck.active);
240
250
  batch(() => {
241
251
  const next = state.deck.solo
242
- ? (subject as string[])
252
+ ? (subject as string[]).map((id) => createEntryId(id, options?.variant))
243
253
  : subject.reduce(
244
254
  (acc, entryId) =>
245
255
  openEntry(acc, entryId, {
246
256
  key: options?.key,
247
257
  positioning: options?.positioning ?? settings?.newPlankPositioning,
248
258
  pivotId: options?.pivotId,
259
+ variant: options?.variant,
249
260
  }),
250
261
  state.deck.active,
251
262
  );
@@ -262,13 +273,13 @@ export default (context: PluginsContext) =>
262
273
  ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: newlyOpen[0] ?? subject[0] })]
263
274
  : []),
264
275
  createIntent(LayoutAction.Expose, { part: 'navigation', subject: newlyOpen[0] ?? subject[0] }),
265
- ...newlyOpen.map((id) => {
266
- const active = graph?.findNode(id)?.data;
276
+ ...newlyOpen.map((subjectId) => {
277
+ const active = graph?.findNode(subjectId)?.data;
267
278
  const typename = isReactiveObject(active) ? getTypename(active) : undefined;
268
279
  return createIntent(ObservabilityAction.SendEvent, {
269
280
  name: 'navigation.activate',
270
281
  properties: {
271
- id,
282
+ subjectId,
272
283
  typename,
273
284
  },
274
285
  });
@@ -287,8 +298,16 @@ export default (context: PluginsContext) =>
287
298
  const active = state.deck.solo ? [state.deck.solo] : state.deck.active;
288
299
  const next = subject.reduce((acc, id) => closeEntry(acc, id), active);
289
300
  const toAttend = setActive({ next, state, attention });
301
+
302
+ const clearCompanionIntents = subject
303
+ .filter((id) => state.deck.activeCompanions && id in state.deck.activeCompanions)
304
+ .map((primary) => createIntent(DeckAction.ChangeCompanion, { primary, companion: null }));
305
+
290
306
  return {
291
- intents: toAttend ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: toAttend })] : [],
307
+ intents: [
308
+ ...clearCompanionIntents,
309
+ ...(toAttend ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: toAttend })] : []),
310
+ ],
292
311
  };
293
312
  },
294
313
  }),
@@ -321,11 +340,29 @@ export default (context: PluginsContext) =>
321
340
  state.deck.plankSizing[data.id] = data.size;
322
341
  },
323
342
  }),
343
+ createResolver({
344
+ intent: DeckAction.ChangeCompanion,
345
+ resolve: (data) => {
346
+ const state = context.requestCapability(DeckCapabilities.MutableDeckState);
347
+ // TODO(thure): Reactivity only works when creating a lexically new `activeCompanions`… Are these not proxy objects?
348
+ if (data.companion === null) {
349
+ const { [data.primary]: _, ...nextActiveCompanions } = state.deck.activeCompanions ?? {};
350
+ state.deck.activeCompanions = nextActiveCompanions;
351
+ } else {
352
+ invariant(data.companion !== data.primary);
353
+ state.deck.activeCompanions = {
354
+ ...state.deck.activeCompanions,
355
+ [data.primary]: data.companion,
356
+ };
357
+ }
358
+ },
359
+ }),
324
360
  createResolver({
325
361
  intent: DeckAction.Adjust,
326
362
  resolve: (adjustment) => {
327
363
  const state = context.requestCapability(DeckCapabilities.MutableDeckState);
328
364
  const attention = context.requestCapability(AttentionCapabilities.Attention);
365
+ const { graph } = context.requestCapability(Capabilities.AppGraph);
329
366
 
330
367
  return batch(() => {
331
368
  if (adjustment.type === 'increment-end' || adjustment.type === 'increment-start') {
@@ -336,6 +373,23 @@ export default (context: PluginsContext) =>
336
373
  });
337
374
  }
338
375
 
376
+ 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
+ }
391
+ }
392
+
339
393
  if (adjustment.type === 'solo') {
340
394
  const entryId = adjustment.id;
341
395
  if (!state.deck.solo) {
@@ -9,14 +9,12 @@ import { Capabilities, contributes, useCapability } from '@dxos/app-framework';
9
9
  import { DeckCapabilities } from './capabilities';
10
10
  import { DeckLayout } from '../components';
11
11
  import { DECK_PLUGIN } from '../meta';
12
- import { type DeckSettingsProps } from '../types';
13
12
 
14
13
  export default () =>
15
14
  contributes(Capabilities.ReactRoot, {
16
15
  id: DECK_PLUGIN,
17
16
  root: () => {
18
17
  const layout = useCapability(DeckCapabilities.MutableDeckState);
19
- const settings = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!.value;
20
18
 
21
19
  const handleDismissToast = useCallback(
22
20
  (id: string) => {
@@ -35,12 +33,6 @@ export default () =>
35
33
  [layout.toasts],
36
34
  );
37
35
 
38
- return (
39
- <DeckLayout
40
- showHints={settings.showHints}
41
- overscroll={settings.overscroll}
42
- onDismissToast={handleDismissToast}
43
- />
44
- );
36
+ return <DeckLayout onDismissToast={handleDismissToast} />;
45
37
  },
46
38
  });
@@ -7,19 +7,18 @@ import React from 'react';
7
7
  import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
8
8
  import { SettingsStore } from '@dxos/local-storage';
9
9
 
10
- import { LayoutSettings } from '../components';
11
- import { Banner } from '../components/DeckLayout/Banner';
10
+ import { DeckSettings, Banner } from '../components';
12
11
  import { DECK_PLUGIN } from '../meta';
13
12
  import { type DeckSettingsProps } from '../types';
14
13
 
15
14
  export default () =>
16
15
  contributes(Capabilities.ReactSurface, [
17
16
  createSurface({
18
- id: `${DECK_PLUGIN}/settings`,
17
+ id: `${DECK_PLUGIN}/plugin-settings`,
19
18
  role: 'article',
20
19
  filter: (data): data is { subject: SettingsStore<DeckSettingsProps> } =>
21
20
  data.subject instanceof SettingsStore && data.subject.prefix === DECK_PLUGIN,
22
- component: ({ data: { subject } }) => <LayoutSettings settings={subject.value} />,
21
+ component: ({ data: { subject } }) => <DeckSettings settings={subject.value} />,
23
22
  }),
24
23
  createSurface({
25
24
  id: `${DECK_PLUGIN}/banner`,
@@ -11,11 +11,16 @@ import { DeckSettingsSchema, type DeckSettingsProps } from '../types';
11
11
  export default () => {
12
12
  const settings = create<DeckSettingsProps>({
13
13
  showHints: false,
14
+ enableDeck: true,
14
15
  enableNativeRedirect: false,
15
- enableStatusbar: true,
16
+ enableStatusbar: false,
16
17
  newPlankPositioning: 'start',
17
18
  overscroll: 'none',
18
19
  });
19
20
 
20
- return contributes(Capabilities.Settings, { schema: DeckSettingsSchema, prefix: DECK_PLUGIN, value: settings });
21
+ return contributes(Capabilities.Settings, {
22
+ schema: DeckSettingsSchema,
23
+ prefix: DECK_PLUGIN,
24
+ value: settings,
25
+ });
21
26
  };
@@ -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 Deck, type DeckState } from '../types';
13
+ import { getMode, type DeckState, type DeckPluginState, defaultDeck } from '../types';
14
14
 
15
15
  const boolean = /true|false/;
16
16
 
@@ -32,7 +32,7 @@ const migrateSidebarState = () => {
32
32
  export default () => {
33
33
  migrateSidebarState();
34
34
 
35
- const state = new LocalStorageStore<DeckState>(DECK_PLUGIN, {
35
+ const state = new LocalStorageStore<DeckPluginState>(DECK_PLUGIN, {
36
36
  sidebarState: 'expanded',
37
37
  complementarySidebarState: 'collapsed',
38
38
  complementarySidebarPanel: undefined,
@@ -48,14 +48,7 @@ export default () => {
48
48
  activeDeck: 'default',
49
49
  previousDeck: 'default',
50
50
  decks: {
51
- default: {
52
- initialized: false,
53
- active: [],
54
- inactive: [],
55
- fullscreen: false,
56
- solo: undefined,
57
- plankSizing: {},
58
- },
51
+ default: { ...defaultDeck },
59
52
  },
60
53
  get deck() {
61
54
  const deck = this.decks[this.activeDeck];
@@ -70,7 +63,7 @@ export default () => {
70
63
  .prop({ key: 'sidebarState', type: LocalStorageStore.enum<SidebarState>() })
71
64
  .prop({ key: 'complementarySidebarState', type: LocalStorageStore.enum<SidebarState>() })
72
65
  .prop({ key: 'complementarySidebarPanel', type: LocalStorageStore.string({ allowUndefined: true }) })
73
- .prop({ key: 'decks', type: LocalStorageStore.json<Record<string, Deck>>() })
66
+ .prop({ key: 'decks', type: LocalStorageStore.json<Record<string, DeckState>>() })
74
67
  .prop({ key: 'activeDeck', type: LocalStorageStore.string() })
75
68
  .prop({ key: 'previousDeck', type: LocalStorageStore.string() });
76
69
 
@@ -14,12 +14,14 @@ import { S } from '@dxos/echo-schema';
14
14
  import { invariant } from '@dxos/invariant';
15
15
 
16
16
  import { meta } from '../meta';
17
+ import { DeckAction } from '../types';
17
18
 
18
19
  // TODO(burdon): Factor out.
19
20
  declare global {
20
21
  interface ToolContextExtensions {
21
22
  dispatch?: PromiseIntentDispatcher;
22
23
  pivotId?: string;
24
+ part?: 'deck' | 'dialog';
23
25
  }
24
26
  }
25
27
 
@@ -27,10 +29,9 @@ export default () =>
27
29
  contributes(Capabilities.Tools, [
28
30
  defineTool(meta.id, {
29
31
  name: 'show',
30
- // TODO(ZaymonFC): We should update the prompt to teach the LLM the difference between object ids and fully qualified ids.
31
32
  description: `
32
- Show an item in the app. Use this tool to open an artifact.
33
- When supplying IDs to show, they must be fully qualified like space:object.
33
+ Show an item as a companion to an existing plank. This will make the item appear alongside the primary content.
34
+ When supplying IDs, they must be fully qualified like space:object.
34
35
  `,
35
36
  caption: 'Showing item...',
36
37
  // TODO(wittjosiah): Refactor Layout/Navigation/Deck actions so that they can be used directly.
@@ -38,29 +39,40 @@ export default () =>
38
39
  id: S.String.annotations({
39
40
  description: 'The ID of the item to show.',
40
41
  }),
41
- pivotId: S.optional(
42
- S.String.annotations({
43
- description: 'The ID of the chat. If provided, the item will be added after the pivot item.',
44
- }),
45
- ),
46
42
  }),
47
43
  execute: async ({ id }, { extensions }) => {
44
+ invariant(extensions?.pivotId, 'No pivot ID');
48
45
  invariant(extensions?.dispatch, 'No intent dispatcher');
49
- const { data, error } = await extensions.dispatch(
50
- createIntent(LayoutAction.Open, {
51
- subject: [id],
52
- part: 'main',
53
- options: {
54
- pivotId: extensions.pivotId,
55
- positioning: 'end',
56
- },
57
- }),
58
- );
59
- if (error) {
60
- return ToolResult.Error(error.message);
61
- }
62
46
 
63
- return ToolResult.Success(data);
47
+ if (extensions.part === 'deck') {
48
+ const { data, error } = await extensions.dispatch(
49
+ createIntent(DeckAction.ChangeCompanion, {
50
+ primary: extensions.pivotId,
51
+ companion: id,
52
+ }),
53
+ );
54
+ if (error) {
55
+ return ToolResult.Error(error.message);
56
+ }
57
+
58
+ return ToolResult.Success(data);
59
+ } else {
60
+ const { data, error } = await extensions.dispatch(
61
+ createIntent(LayoutAction.Open, {
62
+ subject: [id],
63
+ part: 'main',
64
+ options: {
65
+ pivotId: extensions.pivotId,
66
+ positioning: 'end',
67
+ },
68
+ }),
69
+ );
70
+ if (error) {
71
+ return ToolResult.Error(error.message);
72
+ }
73
+
74
+ return ToolResult.Success(data);
75
+ }
64
76
  },
65
77
  }),
66
78
  ]);
@@ -6,6 +6,7 @@ import { Capabilities, contributes, createIntent, LayoutAction, type PluginsCont
6
6
  import { scheduledEffect } from '@dxos/echo-signals/core';
7
7
 
8
8
  import { DeckCapabilities } from './capabilities';
9
+ import { defaultDeck } from '../types';
9
10
 
10
11
  // TODO(wittjosiah): Cleanup the url handling. May justify introducing routing capabilities.
11
12
  export default async (context: PluginsContext) => {
@@ -17,14 +18,7 @@ export default async (context: PluginsContext) => {
17
18
  if (pathname === '/reset') {
18
19
  state.activeDeck = 'default';
19
20
  state.decks = {
20
- default: {
21
- initialized: false,
22
- active: [],
23
- inactive: [],
24
- fullscreen: false,
25
- solo: undefined,
26
- plankSizing: {},
27
- },
21
+ default: { ...defaultDeck },
28
22
  };
29
23
  window.location.pathname = '/';
30
24
  return;
@@ -5,9 +5,10 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Surface, useAppGraph } from '@dxos/app-framework';
8
+ import { useNode } from '@dxos/plugin-graph';
8
9
  import { useAttended } from '@dxos/react-ui-attention';
9
10
 
10
- import { useNode, useNodeActionExpander } from '../../hooks';
11
+ import { useNodeActionExpander } from '../../hooks';
11
12
 
12
13
  // TODO(burdon): Factor out to effect in plugin set document title.
13
14
  export const ActiveNode = () => {
@@ -5,12 +5,14 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Surface } from '@dxos/app-framework';
8
- import { type ThemedClassName } from '@dxos/react-ui';
8
+ import { useTranslation, type ThemedClassName } from '@dxos/react-ui';
9
9
  import { mx } from '@dxos/react-ui-theme';
10
10
 
11
- import { CloseSidebarButton, ToggleSidebarButton } from './SidebarButton';
11
+ import { DECK_PLUGIN } from '../../meta';
12
+ import { CloseSidebarButton, ToggleSidebarButton } from '../Sidebar';
12
13
 
13
14
  export const Banner = ({ variant, classNames }: ThemedClassName<{ variant?: 'topbar' | 'sidebar' }>) => {
15
+ const { t } = useTranslation(DECK_PLUGIN);
14
16
  return (
15
17
  <header
16
18
  className={mx(
@@ -21,7 +23,7 @@ export const Banner = ({ variant, classNames }: ThemedClassName<{ variant?: 'top
21
23
  )}
22
24
  >
23
25
  {variant === 'sidebar' ? <CloseSidebarButton /> : <ToggleSidebarButton />}
24
- <span className='self-center grow mis-1'>Composer</span>
26
+ <span className='self-center grow mis-1'>{t('current app name', { ns: 'appkit' })}</span>
25
27
  {variant === 'topbar' && (
26
28
  <div role='none' className='absolute inset-0 pointer-events-none'>
27
29
  <div role='none' className='grid bs-full pointer-fine:p-1 max-is-md mli-auto pointer-events-auto'>
@@ -6,8 +6,8 @@ import React from 'react';
6
6
 
7
7
  import { Surface } from '@dxos/app-framework';
8
8
 
9
- import { ToggleSidebarButton } from './SidebarButton';
10
9
  import { layoutAppliesTopbar, useBreakpoints } from '../../util';
10
+ import { ToggleSidebarButton } from '../Sidebar';
11
11
  import { fixedSidebarToggleStyles } from '../fragments';
12
12
 
13
13
  export const ContentEmpty = () => {
@@ -6,9 +6,10 @@ import { untracked } from '@preact/signals-core';
6
6
  import React, { useCallback, useEffect, useMemo, useRef, type UIEvent, Fragment, useState } from 'react';
7
7
 
8
8
  import {
9
+ Capabilities,
9
10
  LayoutAction,
10
- createIntent,
11
11
  Surface,
12
+ createIntent,
12
13
  useCapability,
13
14
  useIntentDispatcher,
14
15
  usePluginManager,
@@ -27,32 +28,29 @@ import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-sta
27
28
  import { mainPaddingTransitions } from '@dxos/react-ui-theme';
28
29
 
29
30
  import { ActiveNode } from './ActiveNode';
30
- import { ComplementarySidebar } from './ComplementarySidebar';
31
31
  import { ContentEmpty } from './ContentEmpty';
32
32
  import { Fullscreen } from './Fullscreen';
33
- import { Plank } from './Plank';
34
- import { PlankContentError } from './PlankError';
35
- import { Sidebar } from './Sidebar';
36
- import { ToggleComplementarySidebarButton, ToggleSidebarButton } from './SidebarButton';
37
33
  import { StatusBar } from './StatusBar';
38
34
  import { Toast } from './Toast';
39
35
  import { Topbar } from './Topbar';
40
36
  import { DeckCapabilities } from '../../capabilities';
41
- import { getMode, type Overscroll } from '../../types';
37
+ import { DECK_PLUGIN } from '../../meta';
38
+ import { type DeckSettingsProps, getMode } from '../../types';
42
39
  import { calculateOverscroll, layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
40
+ import { Plank, PlankContentError } from '../Plank';
41
+ import { ComplementarySidebar, Sidebar, ToggleComplementarySidebarButton, ToggleSidebarButton } from '../Sidebar';
43
42
  import { fixedComplementarySidebarToggleStyles, fixedSidebarToggleStyles } from '../fragments';
44
43
 
45
44
  export type DeckLayoutProps = {
46
- overscroll: Overscroll;
47
- showHints: boolean;
48
45
  onDismissToast: (id: string) => void;
49
46
  };
50
47
 
51
- const PlankSeparator = ({ index }: { index: number }) =>
52
- index > 0 ? <span role='separator' className='row-span-2 bg-deck is-4' style={{ gridColumn: index * 2 }} /> : null;
48
+ const PlankSeparator = ({ order }: { order: number }) =>
49
+ order > 0 ? <span role='separator' className='row-span-2 bg-deck is-4' style={{ gridColumn: order }} /> : null;
53
50
 
54
- export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayoutProps) => {
51
+ export const DeckLayout = ({ onDismissToast }: DeckLayoutProps) => {
55
52
  const { dispatchPromise: dispatch } = useIntentDispatcher();
53
+ const settings = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!.value;
56
54
  const context = useCapability(DeckCapabilities.MutableDeckState);
57
55
  const {
58
56
  sidebarState,
@@ -68,7 +66,7 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
68
66
  deck,
69
67
  toasts,
70
68
  } = context;
71
- const { active, fullscreen, solo, plankSizing } = deck;
69
+ const { active, activeCompanions, fullscreen, solo, plankSizing } = deck;
72
70
  const breakpoint = useBreakpoints();
73
71
  const topbar = layoutAppliesTopbar(breakpoint);
74
72
  const hoistStatusbar = useHoistStatusbar(breakpoint);
@@ -120,6 +118,15 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
120
118
  }
121
119
  }, [isNotMobile, deck, dispatch]);
122
120
 
121
+ // If deck is disabled in settings, ensure that the layout is in solo mode.
122
+ useEffect(() => {
123
+ if (!settings.enableDeck) {
124
+ void dispatch(
125
+ createIntent(LayoutAction.SetLayoutMode, { part: 'mode', subject: active[0], options: { mode: 'solo' } }),
126
+ );
127
+ }
128
+ }, [settings.enableDeck, dispatch, active]);
129
+
123
130
  /**
124
131
  * Clear scroll restoration state if the window is resized
125
132
  */
@@ -156,11 +163,11 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
156
163
  const isEmpty = !solo && active.length === 0;
157
164
 
158
165
  const padding = useMemo(() => {
159
- if (!solo && overscroll === 'centering') {
166
+ if (!solo && settings.overscroll === 'centering') {
160
167
  return calculateOverscroll(active.length);
161
168
  }
162
169
  return {};
163
- }, [solo, overscroll, deck]);
170
+ }, [solo, settings.overscroll, deck]);
164
171
 
165
172
  const mainPosition = useMemo(
166
173
  () => [
@@ -187,6 +194,17 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
187
194
  );
188
195
  const handlePopoverClose = useCallback(() => handlePopoverOpenChange(false), [handlePopoverOpenChange]);
189
196
 
197
+ const { order, itemsCount }: { order: Record<string, number>; itemsCount: number } = useMemo(() => {
198
+ return active.reduce(
199
+ (acc: { order: Record<string, number>; itemsCount: number }, entryId) => {
200
+ acc.order[entryId] = acc.itemsCount + 1;
201
+ acc.itemsCount += activeCompanions?.[entryId] ? 3 : 2;
202
+ return acc;
203
+ },
204
+ { order: {}, itemsCount: 0 },
205
+ );
206
+ }, [active, activeCompanions]);
207
+
190
208
  return (
191
209
  <Popover.Root modal open={!!(popoverAnchorId && delayedPopoverVisibility)} onOpenChange={handlePopoverOpenChange}>
192
210
  <ActiveNode />
@@ -249,18 +267,26 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
249
267
  {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
250
268
  {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
251
269
  <Stack
270
+ ref={deckRef}
252
271
  orientation='horizontal'
253
272
  size='contain'
254
273
  classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
255
- onScroll={handleScroll}
256
- itemsCount={2 * (active.length ?? 0) - 1}
274
+ itemsCount={itemsCount - 1}
257
275
  style={padding}
258
- ref={deckRef}
276
+ onScroll={handleScroll}
259
277
  >
260
- {active.map((entryId, index) => (
278
+ {active.map((entryId) => (
261
279
  <Fragment key={entryId}>
262
- <PlankSeparator index={index} />
263
- <Plank id={entryId} part='deck' order={index * 2 + 1} active={active} layoutMode={layoutMode} />
280
+ <PlankSeparator order={order[entryId] - 1} />
281
+ <Plank
282
+ id={entryId}
283
+ companionId={activeCompanions?.[entryId]}
284
+ part='deck'
285
+ order={order[entryId]}
286
+ active={active}
287
+ layoutMode={layoutMode}
288
+ settings={settings}
289
+ />
264
290
  </Fragment>
265
291
  ))}
266
292
  </Stack>
@@ -273,15 +299,23 @@ export const DeckLayout = ({ overscroll, showHints, onDismissToast }: DeckLayout
273
299
  {!topbar && <ToggleSidebarButton classNames={fixedSidebarToggleStyles} />}
274
300
  {!topbar && <ToggleComplementarySidebarButton classNames={fixedComplementarySidebarToggleStyles} />}
275
301
  <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
276
- <Plank id={solo} part='solo' layoutMode={layoutMode} />
302
+ <Plank
303
+ id={solo}
304
+ companionId={solo ? activeCompanions?.[solo] : undefined}
305
+ part='solo'
306
+ layoutMode={layoutMode}
307
+ settings={settings}
308
+ />
277
309
  </StackContext.Provider>
278
310
  </div>
279
311
  </Main.Content>
280
312
  )}
281
313
 
282
- {/* Status bar. */}
314
+ {/* Topbar. */}
283
315
  {topbar && <Topbar />}
284
- {hoistStatusbar && <StatusBar showHints={showHints} />}
316
+
317
+ {/* Status bar. */}
318
+ {hoistStatusbar && <StatusBar showHints={settings.showHints} />}
285
319
  </Main.Root>
286
320
  )}
287
321
 
@@ -5,11 +5,11 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Surface, useAppGraph } from '@dxos/app-framework';
8
+ import { useNode } from '@dxos/plugin-graph';
8
9
  import { fixedInsetFlexLayout } from '@dxos/react-ui-theme';
9
10
 
10
11
  import { Fallback } from './Fallback';
11
12
  import { SURFACE_PREFIX } from './constants';
12
- import { useNode } from '../../hooks';
13
13
 
14
14
  export const Fullscreen = ({ id }: { id?: string }) => {
15
15
  const { graph } = useAppGraph();
@@ -9,9 +9,9 @@ import {
9
9
  Button,
10
10
  Icon,
11
11
  Toast as NaturalToast,
12
+ type ToastRootProps,
12
13
  toLocalizedString,
13
14
  useTranslation,
14
- type ToastRootProps,
15
15
  } from '@dxos/react-ui';
16
16
 
17
17
  import { DECK_PLUGIN } from '../../meta';
@@ -3,4 +3,6 @@
3
3
  //
4
4
 
5
5
  export { NAV_ID } from './constants';
6
+
7
+ export * from './Banner';
6
8
  export * from './DeckLayout';