@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.
- package/dist/lib/browser/{app-graph-builder-DVEKLXB4.mjs → app-graph-builder-D74NTOMK.mjs} +18 -42
- package/dist/lib/browser/app-graph-builder-D74NTOMK.mjs.map +7 -0
- package/dist/lib/browser/{check-app-scheme-3BQJXWEY.mjs → check-app-scheme-HIEVFAAX.mjs} +6 -6
- package/dist/lib/browser/check-app-scheme-HIEVFAAX.mjs.map +7 -0
- package/dist/lib/browser/chunk-5KMJPIQC.mjs +16 -0
- package/dist/lib/browser/chunk-5KMJPIQC.mjs.map +7 -0
- package/dist/lib/browser/{chunk-F5BQOOEG.mjs → chunk-F3VCCHVL.mjs} +8 -6
- package/dist/lib/browser/chunk-F3VCCHVL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-MX7WRVX3.mjs → chunk-QKCGZ45E.mjs} +18 -17
- package/dist/lib/browser/chunk-QKCGZ45E.mjs.map +7 -0
- package/dist/lib/browser/chunk-UXLU6CMW.mjs +16 -0
- package/dist/lib/browser/chunk-UXLU6CMW.mjs.map +7 -0
- package/dist/lib/browser/{chunk-NRCPV6AV.mjs → chunk-VBYJ664A.mjs} +12 -9
- package/dist/lib/browser/chunk-VBYJ664A.mjs.map +7 -0
- package/dist/lib/browser/{chunk-6MQIYIPY.mjs → chunk-VUJ6UNIJ.mjs} +420 -361
- package/dist/lib/browser/chunk-VUJ6UNIJ.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +9 -9
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-2SUIIV6N.mjs → intent-resolver-UA4YQGAC.mjs} +20 -17
- package/dist/lib/browser/intent-resolver-UA4YQGAC.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-root-GVZANZX7.mjs → react-root-JAMHKYWN.mjs} +13 -12
- package/dist/lib/browser/react-root-JAMHKYWN.mjs.map +7 -0
- package/dist/lib/browser/{react-surface-NXSSD2GW.mjs → react-surface-6LW337ZT.mjs} +13 -13
- package/dist/lib/browser/react-surface-6LW337ZT.mjs.map +7 -0
- package/dist/lib/browser/{settings-LUPQPZ27.mjs → settings-SDPTOCCM.mjs} +7 -6
- package/dist/lib/browser/{settings-LUPQPZ27.mjs.map → settings-SDPTOCCM.mjs.map} +3 -3
- package/dist/lib/browser/state-7IFAGZQO.mjs +12 -0
- package/dist/lib/browser/toolkit-L5CFXJCF.mjs +52 -0
- package/dist/lib/browser/toolkit-L5CFXJCF.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -2
- package/dist/lib/browser/{url-handler-LROZYQ26.mjs → url-handler-QEYGYE2H.mjs} +6 -6
- package/dist/lib/browser/{url-handler-LROZYQ26.mjs.map → url-handler-QEYGYE2H.mjs.map} +1 -1
- package/dist/types/src/DeckPlugin.d.ts +1 -1
- package/dist/types/src/DeckPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/check-app-scheme.d.ts +1 -1
- package/dist/types/src/capabilities/index.d.ts +8 -8
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-root.d.ts +2 -2
- package/dist/types/src/capabilities/react-root.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/settings.d.ts +1 -1
- package/dist/types/src/capabilities/settings.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +5 -2
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/capabilities/toolkit.d.ts +23 -3
- package/dist/types/src/capabilities/toolkit.d.ts.map +1 -1
- package/dist/types/src/capabilities/tools.d.ts +1 -1
- package/dist/types/src/capabilities/url-handler.d.ts +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +2 -3
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.stories.d.ts +74 -0
- package/dist/types/src/components/DeckLayout/DeckLayout.stories.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/DeckMain.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/DeckMain.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/Popover.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Toast.d.ts +5 -0
- package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
- package/dist/types/src/components/DeckSettings/DeckSettings.d.ts.map +1 -1
- package/dist/types/src/components/Plank/Plank.d.ts.map +1 -1
- package/dist/types/src/components/Plank/Plank.stories.d.ts +86 -5
- package/dist/types/src/components/Plank/Plank.stories.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankControls.d.ts.map +1 -1
- package/dist/types/src/components/Plank/PlankHeading.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/ComplementarySidebar.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/SidebarButton.d.ts.map +1 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/hooks/useDeckCompanions.d.ts.map +1 -1
- package/dist/types/src/hooks/useHoistStatusbar.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +0 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +4 -3
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +44 -44
- package/src/DeckPlugin.ts +62 -63
- package/src/capabilities/app-graph-builder.ts +31 -30
- package/src/capabilities/capabilities.ts +3 -3
- package/src/capabilities/check-app-scheme.ts +2 -2
- package/src/capabilities/intent-resolver.ts +12 -10
- package/src/capabilities/react-root.tsx +4 -3
- package/src/capabilities/react-surface.tsx +4 -4
- package/src/capabilities/settings.ts +1 -0
- package/src/capabilities/state.ts +12 -4
- package/src/capabilities/toolkit.ts +28 -17
- package/src/capabilities/tools.ts +1 -1
- package/src/components/DeckLayout/ActiveNode.tsx +1 -1
- package/src/components/DeckLayout/Banner.tsx +3 -3
- package/src/components/DeckLayout/ContentEmpty.tsx +1 -1
- package/src/components/DeckLayout/DeckLayout.stories.tsx +63 -0
- package/src/components/DeckLayout/DeckLayout.tsx +8 -278
- package/src/components/DeckLayout/DeckMain.tsx +281 -0
- package/src/components/DeckLayout/Dialog.tsx +1 -1
- package/src/components/DeckLayout/Fallback.tsx +3 -3
- package/src/components/DeckLayout/Popover.tsx +4 -13
- package/src/components/DeckLayout/StatusBar.tsx +1 -1
- package/src/components/DeckLayout/Toast.tsx +28 -3
- package/src/components/DeckSettings/DeckSettings.tsx +80 -65
- package/src/components/Plank/Plank.stories.tsx +7 -9
- package/src/components/Plank/Plank.tsx +69 -37
- package/src/components/Plank/PlankControls.tsx +6 -5
- package/src/components/Plank/PlankError.tsx +2 -2
- package/src/components/Plank/PlankHeading.tsx +10 -9
- package/src/components/Sidebar/ComplementarySidebar.tsx +36 -16
- package/src/components/Sidebar/Sidebar.tsx +3 -3
- package/src/components/Sidebar/SidebarButton.tsx +14 -13
- package/src/events.ts +2 -2
- package/src/hooks/useCompanions.ts +1 -1
- package/src/hooks/useDeckCompanions.ts +2 -1
- package/src/hooks/useHoistStatusbar.ts +6 -4
- package/src/meta.ts +6 -3
- package/src/translations.ts +2 -0
- package/src/types/schema.ts +5 -3
- package/dist/lib/browser/app-graph-builder-DVEKLXB4.mjs.map +0 -7
- package/dist/lib/browser/check-app-scheme-3BQJXWEY.mjs.map +0 -7
- package/dist/lib/browser/chunk-6MQIYIPY.mjs.map +0 -7
- package/dist/lib/browser/chunk-F5BQOOEG.mjs.map +0 -7
- package/dist/lib/browser/chunk-M57WD3V6.mjs +0 -16
- package/dist/lib/browser/chunk-M57WD3V6.mjs.map +0 -7
- package/dist/lib/browser/chunk-MX7WRVX3.mjs.map +0 -7
- package/dist/lib/browser/chunk-NRCPV6AV.mjs.map +0 -7
- package/dist/lib/browser/chunk-Z5KITAZW.mjs +0 -13
- package/dist/lib/browser/chunk-Z5KITAZW.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-2SUIIV6N.mjs.map +0 -7
- package/dist/lib/browser/react-root-GVZANZX7.mjs.map +0 -7
- package/dist/lib/browser/react-surface-NXSSD2GW.mjs.map +0 -7
- package/dist/lib/browser/state-CRXR7X63.mjs +0 -12
- package/dist/lib/browser/toolkit-OBKFXX23.mjs +0 -47
- package/dist/lib/browser/toolkit-OBKFXX23.mjs.map +0 -7
- /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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
168
|
-
|
|
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
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
|
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 {
|
|
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
|
|
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(
|
|
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(
|
|
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 {
|
|
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(
|
|
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,
|
|
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 {
|
|
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(
|
|
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:
|
|
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:
|
|
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/${
|
|
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]
|
|
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}
|
|
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,
|
|
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 {
|
|
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:
|
|
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(
|
|
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=
|
|
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=
|
|
133
|
-
|
|
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(
|
|
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
|
-
<
|
|
174
|
-
|
|
175
|
-
|
|
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 {
|
|
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:
|
|
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
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
65
|
-
if (layoutContext.complementarySidebarState === 'expanded' && !current &&
|
|
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
|
|
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 {
|
|
7
|
+
import { meta } from './meta';
|
|
8
8
|
|
|
9
9
|
export namespace DeckEvents {
|
|
10
|
-
export const StateReady = Events.createStateEvent(`${
|
|
10
|
+
export const StateReady = Events.createStateEvent(`${meta.id}/state-ready`);
|
|
11
11
|
}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Label
|
|
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
|
|
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 {
|
|
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:
|
|
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
|
};
|