@dxos/plugin-deck 0.7.5-labs.a279d8c → 0.7.5-labs.d453967

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 (214) hide show
  1. package/dist/lib/browser/{app-graph-builder-67VRUD5K.mjs → app-graph-builder-CI6ZFMNL.mjs} +57 -31
  2. package/dist/lib/browser/app-graph-builder-CI6ZFMNL.mjs.map +7 -0
  3. package/dist/lib/browser/{check-app-scheme-GEX6W2R5.mjs → check-app-scheme-S3EYUPMF.mjs} +3 -3
  4. package/dist/lib/browser/{check-app-scheme-GEX6W2R5.mjs.map → check-app-scheme-S3EYUPMF.mjs.map} +2 -2
  5. package/dist/lib/browser/chunk-23KS5L3I.mjs +1124 -0
  6. package/dist/lib/browser/chunk-23KS5L3I.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-4URQJVGI.mjs +24 -0
  8. package/dist/lib/browser/chunk-4URQJVGI.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-JQJ5UWVB.mjs → chunk-N7TEPFVR.mjs} +3 -2
  10. package/dist/lib/browser/{chunk-JQJ5UWVB.mjs.map → chunk-N7TEPFVR.mjs.map} +3 -3
  11. package/dist/lib/browser/chunk-PTLNGUND.mjs +126 -0
  12. package/dist/lib/browser/chunk-PTLNGUND.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-5VFDMW5M.mjs → chunk-YQ2GWTDU.mjs} +2 -2
  14. package/dist/lib/browser/chunk-YQ2GWTDU.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +36 -82
  16. package/dist/lib/browser/index.mjs.map +4 -4
  17. package/dist/lib/browser/intent-resolver-WAYXVAFN.mjs +497 -0
  18. package/dist/lib/browser/intent-resolver-WAYXVAFN.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/{react-root-AWYSGU4Q.mjs → react-root-YATKEIAZ.mjs} +10 -14
  21. package/dist/lib/browser/react-root-YATKEIAZ.mjs.map +7 -0
  22. package/dist/lib/browser/react-surface-SS2BX6FS.mjs +38 -0
  23. package/dist/lib/browser/react-surface-SS2BX6FS.mjs.map +7 -0
  24. package/dist/lib/browser/{settings-FNWW6WIJ.mjs → settings-CRQTVMN3.mjs} +6 -7
  25. package/dist/lib/browser/settings-CRQTVMN3.mjs.map +7 -0
  26. package/dist/lib/browser/state-YEQA3IIB.mjs +117 -0
  27. package/dist/lib/browser/state-YEQA3IIB.mjs.map +7 -0
  28. package/dist/lib/browser/{tools-4XY7KFQF.mjs → tools-5LDJNU56.mjs} +14 -9
  29. package/dist/lib/browser/tools-5LDJNU56.mjs.map +7 -0
  30. package/dist/lib/browser/types.mjs +16 -4
  31. package/dist/lib/browser/url-handler-HLF42IHP.mjs +70 -0
  32. package/dist/lib/browser/url-handler-HLF42IHP.mjs.map +7 -0
  33. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  34. package/dist/types/src/capabilities/{layout/app-graph-builder.d.ts → app-graph-builder.d.ts} +22 -22
  35. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/capabilities.d.ts +132 -3
  37. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -0
  39. package/dist/types/src/capabilities/index.d.ts +188 -3
  40. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
  43. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  44. package/dist/types/src/capabilities/set-active.d.ts +9 -0
  45. package/dist/types/src/capabilities/set-active.d.ts.map +1 -0
  46. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/state.d.ts +76 -0
  48. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  49. package/dist/types/src/capabilities/{navigation/tools.d.ts → tools.d.ts} +1 -0
  50. package/dist/types/src/capabilities/tools.d.ts.map +1 -0
  51. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -0
  52. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  53. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  54. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  55. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +1 -4
  56. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  57. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
  58. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +3 -3
  59. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  60. package/dist/types/src/components/DeckLayout/Plank.d.ts +8 -6
  61. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  62. package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
  63. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
  64. package/dist/types/src/components/DeckLayout/PlankError.d.ts +4 -3
  65. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  66. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  67. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +5 -3
  68. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -1
  69. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  70. package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -2
  71. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  72. package/dist/types/src/components/fragments.d.ts +2 -0
  73. package/dist/types/src/components/fragments.d.ts.map +1 -1
  74. package/dist/types/src/components/index.d.ts +0 -2
  75. package/dist/types/src/components/index.d.ts.map +1 -1
  76. package/dist/types/src/hooks/useMainSize.d.ts +2 -2
  77. package/dist/types/src/hooks/useNode.d.ts.map +1 -1
  78. package/dist/types/src/layout.d.ts +5 -19
  79. package/dist/types/src/layout.d.ts.map +1 -1
  80. package/dist/types/src/meta.d.ts +1 -0
  81. package/dist/types/src/meta.d.ts.map +1 -1
  82. package/dist/types/src/translations.d.ts +3 -3
  83. package/dist/types/src/types.d.ts +107 -2
  84. package/dist/types/src/types.d.ts.map +1 -1
  85. package/dist/types/src/util/index.d.ts +2 -1
  86. package/dist/types/src/util/index.d.ts.map +1 -1
  87. package/dist/types/src/util/layoutAppliesTopbar.d.ts +2 -0
  88. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -0
  89. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -1
  90. package/dist/types/tsconfig.tsbuildinfo +1 -1
  91. package/package.json +30 -31
  92. package/src/DeckPlugin.ts +17 -63
  93. package/src/capabilities/{layout/app-graph-builder.ts → app-graph-builder.ts} +36 -28
  94. package/src/capabilities/capabilities.ts +4 -3
  95. package/src/capabilities/{navigation/check-app-scheme.ts → check-app-scheme.ts} +2 -2
  96. package/src/capabilities/index.ts +12 -3
  97. package/src/capabilities/intent-resolver.ts +350 -0
  98. package/src/capabilities/{layout/react-root.tsx → react-root.tsx} +7 -11
  99. package/src/capabilities/react-surface.tsx +31 -0
  100. package/src/capabilities/set-active.ts +47 -0
  101. package/src/capabilities/{settings/settings.ts → settings.ts} +4 -5
  102. package/src/capabilities/state.ts +102 -0
  103. package/src/capabilities/{navigation/tools.ts → tools.ts} +10 -6
  104. package/src/capabilities/url-handler.ts +63 -0
  105. package/src/components/DeckLayout/ActiveNode.tsx +2 -3
  106. package/src/components/DeckLayout/ComplementarySidebar.tsx +120 -69
  107. package/src/components/DeckLayout/ContentEmpty.tsx +7 -10
  108. package/src/components/DeckLayout/DeckLayout.tsx +116 -64
  109. package/src/components/DeckLayout/Fullscreen.tsx +2 -3
  110. package/src/components/DeckLayout/NodePlankHeading.tsx +57 -65
  111. package/src/components/DeckLayout/Plank.tsx +32 -41
  112. package/src/components/DeckLayout/PlankControls.tsx +11 -10
  113. package/src/components/DeckLayout/PlankError.tsx +6 -5
  114. package/src/components/DeckLayout/Sidebar.tsx +17 -20
  115. package/src/components/DeckLayout/SidebarButton.tsx +25 -31
  116. package/src/components/DeckLayout/StatusBar.tsx +5 -11
  117. package/src/components/DeckLayout/Toast.tsx +2 -2
  118. package/src/components/LayoutSettings.tsx +8 -8
  119. package/src/components/fragments.ts +8 -0
  120. package/src/components/index.ts +0 -2
  121. package/src/hooks/useMainSize.ts +3 -3
  122. package/src/hooks/useNode.ts +3 -1
  123. package/src/layout.ts +43 -212
  124. package/src/meta.ts +1 -0
  125. package/src/translations.ts +8 -8
  126. package/src/types.ts +88 -2
  127. package/src/util/index.ts +2 -1
  128. package/src/util/layoutAppliesTopbar.ts +7 -0
  129. package/src/util/useHoistStatusbar.ts +17 -8
  130. package/dist/lib/browser/app-graph-builder-67VRUD5K.mjs.map +0 -7
  131. package/dist/lib/browser/chunk-2PJNBVCY.mjs +0 -39
  132. package/dist/lib/browser/chunk-2PJNBVCY.mjs.map +0 -7
  133. package/dist/lib/browser/chunk-4C2AFTET.mjs +0 -186
  134. package/dist/lib/browser/chunk-4C2AFTET.mjs.map +0 -7
  135. package/dist/lib/browser/chunk-5VFDMW5M.mjs.map +0 -7
  136. package/dist/lib/browser/chunk-KY5WXIXY.mjs +0 -44
  137. package/dist/lib/browser/chunk-KY5WXIXY.mjs.map +0 -7
  138. package/dist/lib/browser/chunk-WUMAJGVA.mjs +0 -1052
  139. package/dist/lib/browser/chunk-WUMAJGVA.mjs.map +0 -7
  140. package/dist/lib/browser/deck-PLCSKPGL.mjs +0 -26
  141. package/dist/lib/browser/deck-PLCSKPGL.mjs.map +0 -7
  142. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs +0 -152
  143. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs.map +0 -7
  144. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs +0 -249
  145. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs.map +0 -7
  146. package/dist/lib/browser/location-AIO6V3MK.mjs +0 -35
  147. package/dist/lib/browser/location-AIO6V3MK.mjs.map +0 -7
  148. package/dist/lib/browser/react-context-G6PDXUI5.mjs +0 -32
  149. package/dist/lib/browser/react-context-G6PDXUI5.mjs.map +0 -7
  150. package/dist/lib/browser/react-root-AWYSGU4Q.mjs.map +0 -7
  151. package/dist/lib/browser/react-surface-CLUABFNX.mjs +0 -28
  152. package/dist/lib/browser/react-surface-CLUABFNX.mjs.map +0 -7
  153. package/dist/lib/browser/settings-FNWW6WIJ.mjs.map +0 -7
  154. package/dist/lib/browser/state-7I5BD7SE.mjs +0 -34
  155. package/dist/lib/browser/state-7I5BD7SE.mjs.map +0 -7
  156. package/dist/lib/browser/tools-4XY7KFQF.mjs.map +0 -7
  157. package/dist/lib/browser/url-handler-JRAQRY73.mjs +0 -76
  158. package/dist/lib/browser/url-handler-JRAQRY73.mjs.map +0 -7
  159. package/dist/types/src/capabilities/layout/app-graph-builder.d.ts.map +0 -1
  160. package/dist/types/src/capabilities/layout/deck.d.ts +0 -4
  161. package/dist/types/src/capabilities/layout/deck.d.ts.map +0 -1
  162. package/dist/types/src/capabilities/layout/index.d.ts +0 -229
  163. package/dist/types/src/capabilities/layout/index.d.ts.map +0 -1
  164. package/dist/types/src/capabilities/layout/intent-resolver.d.ts.map +0 -1
  165. package/dist/types/src/capabilities/layout/react-context.d.ts +0 -8
  166. package/dist/types/src/capabilities/layout/react-context.d.ts.map +0 -1
  167. package/dist/types/src/capabilities/layout/react-root.d.ts.map +0 -1
  168. package/dist/types/src/capabilities/layout/state.d.ts +0 -42
  169. package/dist/types/src/capabilities/layout/state.d.ts.map +0 -1
  170. package/dist/types/src/capabilities/navigation/check-app-scheme.d.ts.map +0 -1
  171. package/dist/types/src/capabilities/navigation/index.d.ts +0 -6
  172. package/dist/types/src/capabilities/navigation/index.d.ts.map +0 -1
  173. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts +0 -4
  174. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts.map +0 -1
  175. package/dist/types/src/capabilities/navigation/location.d.ts +0 -4
  176. package/dist/types/src/capabilities/navigation/location.d.ts.map +0 -1
  177. package/dist/types/src/capabilities/navigation/set-location.d.ts +0 -10
  178. package/dist/types/src/capabilities/navigation/set-location.d.ts.map +0 -1
  179. package/dist/types/src/capabilities/navigation/tools.d.ts.map +0 -1
  180. package/dist/types/src/capabilities/navigation/url-handler.d.ts.map +0 -1
  181. package/dist/types/src/capabilities/settings/index.d.ts +0 -3
  182. package/dist/types/src/capabilities/settings/index.d.ts.map +0 -1
  183. package/dist/types/src/capabilities/settings/react-surface.d.ts.map +0 -1
  184. package/dist/types/src/capabilities/settings/settings.d.ts.map +0 -1
  185. package/dist/types/src/components/DeckContext.d.ts +0 -11
  186. package/dist/types/src/components/DeckContext.d.ts.map +0 -1
  187. package/dist/types/src/components/LayoutContext.d.ts +0 -5
  188. package/dist/types/src/components/LayoutContext.d.ts.map +0 -1
  189. package/dist/types/src/layout.test.d.ts +0 -2
  190. package/dist/types/src/layout.test.d.ts.map +0 -1
  191. package/dist/types/src/util/layout-parts.d.ts +0 -7
  192. package/dist/types/src/util/layout-parts.d.ts.map +0 -1
  193. package/src/capabilities/layout/deck.ts +0 -25
  194. package/src/capabilities/layout/index.ts +0 -12
  195. package/src/capabilities/layout/intent-resolver.ts +0 -128
  196. package/src/capabilities/layout/react-context.tsx +0 -26
  197. package/src/capabilities/layout/state.ts +0 -32
  198. package/src/capabilities/navigation/index.ts +0 -11
  199. package/src/capabilities/navigation/intent-resolver.ts +0 -216
  200. package/src/capabilities/navigation/location.ts +0 -28
  201. package/src/capabilities/navigation/set-location.ts +0 -38
  202. package/src/capabilities/navigation/url-handler.ts +0 -67
  203. package/src/capabilities/settings/index.ts +0 -8
  204. package/src/capabilities/settings/react-surface.tsx +0 -23
  205. package/src/components/DeckContext.ts +0 -19
  206. package/src/components/LayoutContext.ts +0 -12
  207. package/src/layout.test.ts +0 -380
  208. package/src/util/layout-parts.ts +0 -12
  209. /package/dist/types/src/capabilities/{navigation/check-app-scheme.d.ts → check-app-scheme.d.ts} +0 -0
  210. /package/dist/types/src/capabilities/{layout/intent-resolver.d.ts → intent-resolver.d.ts} +0 -0
  211. /package/dist/types/src/capabilities/{layout/react-root.d.ts → react-root.d.ts} +0 -0
  212. /package/dist/types/src/capabilities/{settings/react-surface.d.ts → react-surface.d.ts} +0 -0
  213. /package/dist/types/src/capabilities/{settings/settings.d.ts → settings.d.ts} +0 -0
  214. /package/dist/types/src/capabilities/{navigation/url-handler.d.ts → url-handler.d.ts} +0 -0
