@dxos/plugin-deck 0.7.5-main.9d26e3a → 0.7.5-main.9d2a38b

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 (161) 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-ZC3K6C2W.mjs → chunk-KY5WXIXY.mjs} +11 -4
  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 +131 -1770
  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 +6 -6
  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 -5
  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/Sidebar.d.ts +1 -5
  90. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  91. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +6 -0
  92. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -0
  93. package/dist/types/src/components/DeckLayout/Topbar.d.ts +3 -0
  94. package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +1 -0
  95. package/dist/types/src/components/fragments.d.ts +2 -0
  96. package/dist/types/src/components/fragments.d.ts.map +1 -0
  97. package/dist/types/src/events.d.ts +4 -0
  98. package/dist/types/src/events.d.ts.map +1 -0
  99. package/dist/types/src/index.d.ts +3 -2
  100. package/dist/types/src/index.d.ts.map +1 -1
  101. package/dist/types/src/meta.d.ts +3 -4
  102. package/dist/types/src/meta.d.ts.map +1 -1
  103. package/dist/types/src/translations.d.ts +2 -0
  104. package/dist/types/src/translations.d.ts.map +1 -1
  105. package/dist/types/src/types.d.ts +12 -20
  106. package/dist/types/src/types.d.ts.map +1 -1
  107. package/dist/types/src/util/index.d.ts +1 -1
  108. package/dist/types/src/util/index.d.ts.map +1 -1
  109. package/dist/types/src/util/useBreakpoints.d.ts +2 -0
  110. package/dist/types/src/util/useBreakpoints.d.ts.map +1 -0
  111. package/dist/types/src/util/useHoistStatusbar.d.ts +2 -0
  112. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -0
  113. package/dist/types/tsconfig.tsbuildinfo +1 -1
  114. package/package.json +29 -35
  115. package/src/DeckPlugin.ts +123 -0
  116. package/src/capabilities/capabilities.ts +17 -0
  117. package/src/capabilities/index.ts +8 -0
  118. package/src/capabilities/layout/app-graph-builder.ts +101 -0
  119. package/src/capabilities/layout/deck.ts +25 -0
  120. package/src/capabilities/layout/index.ts +12 -0
  121. package/src/capabilities/layout/intent-resolver.ts +128 -0
  122. package/src/capabilities/layout/react-context.tsx +26 -0
  123. package/src/capabilities/layout/react-root.tsx +52 -0
  124. package/src/capabilities/layout/state.ts +32 -0
  125. package/src/capabilities/navigation/check-app-scheme.ts +44 -0
  126. package/src/capabilities/navigation/index.ts +10 -0
  127. package/src/capabilities/navigation/intent-resolver.ts +216 -0
  128. package/src/capabilities/navigation/location.ts +28 -0
  129. package/src/capabilities/navigation/set-location.ts +38 -0
  130. package/src/capabilities/navigation/url-handler.ts +67 -0
  131. package/src/capabilities/settings/index.ts +8 -0
  132. package/src/capabilities/settings/react-surface.tsx +23 -0
  133. package/src/capabilities/settings/settings.ts +22 -0
  134. package/src/components/DeckContext.ts +6 -1
  135. package/src/components/DeckLayout/Banner.tsx +37 -0
  136. package/src/components/DeckLayout/ComplementarySidebar.tsx +74 -52
  137. package/src/components/DeckLayout/ContentEmpty.tsx +10 -2
  138. package/src/components/DeckLayout/DeckLayout.tsx +28 -33
  139. package/src/components/DeckLayout/NodePlankHeading.tsx +15 -20
  140. package/src/components/DeckLayout/Plank.tsx +7 -7
  141. package/src/components/DeckLayout/Sidebar.tsx +21 -8
  142. package/src/components/DeckLayout/SidebarButton.tsx +74 -0
  143. package/src/components/DeckLayout/StatusBar.tsx +2 -2
  144. package/src/components/DeckLayout/Topbar.tsx +11 -0
  145. package/src/components/fragments.ts +6 -0
  146. package/src/events.ts +11 -0
  147. package/src/index.ts +3 -4
  148. package/src/meta.ts +2 -2
  149. package/src/translations.ts +2 -0
  150. package/src/types.ts +12 -37
  151. package/src/util/index.ts +1 -1
  152. package/src/util/useBreakpoints.ts +11 -0
  153. package/src/util/useHoistStatusbar.ts +15 -0
  154. package/dist/lib/browser/chunk-GVOGPULO.mjs.map +0 -7
  155. package/dist/lib/browser/chunk-ZC3K6C2W.mjs.map +0 -7
  156. package/dist/lib/browser/meta.mjs +0 -9
  157. package/dist/lib/browser/meta.mjs.map +0 -7
  158. package/dist/types/src/util/check-app-scheme.d.ts +0 -2
  159. package/dist/types/src/util/check-app-scheme.d.ts.map +0 -1
  160. package/src/DeckPlugin.tsx +0 -623
  161. 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
  {
@@ -232,8 +226,9 @@ export const DeckLayout = ({ layoutParts, toasts, overscroll, showHints, panels,
232
226
  </Main.Content>
233
227
  )}
234
228
 
235
- {/* Footer status. */}
236
- <StatusBar showHints={showHints} />
229
+ {/* Status bar. */}
230
+ {breakpoint === 'desktop' && <Topbar />}
231
+ {hoistStatusbar && <StatusBar showHints={showHints} />}
237
232
  </Main.Root>
238
233
  )}
