@dxos/plugin-testing 0.0.0 → 0.8.4-main.21d9917

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 (52) hide show
  1. package/dist/lib/browser/chunk-YHPXIILW.mjs +21 -0
  2. package/dist/lib/browser/chunk-YHPXIILW.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +202 -0
  4. package/dist/lib/browser/index.mjs.map +7 -0
  5. package/dist/lib/browser/meta.json +1 -0
  6. package/dist/lib/browser/operation-resolver-B2DOYB7C.mjs +111 -0
  7. package/dist/lib/browser/operation-resolver-B2DOYB7C.mjs.map +7 -0
  8. package/dist/lib/browser/state-2M3RMJYA.mjs +42 -0
  9. package/dist/lib/browser/state-2M3RMJYA.mjs.map +7 -0
  10. package/dist/lib/node-esm/chunk-OWK6XE6C.mjs +23 -0
  11. package/dist/lib/node-esm/chunk-OWK6XE6C.mjs.map +7 -0
  12. package/dist/lib/node-esm/index.mjs +203 -0
  13. package/dist/lib/node-esm/index.mjs.map +7 -0
  14. package/dist/lib/node-esm/meta.json +1 -0
  15. package/dist/lib/node-esm/operation-resolver-DJI7OPBP.mjs +112 -0
  16. package/dist/lib/node-esm/operation-resolver-DJI7OPBP.mjs.map +7 -0
  17. package/dist/lib/node-esm/state-UF2MWBFU.mjs +43 -0
  18. package/dist/lib/node-esm/state-UF2MWBFU.mjs.map +7 -0
  19. package/dist/types/src/StorybookPlugin.d.ts +7 -0
  20. package/dist/types/src/StorybookPlugin.d.ts.map +1 -0
  21. package/dist/types/src/capabilities/index.d.ts +3 -0
  22. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  23. package/dist/types/src/capabilities/operation-resolver/index.d.ts +3 -0
  24. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
  25. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
  26. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
  27. package/dist/types/src/capabilities/state/index.d.ts +14 -0
  28. package/dist/types/src/capabilities/state/index.d.ts.map +1 -0
  29. package/dist/types/src/capabilities/state/state.d.ts +18 -0
  30. package/dist/types/src/capabilities/state/state.d.ts.map +1 -0
  31. package/dist/types/src/components/Layout.d.ts +6 -0
  32. package/dist/types/src/components/Layout.d.ts.map +1 -0
  33. package/dist/types/src/components/index.d.ts +2 -0
  34. package/dist/types/src/components/index.d.ts.map +1 -0
  35. package/dist/types/src/core.d.ts +13 -0
  36. package/dist/types/src/core.d.ts.map +1 -0
  37. package/dist/types/src/index.d.ts +4 -0
  38. package/dist/types/src/index.d.ts.map +1 -0
  39. package/dist/types/src/meta.d.ts +3 -0
  40. package/dist/types/src/meta.d.ts.map +1 -0
  41. package/dist/types/src/types/capabilities.d.ts +25 -0
  42. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  43. package/dist/types/src/types/index.d.ts +2 -0
  44. package/dist/types/src/types/index.d.ts.map +1 -0
  45. package/dist/types/tsconfig.tsbuildinfo +1 -0
  46. package/package.json +19 -14
  47. package/src/StorybookPlugin.ts +2 -2
  48. package/src/capabilities/operation-resolver/operation-resolver.ts +49 -33
  49. package/src/capabilities/state/state.tsx +21 -31
  50. package/src/components/Layout.tsx +106 -66
  51. package/src/core.ts +3 -10
  52. package/src/types/capabilities.ts +7 -2
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@dxos/plugin-testing",
3
- "version": "0.0.0",
3
+ "version": "0.8.4-main.21d9917",
4
4
  "description": "Plugin testing utils",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
13
  "sideEffects": true,
@@ -21,15 +25,16 @@
21
25
  "src"
22
26
  ],
