@dxos/plugin-deck 0.7.4 → 0.7.5-feature-compute.4d9d99a

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 (169) hide show
  1. package/dist/lib/browser/app-graph-builder-67VRUD5K.mjs +121 -0
  2. package/dist/lib/browser/app-graph-builder-67VRUD5K.mjs.map +7 -0
  3. package/dist/lib/browser/check-app-scheme-GEX6W2R5.mjs +33 -0
  4. package/dist/lib/browser/check-app-scheme-GEX6W2R5.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-2M4PXYNB.mjs +1052 -0
  6. package/dist/lib/browser/chunk-2M4PXYNB.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-2PJNBVCY.mjs +39 -0
  8. package/dist/lib/browser/chunk-2PJNBVCY.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-4C2AFTET.mjs +186 -0
  10. package/dist/lib/browser/chunk-4C2AFTET.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-5VFDMW5M.mjs +17 -0
  12. package/dist/lib/browser/chunk-5VFDMW5M.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-GVOGPULO.mjs → chunk-JQJ5UWVB.mjs} +3 -3
  14. package/dist/lib/browser/chunk-JQJ5UWVB.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-KY5WXIXY.mjs +44 -0
  16. package/dist/lib/browser/chunk-KY5WXIXY.mjs.map +7 -0
  17. package/dist/lib/browser/deck-PLCSKPGL.mjs +26 -0
  18. package/dist/lib/browser/deck-PLCSKPGL.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +136 -1803
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs +152 -0
  22. package/dist/lib/browser/intent-resolver-FVOQSTBX.mjs.map +7 -0
  23. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs +249 -0
  24. package/dist/lib/browser/intent-resolver-K7GW4A2I.mjs.map +7 -0
  25. package/dist/lib/browser/location-QHRBQBQN.mjs +35 -0
  26. package/dist/lib/browser/location-QHRBQBQN.mjs.map +7 -0
  27. package/dist/lib/browser/meta.json +1 -1
  28. package/dist/lib/browser/react-context-3BDW7W2N.mjs +32 -0
  29. package/dist/lib/browser/react-context-3BDW7W2N.mjs.map +7 -0
  30. package/dist/lib/browser/react-root-UL7ZDRVZ.mjs +50 -0
  31. package/dist/lib/browser/react-root-UL7ZDRVZ.mjs.map +7 -0
  32. package/dist/lib/browser/react-surface-VPNOGGNN.mjs +28 -0
  33. package/dist/lib/browser/react-surface-VPNOGGNN.mjs.map +7 -0
  34. package/dist/lib/browser/settings-FNWW6WIJ.mjs +29 -0
  35. package/dist/lib/browser/settings-FNWW6WIJ.mjs.map +7 -0
  36. package/dist/lib/browser/state-7I5BD7SE.mjs +34 -0
  37. package/dist/lib/browser/state-7I5BD7SE.mjs.map +7 -0
  38. package/dist/lib/browser/types.mjs +10 -5
  39. package/dist/lib/browser/url-handler-Z5B7LD3N.mjs +76 -0
  40. package/dist/lib/browser/url-handler-Z5B7LD3N.mjs.map +7 -0
  41. package/dist/types/src/DeckPlugin.d.ts +1 -14
  42. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/capabilities.d.ts +13 -0
  44. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  45. package/dist/types/src/capabilities/index.d.ts +5 -0
  46. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/layout/app-graph-builder.d.ts +181 -0
  48. package/dist/types/src/capabilities/layout/app-graph-builder.d.ts.map +1 -0
  49. package/dist/types/src/capabilities/layout/deck.d.ts +4 -0
  50. package/dist/types/src/capabilities/layout/deck.d.ts.map +1 -0
  51. package/dist/types/src/capabilities/layout/index.d.ts +229 -0
  52. package/dist/types/src/capabilities/layout/index.d.ts.map +1 -0
  53. package/dist/types/src/capabilities/layout/intent-resolver.d.ts +4 -0
  54. package/dist/types/src/capabilities/layout/intent-resolver.d.ts.map +1 -0
  55. package/dist/types/src/capabilities/layout/react-context.d.ts +8 -0
  56. package/dist/types/src/capabilities/layout/react-context.d.ts.map +1 -0
  57. package/dist/types/src/capabilities/layout/react-root.d.ts +7 -0
  58. package/dist/types/src/capabilities/layout/react-root.d.ts.map +1 -0
  59. package/dist/types/src/capabilities/layout/state.d.ts +42 -0
  60. package/dist/types/src/capabilities/layout/state.d.ts.map +1 -0
  61. package/dist/types/src/capabilities/navigation/check-app-scheme.d.ts +4 -0
  62. package/dist/types/src/capabilities/navigation/check-app-scheme.d.ts.map +1 -0
  63. package/dist/types/src/capabilities/navigation/index.d.ts +5 -0
  64. package/dist/types/src/capabilities/navigation/index.d.ts.map +1 -0
  65. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts +4 -0
  66. package/dist/types/src/capabilities/navigation/intent-resolver.d.ts.map +1 -0
  67. package/dist/types/src/capabilities/navigation/location.d.ts +4 -0
  68. package/dist/types/src/capabilities/navigation/location.d.ts.map +1 -0
  69. package/dist/types/src/capabilities/navigation/set-location.d.ts +10 -0
  70. package/dist/types/src/capabilities/navigation/set-location.d.ts.map +1 -0
  71. package/dist/types/src/capabilities/navigation/url-handler.d.ts +4 -0
  72. package/dist/types/src/capabilities/navigation/url-handler.d.ts.map +1 -0
  73. package/dist/types/src/capabilities/settings/index.d.ts +3 -0
  74. package/dist/types/src/capabilities/settings/index.d.ts.map +1 -0
  75. package/dist/types/src/capabilities/settings/react-surface.d.ts +4 -0
  76. package/dist/types/src/capabilities/settings/react-surface.d.ts.map +1 -0
  77. package/dist/types/src/capabilities/settings/settings.d.ts +4 -0
  78. package/dist/types/src/capabilities/settings/settings.d.ts.map +1 -0
  79. package/dist/types/src/components/DeckContext.d.ts +3 -0
  80. package/dist/types/src/components/DeckContext.d.ts.map +1 -1
  81. package/dist/types/src/components/DeckLayout/Banner.d.ts +6 -0
  82. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -0
  83. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  84. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  85. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  86. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  87. package/dist/types/src/components/DeckLayout/Plank.d.ts +1 -1
  88. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  89. package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
  90. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
  91. package/dist/types/src/components/DeckLayout/Sidebar.d.ts +1 -5
  92. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  93. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +6 -0
  94. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -0
  95. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  96. package/dist/types/src/components/DeckLayout/Topbar.d.ts +3 -0
  97. package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +1 -0
  98. package/dist/types/src/components/fragments.d.ts +2 -0
  99. package/dist/types/src/components/fragments.d.ts.map +1 -0
  100. package/dist/types/src/events.d.ts +4 -0
  101. package/dist/types/src/events.d.ts.map +1 -0
  102. package/dist/types/src/index.d.ts +3 -2
  103. package/dist/types/src/index.d.ts.map +1 -1
  104. package/dist/types/src/meta.d.ts +3 -4
  105. package/dist/types/src/meta.d.ts.map +1 -1
  106. package/dist/types/src/translations.d.ts +5 -0
  107. package/dist/types/src/translations.d.ts.map +1 -1
  108. package/dist/types/src/types.d.ts +25 -17
  109. package/dist/types/src/types.d.ts.map +1 -1
  110. package/dist/types/src/util/index.d.ts +1 -1
  111. package/dist/types/src/util/index.d.ts.map +1 -1
  112. package/dist/types/src/util/useBreakpoints.d.ts +2 -0
  113. package/dist/types/src/util/useBreakpoints.d.ts.map +1 -0
  114. package/dist/types/src/util/useHoistStatusbar.d.ts +2 -0
  115. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -0
  116. package/dist/types/tsconfig.tsbuildinfo +1 -0
  117. package/package.json +30 -35
  118. package/src/DeckPlugin.ts +123 -0
  119. package/src/capabilities/capabilities.ts +17 -0
  120. package/src/capabilities/index.ts +8 -0
  121. package/src/capabilities/layout/app-graph-builder.ts +101 -0
  122. package/src/capabilities/layout/deck.ts +25 -0
  123. package/src/capabilities/layout/index.ts +12 -0
  124. package/src/capabilities/layout/intent-resolver.ts +128 -0
  125. package/src/capabilities/layout/react-context.tsx +26 -0
  126. package/src/capabilities/layout/react-root.tsx +52 -0
  127. package/src/capabilities/layout/state.ts +32 -0
  128. package/src/capabilities/navigation/check-app-scheme.ts +44 -0
  129. package/src/capabilities/navigation/index.ts +10 -0
  130. package/src/capabilities/navigation/intent-resolver.ts +216 -0
  131. package/src/capabilities/navigation/location.ts +28 -0
  132. package/src/capabilities/navigation/set-location.ts +38 -0
  133. package/src/capabilities/navigation/url-handler.ts +67 -0
  134. package/src/capabilities/settings/index.ts +8 -0
  135. package/src/capabilities/settings/react-surface.tsx +23 -0
  136. package/src/capabilities/settings/settings.ts +22 -0
  137. package/src/components/DeckContext.ts +6 -1
  138. package/src/components/DeckLayout/ActiveNode.tsx +1 -1
  139. package/src/components/DeckLayout/Banner.tsx +37 -0
  140. package/src/components/DeckLayout/ComplementarySidebar.tsx +76 -53
  141. package/src/components/DeckLayout/ContentEmpty.tsx +10 -2
  142. package/src/components/DeckLayout/DeckLayout.tsx +31 -40
  143. package/src/components/DeckLayout/Fullscreen.tsx +1 -1
  144. package/src/components/DeckLayout/NodePlankHeading.tsx +30 -49
  145. package/src/components/DeckLayout/Plank.tsx +13 -11
  146. package/src/components/DeckLayout/PlankControls.tsx +3 -5
  147. package/src/components/DeckLayout/Sidebar.tsx +22 -26
  148. package/src/components/DeckLayout/SidebarButton.tsx +74 -0
  149. package/src/components/DeckLayout/StatusBar.tsx +2 -2
  150. package/src/components/DeckLayout/Toast.tsx +19 -6
  151. package/src/components/DeckLayout/Topbar.tsx +11 -0
  152. package/src/components/fragments.ts +6 -0
  153. package/src/events.ts +11 -0
  154. package/src/index.ts +3 -4
  155. package/src/meta.ts +2 -2
  156. package/src/translations.ts +5 -0
  157. package/src/types.ts +27 -37
  158. package/src/util/index.ts +1 -1
  159. package/src/util/useBreakpoints.ts +11 -0
  160. package/src/util/useHoistStatusbar.ts +15 -0
  161. package/dist/lib/browser/chunk-GVOGPULO.mjs.map +0 -7
  162. package/dist/lib/browser/chunk-NIRHDTX4.mjs +0 -17
  163. package/dist/lib/browser/chunk-NIRHDTX4.mjs.map +0 -7
  164. package/dist/lib/browser/meta.mjs +0 -9
  165. package/dist/lib/browser/meta.mjs.map +0 -7
  166. package/dist/types/src/util/check-app-scheme.d.ts +0 -2
  167. package/dist/types/src/util/check-app-scheme.d.ts.map +0 -1
  168. package/src/DeckPlugin.tsx +0 -657
  169. package/src/util/check-app-scheme.ts +0 -21