@@ -0,0 +1,102 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes } from '@dxos/app-framework';
6
+ import { invariant } from '@dxos/invariant';
7
+ import { create } from '@dxos/live-object';
8
+ import { LocalStorageStore } from '@dxos/local-storage';
9
+ import { type SidebarState } from '@dxos/react-ui';
10
+
11
+ import { DeckCapabilities } from './capabilities';
12
+ import { DECK_PLUGIN } from '../meta';
13
+ import { getMode, type Deck, type DeckState } from '../types';
14
+
15
+ const boolean = /true|false/;
16
+
17
+ // TODO(thure, 18 Feb 2025): Remove after the next release.
18
+
19
+ const migrateSidebarStateDefaults = {
20
+ [`${DECK_PLUGIN}/complementary-sidebar-state`]: 'expanded',
21
+ [`${DECK_PLUGIN}/sidebar-state`]: 'collapsed',
22
+ };
23
+
24
+ const migrateSidebarState = () => {
25
+ Object.entries(migrateSidebarStateDefaults).forEach(([key, defaultValue]) => {
26
+ if (boolean.test(localStorage.getItem(key) ?? 'never')) {
27
+ localStorage.setItem(key, defaultValue);
28
+ }
29
+ });
30
+ };
31
+
32
+ export default () => {
33
+ migrateSidebarState();
34
+
35
+ const state = new LocalStorageStore<DeckState>(DECK_PLUGIN, {
36
+ sidebarState: 'expanded',
37
+ complementarySidebarState: 'collapsed',
38
+ complementarySidebarPanel: undefined,
39
+ dialogContent: null,
40
+ dialogOpen: false,
41
+ dialogBlockAlign: undefined,
42
+ dialogType: undefined,
43
+ popoverContent: null,
44
+ popoverAnchorId: undefined,
45
+ popoverOpen: false,
46
+ toasts: [],
47
+ currentUndoId: undefined,
48
+ activeDeck: 'default',
49
+ decks: {
50
+ default: {
51
+ initialized: false,
52
+ active: [],
53
+ inactive: [],
54
+ fullscreen: false,
55
+ solo: undefined,
56
+ plankSizing: {},
57
+ },
58
+ },
59
+ get deck() {
60
+ const deck = this.decks[this.activeDeck];
61
+ invariant(deck, `Deck not found: ${this.activeDeck}`);
62
+ return deck;
63
+ },
64
+ previousMode: {},
65
+ scrollIntoView: undefined,
66
+ });
67
+
68
+ state
69
+ .prop({ key: 'sidebarState', type: LocalStorageStore.enum<SidebarState>() })
70
+ .prop({ key: 'complementarySidebarState', type: LocalStorageStore.enum<SidebarState>() })
71
+ .prop({ key: 'decks', type: LocalStorageStore.json<Record<string, Deck>>() })
72
+ .prop({ key: 'activeDeck', type: LocalStorageStore.string() });
73
+
74
+ const layout = create<Capabilities.Layout>({
75
+ get mode() {
76
+ return getMode(state.values.deck);
77
+ },
78
+ get dialogOpen() {
79
+ return state.values.dialogOpen;
80
+ },
81
+ get sidebarOpen() {
82
+ return state.values.sidebarState === 'expanded';
83
+ },
84
+ get complementarySidebarOpen() {
85
+ return state.values.complementarySidebarState === 'expanded';
86
+ },
87
+ get active() {
88
+ return state.values.deck.solo ? [state.values.deck.solo] : state.values.deck.active;
89
+ },
90
+ get inactive() {
91
+ return state.values.deck.inactive;
92
+ },
93
+ get scrollIntoView() {
94
+ return state.values.scrollIntoView;
95
+ },
96
+ });
97
+
98
+ return [
99
+ contributes(DeckCapabilities.DeckState, state.values, () => state.close()),
100
+ contributes(Capabilities.Layout, layout),
101
+ ];
102
+ };
@@ -3,10 +3,10 @@
3
3
  //
