@dxos/plugin-deck 0.8.4-main.fd6878d → 0.8.4-main.fffef41

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 (137) hide show
  1. package/dist/lib/browser/{app-graph-builder-DVEKLXB4.mjs → app-graph-builder-D74NTOMK.mjs} +18 -42
  2. package/dist/lib/browser/app-graph-builder-D74NTOMK.mjs.map +7 -0
  3. package/dist/lib/browser/{check-app-scheme-3BQJXWEY.mjs → check-app-scheme-HIEVFAAX.mjs} +6 -6
  4. package/dist/lib/browser/check-app-scheme-HIEVFAAX.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-5KMJPIQC.mjs +16 -0
  6. package/dist/lib/browser/chunk-5KMJPIQC.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-F5BQOOEG.mjs → chunk-F3VCCHVL.mjs} +8 -6
  8. package/dist/lib/browser/chunk-F3VCCHVL.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-MX7WRVX3.mjs → chunk-QKCGZ45E.mjs} +18 -17
  10. package/dist/lib/browser/chunk-QKCGZ45E.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-UXLU6CMW.mjs +16 -0
  12. package/dist/lib/browser/chunk-UXLU6CMW.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-NRCPV6AV.mjs → chunk-VBYJ664A.mjs} +12 -9
  14. package/dist/lib/browser/chunk-VBYJ664A.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-6MQIYIPY.mjs → chunk-VUJ6UNIJ.mjs} +420 -361
  16. package/dist/lib/browser/chunk-VUJ6UNIJ.mjs.map +7 -0
  17. package/dist/lib/browser/index.mjs +9 -9
  18. package/dist/lib/browser/index.mjs.map +3 -3
  19. package/dist/lib/browser/{intent-resolver-2SUIIV6N.mjs → intent-resolver-UA4YQGAC.mjs} +20 -17
  20. package/dist/lib/browser/intent-resolver-UA4YQGAC.mjs.map +7 -0
  21. package/dist/lib/browser/meta.json +1 -1
  22. package/dist/lib/browser/{react-root-GVZANZX7.mjs → react-root-JAMHKYWN.mjs} +13 -12
  23. package/dist/lib/browser/react-root-JAMHKYWN.mjs.map +7 -0
  24. package/dist/lib/browser/{react-surface-NXSSD2GW.mjs → react-surface-6LW337ZT.mjs} +13 -13
  25. package/dist/lib/browser/react-surface-6LW337ZT.mjs.map +7 -0
  26. package/dist/lib/browser/{settings-LUPQPZ27.mjs → settings-SDPTOCCM.mjs} +7 -6
  27. package/dist/lib/browser/{settings-LUPQPZ27.mjs.map → settings-SDPTOCCM.mjs.map} +3 -3
  28. package/dist/lib/browser/state-7IFAGZQO.mjs +12 -0
  29. package/dist/lib/browser/toolkit-L5CFXJCF.mjs +52 -0
  30. package/dist/lib/browser/toolkit-L5CFXJCF.mjs.map +7 -0
  31. package/dist/lib/browser/types/index.mjs +2 -2
  32. package/dist/lib/browser/{url-handler-LROZYQ26.mjs → url-handler-QEYGYE2H.mjs} +6 -6
  33. package/dist/lib/browser/{url-handler-LROZYQ26.mjs.map → url-handler-QEYGYE2H.mjs.map} +1 -1
  34. package/dist/types/src/DeckPlugin.d.ts +1 -1
  35. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  36. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  37. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  39. package/dist/types/src/capabilities/check-app-scheme.d.ts +1 -1
  40. package/dist/types/src/capabilities/index.d.ts +8 -8
  41. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  42. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  43. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  44. package/dist/types/src/capabilities/react-root.d.ts +2 -2
  45. package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
  46. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  47. package/dist/types/src/capabilities/settings.d.ts +1 -1
  48. package/dist/types/src/capabilities/settings.d.ts.map +1 -1
  49. package/dist/types/src/capabilities/state.d.ts +5 -2
  50. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  51. package/dist/types/src/capabilities/toolkit.d.ts +23 -3
  52. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -1
  53. package/dist/types/src/capabilities/tools.d.ts +1 -1
  54. package/dist/types/src/capabilities/url-handler.d.ts +1 -1
  55. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +2 -3
  56. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  57. package/dist/types/src/components/DeckLayout/DeckLayout.stories.d.ts +74 -0
  58. package/dist/types/src/components/DeckLayout/DeckLayout.stories.d.ts.map +1 -0
  59. package/dist/types/src/components/DeckLayout/DeckMain.d.ts +3 -0
  60. package/dist/types/src/components/DeckLayout/DeckMain.d.ts.map +1 -0
  61. package/dist/types/src/components/DeckLayout/Popover.d.ts.map +1 -1
  62. package/dist/types/src/components/DeckLayout/Toast.d.ts +5 -0
  63. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  64. package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
  65. package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
  66. package/dist/types/src/components/Plank/Plank.stories.d.ts +86 -5
  67. package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -1
  68. package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
  69. package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
  70. package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
  71. package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -1
  72. package/dist/types/src/events.d.ts.map +1 -1
  73. package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -1
  74. package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +1 -1
  75. package/dist/types/src/meta.d.ts +0 -1
  76. package/dist/types/src/meta.d.ts.map +1 -1
  77. package/dist/types/src/translations.d.ts +2 -0
  78. package/dist/types/src/translations.d.ts.map +1 -1
  79. package/dist/types/src/types/schema.d.ts +4 -3
  80. package/dist/types/src/types/schema.d.ts.map +1 -1
  81. package/dist/types/tsconfig.tsbuildinfo +1 -1
  82. package/package.json +44 -44
  83. package/src/DeckPlugin.ts +62 -63
  84. package/src/capabilities/app-graph-builder.ts +31 -30
  85. package/src/capabilities/capabilities.ts +3 -3
  86. package/src/capabilities/check-app-scheme.ts +2 -2
  87. package/src/capabilities/intent-resolver.ts +12 -10
  88. package/src/capabilities/react-root.tsx +4 -3
  89. package/src/capabilities/react-surface.tsx +4 -4
  90. package/src/capabilities/settings.ts +1 -0
  91. package/src/capabilities/state.ts +12 -4
  92. package/src/capabilities/toolkit.ts +28 -17
  93. package/src/capabilities/tools.ts +1 -1
  94. package/src/components/DeckLayout/ActiveNode.tsx +1 -1
  95. package/src/components/DeckLayout/Banner.tsx +3 -3
  96. package/src/components/DeckLayout/ContentEmpty.tsx +1 -1
  97. package/src/components/DeckLayout/DeckLayout.stories.tsx +63 -0
  98. package/src/components/DeckLayout/DeckLayout.tsx +8 -278
  99. package/src/components/DeckLayout/DeckMain.tsx +281 -0
  100. package/src/components/DeckLayout/Dialog.tsx +1 -1
  101. package/src/components/DeckLayout/Fallback.tsx +3 -3
  102. package/src/components/DeckLayout/Popover.tsx +4 -13
  103. package/src/components/DeckLayout/StatusBar.tsx +1 -1
  104. package/src/components/DeckLayout/Toast.tsx +28 -3
  105. package/src/components/DeckSettings/DeckSettings.tsx +80 -65
  106. package/src/components/Plank/Plank.stories.tsx +7 -9
  107. package/src/components/Plank/Plank.tsx +69 -37
  108. package/src/components/Plank/PlankControls.tsx +6 -5
  109. package/src/components/Plank/PlankError.tsx +2 -2
  110. package/src/components/Plank/PlankHeading.tsx +10 -9
  111. package/src/components/Sidebar/ComplementarySidebar.tsx +36 -16
  112. package/src/components/Sidebar/Sidebar.tsx +3 -3
  113. package/src/components/Sidebar/SidebarButton.tsx +14 -13
  114. package/src/events.ts +2 -2
  115. package/src/hooks/useCompanions.ts +1 -1
  116. package/src/hooks/useDeckCompanions.ts +2 -1
  117. package/src/hooks/useHoistStatusbar.ts +6 -4
  118. package/src/meta.ts +6 -3
  119. package/src/translations.ts +2 -0
  120. package/src/types/schema.ts +5 -3
  121. package/dist/lib/browser/app-graph-builder-DVEKLXB4.mjs.map +0 -7
  122. package/dist/lib/browser/check-app-scheme-3BQJXWEY.mjs.map +0 -7
  123. package/dist/lib/browser/chunk-6MQIYIPY.mjs.map +0 -7
  124. package/dist/lib/browser/chunk-F5BQOOEG.mjs.map +0 -7
  125. package/dist/lib/browser/chunk-M57WD3V6.mjs +0 -16
  126. package/dist/lib/browser/chunk-M57WD3V6.mjs.map +0 -7
  127. package/dist/lib/browser/chunk-MX7WRVX3.mjs.map +0 -7
  128. package/dist/lib/browser/chunk-NRCPV6AV.mjs.map +0 -7
  129. package/dist/lib/browser/chunk-Z5KITAZW.mjs +0 -13
  130. package/dist/lib/browser/chunk-Z5KITAZW.mjs.map +0 -7
  131. package/dist/lib/browser/intent-resolver-2SUIIV6N.mjs.map +0 -7
  132. package/dist/lib/browser/react-root-GVZANZX7.mjs.map +0 -7
  133. package/dist/lib/browser/react-surface-NXSSD2GW.mjs.map +0 -7
  134. package/dist/lib/browser/state-CRXR7X63.mjs +0 -12
  135. package/dist/lib/browser/toolkit-OBKFXX23.mjs +0 -47
  136. package/dist/lib/browser/toolkit-OBKFXX23.mjs.map +0 -7
  137. /package/dist/lib/browser/{state-CRXR7X63.mjs.map → state-7IFAGZQO.mjs.map} +0 -0