@@ -2,24 +2,20 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Sidebar as MenuIcon } from '@phosphor-icons/react';
6
5
  import { untracked } from '@preact/signals-core';
7
6
  import React, { useCallback, useEffect, useMemo, useRef, type UIEvent, Fragment } from 'react';
8
7
 
9
- import { type LayoutParts, Surface, type Toast as ToastSchema, firstIdInPart, usePlugin } from '@dxos/app-framework';
10
- import { type AttentionPluginProvides } from '@dxos/plugin-attention';
11
8
  import {
12
- AlertDialog,
13
- Button,
14
- Dialog as NaturalDialog,
15
- Main,
16
- Popover,
17
- useOnTransition,
18
- useTranslation,
19
- type MainProps,
20
- } from '@dxos/react-ui';
9
+ firstIdInPart,
10
+ Surface,
11
+ usePluginManager,
12
+ type LayoutParts,
13
+ type Toast as ToastSchema,
14
+ } from '@dxos/app-framework';
15
+ import { AttentionCapabilities } from '@dxos/plugin-attention';
16
+ import { AlertDialog, Dialog as NaturalDialog, Main, Popover, useOnTransition, type MainProps } from '@dxos/react-ui';
21
17
  import { Stack, StackContext, DEFAULT_HORIZONTAL_SIZE } from '@dxos/react-ui-stack';