4
4
 
5
5
  import {
6
- Capabilities,
7
6
  contributes,
8
7
  createIntent,
9
- NavigationAction,
8
+ Capabilities,
9
+ LayoutAction,
10
10
  type PromiseIntentDispatcher,
11
11
  } from '@dxos/app-framework';
12
12
  import { defineTool, ToolResult } from '@dxos/artifact';
@@ -17,6 +17,7 @@ import { invariant } from '@dxos/invariant';
17
17
  declare global {
18
18
  interface ToolContextExtensions {
19
19
  dispatch?: PromiseIntentDispatcher;
20
+ pivotId?: string;
20
21
  }
21
22
  }
22
23
 
@@ -38,13 +39,16 @@ export default () =>
38
39
  }),
39
40
  ),
40
41
  }),
41
- execute: async ({ id, pivotId }, { extensions }) => {
42
+ execute: async ({ id }, { extensions }) => {
42
43
  invariant(extensions?.dispatch, 'No intent dispatcher');
43
44
  const { data, error } = await extensions.dispatch(
44
- createIntent(NavigationAction.AddToActive, {
45
- id,
45
+ createIntent(LayoutAction.Open, {
46
+ subject: [id],
46
47
  part: 'main',
47
- pivotId,
48
+ options: {
49
+ pivotId: extensions.pivotId,
50
+ positioning: 'end',
51
+ },
48
52
  }),
49
53
  );
50
54
  if (error) {
@@ -0,0 +1,63 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes, createIntent, LayoutAction, type PluginsContext } from '@dxos/app-framework';
6
+ import { scheduledEffect } from '@dxos/echo-signals/core';
7
+
8
+ import { DeckCapabilities } from './capabilities';
9
+
10
+ // TODO(wittjosiah): Cleanup the url handling. May justify introducing routing capabilities.
11
+ export default async (context: PluginsContext) => {
12
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher) ?? {};
13
+ const state = context.requestCapability(DeckCapabilities.MutableDeckState);
14
+
15
+ const handleNavigation = async () => {
16
+ const pathname = window.location.pathname;
17
+ if (pathname === '/reset') {
18
+ state.activeDeck = 'default';
19
+ state.decks = {
20
+ default: {
21
+ initialized: false,
22
+ active: [],
23
+ inactive: [],
24
+ fullscreen: false,
25
+ solo: undefined,
26
+ plankSizing: {},
27
+ },
28
+ };
29
+ window.location.pathname = '/';
30
+ return;
31
+ }
32
+
33
+ const [_, nextDeck, nextSolo] = pathname.split('/');
34
+ if (nextDeck) {
35
+ await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: nextDeck }));
36
+ }
37
+
38
+ if (nextSolo) {
39
+ await dispatch(
40
+ createIntent(LayoutAction.SetLayoutMode, { part: 'mode', subject: nextSolo, options: { mode: 'solo' } }),
41
+ );
42
+ } else {
43
+ await dispatch(createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { mode: 'deck' } }));
44
+ }
45
+ };
46
+
47
+ await handleNavigation();
48
+ window.addEventListener('popstate', handleNavigation);
49
+
50
+ const unsubscribe = scheduledEffect(
51
+ () => ({ solo: state.deck.solo, activeDeck: state.activeDeck }),
52
+ ({ solo, activeDeck }) => {
53
+ const path = solo ? `/${activeDeck}/${solo}` : `/${activeDeck}`;
54
+ // TODO(thure): In some browsers, this only preserves the most recent state change, even though this is not `history.replace`…
55
+ history.pushState(null, '', `${path}${window.location.search}`);
56
+ },
57
+ );
58
+
59
+ return contributes(Capabilities.Null, null, () => {
60
+ window.removeEventListener('popstate', handleNavigation);
61
+ unsubscribe();
62
+ });
63
+ };
@@ -4,8 +4,7 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { Surface } from '@dxos/app-framework';
8
- import { useGraph } from '@dxos/plugin-graph';
7
+ import { Surface, useAppGraph } from '@dxos/app-framework';
9
8
  import { useAttended } from '@dxos/react-ui-attention';