23
27
  "dependencies": {
24
- "@preact-signals/safe-react": "^0.9.0",
25
- "@dxos/app-framework": "0.8.3",
26
- "@dxos/operation": "0.0.0",
27
- "@dxos/live-object": "0.8.3",
28
- "@dxos/plugin-attention": "0.8.3",
29
- "@dxos/plugin-client": "0.8.3",
30
- "@dxos/util": "0.8.3",
31
- "@dxos/plugin-theme": "0.8.3",
32
- "@dxos/plugin-graph": "0.8.3"
28
+ "@effect-atom/atom": "^0.4.13",
29
+ "@effect-atom/atom-react": "^0.4.6",
30
+ "@dxos/app-framework": "0.8.4-main.21d9917",
31
+ "@dxos/operation": "0.8.4-main.21d9917",
32
+ "@dxos/plugin-client": "0.8.4-main.21d9917",
33
+ "@dxos/plugin-attention": "0.8.4-main.21d9917",
34
+ "@dxos/plugin-theme": "0.8.4-main.21d9917",
35
+ "@dxos/react-ui-mosaic": "0.8.4-main.21d9917",
36
+ "@dxos/plugin-graph": "0.8.4-main.21d9917",
37
+ "@dxos/util": "0.8.4-main.21d9917"
33
38
  },
34
39
  "devDependencies": {
35
40
  "@types/react": "~19.2.7",
@@ -38,15 +43,15 @@
38
43
  "react": "~19.2.3",
39
44
  "react-dom": "~19.2.3",
40
45
  "vite": "7.1.9",
41
- "@dxos/react-ui": "0.8.3",
42
- "@dxos/ui-theme": "0.0.0"
46
+ "@dxos/react-ui": "0.8.4-main.21d9917",
47
+ "@dxos/ui-theme": "0.8.4-main.21d9917"
43
48
  },
44
49
  "peerDependencies": {
45
50
  "effect": "3.19.11",
46
51
  "react": "~19.2.3",
47
52
  "react-dom": "~19.2.3",
48
- "@dxos/react-ui": "0.8.3",
49
- "@dxos/ui-theme": "0.0.0"
53
+ "@dxos/ui-theme": "0.8.4-main.21d9917",
54
+ "@dxos/react-ui": "0.8.4-main.21d9917"
50
55
  },