22
- import { getSize, mainPaddingTransitions } from '@dxos/react-ui-theme';
18
+ import { mainPaddingTransitions } from '@dxos/react-ui-theme';
23
19
 
24
20
  import { ActiveNode } from './ActiveNode';
25
21
  import { ComplementarySidebar, type ComplementarySidebarProps } from './ComplementarySidebar';
@@ -29,9 +25,10 @@ import { Plank } from './Plank';
29
25
  import { Sidebar } from './Sidebar';
30
26
  import { StatusBar } from './StatusBar';
31
27
  import { Toast } from './Toast';
32
- import { DECK_PLUGIN } from '../../meta';
28
+ import { Topbar } from './Topbar';
33
29
  import { type Overscroll } from '../../types';
34
- import { calculateOverscroll } from '../../util';
30
+ import { calculateOverscroll, useBreakpoints } from '../../util';
31
+ import { useHoistStatusbar } from '../../util/useHoistStatusbar';
35
32
  import { useDeckContext } from '../DeckContext';
36
33
  import { useLayout } from '../LayoutContext';
37
34
 
@@ -60,10 +57,10 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
60
57
  popoverContent,
61
58
  popoverAnchorId,
62
59
  } = context;
63
- const { t } = useTranslation(DECK_PLUGIN);
60
+ const breakpoint = useBreakpoints();
61
+ const hoistStatusbar = useHoistStatusbar(breakpoint);
64
62
  const { plankSizing } = useDeckContext();
65
- // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
66
- const attentionPlugin = usePlugin<AttentionPluginProvides>('dxos.org/plugin/attention');
63
+ const pluginManager = usePluginManager();
67
64
  const fullScreenSlug = useMemo(() => firstIdInPart(layoutParts, 'fullScreen'), [layoutParts]);
68
65
 
69
66
  const scrollLeftRef = useRef<number | null>();
@@ -73,7 +70,11 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
73
70
 
74
71
  // Ensure the first plank is attended when the deck is first rendered.