@@ -2,6 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { useFocusFinders } from '@fluentui/react-tabster';
5
6
  import React, {
6
7
  type KeyboardEvent,
7
8
  type PropsWithChildren,
@@ -12,14 +13,8 @@ import React, {
12
13
  useRef,
13
14
  } from 'react';
14
15
 
15
- import {
16
- LayoutAction,
17
- Surface,
18
- createIntent,
19
- useAppGraph,
20
- useCapability,
21
- useIntentDispatcher,
22
- } from '@dxos/app-framework';
16
+ import { LayoutAction, createIntent } from '@dxos/app-framework';
17
+ import { Surface, useAppGraph, useCapability, useIntentDispatcher } from '@dxos/app-framework/react';
23
18
  import { debounce } from '@dxos/async';
24
19
  import { type Node, useNode } from '@dxos/plugin-graph';
25
20
  import { ATTENDABLE_PATH_SEPARATOR, useAttentionAttributes } from '@dxos/react-ui-attention';
@@ -37,6 +32,10 @@ import { PlankLoading } from './PlankLoading';
37
32
 
38
33
  const UNKNOWN_ID = 'unknown_id';
39
34
 
35
+ //
36
+ // Plank
37
+ //
38
+
40
39
  export type PlankProps = Pick<PlankComponentProps, 'layoutMode' | 'part' | 'path' | 'order' | 'active' | 'settings'> & {
41
40
  id?: string;
42
41
  companionId?: string;
@@ -68,7 +67,11 @@ export const Plank = memo(({ id = UNKNOWN_ID, companionId, ...props }: PlankProp
68
67
  const hasCompanion = !!(companionId && currentCompanion);
69
68
 
70
69
  return (
71
- <PlankContainer solo={props.part === 'solo'} companion={hasCompanion}>
70
+ <PlankContainer
71
+ solo={props.part === 'solo'}
72
+ companion={hasCompanion}
73
+ encapsulate={!!props.settings?.encapsulatedPlanks}
74
+ >
72
75
  <PlankComponent
73
76
  id={id}
74
77
  node={node}
@@ -92,7 +95,13 @@ export const Plank = memo(({ id = UNKNOWN_ID, companionId, ...props }: PlankProp
92
95
  );
93
96
  });
94
97
 
95
- const PlankContainer = ({ children, solo, companion }: PropsWithChildren<{ solo: boolean; companion: boolean }>) => {
98
+ //
99
+ // PlankContainer
100
+ //
101
+
102
+ type PlankContainerProps = PropsWithChildren<{ solo: boolean; companion: boolean; encapsulate: boolean }>;
103
+
104
+ const PlankContainer = ({ children, solo, companion, encapsulate }: PlankContainerProps) => {
96
105
  const sizeAttrs = useMainSize();
97
106
  if (!solo) {
98
107
  return children;
@@ -102,7 +111,14 @@ const PlankContainer = ({ children, solo, companion }: PropsWithChildren<{ solo:
102
111
  return (
103
112
  <div
104
113
  role='none'
105
- className={mx('absolute inset-0 grid', companion && 'grid-cols-[1fr_1fr]', railGridHorizontal, mainIntrinsicSize)}
114
+ data-popover-collision-boundary={true}
115
+ className={mx(
116
+ 'absolute inset-[--main-spacing] grid',
117
+ encapsulate && 'border border-separator rounded overflow-hidden',
118
+ companion && 'grid-cols-[6fr_4fr]', // TODO(burdon): Resize.
119
+ railGridHorizontal,
120
+ mainIntrinsicSize,
121
+ )}
106
122
  {...sizeAttrs}
107
123
  >
108
124
  {children}
@@ -110,6 +126,10 @@ const PlankContainer = ({ children, solo, companion }: PropsWithChildren<{ solo:
110
126
  );
111
127
  };
112
128
 
129
+ //
130
+ // PlankComponent
131
+ //
132
+
113
133
  type PlankComponentProps = {
114
134
  layoutMode: LayoutMode;
115
135
  id: string;
@@ -117,7 +137,6 @@ type PlankComponentProps = {
117
137
  path?: string[];
118
138
  order?: number;
119
139
  active?: string[];
120
- // TODO(burdon): Change to role?
121
140
  companioned?: 'primary' | 'companion';
122
141
  node?: Node;
123
142
  primary?: Node;
@@ -141,6 +160,7 @@ const PlankComponent = memo(
141
160
  }: PlankComponentProps) => {
142
161
  const { dispatchPromise: dispatch } = useIntentDispatcher();
143
162
  const { deck, popoverAnchorId, scrollIntoView } = useCapability(DeckCapabilities.DeckState);
163
+ const { findFirstFocusable } = useFocusFinders();
144
164
  const canResize = layoutMode === 'deck';
145
165
 
146
166
  const attentionAttrs = useAttentionAttributes(primary?.id ?? id);
@@ -164,18 +184,21 @@ const PlankComponent = memo(
164
184
 
165
185
  // TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
166
186
  const handleKeyDown = useCallback((event: KeyboardEvent) => {
167
- if (event.target === event.currentTarget && event.key === 'Escape') {
168
- rootElement.current?.closest('main')?.focus();
187
+ if (event.target === event.currentTarget) {
188
+ switch (event.key) {
189
+ case 'Escape':
190
+ rootElement.current?.closest('main')?.focus();
191
+ break;
192
+ case 'Enter':
193
+ rootElement.current && findFirstFocusable(rootElement.current)?.focus();
194
+ break;
195
+ }
169
196
  }
170
197
  }, []);
171
198
 
172
199
  useLayoutEffect(() => {
173
200
  if (scrollIntoView === id) {
174
- // TODO(wittjosiah): When focused on page load, the focus is always visible.
175
- // Forcing focus to something smaller than the plank prevents large focus ring in the interim.
176
- const focusable = rootElement.current?.querySelector('button') || rootElement.current;
177
- focusable?.focus({ preventScroll: true });
178
- layoutMode === 'deck' && focusable?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
201
+ layoutMode === 'deck' && rootElement.current?.scrollIntoView({ behavior: 'smooth', inline: 'center' });
179
202
  // Clear the scroll into view state once it has been actioned.
180
203
  void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: undefined }));
181
204
  }
@@ -190,34 +213,42 @@ const PlankComponent = memo(
190
213
  const data = useMemo(
191
214
  () =>
192
215
  node && {
216
+ attendableId: id,
193
217
  subject: node.data,
194
218
  companionTo: primary?.data,
219
+ properties: node.properties,
195
220
  variant,
196
221
  path,
197
222
  popoverAnchorId,
198
223
  },
199
- [node, node?.data, path, popoverAnchorId, primary?.data, variant],
224
+ [node, node?.data, node?.properties, path, popoverAnchorId, primary?.data, variant],
200
225
  );
201
226
 
202
227
  // TODO(wittjosiah): Change prop to accept a component.
203
228
  const placeholder = useMemo(() => <PlankLoading />, []);
204
229
 
205
230
  const Root = part.startsWith('solo') ? 'article' : StackItem.Root;
231
+ const fullscreen = layoutMode === 'solo--fullscreen';
206
232
  const className = mx(
207
233
  'attention-surface relative dx-focus-ring-inset-over-all density-coarse',
208
- isSolo && mainIntrinsicSize,
209
- isSolo && railGridHorizontal,
210
234
  isSolo && 'absolute inset-0',
235
+ isSolo && mainIntrinsicSize,
236
+ railGridHorizontal,
211
237
  part.startsWith('solo') && 'grid',
238
+ part.startsWith('solo-') && 'grid-rows-subgrid row-span-2 min-is-0',
239
+ fullscreen && 'grid-rows-1',
212
240
  part === 'deck' && (companioned === 'companion' ? '!border-separator border-ie' : '!border-separator border-li'),
213
- part.startsWith('solo-') && 'row-span-2 grid-rows-subgrid min-is-0',
214
241
  part === 'solo-companion' && '!border-separator border-is',
242
+ settings?.encapsulatedPlanks &&
243
+ !part.startsWith('solo') &&
244
+ 'mli-[--main-spacing] !border-separator border rounded overflow-hidden',
215
245
  );
216
246
 
217
247
  return (
218
248
  <Root
219
249
  ref={rootElement}
220
250
  data-testid='deck.plank'
251
+ data-popover-collision-boundary={true}
221
252
  tabIndex={0}
222
253
  {...(part.startsWith('solo')
223
254
  ? ({ ...sizeAttrs, className } as any)
@@ -234,19 +265,21 @@ const PlankComponent = memo(
234
265
  >
235
266
  {node ? (
236
267
  <>
237
- <PlankHeading
238
- id={id}
239
- part={part.startsWith('solo-') ? 'solo' : part}
240
- node={node}
241
- layoutMode={layoutMode}
242
- deckEnabled={settings?.enableDeck}
243
- canIncrementStart={canIncrementStart}
244
- canIncrementEnd={canIncrementEnd}
245
- popoverAnchorId={popoverAnchorId}
246
- primaryId={primary?.id}
247
- companioned={companioned}
248
- companions={companions}
249
- />
268
+ {!fullscreen && (
269
+ <PlankHeading
270
+ id={id}
271
+ part={part.startsWith('solo-') ? 'solo' : part}
272
+ node={node}
273
+ layoutMode={layoutMode}
274
+ deckEnabled={settings?.enableDeck}
275
+ canIncrementStart={canIncrementStart}
276
+ canIncrementEnd={canIncrementEnd}
277
+ popoverAnchorId={popoverAnchorId}
278
+ primaryId={primary?.id}
279
+ companioned={companioned}
280
+ companions={companions}
281
+ />
282
+ )}
250
283
  <Surface
251
284
  key={node.id}
252
285
  role='article'
@@ -259,7 +292,6 @@ const PlankComponent = memo(
259
292
  ) : (
260
293
  <PlankError id={id} part={part} />
261
294
  )}
262
-
263
295
  {canResize && <StackItem.ResizeHandle />}
264
296
  </Root>
265
297
  );
@@ -4,11 +4,12 @@
4
4
 
5
5
  import React, { forwardRef, useCallback } from 'react';
6
6
 
7
- import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
7
+ import { createIntent } from '@dxos/app-framework';
8
+ import { useIntentDispatcher } from '@dxos/app-framework/react';
8
9
  import { invariant } from '@dxos/invariant';
9
10
  import { ButtonGroup, type ButtonGroupProps, type ButtonProps, IconButton, useTranslation } from '@dxos/react-ui';
10
11
 
11
- import { DECK_PLUGIN } from '../../meta';
12
+ import { meta } from '../../meta';
12
13
  import { DeckAction, type LayoutMode } from '../../types';
13
14
 
14
15
  export type PlankControlHandler = (event: DeckAction.PartAdjustment) => void;
@@ -32,7 +33,7 @@ export type PlankControlsProps = Omit<ButtonGroupProps, 'onClick'> & {
32
33
  };
33
34
 
34
35
  const PlankControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> & { label: string; icon: string }) => {
35
- return <IconButton iconOnly label={label} icon={icon} size={5} variant='ghost' tooltipSide='bottom' {...props} />;
36
+ return <IconButton label={label} icon={icon} iconOnly variant='ghost' tooltipSide='bottom' {...props} />;
36
37
  };
37
38
 
38
39
  const plankControlSpacing = 'pli-2';
@@ -43,7 +44,7 @@ type PlankComplimentControlsProps = {
43
44
 
44
45
  export const PlankCompanionControls = forwardRef<HTMLDivElement, PlankComplimentControlsProps>(
45
46
  ({ primary }, forwardedRef) => {
46
- const { t } = useTranslation(DECK_PLUGIN);
47
+ const { t } = useTranslation(meta.id);
47
48
  const { dispatchPromise: dispatch } = useIntentDispatcher();
48
49
  const handleCloseCompanion = useCallback(() => {
49
50
  invariant(primary);
@@ -71,7 +72,7 @@ export const PlankControls = forwardRef<HTMLDivElement, PlankControlsProps>(
71
72
  { children, classNames, variant = 'default', capabilities, layoutMode, pin, close = false, onClick, ...props },
72
73
  forwardedRef,
73
74
  ) => {
74
- const { t } = useTranslation(DECK_PLUGIN);
75
+ const { t } = useTranslation(meta.id);
75
76
  const buttonClassNames =
76
77
  variant === 'hide-disabled' ? `disabled:hidden ${plankControlSpacing}` : plankControlSpacing;
77
78
 
@@ -8,13 +8,13 @@ import { type Node } from '@dxos/plugin-graph';
8
8
  import { useTranslation } from '@dxos/react-ui';
9
9
  import { descriptionMessage, mx } from '@dxos/react-ui-theme';
10
10
 
11
- import { DECK_PLUGIN } from '../../meta';
11
+ import { meta } from '../../meta';
12
12
 
13
13
  import { PlankHeading, type PlankHeadingProps } from './PlankHeading';
14
14
  import { PlankLoading } from './PlankLoading';
15
15
 
16
16
  export const PlankContentError = ({ error }: { error?: Error }) => {
17
- const { t } = useTranslation(DECK_PLUGIN);
17
+ const { t } = useTranslation(meta.id);
18
18
  const errorString = error?.toString() ?? '';
19
19
  return (
20
20
  <div role='none' className='overflow-y-auto p-8 attention-surface grid place-items-center'>
@@ -4,7 +4,8 @@
4
4
 
5
5
  import React, { Fragment, type MouseEvent, memo, useCallback, useEffect, useMemo } from 'react';
6
6
 
7
- import { LayoutAction, Surface, createIntent, useAppGraph, useIntentDispatcher } from '@dxos/app-framework';
7
+ import { LayoutAction, createIntent } from '@dxos/app-framework';
8
+ import { Surface, useAppGraph, useIntentDispatcher } from '@dxos/app-framework/react';
8
9
  import { type Node } from '@dxos/plugin-graph';
9
10
  import { Icon, IconButton, Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
10
11
  import { StackItem, type StackItemSigilAction } from '@dxos/react-ui-stack';
@@ -13,7 +14,7 @@ import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-u
13
14
 
14
15
  import { useBreakpoints } from '../../hooks';
15
16
  import { parseEntryId } from '../../layout';
16
- import { DECK_PLUGIN } from '../../meta';
17
+ import { meta } from '../../meta';
17
18
  import { DeckAction, type LayoutMode, PLANK_COMPANION_TYPE, type ResolvedPart } from '../../types';
18
19
  import { soloInlinePadding } from '../fragments';
19
20
 
@@ -53,14 +54,14 @@ export const PlankHeading = memo(
53
54
  layoutMode,
54
55
  actions = [],
55
56
  }: PlankHeadingProps) => {
56
- const { t } = useTranslation(DECK_PLUGIN);
57
+ const { t } = useTranslation(meta.id);
57
58
  const { dispatchPromise: dispatch } = useIntentDispatcher();
58
59
  const { graph } = useAppGraph();
59
60
  const breakpoint = useBreakpoints();
60
61
  const icon = node?.properties?.icon ?? 'ph--placeholder--regular';
61
62
  const label = pending
62
63
  ? t('pending heading')
63
- : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: DECK_PLUGIN }], t);
64
+ : toLocalizedString(node?.properties?.label ?? ['plank heading fallback label', { ns: meta.id }], t);
64
65
 
65
66
  const isCompanionNode = node?.type === PLANK_COMPANION_TYPE;
66
67
 
@@ -106,7 +107,7 @@ export const PlankHeading = memo(
106
107
 
107
108
  const handleAction = useCallback(
108
109
  (action: StackItemSigilAction) => {
109
- typeof action.data === 'function' && void action.data?.({ parent: node, caller: DECK_PLUGIN });
110
+ typeof action.data === 'function' && void action.data?.({ parent: node, caller: meta.id });
110
111
  },
111
112
  [node],
112
113
  );
@@ -135,7 +136,7 @@ export const PlankHeading = memo(
135
136
  [dispatch, id, part],
136
137
  );
137
138
 
138
- const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${DECK_PLUGIN}/${node.id}` ? Popover.Anchor : Fragment;
139
+ const ActionRoot = node && popoverAnchorId === `dxos.org/ui/${meta.id}/${node.id}` ? Popover.Anchor : Fragment;
139
140
 
140
141
  const handleTabClick = useCallback(
141
142
  (event: MouseEvent) => {
@@ -162,7 +163,8 @@ export const PlankHeading = memo(
162
163
  ? [
163
164
  hoverableControls,
164
165
  hoverableFocusedWithinControls,
165
- '*:transition-opacity *:opacity-[--controls-opacity] bg-transparent border-transparent transition-[background-color,border-color] hover-hover:hover:bg-headerSurface focus-within:bg-headerSurface hover-hover:hover:border-subduedSeparator focus-within:border-subduedSeparator',
166
+ '*:transition-opacity *:opacity-[--controls-opacity] bg-transparent border-transparent transition-[background-color,border-color]',
167
+ 'hover-hover:hover:bg-headerSurface focus-within:bg-headerSurface hover-hover:hover:border-subduedSeparator focus-within:border-subduedSeparator',
166
168
  ]
167
169
  : []),
168
170
  ]}
@@ -178,7 +180,6 @@ export const PlankHeading = memo(
178
180
  icon={icon}
179
181
  iconOnly={companions.length > MAX_COMPANIONS && node?.id !== id}
180
182
  label={toLocalizedString(label, t)}
181
- size={5}
182
183
  variant={node?.id === id ? 'primary' : 'ghost'}
183
184
  onClick={handleTabClick}
184
185
  />
@@ -201,7 +202,7 @@ export const PlankHeading = memo(
201
202
  ) : (
202
203
  <StackItem.SigilButton>
203
204
  <span className='sr-only'>{label}</span>
204
- <Icon icon={icon} size={5} />
205
+ <Icon icon={icon} />
205
206
  </StackItem.SigilButton>
206
207
  )}
207
208
  </ActionRoot>
@@ -12,27 +12,29 @@ import React, {
12
12
  useState,
13
13
  } from 'react';
14
14
 
15
- import { LayoutAction, Surface, createIntent, useCapability, useIntentDispatcher } from '@dxos/app-framework';
15
+ import { LayoutAction, createIntent } from '@dxos/app-framework';
16
+ import { Surface, useCapability, useIntentDispatcher } from '@dxos/app-framework/react';
16
17
  import { IconButton, type Label, Main, toLocalizedString, useTranslation } from '@dxos/react-ui';
17
18
  import { Tabs } from '@dxos/react-ui-tabs';
19
+ import { mx } from '@dxos/react-ui-theme';
18
20
 
19
21
  import { DeckCapabilities } from '../../capabilities';
20
22
  import { type DeckCompanion, getCompanionId, useBreakpoints, useDeckCompanions, useHoistStatusbar } from '../../hooks';
21
- import { DECK_PLUGIN } from '../../meta';
23
+ import { meta } from '../../meta';
22
24
  import { getMode } from '../../types';
23
25
  import { layoutAppliesTopbar } from '../../util';
24
26
  import { PlankContentError, PlankLoading } from '../Plank';
25
27
 
26
28
  import { ToggleComplementarySidebarButton } from './SidebarButton';
27
29
 
28
- const label = ['complementary sidebar title', { ns: DECK_PLUGIN }] satisfies Label;
30
+ const label = ['complementary sidebar title', { ns: meta.id }] satisfies Label;
29
31
 
30
32
  export type ComplementarySidebarProps = {
31
33
  current?: string;
32
34
  };
33
35
 
34
36
  export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) => {
35
- const { t } = useTranslation(DECK_PLUGIN);
37
+ const { t } = useTranslation(meta.id);
36
38
  const { dispatchPromise: dispatch } = useIntentDispatcher();
37
39
  const layout = useCapability(DeckCapabilities.MutableDeckState);
38
40
  const layoutMode = getMode(layout.deck);
@@ -91,7 +93,11 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
91
93
  <Tabs.Root orientation='vertical' verticalVariant='stateless' value={internalValue} classNames='contents'>
92
94
  <div
93
95
  role='none'
94
- className='absolute z-[1] inset-block-0 inline-end-0 !is-[--r0-size] pbs-[env(safe-area-inset-top)] pbe-[env(safe-area-inset-bottom)] border-is border-subduedSeparator grid grid-cols-1 grid-rows-[1fr_min-content] bg-baseSurface contain-layout app-drag'
96
+ className={mx(
97
+ 'absolute z-[1] inset-block-0 inline-end-0 !is-[--r0-size]',
98
+ 'pbs-[env(safe-area-inset-top)] pbe-[env(safe-area-inset-bottom)] border-is border-subduedSeparator',
99
+ 'grid grid-cols-1 grid-rows-[1fr_min-content] bg-baseSurface contain-layout app-drag',
100
+ )}
95
101
  >
96
102
  <Tabs.Tablist classNames='grid grid-cols-1 auto-rows-[--rail-action] p-1 gap-1 !overflow-y-auto'>
97
103
  {companions.map((companion) => (
@@ -99,7 +105,6 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
99
105
  <IconButton
100
106
  label={toLocalizedString(companion.properties.label, t)}
101
107
  icon={companion.properties.icon}
102
- size={5}
103
108
  iconOnly
104
109
  tooltipSide='left'
105
110
  data-value={getCompanionId(companion.id)}
@@ -129,8 +134,12 @@ export const ComplementarySidebar = ({ current }: ComplementarySidebarProps) =>
129
134
  <Tabs.Tabpanel
130
135
  key={getCompanionId(companion.id)}
131
136
  value={getCompanionId(companion.id)}
132
- classNames='absolute data-[state="inactive"]:-z-[1] inset-block-0 inline-start-0 is-[calc(100%-var(--r0-size))] lg:is-[--r1-size] grid grid-cols-1 grid-rows-[var(--rail-size)_1fr_min-content] pbs-[env(safe-area-inset-top)]'
133
- {...(layout.complementarySidebarState !== 'expanded' && { inert: 'true' })}
137
+ classNames={[
138
+ 'absolute data-[state="inactive"]:-z-[1] overflow-hidden',
139
+ 'inset-block-0 inline-start-0 is-[calc(100%-var(--r0-size))] lg:is-[--r1-size]',
140
+ 'grid grid-cols-1 grid-rows-[var(--rail-size)_1fr_min-content] pbs-[env(safe-area-inset-top)]',
141
+ ]}
142
+ {...(layout.complementarySidebarState !== 'expanded' && { inert: true })}
134
143
  >
135
144
  <ComplementarySidebarPanel
136
145
  companion={companion}
@@ -155,12 +164,8 @@ type ComplementarySidebarPanelProps = {
155
164
  hoistStatusbar: boolean;
156
165
  };
157
166
 
158
- const ScrollArea = ({ children }: PropsWithChildren) => {
159
- return <div className='flex flex-col grow overflow-x-hidden overflow-y-auto scrollbar-thin'>{children}</div>;
160
- };
161
-
162
167
  const ComplementarySidebarPanel = ({ companion, activeId, data, hoistStatusbar }: ComplementarySidebarPanelProps) => {
163
- const { t } = useTranslation(DECK_PLUGIN);
168
+ const { t } = useTranslation(meta.id);
164
169
 
165
170
  if (getCompanionId(companion.id) !== activeId && !data) {
166
171
  return null;
@@ -170,9 +175,20 @@ const ComplementarySidebarPanel = ({ companion, activeId, data, hoistStatusbar }
170
175
 
171
176
  return (
172
177
  <>
173
- <h2 className='flex items-center pli-2 border-subduedSeparator border-be font-medium'>
174
- {toLocalizedString(companion.properties.label, t)}
175
- </h2>
178
+ <div role='none' className='flex items-center p-1 gap-1 border-be border-subduedSeparator'>
179
+ <IconButton
180
+ label={toLocalizedString(companion.properties.label, t)}
181
+ icon={companion.properties.icon}
182
+ iconOnly
183
+ tooltipSide='left'
184
+ data-value={getCompanionId(companion.id)}
185
+ classNames='bs-10 is-10'
186
+ variant='default'
187
+ />
188
+ <div role='none' className='pli-1'>
189
+ {toLocalizedString(companion.properties.label, t)}
190
+ </div>
191
+ </div>
176
192
  <Wrapper>
177
193
  <Surface
178
194
  role={`deck-companion--${getCompanionId(companion.id)}`}
@@ -192,3 +208,7 @@ const ComplementarySidebarPanel = ({ companion, activeId, data, hoistStatusbar }
192
208
  </>
193
209
  );
194
210
  };
211
+
212
+ const ScrollArea = ({ children }: PropsWithChildren) => {
213
+ return <div className='flex flex-col grow overflow-x-hidden overflow-y-auto scrollbar-thin'>{children}</div>;
214
+ };
@@ -4,16 +4,16 @@
4
4
 
5
5
  import React, { useMemo } from 'react';
6
6
 
7
- import { Surface, useCapability } from '@dxos/app-framework';
7
+ import { Surface, useCapability } from '@dxos/app-framework/react';
8
8
  import { type Label, Main } from '@dxos/react-ui';
9
9
 
10
10
  import { DeckCapabilities } from '../../capabilities';
11
11
  import { useBreakpoints, useHoistStatusbar } from '../../hooks';
12
- import { DECK_PLUGIN } from '../../meta';
12
+ import { meta } from '../../meta';
13
13
  import { getMode } from '../../types';
14
14
  import { layoutAppliesTopbar } from '../../util';
15
15
 
16
- const label = ['sidebar title', { ns: DECK_PLUGIN }] satisfies Label;
16
+ const label = ['sidebar title', { ns: meta.id }] satisfies Label;
17
17
 
18
18
  export const Sidebar = () => {
19
19
  const { popoverAnchorId, activeDeck: current, deck } = useCapability(DeckCapabilities.DeckState);
@@ -4,24 +4,25 @@
4
4
 
5
5
  import React, { useCallback } from 'react';
6
6
 
7
- import { LayoutAction, createIntent, useCapability, useIntentDispatcher } from '@dxos/app-framework';
7
+ import { LayoutAction, createIntent } from '@dxos/app-framework';
8
+ import { useCapability, useIntentDispatcher } from '@dxos/app-framework/react';
8
9
  import { IconButton, type IconButtonProps, type ThemedClassName, useTranslation } from '@dxos/react-ui';
9
10
 
10
11
  import { DeckCapabilities } from '../../capabilities';
11
12
  import { getCompanionId, useDeckCompanions } from '../../hooks';
12
- import { DECK_PLUGIN } from '../../meta';
13
+ import { meta } from '../../meta';
13
14
 
14
15
  export const ToggleSidebarButton = ({
15
16
  classNames,
16
17
  variant = 'ghost',
17
18
  }: ThemedClassName<Pick<IconButtonProps, 'variant'>>) => {
18
19
  const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
19
- const { t } = useTranslation(DECK_PLUGIN);
20
+ const { t } = useTranslation(meta.id);
20
21
  return (
21
22
  <IconButton
22
23
  variant={variant}
23
- iconOnly
24
24
  icon='ph--sidebar--regular'
25
+ iconOnly
25
26
  size={4}
26
27
  label={t('open navigation sidebar label')}
27
28
  onClick={() =>
@@ -34,12 +35,12 @@ export const ToggleSidebarButton = ({
34
35
 
35
36
  export const CloseSidebarButton = () => {
36
37
  const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
37
- const { t } = useTranslation(DECK_PLUGIN);
38
+ const { t } = useTranslation(meta.id);
38
39
  return (
39
40
  <IconButton
40
41
  variant='ghost'
41
- iconOnly
42
42
  icon='ph--caret-line-left--regular'
43
+ iconOnly
43
44
  size={4}
44
45
  label={t('close navigation sidebar label')}
45
46
  onClick={() => (layoutContext.sidebarState = 'collapsed')}
@@ -55,18 +56,18 @@ export const ToggleComplementarySidebarButton = ({
55
56
  }: ThemedClassName<{ inR0?: boolean; current?: string }>) => {
56
57
  const { dispatchPromise: dispatch } = useIntentDispatcher();
57
58
  const layoutContext = useCapability(DeckCapabilities.MutableDeckState);
58
- const { t } = useTranslation(DECK_PLUGIN);
59
+ const { t } = useTranslation(meta.id);
59
60
 
60
61
  const companions = useDeckCompanions();
61
62
  const handleClick = useCallback(async () => {
62
63
  layoutContext.complementarySidebarState =
63
64
  layoutContext.complementarySidebarState === 'expanded' ? 'collapsed' : 'expanded';
64
- const firstCompanion = companions[0];
65
- if (layoutContext.complementarySidebarState === 'expanded' && !current && firstCompanion) {
65
+ const subject = layoutContext.complementarySidebarPanel ?? (companions[0] && getCompanionId(companions[0].id));
66
+ if (layoutContext.complementarySidebarState === 'expanded' && !current && subject) {
66
67
  await dispatch(
67
68
  createIntent(LayoutAction.UpdateComplementary, {
68
69
  part: 'complementary',
69
- subject: getCompanionId(firstCompanion.id),
70
+ subject,
70
71
  }),
71
72
  );
72
73
  }
@@ -74,14 +75,14 @@ export const ToggleComplementarySidebarButton = ({
74
75
 
75
76
  return (
76
77
  <IconButton
77
- iconOnly
78
- onClick={handleClick}
79
78
  variant='ghost'
80
- label={t('open complementary sidebar label')}
81
79
  classNames={['[&>svg]:-scale-x-100', classNames]}
82
80
  icon='ph--sidebar-simple--regular'
81
+ iconOnly
82
+ label={t('open complementary sidebar label')}
83
83
  size={inR0 ? 5 : 4}
84
84
  tooltipSide={inR0 ? 'left' : undefined}
85
+ onClick={handleClick}
85
86
  />
86
87
  );
87
88
  };
package/src/events.ts CHANGED
@@ -4,8 +4,8 @@
4
4
 
5
5
  import { Events } from '@dxos/app-framework';
6
6
 
7
- import { DECK_PLUGIN } from './meta';
7
+ import { meta } from './meta';
8
8
 
9
9
  export namespace DeckEvents {
10
- export const StateReady = Events.createStateEvent(`${DECK_PLUGIN}/state-ready`);
10
+ export const StateReady = Events.createStateEvent(`${meta.id}/state-ready`);
11
11
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { useMemo } from 'react';
6
6
 
7
- import { useAppGraph } from '@dxos/app-framework';
7
+ import { useAppGraph } from '@dxos/app-framework/react';
8
8
  import { useConnections } from '@dxos/plugin-graph';
9
9
  import { byPosition } from '@dxos/util';
10
10
 
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type Label, useAppGraph } from '@dxos/app-framework';
5
+ import { type Label } from '@dxos/app-framework';
6
+ import { useAppGraph } from '@dxos/app-framework/react';
6
7
  import { type Node, ROOT_ID, useConnections } from '@dxos/plugin-graph';
7
8
  import { type Position, byPosition } from '@dxos/util';
8
9
 
@@ -4,16 +4,18 @@
4
4
 
5
5
  import { useMemo } from 'react';
6
6
 
7
- import { Capabilities, useCapability } from '@dxos/app-framework';
7
+ import { Capabilities } from '@dxos/app-framework';
8
+ import { useCapability } from '@dxos/app-framework/react';
8
9
  import { useThemeContext } from '@dxos/react-ui';
9
10
 
10
- import { DECK_PLUGIN } from '../meta';
11
+ import { meta } from '../meta';
11
12
  import type { DeckSettingsProps, LayoutMode } from '../types';
12
13
 
13
14
  export const useHoistStatusbar = (breakpoint: string, layoutMode?: LayoutMode): boolean => {
14
- const enableStatusbar = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!.value
15
- .enableStatusbar;
16
15
  const { safeAreaPadding } = useThemeContext();
16
+ const enableStatusbar = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(meta.id)?.value
17
+ .enableStatusbar;
18
+
17
19
  return useMemo(() => {
18
20
  return (
19
21
  breakpoint === 'desktop' &&
package/src/meta.ts CHANGED
@@ -3,11 +3,14 @@
3
3
  //
4
4
 
5
5
  import { type PluginMeta } from '@dxos/app-framework';
6
-
7
- export const DECK_PLUGIN = 'dxos.org/plugin/deck' as const;
6
+ import { trim } from '@dxos/util';
8
7
 
9
8
  export const meta: PluginMeta = {
10
- id: DECK_PLUGIN,
9
+ id: 'dxos.org/plugin/deck',
11
10
  name: 'Layout',
11
+ description: trim`
12
+ Flexible layout system for arranging workspace views in tabs, splits, and panels.
13
+ Customize your workspace organization with drag-and-drop layout management.
14
+ `,
12
15
  icon: 'ph--layout--regular',
13
16
  };