@dxos/plugin-deck 0.7.4 → 0.7.5-labs.071a3e2

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 (173) hide show
  1. package/dist/lib/browser/app-graph-builder-CI6ZFMNL.mjs +147 -0
  2. package/dist/lib/browser/app-graph-builder-CI6ZFMNL.mjs.map +7 -0
  3. package/dist/lib/browser/check-app-scheme-S3EYUPMF.mjs +33 -0
  4. package/dist/lib/browser/check-app-scheme-S3EYUPMF.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-BTDY6SES.mjs +1119 -0
  6. package/dist/lib/browser/chunk-BTDY6SES.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-FZOBKOA2.mjs +24 -0
  8. package/dist/lib/browser/chunk-FZOBKOA2.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-M2L53AIH.mjs +126 -0
  10. package/dist/lib/browser/chunk-M2L53AIH.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-GVOGPULO.mjs → chunk-N7TEPFVR.mjs} +5 -4
  12. package/dist/lib/browser/chunk-N7TEPFVR.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-YQ2GWTDU.mjs +17 -0
  14. package/dist/lib/browser/chunk-YQ2GWTDU.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +100 -1807
  16. package/dist/lib/browser/index.mjs.map +4 -4
  17. package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs +494 -0
  18. package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/react-root-HIHLRMCW.mjs +46 -0
  21. package/dist/lib/browser/react-root-HIHLRMCW.mjs.map +7 -0
  22. package/dist/lib/browser/react-surface-4QVWKQYY.mjs +38 -0
  23. package/dist/lib/browser/react-surface-4QVWKQYY.mjs.map +7 -0
  24. package/dist/lib/browser/settings-WACNLCPB.mjs +28 -0
  25. package/dist/lib/browser/settings-WACNLCPB.mjs.map +7 -0
  26. package/dist/lib/browser/state-VPOYUKK6.mjs +117 -0
  27. package/dist/lib/browser/state-VPOYUKK6.mjs.map +7 -0
  28. package/dist/lib/browser/tools-5LDJNU56.mjs +51 -0
  29. package/dist/lib/browser/tools-5LDJNU56.mjs.map +7 -0
  30. package/dist/lib/browser/types.mjs +20 -3
  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 +1 -14
  34. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  35. package/dist/types/src/capabilities/app-graph-builder.d.ts +181 -0
  36. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  37. package/dist/types/src/capabilities/capabilities.d.ts +142 -0
  38. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  39. package/dist/types/src/capabilities/check-app-scheme.d.ts +4 -0
  40. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -0
  41. package/dist/types/src/capabilities/index.d.ts +190 -0
  42. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  43. package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
  44. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  45. package/dist/types/src/capabilities/react-root.d.ts +7 -0
  46. package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/react-surface.d.ts +4 -0
  48. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  49. package/dist/types/src/capabilities/set-active.d.ts +9 -0
  50. package/dist/types/src/capabilities/set-active.d.ts.map +1 -0
  51. package/dist/types/src/capabilities/settings.d.ts +4 -0
  52. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  53. package/dist/types/src/capabilities/state.d.ts +76 -0
  54. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  55. package/dist/types/src/capabilities/tools.d.ts +10 -0
  56. package/dist/types/src/capabilities/tools.d.ts.map +1 -0
  57. package/dist/types/src/capabilities/url-handler.d.ts +4 -0
  58. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -0
  59. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  60. package/dist/types/src/components/DeckLayout/Banner.d.ts +6 -0
  61. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -0
  62. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  63. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  64. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +1 -4
  65. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  66. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
  67. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +3 -3
  68. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  69. package/dist/types/src/components/DeckLayout/Plank.d.ts +8 -6
  70. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  71. package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
  72. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
  73. package/dist/types/src/components/DeckLayout/PlankError.d.ts +4 -3
  74. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  75. package/dist/types/src/components/DeckLayout/Sidebar.d.ts +1 -5
  76. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  77. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +8 -0
  78. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -0
  79. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  80. package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -2
  81. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  82. package/dist/types/src/components/DeckLayout/Topbar.d.ts +3 -0
  83. package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +1 -0
  84. package/dist/types/src/components/fragments.d.ts +4 -0
  85. package/dist/types/src/components/fragments.d.ts.map +1 -0
  86. package/dist/types/src/components/index.d.ts +0 -2
  87. package/dist/types/src/components/index.d.ts.map +1 -1
  88. package/dist/types/src/events.d.ts +4 -0
  89. package/dist/types/src/events.d.ts.map +1 -0
  90. package/dist/types/src/hooks/useMainSize.d.ts +2 -2
  91. package/dist/types/src/index.d.ts +3 -2
  92. package/dist/types/src/index.d.ts.map +1 -1
  93. package/dist/types/src/layout.d.ts +5 -19
  94. package/dist/types/src/layout.d.ts.map +1 -1
  95. package/dist/types/src/meta.d.ts +4 -4
  96. package/dist/types/src/meta.d.ts.map +1 -1
  97. package/dist/types/src/translations.d.ts +7 -2
  98. package/dist/types/src/translations.d.ts.map +1 -1
  99. package/dist/types/src/types.d.ts +130 -17
  100. package/dist/types/src/types.d.ts.map +1 -1
  101. package/dist/types/src/util/index.d.ts +3 -2
  102. package/dist/types/src/util/index.d.ts.map +1 -1
  103. package/dist/types/src/util/layoutAppliesTopbar.d.ts +2 -0
  104. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -0
  105. package/dist/types/src/util/useBreakpoints.d.ts +2 -0
  106. package/dist/types/src/util/useBreakpoints.d.ts.map +1 -0
  107. package/dist/types/src/util/useHoistStatusbar.d.ts +2 -0
  108. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -0
  109. package/dist/types/tsconfig.tsbuildinfo +1 -0
  110. package/package.json +32 -36
  111. package/src/DeckPlugin.ts +83 -0
  112. package/src/capabilities/app-graph-builder.ts +109 -0
  113. package/src/capabilities/capabilities.ts +18 -0
  114. package/src/capabilities/check-app-scheme.ts +44 -0
  115. package/src/capabilities/index.ts +17 -0
  116. package/src/capabilities/intent-resolver.ts +350 -0
  117. package/src/capabilities/react-root.tsx +48 -0
  118. package/src/capabilities/react-surface.tsx +31 -0
  119. package/src/capabilities/set-active.ts +43 -0
  120. package/src/capabilities/settings.ts +21 -0
  121. package/src/capabilities/state.ts +102 -0
  122. package/src/capabilities/tools.ts +61 -0
  123. package/src/capabilities/url-handler.ts +63 -0
  124. package/src/components/DeckLayout/ActiveNode.tsx +3 -4
  125. package/src/components/DeckLayout/Banner.tsx +37 -0
  126. package/src/components/DeckLayout/ComplementarySidebar.tsx +130 -56
  127. package/src/components/DeckLayout/ContentEmpty.tsx +9 -4
  128. package/src/components/DeckLayout/DeckLayout.tsx +116 -83
  129. package/src/components/DeckLayout/Fullscreen.tsx +3 -4
  130. package/src/components/DeckLayout/NodePlankHeading.tsx +66 -93
  131. package/src/components/DeckLayout/Plank.tsx +36 -43
  132. package/src/components/DeckLayout/PlankControls.tsx +12 -13
  133. package/src/components/DeckLayout/PlankError.tsx +6 -5
  134. package/src/components/DeckLayout/Sidebar.tsx +19 -26
  135. package/src/components/DeckLayout/SidebarButton.tsx +68 -0
  136. package/src/components/DeckLayout/StatusBar.tsx +6 -12
  137. package/src/components/DeckLayout/Toast.tsx +21 -8
  138. package/src/components/DeckLayout/Topbar.tsx +11 -0
  139. package/src/components/LayoutSettings.tsx +8 -8
  140. package/src/components/fragments.ts +14 -0
  141. package/src/components/index.ts +0 -2
  142. package/src/events.ts +11 -0
  143. package/src/hooks/useMainSize.ts +3 -3
  144. package/src/index.ts +3 -4
  145. package/src/layout.ts +43 -212
  146. package/src/meta.ts +3 -2
  147. package/src/translations.ts +11 -6
  148. package/src/types.ts +110 -34
  149. package/src/util/index.ts +3 -2
  150. package/src/util/layoutAppliesTopbar.ts +7 -0
  151. package/src/util/useBreakpoints.ts +11 -0
  152. package/src/util/useHoistStatusbar.ts +24 -0
  153. package/dist/lib/browser/chunk-GVOGPULO.mjs.map +0 -7
  154. package/dist/lib/browser/chunk-NIRHDTX4.mjs +0 -17
  155. package/dist/lib/browser/chunk-NIRHDTX4.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/components/DeckContext.d.ts +0 -8
  159. package/dist/types/src/components/DeckContext.d.ts.map +0 -1
  160. package/dist/types/src/components/LayoutContext.d.ts +0 -5
  161. package/dist/types/src/components/LayoutContext.d.ts.map +0 -1
  162. package/dist/types/src/layout.test.d.ts +0 -2
  163. package/dist/types/src/layout.test.d.ts.map +0 -1
  164. package/dist/types/src/util/check-app-scheme.d.ts +0 -2
  165. package/dist/types/src/util/check-app-scheme.d.ts.map +0 -1
  166. package/dist/types/src/util/layout-parts.d.ts +0 -7
  167. package/dist/types/src/util/layout-parts.d.ts.map +0 -1
  168. package/src/DeckPlugin.tsx +0 -657
  169. package/src/components/DeckContext.ts +0 -14
  170. package/src/components/LayoutContext.ts +0 -12
  171. package/src/layout.test.ts +0 -380
  172. package/src/util/check-app-scheme.ts +0 -21
  173. package/src/util/layout-parts.ts +0 -12