10
9
 
11
10
  import { useNode, useNodeActionExpander } from '../../hooks';
@@ -13,7 +12,7 @@ import { useNode, useNodeActionExpander } from '../../hooks';
13
12
  // TODO(burdon): Factor out to effect in plugin set document title.
14
13
  export const ActiveNode = () => {
15
14
  const [id] = useAttended();
16
- const { graph } = useGraph();
15
+ const { graph } = useAppGraph();
17
16
  const activeNode = useNode(graph, id);
18
17
  useNodeActionExpander(activeNode);
19
18
 
@@ -2,23 +2,28 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
5
+ import React, { useCallback, useEffect, useMemo, useState, type MouseEvent } from 'react';
6
6
 
7
- import { createIntent, NavigationAction, SLUG_PATH_SEPARATOR, Surface, useIntentDispatcher } from '@dxos/app-framework';
8
- import { useGraph } from '@dxos/plugin-graph';
9
- import { Main, ScrollArea, useTranslation, toLocalizedString } from '@dxos/react-ui';
7
+ import {
8
+ createIntent,
9
+ LayoutAction,
10
+ Surface,
11
+ useAppGraph,
12
+ useCapability,
13
+ useIntentDispatcher,
14
+ } from '@dxos/app-framework';
15
+ import { Main, useTranslation, toLocalizedString, IconButton, ScrollArea } from '@dxos/react-ui';
10
16
  import { useAttended } from '@dxos/react-ui-attention';
11
- import { railGridHorizontal, StackContext, StackItem } from '@dxos/react-ui-stack';
12
17
  import { Tabs } from '@dxos/react-ui-tabs';
13
- import { mx } from '@dxos/react-ui-theme';
14
18
 
15
19
  import { PlankContentError } from './PlankError';
16
20
  import { PlankLoading } from './PlankLoading';
17
- import { CloseComplementarySidebarButton } from './SidebarButton';
21
+ import { ToggleComplementarySidebarButton } from './SidebarButton';
22
+ import { DeckCapabilities } from '../../capabilities';
18
23
  import { useNode, useNodeActionExpander } from '../../hooks';
19
24
  import { DECK_PLUGIN } from '../../meta';
20
- import { type Panel } from '../../types';
21
- import { useLayout } from '../LayoutContext';
25
+ import { SLUG_PATH_SEPARATOR, type Panel } from '../../types';
26
+ import { layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
22
27
 
23
28
  export type ComplementarySidebarProps = {
24
29
  panels: Panel[];
@@ -26,16 +31,19 @@ export type ComplementarySidebarProps = {
26
31
  };
27
32
 
28
33
  export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarProps) => {
29
- const { popoverAnchorId } = useLayout();
34
+ const layout = useCapability(DeckCapabilities.MutableDeckState);
30
35
  const attended = useAttended();
31
- const panelIds = useMemo(() => panels.map((p) => p.id), [panels]);
32
- const activePanelId = panelIds.find((p) => p === current) ?? panels[0].id;
36
+ const panelIds = useMemo(() => panels.map((panel) => panel.id), [panels]);
37
+ const activePanelId = panelIds.find((panelId) => panelId === current) ?? panels[0].id;
33
38
  const activeEntryId = attended[0] ? `${attended[0]}${SLUG_PATH_SEPARATOR}${activePanelId}` : undefined;
34
- const { graph } = useGraph();
39
+ const { graph } = useAppGraph();
35
40
  const node = useNode(graph, activeEntryId);
36
41
  const { t } = useTranslation(DECK_PLUGIN);
37
42
  const { dispatchPromise: dispatch } = useIntentDispatcher();
38
43
  useNodeActionExpander(node);
44
+ const breakpoint = useBreakpoints();
45
+ const topbar = layoutAppliesTopbar(breakpoint);
46
+ const hoistStatusbar = useHoistStatusbar(breakpoint);
39
47
 
40
48
  const [internalValue, setInternalValue] = useState(activePanelId);
41
49
 
@@ -43,70 +51,113 @@ export const ComplementarySidebar = ({ panels, current }: ComplementarySidebarPr
43
51
  setInternalValue(activePanelId);
44
52
  }, [activePanelId]);
45
53
 
46
- const handleValueChange = useCallback(
47
- (nextValue: string) => {
48
- setInternalValue(nextValue);
49
- void dispatch(createIntent(NavigationAction.Open, { activeParts: { complementary: nextValue } }));
54
+ const handleTabClick = useCallback(
55
+ (event: MouseEvent) => {
56
+ const nextValue = event.currentTarget.getAttribute('data-value') as string;
57
+ if (nextValue === activePanelId) {
58
+ layout.complementarySidebarState = layout.complementarySidebarState === 'expanded' ? 'collapsed' : 'expanded';
59
+ } else {
60
+ setInternalValue(nextValue);
61
+ layout.complementarySidebarState = 'expanded';
62
+ void dispatch(createIntent(LayoutAction.UpdateComplementary, { part: 'complementary', subject: nextValue }));
63
+ }
50
64
  },
51
- [dispatch],
65
+ [layout, activePanelId, dispatch],
52
66
  );
53
67
 
54
68
  // TODO(burdon): Scroll area should be controlled by surface.
55
69
  return (
56
- <Main.ComplementarySidebar classNames='lg:block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]'>
57
- <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
58
- <div role='none' className={mx(railGridHorizontal, 'grid grid-cols-[100%] bs-full')}>
59
- <Tabs.Root
60
- orientation='horizontal'
61
- value={internalValue}
62
- onValueChange={handleValueChange}
63
- attendableId={attended[0]}
64
- classNames='contents'
70
+ <Main.ComplementarySidebar
71
+ classNames={[
72
+ topbar && 'block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]',
73
+ hoistStatusbar && 'block-end-[--statusbar-size]',
74
+ ]}
75
+ >
76
+ <Tabs.Root
77
+ orientation='vertical'
78
+ verticalVariant='stateless'
79
+ value={internalValue}
80
+ attendableId={attended[0]}
81
+ classNames='contents'
82
+ >
83
+ <div
84
+ role='none'
85
+ className='absolute z-[1] inset-block-0 inline-end-0 !is-[--r0-size] border-is border-separator grid grid-cols-1 grid-rows-[1fr_min-content] bg-baseSurface contain-layout app-drag'
86
+ >
87
+ <Tabs.Tablist classNames='grid grid-cols-1 auto-rows-[--rail-action] p-1 gap-1 !overflow-y-auto'>
88
+ {panels.map((panel) => (
89
+ <Tabs.Tab key={panel.id} value={panel.id} asChild>
90
+ <IconButton
91
+ label={toLocalizedString(panel.label, t)}
92
+ icon={panel.icon}
93
+ size={5}
94
+ iconOnly
95
+ tooltipSide='left'
96
+ data-value={panel.id}
97
+ variant={
98
+ activePanelId === panel.id
99
+ ? layout.complementarySidebarState === 'expanded'
100
+ ? 'primary'
101
+ : 'default'
102
+ : 'ghost'
103
+ }
104
+ onClick={handleTabClick}
105
+ />
106
+ </Tabs.Tab>
107
+ ))}
108
+ </Tabs.Tablist>
109
+ {!hoistStatusbar && (
110
+ <div role='none' className='grid grid-cols-1 auto-rows-[--rail-item] p-1 overflow-y-auto'>
111
+ <Surface role='status-bar--r0-footer' limit={1} />
112
+ </div>
113
+ )}
114
+ <div role='none' className='hidden lg:grid grid-cols-1 auto-rows-[--rail-action] p-1'>
115
+ <ToggleComplementarySidebarButton />
116
+ </div>
117
+ </div>
118
+ {panels.map((panel) => (
119
+ <Tabs.Tabpanel
120
+ key={panel.id}
121
+ value={panel.id}
122
+ classNames='absolute data-[state="inactive"]:-z-[1] inset-block-0 inline-start-0 is-[calc(100%-var(--r0-size))] lg:is-[--r1-size] grid grid-cols-1 grid-rows-[var(--rail-size)_1fr_min-content]'
123
+ {...(layout.complementarySidebarState !== 'expanded' && { inert: 'true' })}
65
124
  >
66
- <StackItem.Heading classNames='border-be border-separator grid grid-cols-[1fr_min-content] items-stretch'>
67
- <ScrollArea.Root classNames='flex-1 min-is-0'>
68
- <ScrollArea.Viewport>
69
- <Tabs.Tablist classNames='bs-[--rail-content] is-min items-stretch pis-[max(.5rem,env(safe-area-inset-left))] sm:pis-2'>
70
- {panels.map((panel) => (
71
- <Tabs.Tab key={panel.id} value={panel.id} classNames='!min-bs-0'>
72
- {toLocalizedString(panel.label, t)}
73
- </Tabs.Tab>
74
- ))}
75
- </Tabs.Tablist>
76
- <ScrollArea.Scrollbar orientation='horizontal'>
125
+ {panel.id === activePanelId && node && (
126
+ <>
127
+ <h2 className='flex items-center pli-2 border-separator border-be'>
128
+ {toLocalizedString(panel.label, t)}
129
+ </h2>
130
+ <ScrollArea.Root>
131
+ <ScrollArea.Viewport>
132
+ <Surface
133
+ key={activeEntryId}
134
+ role={`complementary--${activePanelId}`}
135
+ data={{
136
+ id: activeEntryId,
137
+ subject: node.properties.object ?? node.properties.space,
138
+ popoverAnchorId: layout.popoverAnchorId,
139
+ }}
140
+ fallback={PlankContentError}
141
+ placeholder={<PlankLoading />}
142
+ />
143
+ </ScrollArea.Viewport>
144
+ <ScrollArea.Scrollbar orientation='vertical'>
77
145
  <ScrollArea.Thumb />
78
146
  </ScrollArea.Scrollbar>
79
- </ScrollArea.Viewport>
80
- </ScrollArea.Root>
81
- <CloseComplementarySidebarButton />
82
- </StackItem.Heading>
83
- <ScrollArea.Root>
84
- <ScrollArea.Viewport>
85
- {panels.map((panel) => (
86
- <Tabs.Tabpanel key={panel.id} value={panel.id} classNames='pbe-[env(safe-area-inset-bottom)]'>
87
- {panel.id === activePanelId && node && (
88
- <Surface
89
- key={activeEntryId}
90
- role={`complementary--${activePanelId}`}
91
- data={{
92
- id: activeEntryId,
93
- subject: node.properties.object ?? node.properties.space,
94
- popoverAnchorId,
95
- }}
96
- fallback={PlankContentError}
97
- placeholder={<PlankLoading />}
98
- />
99
- )}
100
- </Tabs.Tabpanel>
101
- ))}
102
- <ScrollArea.Scrollbar orientation='vertical'>
103
- <ScrollArea.Thumb />
104
- </ScrollArea.Scrollbar>
105
- </ScrollArea.Viewport>
106
- </ScrollArea.Root>
107
- </Tabs.Root>
108
- </div>
109
- </StackContext.Provider>
147
+ </ScrollArea.Root>
148
+ {!hoistStatusbar && (
149
+ <div
150
+ role='contentinfo'
151
+ className='flex flex-wrap justify-center items-center border-bs border-separator plb-1'
152
+ >
153
+ <Surface role='status-bar--r1-footer' limit={1} />
154
+ </div>
155
+ )}
156
+ </>
157
+ )}
158
+ </Tabs.Tabpanel>
159
+ ))}
160
+ </Tabs.Root>
110
161
  </Main.ComplementarySidebar>
111
162
  );
112
163
  };
@@ -5,25 +5,22 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Surface } from '@dxos/app-framework';
8
- import { mx } from '@dxos/react-ui-theme';
9
8
 
10
9
  import { ToggleSidebarButton } from './SidebarButton';
11
- import { soloInlinePadding } from '../fragments';
10
+ import { layoutAppliesTopbar, useBreakpoints } from '../../util';
11
+ import { fixedSidebarToggleStyles } from '../fragments';
12
12
 
13
13
  export const ContentEmpty = () => {
14
+ const breakpoint = useBreakpoints();
15
+ const topbar = layoutAppliesTopbar(breakpoint);
14
16
  return (
15
17
  <div
16
18
  role='none'
17
- className='min-bs-screen is-dvw sm:is-full p-8 grid grid-rows-[var(--rail-size)_1fr] lg:grid-rows-1'
19
+ className='grid place-items-center p-8 relative bg-deck'
18
20
  data-testid='layoutPlugin.firstRunMessage'
19
21
  >
20
- <div role='toolbar' className={mx(soloInlinePadding, 'bs-[--rail-action] flex items-stretch lg:hidden')}>
21
- <ToggleSidebarButton />
22
- <span role='none' className='grow' />
23
- </div>
24
- <div role='none' className='grid place-items-center'>
25
- <Surface role='keyshortcuts' />
26
- </div>
22
+ <Surface role='keyshortcuts' />
23
+ {!topbar && <ToggleSidebarButton variant='default' classNames={fixedSidebarToggleStyles} />}
27
24
  </div>
28
25
  );
29
26
  };