51
56
  "publishConfig": {
52
57
  "access": "public"
@@ -9,10 +9,10 @@ import { Capability, Common, Plugin } from '@dxos/app-framework';
9
9
  import { OperationResolver, State } from './capabilities';
10
10
  import { Layout } from './components';
11
11
  import { meta } from './meta';
12
- import { type LayoutState } from './types';
12
+ import { type LayoutStateProps } from './types';
13
13
 
14
14
  export type StorybookPluginOptions = {
15
- initialState?: Partial<LayoutState>;
15
+ initialState?: Partial<LayoutStateProps>;
16
16
  };
17
17
 
18
18
  export const StorybookPlugin = Plugin.define<StorybookPluginOptions>(meta).pipe(
@@ -7,29 +7,41 @@ import * as Effect from 'effect/Effect';
7
7
  import { Capability, Common } from '@dxos/app-framework';
8
8
  import { OperationResolver } from '@dxos/operation';
9
9
 
10
- import { LayoutState } from '../../types';
10
+ import { LayoutState, type LayoutStateProps } from '../../types';
11
11
 
12
12
  export default Capability.makeModule(
13
13
  Effect.fnUntraced(function* () {
14
+ const registry = yield* Capability.get(Common.Capability.AtomRegistry);
15
+ const stateAtom = yield* Capability.get(LayoutState);
16
+
17
+ const updateState = (fn: (state: LayoutStateProps) => Partial<LayoutStateProps>) => {
18
+ const current = registry.get(stateAtom);
19
+ registry.set(stateAtom, { ...current, ...fn(current) });
20
+ };
21
+
14
22
  return Capability.contributes(Common.Capability.OperationResolver, [
15
23
  OperationResolver.make({
16
24
  operation: Common.LayoutOperation.UpdateSidebar,
17
25
  handler: Effect.fnUntraced(function* ({ state }) {
18
- const layout = yield* Capability.get(LayoutState);
19
- const next = state ?? layout.sidebarState;
20
- if (next !== layout.sidebarState) {
21
- layout.sidebarState = next;
22
- }
26
+ updateState((layout) => {
27
+ const next = state ?? layout.sidebarState;
28
+ if (next !== layout.sidebarState) {
29
+ return { sidebarState: next };
30
+ }
31
+ return {};
32
+ });
23
33
  }),
24
34
  }),
25
35
  OperationResolver.make({
26
36
  operation: Common.LayoutOperation.UpdateComplementary,
27
37
  handler: Effect.fnUntraced(function* ({ state }) {
28
- const layout = yield* Capability.get(LayoutState);
29
- const next = state ?? layout.complementarySidebarState;
30
- if (next !== layout.complementarySidebarState) {
31
- layout.complementarySidebarState = next;
32
- }
38
+ updateState((layout) => {
39
+ const next = state ?? layout.complementarySidebarState;
40
+ if (next !== layout.complementarySidebarState) {
41
+ return { complementarySidebarState: next };
42
+ }
43
+ return {};
44
+ });
33
45
  }),
34
46
  }),
35
47
  OperationResolver.make({
@@ -43,38 +55,42 @@ export default Capability.makeModule(
43
55
  overlayStyle,
44
56
  props,
45
57
  }) {
46
- const layout = yield* Capability.get(LayoutState);
47
- layout.dialogOpen = state ?? Boolean(subject);
48
- layout.dialogType = type ?? 'default';
49
- layout.dialogBlockAlign = blockAlign ?? 'center';
50
- layout.dialogOverlayClasses = overlayClasses;
51
- layout.dialogOverlayStyle = overlayStyle;
52
- layout.dialogContent = subject ? { component: subject, props } : null;
58
+ updateState(() => ({
59
+ dialogOpen: state ?? Boolean(subject),
60
+ dialogType: type ?? 'default',
61
+ dialogBlockAlign: blockAlign ?? 'center',
62
+ dialogOverlayClasses: overlayClasses,
63
+ dialogOverlayStyle: overlayStyle,
64
+ dialogContent: subject ? { component: subject, props } : null,
65
+ }));
53
66
  }),
54
67
  }),
55
68
  OperationResolver.make({
56
69
  operation: Common.LayoutOperation.UpdatePopover,
57
70
  handler: Effect.fnUntraced(function* (input) {
58
- const layout = yield* Capability.get(LayoutState);
59
- const { subject, state, side, props } = input;
60
- layout.popoverContent =
61
- typeof subject === 'string' ? { component: subject, props } : subject ? { subject } : undefined;
62
- layout.popoverOpen = state ?? Boolean(subject);
63
- layout.popoverSide = side;
64
- if ('variant' in input && input.variant === 'virtual') {
65
- layout.popoverVariant = 'virtual';
66
- layout.popoverAnchor = input.anchor;
67
- } else if ('anchorId' in input) {
68
- layout.popoverVariant = 'react';
69
- layout.popoverAnchorId = input.anchorId;
70
- }
71
+ const { subject, state, side, kind, props } = input;
72
+ updateState(() => {
73
+ const base: Partial<LayoutStateProps> = {
74
+ popoverKind: kind ?? 'base',
75
+ popoverTitle: kind === 'card' ? input.title : undefined,
76
+ popoverContent:
77
+ typeof subject === 'string' ? { component: subject, props } : subject ? { subject } : undefined,
78
+ popoverOpen: state ?? Boolean(subject),
79
+ popoverSide: side,
80
+ };
81
+ if ('variant' in input && input.variant === 'virtual') {
82
+ return { ...base, popoverVariant: 'virtual', popoverAnchor: input.anchor };
83
+ } else if ('anchorId' in input) {
84
+ return { ...base, popoverVariant: 'react', popoverAnchorId: input.anchorId };
85
+ }
86
+ return base;
87
+ });
71
88
  }),
72
89
  }),
73
90
  OperationResolver.make({
74
91
  operation: Common.LayoutOperation.SwitchWorkspace,
75
92
  handler: Effect.fnUntraced(function* ({ subject }) {
76
- const layout = yield* Capability.get(LayoutState);
77
- layout.workspace = subject;
93
+ updateState(() => ({ workspace: subject }));
78
94
  }),
79
95
  }),
80
96
  ]);
@@ -2,14 +2,14 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { Atom } from '@effect-atom/atom-react';
5
6
  import * as Effect from 'effect/Effect';
6
7
 
7
8
  import { Capability, Common } from '@dxos/app-framework';
8
- import { live } from '@dxos/live-object';
9
9
 
10
- import { LayoutState } from '../../types';
10
+ import { LayoutState, type LayoutStateProps } from '../../types';
11
11
 
12
- const defaultState: LayoutState = {
12
+ const defaultState: LayoutStateProps = {
13
13
  sidebarState: 'closed',
14
14
  complementarySidebarState: 'closed',
15
15
  dialogOpen: false,
@@ -17,37 +17,27 @@ const defaultState: LayoutState = {
17
17
  };
18
18
 
19
19
  export default Capability.makeModule(
20
- Effect.fnUntraced(function* (props?: { initialState?: Partial<LayoutState> }) {
20
+ Effect.fnUntraced(function* (props?: { initialState?: Partial<LayoutStateProps> }) {
21
21
  const { initialState } = props ?? {};
22
- const state = live<LayoutState>({ ...defaultState, ...initialState });
22
+ const stateAtom = Atom.make<LayoutStateProps>({ ...defaultState, ...initialState });
23
23
 
24
- const layout = live<Common.Capability.Layout>({
25
- get mode() {
26
- return 'storybook';
27
- },
28
- get dialogOpen() {
29
- return state.dialogOpen;
30
- },
31
- get sidebarOpen() {
32
- return state.sidebarState === 'expanded';
33
- },
34
- get complementarySidebarOpen() {
35
- return state.complementarySidebarState === 'expanded';
36
- },
37
- get workspace() {
38
- return state.workspace;
39
- },
40
- get active() {
41
- return [];
42
- },
43
- get inactive() {
44
- return [];
45
- },
46
- get scrollIntoView() {
47
- return undefined;
48
- },
24
+ const layoutAtom = Atom.make((get): Common.Capability.Layout => {
25
+ const state = get(stateAtom);
26
+ return {
27
+ mode: 'storybook',
28
+ dialogOpen: state.dialogOpen,
29
+ sidebarOpen: state.sidebarState === 'expanded',
30
+ complementarySidebarOpen: state.complementarySidebarState === 'expanded',
31
+ workspace: state.workspace,
32
+ active: [],
33
+ inactive: [],
34
+ scrollIntoView: undefined,
35
+ };
49
36
  });
50
37
 
51
- return [Capability.contributes(LayoutState, state), Capability.contributes(Common.Capability.Layout, layout)];
38
+ return [
39
+ Capability.contributes(LayoutState, stateAtom),
40
+ Capability.contributes(Common.Capability.Layout, layoutAtom),
41
+ ];
52
42
  }),
53
43
  );
@@ -2,7 +2,8 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React, { type PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react';
5
+ import { RegistryContext, useAtomValue } from '@effect-atom/atom-react';
6
+ import React, { type PropsWithChildren, useCallback, useContext, useEffect, useRef, useState } from 'react';
6
7
 
7
8
  import { Surface, useCapability } from '@dxos/app-framework/react';
8
9
  import {
@@ -11,23 +12,36 @@ import {
11
12
  Main,
12
13
  Popover,
13
14
  type PopoverContentInteractOutsideEvent,
15
+ toLocalizedString,
14
16
  useTranslation,
15
17
  } from '@dxos/react-ui';
18
+ import { Card, Mosaic } from '@dxos/react-ui-mosaic';
16
19
  import { descriptionMessage, mx } from '@dxos/ui-theme';
17
20
 
18
21
  import { meta } from '../meta';
19
- import { LayoutState } from '../types';
22
+ import { LayoutState, type LayoutStateProps } from '../types';
20
23
 
21
24
  const debounce_delay = 100;
22
25
 
23
26
  // TODO(wittjosiah): Support dialogs, tooltips, maybe toast.
24
27
  export const Layout = ({ children }: PropsWithChildren<{}>) => {
28
+ const { t } = useTranslation(meta.id);
25
29
  const trigger = useRef<HTMLButtonElement | null>(null);
26
- const layout = useCapability(LayoutState);
30
+ const registry = useContext(RegistryContext);
31
+ const stateAtom = useCapability(LayoutState);
32
+ const layout = useAtomValue(stateAtom);
27
33
  const [iter, setIter] = useState(0);
28
34
  const [open, setOpen] = useState(false);
29
35
  const debounceRef = useRef<NodeJS.Timeout | null>(null);
30
36
 
37
+ const updateState = useCallback(
38
+ (updates: Partial<LayoutStateProps>) => {
39
+ const current = registry.get(stateAtom);
40
+ registry.set(stateAtom, { ...current, ...updates });
41
+ },
42
+ [registry, stateAtom],
43
+ );
44
+
31
45
  useEffect(() => {
32
46
  setOpen(false);
33
47
  if (debounceRef.current) {
@@ -41,77 +55,103 @@ export const Layout = ({ children }: PropsWithChildren<{}>) => {
41
55
  }
42
56
  }, [layout.popoverAnchor, layout.popoverContent, layout.popoverOpen]);
43
57
 
44
- const handleInteractOutside = useCallback((event: KeyboardEvent | PopoverContentInteractOutsideEvent) => {
45
- if (
46
- // TODO(thure): CodeMirror should not focus itself when it updates.
47
- event.type === 'dismissableLayer.focusOutside' &&
48
- (event.currentTarget as HTMLElement | undefined)?.classList.contains('cm-content')
49
- ) {
50
- event.preventDefault();
51
- } else {
52
- setOpen(false);
53
- layout.popoverOpen = false;
54
- layout.popoverAnchor = undefined;
55
- layout.popoverAnchorId = undefined;
56
- layout.popoverSide = undefined;
57
- }
58
- }, []);
58
+ const handleClose = useCallback(() => {
59
+ setOpen(false);
60
+ updateState({
61
+ popoverOpen: false,
62
+ popoverAnchor: undefined,
63
+ popoverAnchorId: undefined,
64
+ popoverSide: undefined,
65
+ });
66
+ }, [updateState]);
67
+
68
+ const handleInteractOutside = useCallback(
69
+ (event: KeyboardEvent | PopoverContentInteractOutsideEvent) => {
70
+ if (
71
+ // TODO(thure): CodeMirror should not focus itself when it updates.
72
+ event.type === 'dismissableLayer.focusOutside' &&
73
+ (event.currentTarget as HTMLElement | undefined)?.classList.contains('cm-content')
74
+ ) {
75
+ event.preventDefault();
76
+ } else {
77
+ handleClose();
78
+ }
79
+ },
80
+ [handleClose],
81
+ );
59
82
 
60
83
  const DialogRoot = layout.dialogType === 'alert' ? AlertDialog.Root : Dialog.Root;
61
84
  const DialogOverlay = layout.dialogType === 'alert' ? AlertDialog.Overlay : Dialog.Overlay;
62
85
 
63
86
  return (
64
87
  <div role='none' className='fixed inset-0 flex overflow-hidden'>
65
- <Popover.Root open={open}>
66
- <Main.Root
67
- navigationSidebarState={layout.sidebarState}
68
- complementarySidebarState={layout.complementarySidebarState}
69
- onNavigationSidebarStateChange={(next) => (layout.sidebarState = next)}
70
- onComplementarySidebarStateChange={(next) => (layout.complementarySidebarState = next)}
71
- >
72
- {children}
73
- </Main.Root>
74
-
75
- <DialogRoot
76
- modal={layout.dialogBlockAlign !== 'end'}
77
- open={layout.dialogOpen}
78
- onOpenChange={(nextOpen) => (layout.dialogOpen = nextOpen)}
79
- >
80
- {layout.dialogBlockAlign === 'end' ? (
81
- <Surface
82
- role='dialog'
83
- data={layout.dialogContent}
84
- limit={1}
85
- fallback={ContentError}
86
- placeholder={<div />}
87
- />
88
- ) : (
89
- <DialogOverlay
90
- blockAlign={layout.dialogBlockAlign}
91
- classNames={layout.dialogOverlayClasses}
92
- style={layout.dialogOverlayStyle}
93
- >
94
- <Surface role='dialog' data={layout.dialogContent} limit={1} fallback={ContentError} />
95
- </DialogOverlay>
96
- )}
97
- </DialogRoot>
88
+ <Mosaic.Root>
89
+ <Popover.Root open={open}>
90
+ <Main.Root
91
+ navigationSidebarState={layout.sidebarState}
92
+ complementarySidebarState={layout.complementarySidebarState}
93
+ onNavigationSidebarStateChange={(next) => updateState({ sidebarState: next })}
94
+ onComplementarySidebarStateChange={(next) => updateState({ complementarySidebarState: next })}
95
+ >
96
+ {children}
97
+ </Main.Root>
98
98
 
99
- <Popover.VirtualTrigger key={iter} virtualRef={trigger} />
100
- <Popover.Portal>
101
- <Popover.Content
102
- side={layout.popoverSide}
103
- onInteractOutside={handleInteractOutside}
104
- onEscapeKeyDown={handleInteractOutside}
105
- sticky='always'
106
- hideWhenDetached
99
+ <DialogRoot
100
+ modal={layout.dialogBlockAlign !== 'end'}
101
+ open={layout.dialogOpen}
102
+ onOpenChange={(nextOpen) => updateState({ dialogOpen: nextOpen })}
107
103
  >
108
- <Popover.Viewport>
109
- <Surface role='card--popover' data={layout.popoverContent} limit={1} />
110
- </Popover.Viewport>
111
- <Popover.Arrow />
112
- </Popover.Content>
113
- </Popover.Portal>
114
- </Popover.Root>
104
+ {layout.dialogBlockAlign === 'end' ? (
105
+ <Surface
106
+ role='dialog'
107
+ data={layout.dialogContent}
108
+ limit={1}
109
+ fallback={ContentError}
110
+ placeholder={<div />}
111
+ />
112
+ ) : (
113
+ <DialogOverlay
114
+ blockAlign={layout.dialogBlockAlign}
115
+ classNames={layout.dialogOverlayClasses}
116
+ style={layout.dialogOverlayStyle}
117
+ >
118
+ <Surface role='dialog' data={layout.dialogContent} limit={1} fallback={ContentError} />
119
+ </DialogOverlay>
120
+ )}
121
+ </DialogRoot>
122
+
123
+ <Popover.VirtualTrigger key={iter} virtualRef={trigger} />
124
+ <Popover.Portal>
125
+ <Popover.Content
126
+ side={layout.popoverSide}
127
+ onInteractOutside={handleInteractOutside}
128
+ onEscapeKeyDown={handleInteractOutside}
129
+ sticky='always'
130
+ hideWhenDetached
131
+ >
132
+ <Popover.Viewport>
133
+ {layout.popoverKind === 'card' && (
134
+ <Card.Root>
135
+ <Card.Toolbar>
136
+ {/* TODO(wittjosiah): Cleaner way to handle no drag handle in toolbar? */}
137
+ <span />
138
+ {layout.popoverTitle ? (
139
+ <Card.Title>{toLocalizedString(layout.popoverTitle, t)}</Card.Title>
140
+ ) : (
141
+ <span />
142
+ )}
143
+ <Card.Close onClick={handleClose} />
144
+ </Card.Toolbar>
145
+ <Surface role='card--content' data={layout.popoverContent} limit={1} />
146
+ </Card.Root>
147
+ )}
148
+ {layout.popoverKind === 'base' && <Surface role='popover' data={layout.popoverContent} limit={1} />}
149
+ </Popover.Viewport>
150
+ <Popover.Arrow />
151
+ </Popover.Content>
152
+ </Popover.Portal>
153
+ </Popover.Root>
154
+ </Mosaic.Root>
115
155
  </div>
116
156
  );
117
157
  };
package/src/core.ts CHANGED
@@ -9,16 +9,9 @@ import { GraphPlugin } from '@dxos/plugin-graph';
9
9
  import { ThemePlugin } from '@dxos/plugin-theme';
10
10
  import { defaultTx } from '@dxos/ui-theme';
11
11
 
12
- export {
13
- // Re-export common framework plugins.
14
- AttentionPlugin,
15
- ClientPlugin,
16
- GraphPlugin,
17
- OperationPlugin,
18
- RuntimePlugin,
19
- SettingsPlugin,
20
- ThemePlugin,
21
- };
12
+ // TODO(burdon): Remove this.
13
+ // Re-export common framework plugins.
14
+ export { AttentionPlugin, ClientPlugin, GraphPlugin, OperationPlugin, RuntimePlugin, SettingsPlugin, ThemePlugin };
22
15
 
23
16
  /**
24
17
  * Core plugins for testing/storybook environments.
@@ -2,11 +2,14 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { type Atom } from '@effect-atom/atom-react';
6
+
5
7
  import { Capability } from '@dxos/app-framework';
8
+ import { type Label } from '@dxos/react-ui';
6
9
 
7
10
  import { meta } from '../meta';
8
11
 
9
- export type LayoutState = {
12
+ export type LayoutStateProps = {
10
13
  sidebarState?: 'expanded' | 'collapsed' | 'closed';
11
14
  complementarySidebarState?: 'expanded' | 'collapsed' | 'closed';
12
15
 
@@ -23,9 +26,11 @@ export type LayoutState = {
23
26
  popoverVariant?: 'virtual' | 'react';
24
27
  popoverAnchor?: HTMLButtonElement;
25
28
  popoverAnchorId?: string;
29
+ popoverKind?: 'base' | 'card';
30
+ popoverTitle?: Label;
26
31
  popoverContent?: any;
27
32
 
28
33
  workspace: string;
29
34
  };
30
35
 
31
- export const LayoutState = Capability.make<LayoutState>(`${meta.id}/state`);
36
+ export const LayoutState = Capability.make<Atom.Writable<LayoutStateProps>>(`${meta.id}/state`);