@@ -5,65 +5,57 @@
5
5
  import React, { type KeyboardEvent, memo, useCallback, useLayoutEffect, useMemo, useRef } from 'react';
6
6
 
7
7
  import {
8
- type LayoutCoordinate,
9
- type LayoutEntry,
10
- type LayoutPart,
11
- type LayoutParts,
8
+ createIntent,
9
+ LayoutAction,
12
10
  Surface,
11
+ useCapability,
12
+ useAppGraph,
13
13
  useIntentDispatcher,
14
- type Layout,
15
- indexInPart,
16
- partLength,
17
- LayoutAction,
18
14
  } from '@dxos/app-framework';
19
15
  import { debounce } from '@dxos/async';
20
- import { useGraph } from '@dxos/plugin-graph';
21
16
  import { useAttendableAttributes } from '@dxos/react-ui-attention';
22
17
  import { StackItem, railGridHorizontal } from '@dxos/react-ui-stack';
23
18
  import { mainIntrinsicSize, mx } from '@dxos/react-ui-theme';
24
19
 
25
- import { NodePlankHeading } from './NodePlankHeading';
20
+ import { NodePlankHeading, type NodePlankHeadingProps } from './NodePlankHeading';
26
21
  import { PlankContentError, PlankError } from './PlankError';