239
234
 
@@ -14,13 +14,15 @@ import {
14
14
  type LayoutCoordinate,
15
15
  } from '@dxos/app-framework';
16
16
  import { type Node, useGraph } from '@dxos/plugin-graph';
17
- import { Icon, Popover, toLocalizedString, useMediaQuery, useTranslation, IconButton } from '@dxos/react-ui';
17
+ import { Icon, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
18
18
  import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
19
19
  import { TextTooltip } from '@dxos/react-ui-text-tooltip';
20
20
 
21
21
  import { PlankControls } from './PlankControls';
22
+ import { ToggleComplementarySidebarButton, ToggleSidebarButton } from './SidebarButton';
22
23
  import { DECK_PLUGIN } from '../../meta';
23
- import { useLayout } from '../LayoutContext';
24
+ import { useBreakpoints } from '../../util';
25
+ import { soloInlinePadding } from '../fragments';
24
26
 
25
27
  export type NodePlankHeadingProps = {
26
28
  coordinate: LayoutCoordinate;
@@ -42,7 +44,6 @@ export const NodePlankHeading = memo(
42
44
  pending,
43
45
  actions = [],
44
46
  }: NodePlankHeadingProps) => {
45
- const layoutContext = useLayout();
46
47
  const { t } = useTranslation(DECK_PLUGIN);
47
48
  const { graph } = useGraph();
48
49
  const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
@@ -51,7 +52,7 @@ export const NodePlankHeading = memo(
51
52
  : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
52
53
  const { dispatchPromise: dispatch } = useIntentDispatcher();
53
54
  const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
54
- const [isNotMobile] = useMediaQuery('md');
55
+ const breakpoint = useBreakpoints();
55
56
 
56
57
  useEffect(() => {
57
58
  const frame = requestAnimationFrame(() => {
@@ -67,15 +68,20 @@ export const NodePlankHeading = memo(
67
68
  const attendableId = coordinate.entryId.split(SLUG_PATH_SEPARATOR).at(0);
68
69
  const capabilities = useMemo(
69
70
  () => ({
70
- solo: (layoutPart === 'solo' || layoutPart === 'main') && isNotMobile,
71
+ solo: layoutPart === 'solo' || layoutPart === 'main',
71
72
  incrementStart: canIncrementStart,
72
73
  incrementEnd: canIncrementEnd,
73
74
  }),
74
- [isNotMobile, layoutPart, canIncrementStart, canIncrementEnd],
75
+ [breakpoint, layoutPart, canIncrementStart, canIncrementEnd],
75
76
  );
76
77
 
77
78
  return (
78
- <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
+ >
79
85
  <ActionRoot>
80
86
  {node ? (
81
87
  <StackItem.Sigil
@@ -97,6 +103,7 @@ export const NodePlankHeading = memo(
97
103
  </StackItem.SigilButton>
98
104
  )}
99
105
  </ActionRoot>
106
+ {breakpoint !== 'desktop' && <ToggleSidebarButton />}
100
107
  <TextTooltip text={label} onlyWhenTruncating>
101
108
  <StackItem.HeadingLabel
102
109
  attendableId={attendableId}
@@ -114,7 +121,6 @@ export const NodePlankHeading = memo(
114
121
  <PlankControls
115
122
  capabilities={capabilities}
116
123
  isSolo={layoutPart === 'solo'}
117
- classNames='mx-1'
118
124
  onClick={(eventType) => {
119
125
  if (!layoutPart) {
120
126
  return;
@@ -142,18 +148,7 @@ export const NodePlankHeading = memo(
142
148
  }}
143
149
  close={layoutPart === 'complementary' ? 'minify-end' : true}
144
150
  >
145
- {/* TODO(wittjosiah): This doesn't behave exactly the same as the rest of the button group. */}
146
- {layoutPart !== 'complementary' && (
147
- <IconButton
148
- iconOnly
149
- onClick={() => (layoutContext.complementarySidebarOpen = !layoutContext.complementarySidebarOpen)}
150
- variant='ghost'
151
- label={t('open complementary sidebar label')}
152
- classNames='!pli-2 !plb-3 [&>svg]:-scale-x-100'
153
- icon='ph--sidebar-simple--regular'
154
- size={4}
155
- />
156
- )}
151
+ <ToggleComplementarySidebarButton />
157
152
  </PlankControls>
158
153
  </StackItem.Heading>
159
154
  );
@@ -5,17 +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
- createIntent,
19
19
  } from '@dxos/app-framework';
20
20
  import { debounce } from '@dxos/async';
21
21
  import { useGraph } from '@dxos/plugin-graph';
@@ -4,22 +4,35 @@
4
4
 
5
5
  import React, { useMemo } from 'react';
6
6
 
7
- import { type LayoutParts, 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
- };
16
+ export const Sidebar = () => {
17
+ const layoutContext = useLayout();
18
+ const { popoverAnchorId } = layoutContext;
19
+ const breakpoint = useBreakpoints();
20
+ const hoistStatusbar = useHoistStatusbar(breakpoint);
15
21
 
16
- export const Sidebar = ({ layoutParts }: SidebarProps) => {
17
- const { popoverAnchorId } = useLayout();
18
22
  const navigationData = useMemo(() => ({ popoverAnchorId }), [popoverAnchorId]);
19
23
 
20
24
  return (
21
- <Main.NavigationSidebar>
22
- <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} />}
23
36
  </Main.NavigationSidebar>
24
37
  );
25
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
  )}
@@ -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)]';
package/src/events.ts ADDED
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Events } from '@dxos/app-framework';
6
+
7
+ import { DECK_PLUGIN } from './meta';
8
+
9
+ export namespace DeckEvents {
10
+ export const StateReady = Events.createStateEvent(`${DECK_PLUGIN}/state-ready`);
11
+ }
package/src/index.ts CHANGED
@@ -2,8 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { DeckPlugin } from './DeckPlugin';
6
-
7
- export default DeckPlugin;
8
-
5
+ export { DeckCapabilities } from './capabilities';
6
+ export { DeckEvents } from './events';
9
7
  export * from './DeckPlugin';
8
+ export * from './meta';
package/src/meta.ts CHANGED
@@ -4,9 +4,9 @@
4
4
 
5
5
  import { type PluginMeta } from '@dxos/app-framework';
6
6
 
7
- export const DECK_PLUGIN = 'dxos.org/plugin/deck';
7
+ export const DECK_PLUGIN = 'dxos.org/plugin/deck' as const;
8
8
 
9
- export default {
9
+ export const meta = {
10
10
  id: DECK_PLUGIN,
11
11
  name: 'Deck',
12
12
  } satisfies PluginMeta;
@@ -10,7 +10,9 @@ export default [
10
10
  [DECK_PLUGIN]: {
11
11
  'main header label': 'Main header',
12
12
  'open navigation sidebar label': 'Open navigation sidebar',
13
+ 'close navigation sidebar label': 'Minimize navigation sidebar',
13
14
  'open complementary sidebar label': 'Open sidebar',
15
+ 'close complementary sidebar label': 'Minimize sidebar',
14
16
  'plugin error message': 'Content failed to render.',
15
17
  'content fallback message': 'Unsupported',
16
18
  'content fallback description':
package/src/types.ts CHANGED
@@ -2,16 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import type {
6
- Plugin,
7
- GraphBuilderProvides,
8
- IntentResolverProvides,
9
- LayoutProvides,
10
- LocationProvides,
11
- SettingsProvides,
12
- SurfaceProvides,
13
- TranslationsProvides,
14
- } from '@dxos/app-framework';
15
5
  import { S } from '@dxos/echo-schema';
16
6
  import { type Label } from '@dxos/react-ui';
17
7
 
@@ -27,33 +17,18 @@ export type Overscroll = (typeof OverscrollOptions)[number];
27
17
  // TODO(wittjosiah): Include a predicate for whether the panel is visible for the current subject.
28
18
  export type Panel = { id: string; label: Label; icon: string };
29
19
 
30
- // TODO(wittjosiah): Is this generic enough to be in the app framework?
31
- export type PanelProvides = {
32
- complementary: {
33
- panels: Panel[];
34
- };
35
- };
36
-
37
- export const parsePanelPlugin = (plugin?: Plugin) =>
38
- Array.isArray((plugin?.provides as any).complementary?.panels) ? (plugin as Plugin<PanelProvides>) : undefined;
39
-
40
- export type DeckSettingsProps = {
41
- showHints: boolean;
42
- customSlots: boolean;
43
- flatDeck: boolean;
44
- enableNativeRedirect: boolean;
45
- disableDeck: boolean;
46
- newPlankPositioning: NewPlankPositioning;
47
- overscroll: Overscroll;
48
- };
49
-
50
- export type DeckPluginProvides = SurfaceProvides &
51
- IntentResolverProvides &
52
- GraphBuilderProvides &
53
- TranslationsProvides &
54
- SettingsProvides<DeckSettingsProps> &
55
- LayoutProvides &
56
- LocationProvides;
20
+ export const DeckSettingsSchema = S.mutable(
21
+ S.Struct({
22
+ showHints: S.Boolean,
23
+ customSlots: S.Boolean,
24
+ flatDeck: S.Boolean,
25
+ enableNativeRedirect: S.Boolean,
26
+ newPlankPositioning: S.Literal(...NewPlankPositions),
27
+ overscroll: S.Literal(...OverscrollOptions),
28
+ }),
29
+ );
30
+
31
+ export type DeckSettingsProps = S.Schema.Type<typeof DeckSettingsSchema>;
57
32
 
58
33
  export const DECK_ACTION = `${DECK_PLUGIN}/action`;
59
34
 
package/src/util/index.ts CHANGED
@@ -2,6 +2,6 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './check-app-scheme';
6
5
  export * from './layout-parts';
7
6
  export * from './overscroll';
7
+ export * from './useBreakpoints';
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useMediaQuery } from '@dxos/react-ui';
6
+
7
+ export const useBreakpoints = () => {
8
+ const [isNotMobile] = useMediaQuery('md');
9
+ const [isDesktop] = useMediaQuery('lg');
10
+ return isDesktop ? 'desktop' : isNotMobile ? 'tablet' : 'mobile';
11
+ };
@@ -0,0 +1,15 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useLayoutEffect, useState } from 'react';
6
+
7
+ export const useHoistStatusbar = (breakpoint: string) => {
8
+ const [safeAreaBottom, setSafeAreaBottom] = useState(Infinity);
9
+ useLayoutEffect(
10
+ () =>
11
+ setSafeAreaBottom(parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-bottom'))),
12
+ [],
13
+ );
14
+ return Number.isFinite(safeAreaBottom) && safeAreaBottom < 1 && breakpoint === 'desktop';
15
+ };
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/meta.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type PluginMeta } from '@dxos/app-framework';\n\nexport const DECK_PLUGIN = 'dxos.org/plugin/deck';\n\nexport default {\n id: DECK_PLUGIN,\n name: 'Deck',\n} satisfies PluginMeta;\n"],
5
- "mappings": ";AAMO,IAAMA,cAAc;AAE3B,IAAA,eAAe;EACbC,IAAID;EACJE,MAAM;AACR;",
6
- "names": ["DECK_PLUGIN", "id", "name"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/types.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport type {\n Plugin,\n GraphBuilderProvides,\n IntentResolverProvides,\n LayoutProvides,\n LocationProvides,\n SettingsProvides,\n SurfaceProvides,\n TranslationsProvides,\n} from '@dxos/app-framework';\nimport { S } from '@dxos/echo-schema';\nimport { type Label } from '@dxos/react-ui';\n\nimport { DECK_PLUGIN } from './meta';\n\n// TODO(Zan): In the future we should consider adding new planks adjacent to the attended plank.\nexport const NewPlankPositions = ['start', 'end'] as const;\nexport type NewPlankPositioning = (typeof NewPlankPositions)[number];\n\nexport const OverscrollOptions = ['none', 'centering'] as const;\nexport type Overscroll = (typeof OverscrollOptions)[number];\n\n// TODO(wittjosiah): Include a predicate for whether the panel is visible for the current subject.\nexport type Panel = { id: string; label: Label; icon: string };\n\n// TODO(wittjosiah): Is this generic enough to be in the app framework?\nexport type PanelProvides = {\n complementary: {\n panels: Panel[];\n };\n};\n\nexport const parsePanelPlugin = (plugin?: Plugin) =>\n Array.isArray((plugin?.provides as any).complementary?.panels) ? (plugin as Plugin<PanelProvides>) : undefined;\n\nexport type DeckSettingsProps = {\n showHints: boolean;\n customSlots: boolean;\n flatDeck: boolean;\n enableNativeRedirect: boolean;\n disableDeck: boolean;\n newPlankPositioning: NewPlankPositioning;\n overscroll: Overscroll;\n};\n\nexport type DeckPluginProvides = SurfaceProvides &\n IntentResolverProvides &\n GraphBuilderProvides &\n TranslationsProvides &\n SettingsProvides<DeckSettingsProps> &\n LayoutProvides &\n LocationProvides;\n\nexport const DECK_ACTION = `${DECK_PLUGIN}/action`;\n\nexport namespace DeckAction {\n export class UpdatePlankSize extends S.TaggedClass<UpdatePlankSize>()(`${DECK_ACTION}/update-plank-size`, {\n input: S.Struct({\n id: S.String,\n size: S.Number,\n }),\n output: S.Void,\n }) {}\n}\n"],
5
- "mappings": ";;;;;AAcA,SAASA,SAAS;AAMX,IAAMC,oBAAoB;EAAC;EAAS;;AAGpC,IAAMC,oBAAoB;EAAC;EAAQ;;AAanC,IAAMC,mBAAmB,CAACC,WAC/BC,MAAMC,QAASF,QAAQG,SAAiBC,eAAeC,MAAAA,IAAWL,SAAmCM;AAoBhG,IAAMC,cAAc,GAAGC,WAAAA;;UAEbC,aAAAA;EACR,MAAMC,wBAAwBC,EAAEC,YAAW,EAAoB,GAAGL,WAAAA,sBAAiC;IACxGM,OAAOF,EAAEG,OAAO;MACdC,IAAIJ,EAAEK;MACNC,MAAMN,EAAEO;IACV,CAAA;IACAC,QAAQR,EAAES;EACZ,CAAA,EAAA;EAAI;cANSV,kBAAAA;AAOf,GARiBD,eAAAA,aAAAA,CAAAA,EAAAA;",
6
- "names": ["S", "NewPlankPositions", "OverscrollOptions", "parsePanelPlugin", "plugin", "Array", "isArray", "provides", "complementary", "panels", "undefined", "DECK_ACTION", "DECK_PLUGIN", "DeckAction", "UpdatePlankSize", "S", "TaggedClass", "input", "Struct", "id", "String", "size", "Number", "output", "Void"]
7
- }
@@ -1,9 +0,0 @@
1
- import {
2
- DECK_PLUGIN,
3
- meta_default
4
- } from "./chunk-GVOGPULO.mjs";
5
- export {
6
- DECK_PLUGIN,
7
- meta_default as default
8
- };
9
- //# sourceMappingURL=meta.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": [],
4
- "sourcesContent": [],
5
- "mappings": "",
6
- "names": []
7
- }
@@ -1,2 +0,0 @@
1
- export declare const checkAppScheme: (url: string) => void;
2
- //# sourceMappingURL=check-app-scheme.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"check-app-scheme.d.ts","sourceRoot":"","sources":["../../../../src/util/check-app-scheme.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,cAAc,QAAS,MAAM,SAezC,CAAC"}