75
72
  useEffect(() => {
76
- const attended = untracked(() => attentionPlugin?.provides.attention.attended ?? []);
73
+ // NOTE: Not `useAttended` so that the layout component is not re-rendered when the attended list changes.
74
+ const attended = untracked(() => {
75
+ const attention = pluginManager.context.requestCapability(AttentionCapabilities.Attention);
76
+ return attention.current;
77
+ });
77
78
  const firstId = isSoloModeLoaded ? firstIdInPart(layoutParts, 'solo') : firstIdInPart(layoutParts, 'main');
78
79
  if (attended.length === 0 && firstId) {
79
80
  // TODO(wittjosiah): Focusing the type button is a workaround.
@@ -149,18 +150,8 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
149
150
  complementarySidebarOpen={context.complementarySidebarOpen}
150
151
  onComplementarySidebarOpenChange={(next) => (context.complementarySidebarOpen = next)}
151
152
  >
152
- {/* Notch */}
153
- <Main.Notch classNames='z-[21]'>
154
- <Surface role='notch-start' />
155
- <Button onClick={() => (context.sidebarOpen = !context.sidebarOpen)} variant='ghost' classNames='p-1'>
156
- <span className='sr-only'>{t('open navigation sidebar label')}</span>
157
- <MenuIcon weight='light' className={getSize(5)} />
158
- </Button>
159
- <Surface role='notch-end' />
160
- </Main.Notch>
161
-
162
153
  {/* Left sidebar. */}
163
- <Sidebar layoutParts={layoutParts} />
154
+ <Sidebar />
164
155
 
165
156
  {/* Right sidebar. */}
166
157
  <ComplementarySidebar panels={panels} current={layoutParts.complementary?.[0].id} />
@@ -179,7 +170,10 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
179
170
  {!isEmpty && (
180
171
  <Main.Content
181
172
  bounce
182
- classNames='grid block-end-[--statusbar-size]'
173
+ classNames={[
174
+ 'grid !block-start-[env(safe-area-inset-top)] lg:!block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]',
175
+ hoistStatusbar && 'lg:block-end-[--statusbar-size]',
176
+ ]}
183
177
  handlesFocus
184
178
  style={
185
179
  {
@@ -198,7 +192,6 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
198
192
  {...(isSoloModeLoaded && { inert: '' })}
199
193
  >
200
194
  <Stack
201
- separators={false}
202
195
  orientation='horizontal'
203
196
  size='contain'
204
197
  classNames={['absolute inset-block-0 -inset-inline-px', mainPaddingTransitions]}
@@ -226,31 +219,29 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
226
219
  className={isSoloModeLoaded ? 'relative bg-deck overflow-hidden' : 'sr-only'}
227
220
  {...(!isSoloModeLoaded && { inert: '' })}
228
221
  >
229
- <StackContext.Provider
230
- value={{ size: 'contain', orientation: 'horizontal', separators: false, rail: true }}
231
- >
222
+ <StackContext.Provider value={{ size: 'contain', orientation: 'horizontal', rail: true }}>
232
223
  <Plank entry={layoutParts.solo?.[0]} layoutParts={layoutParts} part='solo' layoutMode={layoutMode} />
233
224
  </StackContext.Provider>
234
225
  </div>
235
226
  </Main.Content>
236
227
  )}
237
228
 
238
- {/* Footer status. */}
239
- <StatusBar showHints={showHints} />
229
+ {/* Status bar. */}
230
+ {breakpoint === 'desktop' && <Topbar />}
231
+ {hoistStatusbar && <StatusBar showHints={showHints} />}
240
232
  </Main.Root>
241
233
  )}
242
234
 
243
235
  {/* Global popovers. */}
244
236
  <Popover.Portal>
245
237
  <Popover.Content
246
- classNames='z-[60]'
247
238
  onEscapeKeyDown={() => {
248
239
  context.popoverOpen = false;
249
240
  context.popoverAnchorId = undefined;
250
241
  }}
251
242
  >
252
243
  <Popover.Viewport>
253
- <Surface role='popover' data={popoverContent} />
244
+ <Surface role='popover' data={popoverContent} limit={1} />
254
245
  </Popover.Viewport>
255
246
  <Popover.Arrow />
256
247
  </Popover.Content>
@@ -259,7 +250,7 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
259
250
  {/* Global dialog. */}
260
251
  <Dialog.Root open={dialogOpen} onOpenChange={(nextOpen) => (context.dialogOpen = nextOpen)}>
261
252
  <Dialog.Overlay blockAlign={dialogBlockAlign}>
262
- <Surface role='dialog' data={dialogContent} />
253
+ <Surface role='dialog' data={dialogContent} limit={1} />
263
254
  </Dialog.Overlay>
264
255
  </Dialog.Root>
265
256
 
@@ -23,7 +23,7 @@ export const Fullscreen = ({ id }: { id?: string }) => {
23
23
  limit={1}
24
24
  fallback={Fallback}
25
25
  data={{
26
- active: fullScreenNode?.data,
26
+ subject: fullScreenNode?.data,
27
27
  component: id?.startsWith(SURFACE_PREFIX) ? id.slice(SURFACE_PREFIX.length) : undefined,
28
28
  }}
29
29
  />
@@ -5,6 +5,7 @@
5
5
  import React, { Fragment, memo, useEffect, useMemo } from 'react';
6
6
 
7
7
  import {
8
+ createIntent,
8
9
  LayoutAction,
9
10
  NavigationAction,
10
11
  SLUG_PATH_SEPARATOR,
@@ -13,13 +14,15 @@ import {
13
14
  type LayoutCoordinate,
14
15
  } from '@dxos/app-framework';
15
16
  import { type Node, useGraph } from '@dxos/plugin-graph';
16
- import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation, IconButton } from '@dxos/react-ui';
17
+ import { Icon, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
17
18
  import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
18
19
  import { TextTooltip } from '@dxos/react-ui-text-tooltip';
19
20
 
20
21
  import { PlankControls } from './PlankControls';
22
+ import { ToggleComplementarySidebarButton, ToggleSidebarButton } from './SidebarButton';
21
23
  import { DECK_PLUGIN } from '../../meta';
22
- import { useLayout } from '../LayoutContext';
24
+ import { useBreakpoints } from '../../util';
25
+ import { soloInlinePadding } from '../fragments';
23
26
 
24
27
  export type NodePlankHeadingProps = {
25
28
  coordinate: LayoutCoordinate;
@@ -41,16 +44,15 @@ export const NodePlankHeading = memo(
41
44
  pending,
42
45
  actions = [],
43
46
  }: NodePlankHeadingProps) => {
44
- const layoutContext = useLayout();
45
47
  const { t } = useTranslation(DECK_PLUGIN);
46
48
  const { graph } = useGraph();
47
49
  const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
48
50
  const label = pending
49
51
  ? t('pending heading')
50
52
  : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
51
- const dispatch = useIntentDispatcher();
53
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
52
54
  const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
53
- const [isNotMobile] = useMediaQuery('md');
55
+ const breakpoint = useBreakpoints();
54
56
 
55
57
  useEffect(() => {
56
58
  const frame = requestAnimationFrame(() => {
@@ -66,15 +68,20 @@ export const NodePlankHeading = memo(
66
68
  const attendableId = coordinate.entryId.split(SLUG_PATH_SEPARATOR).at(0);
67
69
  const capabilities = useMemo(
68
70
  () => ({
69
- solo: (layoutPart === 'solo' || layoutPart === 'main') && isNotMobile,
71
+ solo: layoutPart === 'solo' || layoutPart === 'main',
70
72
  incrementStart: canIncrementStart,
71
73
  incrementEnd: canIncrementEnd,
72
74
  }),
73
- [isNotMobile, layoutPart, canIncrementStart, canIncrementEnd],
75
+ [breakpoint, layoutPart, canIncrementStart, canIncrementEnd],
74
76
  );
75
77
 
76
78
  return (
77
- <StackItem.Heading classNames='pie-1 border-be border-separator'>
79
+ <StackItem.Heading
80
+ classNames={[
81
+ 'plb-1 border-be border-separator items-stretch gap-1',
82
+ layoutPart === 'solo' ? soloInlinePadding : 'pli-1',
83
+ ]}
84
+ >
78
85
  <ActionRoot>
79
86
  {node ? (
80
87
  <StackItem.Sigil
@@ -87,7 +94,7 @@ export const NodePlankHeading = memo(
87
94
  typeof action.data === 'function' && action.data?.({ node: action as Node, caller: DECK_PLUGIN })
88
95
  }
89
96
  >
90
- <Surface role='menu-footer' data={{ object: node.data }} />
97
+ <Surface role='menu-footer' data={{ subject: node.data }} />
91
98
  </StackItem.Sigil>
92
99
  ) : (
93
100
  <StackItem.SigilButton>
@@ -96,6 +103,7 @@ export const NodePlankHeading = memo(
96
103
  </StackItem.SigilButton>
97
104
  )}
98
105
  </ActionRoot>
106
+ {breakpoint !== 'desktop' && <ToggleSidebarButton />}
99
107
  <TextTooltip text={label} onlyWhenTruncating>
100
108
  <StackItem.HeadingLabel
101
109
  attendableId={attendableId}
@@ -107,13 +115,12 @@ export const NodePlankHeading = memo(
107
115
  </TextTooltip>
108
116
  {node && layoutPart !== 'complementary' && (
109
117
  // TODO(Zan): What are we doing with layout coordinate here?
110
- <Surface role='navbar-end' direction='inline-reverse' data={{ object: node.data }} />
118
+ <Surface role='navbar-end' data={{ subject: node.data }} />
111
119
  )}
112
120
  {/* NOTE(thure): Pinning & unpinning are temporarily disabled */}
113
121
  <PlankControls
114
122
  capabilities={capabilities}
115
123
  isSolo={layoutPart === 'solo'}
116
- classNames='mx-1'
117
124
  onClick={(eventType) => {
118
125
  if (!layoutPart) {
119
126
  return;
@@ -121,53 +128,27 @@ export const NodePlankHeading = memo(
121
128
 
122
129
  // TODO(Zan): Update this to use the new layout actions.
123
130
  if (eventType === 'solo') {
124
- return dispatch([
125
- {
126
- action: NavigationAction.ADJUST,
127
- data: { type: eventType, layoutCoordinate: { part: 'main', entryId: coordinate.entryId } },
128
- },
129
- ]);
131
+ return dispatch(
132
+ createIntent(NavigationAction.Adjust, {
133
+ type: eventType,
134
+ layoutCoordinate: { part: 'main', entryId: coordinate.entryId },
135
+ }),
136
+ );
130
137
  } else if (eventType === 'close') {
131
138
  if (layoutPart === 'complementary') {
132
- return dispatch({
133
- action: LayoutAction.SET_LAYOUT,
134
- data: {
135
- element: 'complementary',
136
- state: false,
137
- },
138
- });
139
+ return dispatch(createIntent(LayoutAction.SetLayout, { element: 'complementary', state: false }));
139
140
  } else {
140
- return dispatch({
141
- action: NavigationAction.CLOSE,
142
- data: {
143
- activeParts: {
144
- [layoutPart]: [coordinate.entryId],
145
- },
146
- },
147
- });
141
+ return dispatch(
142
+ createIntent(NavigationAction.Close, { activeParts: { [layoutPart]: [coordinate.entryId] } }),
143
+ );
148
144
  }
149
145
  } else {
150
- return dispatch({
151
- action: NavigationAction.ADJUST,
152
- data: { type: eventType, layoutCoordinate: coordinate },
153
- });
146
+ return dispatch(createIntent(NavigationAction.Adjust, { type: eventType, layoutCoordinate: coordinate }));
154
147
  }
155
148
  }}
156
149
  close={layoutPart === 'complementary' ? 'minify-end' : true}
157
150
  >
158
- {/* TODO(wittjosiah): This doesn't behave exactly the same as the rest of the button group. */}
159
- {layoutPart !== 'complementary' && (
160
- <IconButton
161
- iconOnly
162
- onClick={() => (layoutContext.complementarySidebarOpen = !layoutContext.complementarySidebarOpen)}
163
- variant='ghost'
164
- label={t('open complementary sidebar label')}
165
- classNames='!pli-2 !plb-3 [&>svg]:-scale-x-100'
166
- icon='ph--sidebar-simple--regular'
167
- size={4}
168
- tooltipZIndex='70'
169
- />
170
- )}
151
+ <ToggleComplementarySidebarButton />
171
152
  </PlankControls>
172
153
  </StackItem.Heading>
173
154
  );
@@ -5,16 +5,17 @@
5
5
  import React, { type KeyboardEvent, memo, useCallback, useLayoutEffect, useMemo, useRef } from 'react';
6
6
 
7
7
  import {
8
+ createIntent,
9
+ indexInPart,
10
+ LayoutAction,
11
+ partLength,
12
+ Surface,
13
+ useIntentDispatcher,
14
+ type Layout,
8
15
  type LayoutCoordinate,
9
16
  type LayoutEntry,
10
17
  type LayoutPart,
11
18
  type LayoutParts,
12
- Surface,
13
- useIntentDispatcher,
14
- type Layout,
15
- indexInPart,
16
- partLength,
17
- LayoutAction,
18
19
  } from '@dxos/app-framework';
19
20
  import { debounce } from '@dxos/async';
20
21
  import { useGraph } from '@dxos/plugin-graph';
@@ -25,8 +26,8 @@ import { mainIntrinsicSize, mx } from '@dxos/react-ui-theme';
25
26
  import { NodePlankHeading } from './NodePlankHeading';
26
27
  import { PlankContentError, PlankError } from './PlankError';
27
28
  import { PlankLoading } from './PlankLoading';
28
- import { DeckAction } from '../../DeckPlugin';
29
29
  import { useNode, useMainSize } from '../../hooks';
30
+ import { DeckAction } from '../../types';
30
31
  import { useDeckContext } from '../DeckContext';
31
32
  import { useLayout } from '../LayoutContext';
32
33
 
@@ -42,7 +43,7 @@ export type PlankProps = {
42
43
  };
43
44
 
44
45
  export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: PlankProps) => {
45
- const dispatch = useIntentDispatcher();
46
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
46
47
  const coordinate: LayoutCoordinate = useMemo(() => ({ part, entryId: entry?.id ?? UNKNOWN_ID }), [entry?.id, part]);
47
48
  const { popoverAnchorId, scrollIntoView } = useLayout();
48
49
  const { plankSizing } = useDeckContext();
@@ -61,7 +62,7 @@ export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: Plan
61
62
  const size = plankSizing?.[coordinate.entryId] as number | undefined;
62
63
  const setSize = useCallback(
63
64
  debounce((nextSize: number) => {
64
- return dispatch({ action: DeckAction.UPDATE_PLANK_SIZE, data: { id: coordinate.entryId, size: nextSize } });
65
+ return dispatch(createIntent(DeckAction.UpdatePlankSize, { id: coordinate.entryId, size: nextSize }));
65
66
  }, 200),
66
67
  [dispatch, coordinate.entryId],
67
68
  );
@@ -81,7 +82,7 @@ export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: Plan
81
82
  focusable?.focus({ preventScroll: true });
82
83
  layoutMode === 'deck' && focusable?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
83
84
  // Clear the scroll into view state once it has been actioned.
84
- void dispatch({ action: LayoutAction.SCROLL_INTO_VIEW, data: { id: undefined } });
85
+ void dispatch(createIntent(LayoutAction.ScrollIntoView, { id: undefined }));
85
86
  }
86
87
  }, [coordinate.entryId, scrollIntoView, layoutMode]);
87
88
 
@@ -93,7 +94,8 @@ export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: Plan
93
94
  const data = useMemo(
94
95
  () =>
95
96
  node && {
96
- ...(entry?.path ? { subject: node.data, path: entry.path } : { object: node.data }),
97
+ subject: node.data,
98
+ path: entry?.path,
97
99
  coordinate,
98
100
  popoverAnchorId,
99
101
  },
@@ -4,6 +4,7 @@
4
4
 
5
5
  import React, { forwardRef } from 'react';
6
6
 
7
+ import { type PartAdjustment } from '@dxos/app-framework';
7
8
  import {
8
9
  Button,
9
10
  ButtonGroup,
@@ -16,8 +17,7 @@ import {
16
17
 
17
18
  import { DECK_PLUGIN } from '../../meta';
18
19
 
19
- export type PlankControlEvent = 'solo' | 'close' | `${'pin' | 'increment'}-${'start' | 'end'}`;
20
- export type PlankControlHandler = (event: PlankControlEvent) => void;
20
+ export type PlankControlHandler = (event: PartAdjustment) => void;
21
21
 
22
22
  export type PlankCapabilities = {
23
23
  incrementStart?: boolean;
@@ -44,9 +44,7 @@ const PlankControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> &
44
44
  </Button>
45
45
  </Tooltip.Trigger>
46
46
  <Tooltip.Portal>
47
- <Tooltip.Content side='bottom' classNames='z-[70]'>
48
- {label}
49
- </Tooltip.Content>
47
+ <Tooltip.Content side='bottom'>{label}</Tooltip.Content>
50
48
  </Tooltip.Portal>
51
49
  </Tooltip.Root>
52
50
  );
@@ -4,39 +4,35 @@
4
4
 
5
5
  import React, { useMemo } from 'react';
6
6
 
7
- import { type LayoutParts, openIds, Surface } from '@dxos/app-framework';
7
+ import { Surface } from '@dxos/app-framework';
8
8
  import { Main } from '@dxos/react-ui';
9
+ import { mx } from '@dxos/react-ui-theme';
9
10
 
11
+ import { Banner } from './Banner';
12
+ import { useBreakpoints } from '../../util';
13
+ import { useHoistStatusbar } from '../../util/useHoistStatusbar';
10
14
  import { useLayout } from '../LayoutContext';
11
15
 
12
- export type SidebarProps = {
13
- layoutParts: LayoutParts;
14
- };
15
-
16
- export const Sidebar = ({ layoutParts }: SidebarProps) => {
17
- const { layoutMode, popoverAnchorId } = useLayout();
16
+ export const Sidebar = () => {
17
+ const layoutContext = useLayout();
18
+ const { popoverAnchorId } = layoutContext;
19
+ const breakpoint = useBreakpoints();
20
+ const hoistStatusbar = useHoistStatusbar(breakpoint);
18
21
 
19
- // TODO(wittjosiah): The activeIds should be path-based to avoid conflicts.
20
- const activeIds = useMemo(() => {
21
- if (layoutMode === 'solo') {
22
- return Array.from(new Set<string>(layoutParts?.solo?.map((e) => e.id) ?? []));
23
- } else if (layoutMode === 'deck') {
24
- return Array.from(new Set<string>(layoutParts?.main?.map((e) => e.id) ?? []));
25
- }
22
+ const navigationData = useMemo(() => ({ popoverAnchorId }), [popoverAnchorId]);
26
23
 
27
- return Array.from(new Set<string>(openIds(layoutParts)));
28
- }, [layoutParts, layoutMode]);
29
-
30
- const navigationData = useMemo(
31
- () => ({
32
- popoverAnchorId,
33
- activeIds,
34
- }),
35
- [popoverAnchorId, activeIds],
36
- );
37
24
  return (
38
- <Main.NavigationSidebar>
39
- <Surface role='navigation' data={{ ...navigationData }} limit={1} />
25
+ <Main.NavigationSidebar classNames='grid grid-cols-1 grid-rows-[var(--rail-size)_var(--rail-action)_1fr_min-content_min-content] md:grid-rows-[var(--rail-size)_var(--rail-action)_1fr_min-content] lg:grid-rows-[1fr_min-content] overflow-hidden lg:block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]'>
26
+ {breakpoint !== 'desktop' && (
27
+ <>
28
+ <Banner variant='sidebar' />
29
+ <Surface role='search-input' limit={1} />
30
+ </>
31
+ )}
32
+ <div role='none' className={mx('!overflow-y-auto', breakpoint !== 'desktop' && 'border-be border-separator')}>
33
+ <Surface role='navigation' data={navigationData} limit={1} />
34
+ </div>
35
+ {!hoistStatusbar && <Surface role='status-bar--sidebar-footer' limit={1} />}
40
36
  </Main.NavigationSidebar>
41
37
  );
42
38
  };
@@ -0,0 +1,74 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { IconButton, useTranslation } from '@dxos/react-ui';
8
+
9
+ import { DECK_PLUGIN } from '../../meta';
10
+ import { useLayout } from '../LayoutContext';
11
+
12
+ export const ToggleSidebarButton = () => {
13
+ const layoutContext = useLayout();
14
+ const { t } = useTranslation(DECK_PLUGIN);
15
+ return (
16
+ <IconButton
17
+ variant='ghost'
18
+ iconOnly
19
+ icon='ph--sidebar--regular'
20
+ size={4}
21
+ label={t('open navigation sidebar label')}
22
+ onClick={() => (layoutContext.sidebarOpen = !layoutContext.sidebarOpen)}
23
+ classNames='!pli-2 order-first'
24
+ />
25
+ );
26
+ };
27
+
28
+ export const CloseSidebarButton = () => {
29
+ const layoutContext = useLayout();
30
+ const { t } = useTranslation(DECK_PLUGIN);
31
+ return (
32
+ <IconButton
33
+ variant='ghost'
34
+ iconOnly
35
+ icon='ph--caret-line-left--regular'
36
+ size={4}
37
+ label={t('close navigation sidebar label')}
38
+ onClick={() => (layoutContext.sidebarOpen = false)}
39
+ classNames='!rounded-none !pli-1 ch-focus-ring-inset pie-[max(.5rem,env(safe-area-inset-left))]'
40
+ />
41
+ );
42
+ };
43
+
44
+ export const ToggleComplementarySidebarButton = () => {
45
+ const layoutContext = useLayout();
46
+ const { t } = useTranslation(DECK_PLUGIN);
47
+ return (
48
+ <IconButton
49
+ iconOnly
50
+ onClick={() => (layoutContext.complementarySidebarOpen = !layoutContext.complementarySidebarOpen)}
51
+ variant='ghost'
52
+ label={t('open complementary sidebar label')}
53
+ classNames='!pli-2 !plb-3 [&>svg]:-scale-x-100'
54
+ icon='ph--sidebar-simple--regular'
55
+ size={4}
56
+ />
57
+ );
58
+ };
59
+
60
+ export const CloseComplementarySidebarButton = () => {
61
+ const layoutContext = useLayout();
62
+ const { t } = useTranslation(DECK_PLUGIN);
63
+ return (
64
+ <IconButton
65
+ iconOnly
66
+ variant='ghost'
67
+ size={4}
68
+ icon='ph--caret-line-right--regular'
69
+ label={t('close complementary sidebar label')}
70
+ classNames='!rounded-none border-is border-separator ch-focus-ring-inset pie-2 lg:pie-[max(.5rem,env(safe-area-inset-right))]'
71
+ onClick={() => (layoutContext.complementarySidebarOpen = false)}
72
+ />
73
+ );
74
+ };
@@ -13,10 +13,10 @@ export const StatusBar = ({ showHints }: { showHints?: boolean }) => {
13
13
  const sizeAttrs = useMainSize();
14
14
  return (
15
15
  <div
16
- role='none'
16
+ role='contentinfo'
17
17
  {...sizeAttrs}
18
18
  className={mx(
19
- 'fixed flex justify-between block-end-0 inset-inline-0 items-center border-bs border-separator z-[2]',
19
+ 'fixed block-end-0 inset-inline-0 flex justify-between items-center border-bs border-separator z-[2] pbe-[env(safe-area-inset-bottom)]',
20
20
  mainPadding,
21
21
  mainPaddingTransitions,
22
22
  )}
@@ -5,7 +5,16 @@
5
5
  import React from 'react';
6
6
 
7
7
  import type { Toast as ToastProps } from '@dxos/app-framework';
8
- import { Button, Icon, Toast as NaturalToast, type ToastRootProps } from '@dxos/react-ui';
8
+ import {
9
+ Button,
10
+ Icon,
11
+ Toast as NaturalToast,
12
+ toLocalizedString,
13
+ useTranslation,
14
+ type ToastRootProps,
15
+ } from '@dxos/react-ui';
16
+
17
+ import { DECK_PLUGIN } from '../../meta';
9
18
 
10
19
  // TODO(wittjosiah): Render remaining duration as a progress bar within the toast.
11
20
  export const Toast = ({
@@ -20,26 +29,30 @@ export const Toast = ({
20
29
  onAction,
21
30
  onOpenChange,
22
31
  }: ToastProps & Pick<ToastRootProps, 'onOpenChange'>) => {
32
+ const { t } = useTranslation(DECK_PLUGIN);
33
+
23
34
  return (
24
35
  <NaturalToast.Root data-testid={id} defaultOpen duration={duration} onOpenChange={onOpenChange}>
25
36
  <NaturalToast.Body>
26
37
  <NaturalToast.Title classNames='items-center'>
27
38
  {icon && <Icon icon={icon} size={5} classNames='inline mr-1' />}
28
- <span>{title}</span>
39
+ {title && <span>{toLocalizedString(title, t)}</span>}
29
40
  </NaturalToast.Title>
30
- {description && <NaturalToast.Description>{description}</NaturalToast.Description>}
41
+ {description && (
42
+ <NaturalToast.Description>{description && toLocalizedString(description, t)}</NaturalToast.Description>
43
+ )}
31
44
  </NaturalToast.Body>
32
45
  <NaturalToast.Actions>
33
46
  {onAction && actionAlt && actionLabel && (
34
- <NaturalToast.Action altText={actionAlt} asChild>
47
+ <NaturalToast.Action altText={toLocalizedString(actionAlt, t)} asChild>
35
48
  <Button data-testid='toast.action' variant='primary' onClick={() => onAction?.()}>
36
- {actionLabel}
49
+ {toLocalizedString(actionLabel, t)}
37
50
  </Button>
38
51
  </NaturalToast.Action>
39
52
  )}
40
53
  {closeLabel && (
41
54
  <NaturalToast.Close asChild>
42
- <Button data-testid='toast.close'>{closeLabel}</Button>
55
+ <Button data-testid='toast.close'>{toLocalizedString(closeLabel, t)}</Button>
43
56
  </NaturalToast.Close>
44
57
  )}
45
58
  </NaturalToast.Actions>
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { Banner } from './Banner';
8
+
9
+ export const Topbar = () => {
10
+ return <Banner variant='topbar' />;
11
+ };
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export const soloInlinePadding =
6
+ 'pis-[calc(env(safe-area-inset-left)+.25rem)] pie-[calc(env(safe-area-inset-left)+.25rem)]';