27
22
  import { PlankLoading } from './PlankLoading';
28
- import { DeckAction } from '../../DeckPlugin';
23
+ import { DeckCapabilities } from '../../capabilities';
29
24
  import { useNode, useMainSize } from '../../hooks';
30
- import { useDeckContext } from '../DeckContext';
31
- import { useLayout } from '../LayoutContext';
25
+ import { DeckAction, type LayoutMode } from '../../types';
32
26
 
33
27
  const UNKNOWN_ID = 'unknown_id';
34
28
 
35
29
  export type PlankProps = {
36
- entry?: LayoutEntry;
37
- layoutParts: LayoutParts;
38
- // TODO(wittjosiah): Remove. Pass in LayoutCoordinate instead of LayoutEntry.
39
- part: LayoutPart;
40
- layoutMode: Layout['layoutMode'];
30
+ id?: string;
31
+ part: NodePlankHeadingProps['part'];
32
+ path?: string[];
41
33
  order?: number;
34
+ active?: string[];
35
+ layoutMode: LayoutMode;
42
36
  };
43
37
 
44
- export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: PlankProps) => {
45
- const dispatch = useIntentDispatcher();
46
- const coordinate: LayoutCoordinate = useMemo(() => ({ part, entryId: entry?.id ?? UNKNOWN_ID }), [entry?.id, part]);
47
- const { popoverAnchorId, scrollIntoView } = useLayout();
48
- const { plankSizing } = useDeckContext();
49
- const { graph } = useGraph();
50
- const node = useNode(graph, entry?.id);
38
+ export const Plank = memo(({ id = UNKNOWN_ID, part, path, order, active, layoutMode }: PlankProps) => {
39
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
40
+ const { deck, popoverAnchorId, scrollIntoView } = useCapability(DeckCapabilities.DeckState);
41
+ const { graph } = useAppGraph();
42
+ const node = useNode(graph, id);
51
43
  const rootElement = useRef<HTMLDivElement | null>(null);
52
44
  const canResize = layoutMode === 'deck';
53
45
  const Root = part === 'solo' ? 'article' : StackItem.Root;
54
46
 
55
- const attendableAttrs = useAttendableAttributes(coordinate.entryId);
56
- const index = indexInPart(layoutParts, coordinate);
57
- const length = partLength(layoutParts, part);
58
- const canIncrementStart = part === 'main' && index !== undefined && index > 0 && length !== undefined && length > 1;
59
- const canIncrementEnd = part === 'main' && index !== undefined && index < length - 1 && length !== undefined;
47
+ const attendableAttrs = useAttendableAttributes(id);
48
+ const index = active ? active.findIndex((entryId) => entryId === id) : 0;
49
+ const length = active?.length ?? 1;
50
+ const canIncrementStart = active && index !== undefined && index > 0 && length !== undefined && length > 1;
51
+ const canIncrementEnd = active && index !== undefined && index < length - 1 && length !== undefined;
60
52
 
61
- const size = plankSizing?.[coordinate.entryId] as number | undefined;
53
+ const size = deck.plankSizing[id] as number | undefined;
62
54
  const setSize = useCallback(
63
55
  debounce((nextSize: number) => {
64
- return dispatch({ action: DeckAction.UPDATE_PLANK_SIZE, data: { id: coordinate.entryId, size: nextSize } });
56
+ return dispatch(createIntent(DeckAction.UpdatePlankSize, { id, size: nextSize }));
65
57
  }, 200),
66
- [dispatch, coordinate.entryId],
58
+ [dispatch, id],
67
59
  );
68
60
 
69
61
  // TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
@@ -74,30 +66,30 @@ export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: Plan
74
66
  }, []);
75
67
 
76
68
  useLayoutEffect(() => {
77
- if (scrollIntoView === coordinate.entryId) {
69
+ if (scrollIntoView === id) {
78
70
  // TODO(wittjosiah): When focused on page load, the focus is always visible.
79
71
  // Forcing focus to something smaller than the plank prevents large focus ring in the interim.
80
72
  const focusable = rootElement.current?.querySelector('button') || rootElement.current;
81
73
  focusable?.focus({ preventScroll: true });
82
74
  layoutMode === 'deck' && focusable?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
83
75
  // Clear the scroll into view state once it has been actioned.
84
- void dispatch({ action: LayoutAction.SCROLL_INTO_VIEW, data: { id: undefined } });
76
+ void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: undefined }));
85
77
  }
86
- }, [coordinate.entryId, scrollIntoView, layoutMode]);
78
+ }, [id, scrollIntoView, layoutMode]);
87
79
 
88
80
  const isSolo = layoutMode === 'solo' && part === 'solo';
89
- const isAttendable = isSolo || (layoutMode === 'deck' && part === 'main');
81
+ const isAttendable = isSolo || (layoutMode === 'deck' && part === 'deck');
90
82
 
91
83
  const sizeAttrs = useMainSize();
92
84
 
93
85
  const data = useMemo(
94
86
  () =>
95
87
  node && {
96
- ...(entry?.path ? { subject: node.data, path: entry.path } : { object: node.data }),
97
- coordinate,
88
+ subject: node.data,
89
+ path,
98
90
  popoverAnchorId,
99
91
  },
100
- [node, node?.data, entry?.path, coordinate, popoverAnchorId],
92
+ [node, node?.data, path, popoverAnchorId],
101
93
  );
102
94
 
103
95
  // TODO(wittjosiah): Change prop to accept a component.
@@ -118,7 +110,7 @@ export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: Plan
118
110
  {...(part === 'solo'
119
111
  ? ({ ...sizeAttrs, className } as any)
120
112
  : {
121
- item: { id: entry?.id ?? 'never' },
113
+ item: { id },
122
114
  size,
123
115
  onSizeChange: setSize,
124
116
  classNames: className,
@@ -131,7 +123,8 @@ export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: Plan
131
123
  {node ? (
132
124
  <>
133
125
  <NodePlankHeading
134
- coordinate={coordinate}
126
+ id={id}
127
+ part={part}
135
128
  node={node}
136
129
  canIncrementStart={canIncrementStart}
137
130
  canIncrementEnd={canIncrementEnd}
@@ -147,7 +140,7 @@ export const Plank = memo(({ entry, layoutParts, part, layoutMode, order }: Plan
147
140
  />
148
141
  </>
149
142
  ) : (
150
- <PlankError layoutCoordinate={coordinate} />
143
+ <PlankError id={id} part={part} />
151
144
  )}
152
145
  {canResize && <StackItem.ResizeHandle />}
153
146
  </Root>
@@ -15,9 +15,9 @@ import {
15
15
  } from '@dxos/react-ui';
16
16
 
17
17
  import { DECK_PLUGIN } from '../../meta';
18
+ import { type DeckAction } from '../../types';
18
19
 
19
- export type PlankControlEvent = 'solo' | 'close' | `${'pin' | 'increment'}-${'start' | 'end'}`;
20
- export type PlankControlHandler = (event: PlankControlEvent) => void;
20
+ export type PlankControlHandler = (event: DeckAction.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
  );
@@ -54,17 +52,18 @@ const PlankControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> &
54
52
 
55
53
  // TODO(wittjosiah): Duplicate of stack LayoutControls?
56
54
  // Translations were to be duplicated between packages.
55
+ // NOTE(thure): Pinning & unpinning are disabled indefinitely.
57
56
  export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
58
57
  (
59
- { onClick, variant = 'default', capabilities: can, isSolo, pin, close = false, children, ...props },
58
+ { onClick, variant = 'default', capabilities: can, isSolo, pin, close = false, children, classNames, ...props },
60
59
  forwardedRef,
61
60
  ) => {
62
61
  const { t } = useTranslation(DECK_PLUGIN);
63
- const buttonClassNames = variant === 'hide-disabled' ? 'disabled:hidden !pli-2 !plb-3' : '!pli-2 !plb-3';
62
+ const buttonClassNames = variant === 'hide-disabled' ? 'disabled:hidden pli-2 plb-3' : 'pli-2 plb-3';
64
63
 
65
64
  return (
66
- <ButtonGroup {...props} ref={forwardedRef}>
67
- {pin && !isSolo && ['both', 'start'].includes(pin) && (
65
+ <ButtonGroup {...props} classNames={['app-no-drag', classNames]} ref={forwardedRef}>
66
+ {/* {pin && !isSolo && ['both', 'start'].includes(pin) && (
68
67
  <PlankControl
69
68
  label={t('pin start label')}
70
69
  variant='ghost'
@@ -72,14 +71,14 @@ export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
72
71
  onClick={() => onClick?.('pin-start')}
73
72
  icon='ph--caret-line-left--regular'
74
73
  />
75
- )}
74
+ )} */}
76
75
 
77
76
  {can.solo && (
78
77
  <PlankControl
79
78
  label={isSolo ? t('show deck plank label') : t('show solo plank label')}
80
79
  classNames={buttonClassNames}
81
80
  onClick={() => onClick?.('solo')}
82
- icon={isSolo ? 'ph--arrow-u-down-left--regular' : 'ph--arrows-out--regular'}
81
+ icon={isSolo ? 'ph--corners-in--regular' : 'ph--corners-out--regular'}
83
82
  />
84
83
  )}
85
84
 
@@ -102,14 +101,14 @@ export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
102
101
  </>
103
102
  )}
104
103
 
105
- {pin && !isSolo && ['both', 'end'].includes(pin) && (
104
+ {/* {pin && !isSolo && ['both', 'end'].includes(pin) && (
106
105
  <PlankControl
107
106
  label={t('pin end label')}
108
107
  classNames={buttonClassNames}
109
108
  onClick={() => onClick?.('pin-end')}
110
109
  icon='ph--caret-line-right--regular'
111
110
  />
112
- )}
111
+ )} */}
113
112
 
114
113
  {close && !isSolo && (
115
114
  <PlankControl
@@ -4,12 +4,11 @@
4
4
 
5
5
  import React, { useEffect, useState } from 'react';
6
6
 
7
- import { type LayoutCoordinate } from '@dxos/app-framework';
8
7
  import { type Node } from '@dxos/plugin-graph';
9
8
  import { useTranslation } from '@dxos/react-ui';
10
9
  import { descriptionText, mx } from '@dxos/react-ui-theme';
11
10
 
12
- import { NodePlankHeading } from './NodePlankHeading';
11
+ import { NodePlankHeading, type NodePlankHeadingProps } from './NodePlankHeading';
13
12
  import { PlankLoading } from './PlankLoading';
14
13
  import { DECK_PLUGIN } from '../../meta';
15
14
 
@@ -33,11 +32,13 @@ export const PlankContentError = ({ error }: { error?: Error }) => {
33
32
  };
34
33
 
35
34
  export const PlankError = ({
36
- layoutCoordinate,
35
+ id,
36
+ part,
37
37
  node,
38
38
  error,
39
39
  }: {
40
- layoutCoordinate: LayoutCoordinate;
40
+ id: string;
41
+ part: NodePlankHeadingProps['part'];
41
42
  node?: Node;
42
43
  error?: Error;
43
44
  }) => {
@@ -47,7 +48,7 @@ export const PlankError = ({
47
48
  }, []);
48
49
  return (
49
50
  <>
50
- <NodePlankHeading coordinate={layoutCoordinate} node={node} pending={!timedOut} />
51
+ <NodePlankHeading id={id} part={part} node={node} pending={!timedOut} />
51
52
  {timedOut ? <PlankContentError error={error} /> : <PlankLoading />}
52
53
  </>
53
54
  );
@@ -4,39 +4,32 @@
4
4
 
5
5
  import React, { useMemo } from 'react';
6
6
 
7
- import { type LayoutParts, openIds, Surface } from '@dxos/app-framework';
7
+ import { Surface, useCapability } from '@dxos/app-framework';
8
8
  import { Main } from '@dxos/react-ui';
9
9
 
10
- import { useLayout } from '../LayoutContext';
10
+ import { DeckCapabilities } from '../../capabilities';
11
+ import { layoutAppliesTopbar, useBreakpoints, useHoistStatusbar } from '../../util';
11
12
 
12
- export type SidebarProps = {
13
- layoutParts: LayoutParts;
14
- };
15
-
16
- export const Sidebar = ({ layoutParts }: SidebarProps) => {
17
- const { layoutMode, popoverAnchorId } = useLayout();
18
-
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
- }
26
-
27
- return Array.from(new Set<string>(openIds(layoutParts)));
28
- }, [layoutParts, layoutMode]);
13
+ export const Sidebar = () => {
14
+ const { popoverAnchorId, activeDeck: current } = useCapability(DeckCapabilities.DeckState);
15
+ const breakpoint = useBreakpoints();
16
+ const topbar = layoutAppliesTopbar(breakpoint);
17
+ const hoistStatusbar = useHoistStatusbar(breakpoint);
29
18
 
30
19
  const navigationData = useMemo(
31
- () => ({
32
- popoverAnchorId,
33
- activeIds,
34
- }),
35
- [popoverAnchorId, activeIds],
20
+ () => ({ popoverAnchorId, topbar, hoistStatusbar, current }),
21
+ [popoverAnchorId, topbar, hoistStatusbar, current],
36
22
  );
23
+
37
24
  return (
38
- <Main.NavigationSidebar>
39
- <Surface role='navigation' data={{ ...navigationData }} limit={1} />
25
+ <Main.NavigationSidebar
26
+ classNames={[
27
+ 'grid',
28
+ topbar && 'block-start-[calc(env(safe-area-inset-top)+var(--rail-size))]',
29
+ hoistStatusbar && 'block-end-[--statusbar-size]',
30
+ ]}
31
+ >
32
+ <Surface role='navigation' data={navigationData} limit={1} />
40
33
  </Main.NavigationSidebar>
41
34
  );
42
35
  };
@@ -0,0 +1,68 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { useCapability } from '@dxos/app-framework';
8
+ import { IconButton, type IconButtonProps, type ThemedClassName, useTranslation } from '@dxos/react-ui';
9
+
10
+ import { DeckCapabilities } from '../../capabilities';
11
+ import { DECK_PLUGIN } from '../../meta';
12
+
13
+ export const ToggleSidebarButton = ({
14
+ classNames,
15
+ variant = 'ghost',
16
+ }: ThemedClassName<Pick<IconButtonProps, 'variant'>>) => {
17
+ const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
18
+ const { t } = useTranslation(DECK_PLUGIN);
19
+ return (
20
+ <IconButton
21
+ variant={variant}
22
+ iconOnly
23
+ icon='ph--sidebar--regular'
24
+ size={4}
25
+ label={t('open navigation sidebar label')}
26
+ onClick={() =>
27
+ (layoutContext.sidebarState = layoutContext.sidebarState === 'expanded' ? 'collapsed' : 'expanded')
28
+ }
29
+ classNames={classNames}
30
+ />
31
+ );
32
+ };
33
+
34
+ export const CloseSidebarButton = () => {
35
+ const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
36
+ const { t } = useTranslation(DECK_PLUGIN);
37
+ return (
38
+ <IconButton
39
+ variant='ghost'
40
+ iconOnly
41
+ icon='ph--caret-line-left--regular'
42
+ size={4}
43
+ label={t('close navigation sidebar label')}
44
+ onClick={() => (layoutContext.sidebarState = 'collapsed')}
45
+ classNames='rounded-none pli-1 dx-focus-ring-inset pie-[max(.5rem,env(safe-area-inset-left))]'
46
+ />
47
+ );
48
+ };
49
+
50
+ export const ToggleComplementarySidebarButton = ({ inR0, classNames }: ThemedClassName<{ inR0?: boolean }>) => {
51
+ const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
52
+ const { t } = useTranslation(DECK_PLUGIN);
53
+ return (
54
+ <IconButton
55
+ iconOnly
56
+ onClick={() =>
57
+ (layoutContext.complementarySidebarState =
58
+ layoutContext.complementarySidebarState === 'expanded' ? 'collapsed' : 'expanded')
59
+ }
60
+ variant='ghost'
61
+ label={t('open complementary sidebar label')}
62
+ classNames={['[&>svg]:-scale-x-100', classNames]}
63
+ icon='ph--sidebar-simple--regular'
64
+ size={inR0 ? 5 : 4}
65
+ tooltipSide={inR0 ? 'left' : undefined}
66
+ />
67
+ );
68
+ };
@@ -5,23 +5,17 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Surface } from '@dxos/app-framework';
8
- import { mainPadding, mainPaddingTransitions, mx } from '@dxos/react-ui-theme';
9
-
10
- import { useMainSize } from '../../hooks';
8
+ import { useLandmarkMover } from '@dxos/react-ui';
11
9
 
12
10
  export const StatusBar = ({ showHints }: { showHints?: boolean }) => {
13
- const sizeAttrs = useMainSize();
11
+ const mover = useLandmarkMover(undefined, '3');
14
12
  return (
15
13
  <div
16
- role='none'
17
- {...sizeAttrs}
18
- className={mx(
19
- 'fixed flex justify-between block-end-0 inset-inline-0 items-center border-bs border-separator z-[2]',
20
- mainPadding,
21
- mainPaddingTransitions,
22
- )}
14
+ role='contentinfo'
15
+ className='fixed block-end-0 inset-inline-0 bs-[--statusbar-size] border-bs border-separator z-[2] flex text-description'
16
+ {...mover}
23
17
  >
24
- <div role='none'>{showHints && <Surface role='hints' limit={1} />}</div>
18
+ {showHints && <Surface role='hints' limit={1} />}
25
19
  <Surface role='status-bar' limit={1} />
26
20
  </div>
27
21
  );
@@ -4,8 +4,17 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import type { Toast as ToastProps } from '@dxos/app-framework';
8
- import { Button, Icon, Toast as NaturalToast, type ToastRootProps } from '@dxos/react-ui';
7
+ import { type LayoutAction } from '@dxos/app-framework';
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 = ({
@@ -19,27 +28,31 @@ export const Toast = ({
19
28
  closeLabel,
20
29
  onAction,
21
30
  onOpenChange,
22
- }: ToastProps & Pick<ToastRootProps, 'onOpenChange'>) => {
31
+ }: LayoutAction.Toast & 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
+ };
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Input, Select, useTranslation } from '@dxos/react-ui';
8
- import { DeprecatedFormInput } from '@dxos/react-ui-form';
8
+ import { DeprecatedFormContainer, DeprecatedFormInput } from '@dxos/react-ui-form';
9
9
 
10
10
  import { DECK_PLUGIN } from '../meta';
11
11
  import {
@@ -22,7 +22,7 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
22
22
  const { t } = useTranslation(DECK_PLUGIN);
23
23
 
24
24
  return (
25
- <>
25
+ <DeprecatedFormContainer>
26
26
  <DeprecatedFormInput label={t('select new plank positioning label')}>
27
27
  <Select.Root
28
28
  value={settings.newPlankPositioning ?? 'start'}
@@ -72,12 +72,12 @@ export const LayoutSettings = ({ settings }: { settings: DeckSettingsProps }) =>
72
72
  />
73
73
  </DeprecatedFormInput>
74
74
  )}
75
- <DeprecatedFormInput label={t('settings custom slots')}>
76
- <Input.Switch checked={settings.customSlots} onCheckedChange={(checked) => (settings.customSlots = checked)} />
75
+ <DeprecatedFormInput label={t('settings enable ide-style statusbar label')}>
76
+ <Input.Switch
77
+ checked={settings.enableIdeStyleStatusbar}
78
+ onCheckedChange={(checked) => (settings.enableIdeStyleStatusbar = checked)}
79
+ />
77
80
  </DeprecatedFormInput>
78
- <DeprecatedFormInput label={t('settings flat deck')}>
79
- <Input.Switch checked={settings.flatDeck} onCheckedChange={(checked) => (settings.flatDeck = checked)} />
80
- </DeprecatedFormInput>
81
- </>
81
+ </DeprecatedFormContainer>
82
82
  );
83
83
  };
@@ -0,0 +1,14 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { mx } from '@dxos/react-ui-theme';
6
+
7
+ export const soloInlinePadding =
8
+ 'pis-[calc(env(safe-area-inset-left)+.25rem)] pie-[calc(env(safe-area-inset-left)+.25rem)]';
9
+
10
+ const sidebarToggleStyles = 'bs-[--rail-item] is-[--rail-item] absolute block-end-2 z-[1] !bg-deck lg:hidden';
11
+
12
+ export const fixedSidebarToggleStyles = mx(sidebarToggleStyles, 'inline-start-2');
13
+
14
+ export const fixedComplementarySidebarToggleStyles = mx(sidebarToggleStyles, 'inline-end-2');
@@ -3,6 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './DeckLayout';
6
- export * from './DeckContext';
7
- export * from './LayoutContext';
8
6
  export * from './LayoutSettings';
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
+ }
@@ -5,9 +5,9 @@
5
5
  import { useMainContext } from '@dxos/react-ui';
6
6
 
7
7
  export const useMainSize = () => {
8
- const { navigationSidebarOpen, complementarySidebarOpen } = useMainContext('DeckPluginPlank');
8
+ const { navigationSidebarState, complementarySidebarState } = useMainContext('DeckPluginPlank');
9
9
  return {
10
- 'data-sidebar-inline-start-state': navigationSidebarOpen ? 'open' : 'closed',
11
- 'data-sidebar-inline-end-state': complementarySidebarOpen ? 'open' : 'closed',
10
+ 'data-sidebar-inline-start-state': navigationSidebarState,
11
+ 'data-sidebar-inline-end-state': complementarySidebarState,
12
12
  };
13
13
  };
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';