@dxos/plugin-simple-layout 0.9.0 → 0.9.1-main.c7dcc2e112

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 (83) hide show
  1. package/dist/lib/neutral/capabilities/index.mjs +3 -3
  2. package/dist/lib/neutral/{chunk-7UDV3JDT.mjs → chunk-5DF656PN.mjs} +2 -2
  3. package/dist/lib/neutral/{chunk-FD2CAY4Q.mjs → chunk-LLTNQSEB.mjs} +2 -2
  4. package/dist/lib/neutral/chunk-LLTNQSEB.mjs.map +7 -0
  5. package/dist/lib/neutral/chunk-NTI42Y3O.mjs +8 -0
  6. package/dist/lib/neutral/chunk-T2JFEPBT.mjs +31 -0
  7. package/dist/lib/neutral/chunk-T2JFEPBT.mjs.map +7 -0
  8. package/dist/lib/neutral/{close-WKMURGUB.mjs → close-5ZAMG3N7.mjs} +3 -3
  9. package/dist/lib/neutral/components/index.mjs +10 -12
  10. package/dist/lib/neutral/components/index.mjs.map +3 -3
  11. package/dist/lib/neutral/hooks/index.mjs +4 -4
  12. package/dist/lib/neutral/hooks/index.mjs.map +3 -3
  13. package/dist/lib/neutral/index.mjs +2 -2
  14. package/dist/lib/neutral/meta.json +1 -1
  15. package/dist/lib/neutral/meta.mjs +1 -1
  16. package/dist/lib/neutral/{open-Y4KNROHW.mjs → open-TI35QPEC.mjs} +6 -6
  17. package/dist/lib/neutral/open-TI35QPEC.mjs.map +7 -0
  18. package/dist/lib/neutral/operations/index.mjs +1 -1
  19. package/dist/lib/neutral/plugin.mjs +2 -2
  20. package/dist/lib/neutral/{react-root-VE265VX4.mjs → react-root-JX7QKKR2.mjs} +2 -2
  21. package/dist/lib/neutral/{react-root-VE265VX4.mjs.map → react-root-JX7QKKR2.mjs.map} +3 -3
  22. package/dist/lib/neutral/{react-surface-N3PGKIYV.mjs → react-surface-GV3O3I25.mjs} +6 -12
  23. package/dist/lib/neutral/react-surface-GV3O3I25.mjs.map +7 -0
  24. package/dist/lib/neutral/{revert-workspace-ST6NZUNG.mjs → revert-workspace-KI6QHPTX.mjs} +3 -3
  25. package/dist/lib/neutral/{set-6ZRLWPJS.mjs → set-C4HII3NF.mjs} +3 -3
  26. package/dist/lib/neutral/{switch-workspace-PYWPTMFO.mjs → switch-workspace-E664GRHY.mjs} +5 -5
  27. package/dist/lib/neutral/switch-workspace-E664GRHY.mjs.map +7 -0
  28. package/dist/lib/neutral/translations.mjs +1 -1
  29. package/dist/lib/neutral/translations.mjs.map +3 -3
  30. package/dist/lib/neutral/types/index.mjs +1 -1
  31. package/dist/lib/neutral/{update-complementary-HKWF5OXT.mjs → update-complementary-BCWHK4SM.mjs} +3 -3
  32. package/dist/lib/neutral/{update-dialog-P4ASXCE7.mjs → update-dialog-TCKGS26T.mjs} +3 -3
  33. package/dist/lib/neutral/{update-popover-REAKC2GN.mjs → update-popover-NRI2OATE.mjs} +3 -3
  34. package/dist/lib/neutral/{url-handler-DGZ4WISE.mjs → url-handler-BJX33VLR.mjs} +5 -5
  35. package/dist/lib/neutral/url-handler-BJX33VLR.mjs.map +7 -0
  36. package/dist/types/dx.config.d.ts +28 -0
  37. package/dist/types/dx.config.d.ts.map +1 -0
  38. package/dist/types/src/capabilities/index.d.ts +6 -18
  39. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  40. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  41. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  42. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  43. package/dist/types/src/hooks/useAppBarProps.d.ts.map +1 -1
  44. package/dist/types/src/meta.d.ts +28 -2
  45. package/dist/types/src/meta.d.ts.map +1 -1
  46. package/dist/types/src/operations/open.d.ts.map +1 -1
  47. package/dist/types/src/operations/switch-workspace.d.ts.map +1 -1
  48. package/dist/types/src/types/SimpleLayoutCapabilities.d.ts +1 -1
  49. package/dist/types/src/types/SimpleLayoutCapabilities.d.ts.map +1 -1
  50. package/dist/types/tsconfig.tsbuildinfo +1 -1
  51. package/dx.config.ts +20 -0
  52. package/package.json +29 -28
  53. package/src/capabilities/react-root.tsx +1 -1
  54. package/src/capabilities/react-surface.tsx +8 -16
  55. package/src/capabilities/url-handler.ts +4 -4
  56. package/src/components/Home/Home.tsx +3 -3
  57. package/src/components/NavBranch/NavBranch.tsx +2 -2
  58. package/src/components/Popover/Popover.tsx +7 -5
  59. package/src/components/SimpleLayout/AppBar.tsx +2 -2
  60. package/src/hooks/useAppBarProps.ts +3 -2
  61. package/src/hooks/useDrawerActions.ts +1 -1
  62. package/src/hooks/useNavbarActions.ts +1 -1
  63. package/src/meta.ts +3 -13
  64. package/src/operations/open.ts +5 -8
  65. package/src/operations/switch-workspace.ts +2 -2
  66. package/src/translations.ts +1 -1
  67. package/src/types/SimpleLayoutCapabilities.ts +2 -2
  68. package/dist/lib/neutral/chunk-FD2CAY4Q.mjs.map +0 -7
  69. package/dist/lib/neutral/chunk-NKAPURKM.mjs +0 -8
  70. package/dist/lib/neutral/chunk-OK5336TS.mjs +0 -22
  71. package/dist/lib/neutral/chunk-OK5336TS.mjs.map +0 -7
  72. package/dist/lib/neutral/open-Y4KNROHW.mjs.map +0 -7
  73. package/dist/lib/neutral/react-surface-N3PGKIYV.mjs.map +0 -7
  74. package/dist/lib/neutral/switch-workspace-PYWPTMFO.mjs.map +0 -7
  75. package/dist/lib/neutral/url-handler-DGZ4WISE.mjs.map +0 -7
  76. /package/dist/lib/neutral/{chunk-7UDV3JDT.mjs.map → chunk-5DF656PN.mjs.map} +0 -0
  77. /package/dist/lib/neutral/{chunk-NKAPURKM.mjs.map → chunk-NTI42Y3O.mjs.map} +0 -0
  78. /package/dist/lib/neutral/{close-WKMURGUB.mjs.map → close-5ZAMG3N7.mjs.map} +0 -0
  79. /package/dist/lib/neutral/{revert-workspace-ST6NZUNG.mjs.map → revert-workspace-KI6QHPTX.mjs.map} +0 -0
  80. /package/dist/lib/neutral/{set-6ZRLWPJS.mjs.map → set-C4HII3NF.mjs.map} +0 -0
  81. /package/dist/lib/neutral/{update-complementary-HKWF5OXT.mjs.map → update-complementary-BCWHK4SM.mjs.map} +0 -0
  82. /package/dist/lib/neutral/{update-dialog-P4ASXCE7.mjs.map → update-dialog-TCKGS26T.mjs.map} +0 -0
  83. /package/dist/lib/neutral/{update-popover-REAKC2GN.mjs.map → update-popover-NRI2OATE.mjs.map} +0 -0
package/dx.config.ts ADDED
@@ -0,0 +1,20 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Config2 } from '@dxos/app-framework/config';
6
+ import { trim } from '@dxos/util';
7
+
8
+ export default Config2.make({
9
+ plugin: {
10
+ key: 'org.dxos.plugin.simpleLayout',
11
+ name: 'Simple Layout',
12
+ author: 'DXOS',
13
+ description: trim`
14
+ Minimal layout plugin for simplified UI contexts like popover windows.
15
+ Provides basic content rendering without sidebars or complex navigation.
16
+ `,
17
+ icon: { key: 'ph--layout--regular' },
18
+ tags: ['system'],
19
+ },
20
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-simple-layout",
3
- "version": "0.9.0",
3
+ "version": "0.9.1-main.c7dcc2e112",
4
4
  "description": "Simple layout plugin for minimal UI contexts like popover windows.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -74,28 +74,29 @@
74
74
  "types": "dist/types/src/index.d.ts",
75
75
  "files": [
76
76
  "dist",
77
+ "dx.config.ts",
77
78
  "src"
78
79
  ],
79
80
  "dependencies": {
80
81
  "@effect-atom/atom-react": "^0.5.0",
81
82
  "@radix-ui/react-context": "1.1.1",
82
83
  "@tauri-apps/plugin-deep-link": "^2.4.9",
83
- "@dxos/async": "0.9.0",
84
- "@dxos/app-framework": "0.9.0",
85
- "@dxos/context": "0.9.0",
86
- "@dxos/compute": "0.9.0",
87
- "@dxos/echo": "0.9.0",
88
- "@dxos/app-toolkit": "0.9.0",
89
- "@dxos/effect": "0.9.0",
90
- "@dxos/plugin-client": "0.9.0",
91
- "@dxos/plugin-graph": "0.9.0",
92
- "@dxos/react-ui-attention": "0.9.0",
93
- "@dxos/react-ui-menu": "0.9.0",
94
- "@dxos/log": "0.9.0",
95
- "@dxos/react-ui-mosaic": "0.9.0",
96
- "@dxos/util": "0.9.0",
97
- "@dxos/react-ui-search": "0.9.0",
98
- "@dxos/keys": "0.9.0"
84
+ "@dxos/async": "0.9.1-main.c7dcc2e112",
85
+ "@dxos/app-framework": "0.9.1-main.c7dcc2e112",
86
+ "@dxos/app-toolkit": "0.9.1-main.c7dcc2e112",
87
+ "@dxos/context": "0.9.1-main.c7dcc2e112",
88
+ "@dxos/compute": "0.9.1-main.c7dcc2e112",
89
+ "@dxos/echo": "0.9.1-main.c7dcc2e112",
90
+ "@dxos/effect": "0.9.1-main.c7dcc2e112",
91
+ "@dxos/log": "0.9.1-main.c7dcc2e112",
92
+ "@dxos/keys": "0.9.1-main.c7dcc2e112",
93
+ "@dxos/plugin-graph": "0.9.1-main.c7dcc2e112",
94
+ "@dxos/react-ui-menu": "0.9.1-main.c7dcc2e112",
95
+ "@dxos/react-ui-attention": "0.9.1-main.c7dcc2e112",
96
+ "@dxos/plugin-client": "0.9.1-main.c7dcc2e112",
97
+ "@dxos/react-ui-mosaic": "0.9.1-main.c7dcc2e112",
98
+ "@dxos/react-ui-search": "0.9.1-main.c7dcc2e112",
99
+ "@dxos/util": "0.9.1-main.c7dcc2e112"
99
100
  },
100
101
  "devDependencies": {
101
102
  "@types/react": "~19.2.7",
@@ -104,22 +105,22 @@
104
105
  "react": "~19.2.3",
105
106
  "react-dom": "~19.2.3",
106
107
  "vite": "^8.0.16",
107
- "@dxos/app-graph": "0.9.0",
108
- "@dxos/plugin-preview": "0.9.0",
109
- "@dxos/plugin-search": "0.9.0",
110
- "@dxos/plugin-space": "0.9.0",
111
- "@dxos/plugin-testing": "0.9.0",
112
- "@dxos/react-ui": "0.9.0",
113
- "@dxos/schema": "0.9.0",
114
- "@dxos/storybook-utils": "0.9.0",
115
- "@dxos/ui-theme": "0.9.0"
108
+ "@dxos/app-graph": "0.9.1-main.c7dcc2e112",
109
+ "@dxos/plugin-preview": "0.9.1-main.c7dcc2e112",
110
+ "@dxos/plugin-search": "0.9.1-main.c7dcc2e112",
111
+ "@dxos/plugin-space": "0.9.1-main.c7dcc2e112",
112
+ "@dxos/react-ui": "0.9.1-main.c7dcc2e112",
113
+ "@dxos/plugin-testing": "0.9.1-main.c7dcc2e112",
114
+ "@dxos/schema": "0.9.1-main.c7dcc2e112",
115
+ "@dxos/storybook-utils": "0.9.1-main.c7dcc2e112",
116
+ "@dxos/ui-theme": "0.9.1-main.c7dcc2e112"
116
117
  },
117
118
  "peerDependencies": {
118
119
  "effect": "3.21.3",
119
120
  "react": "~19.2.3",
120
121
  "react-dom": "~19.2.3",
121
- "@dxos/ui-theme": "0.9.0",
122
- "@dxos/react-ui": "0.9.0"
122
+ "@dxos/ui-theme": "0.9.1-main.c7dcc2e112",
123
+ "@dxos/react-ui": "0.9.1-main.c7dcc2e112"
123
124
  },
124
125
  "publishConfig": {
125
126
  "access": "public"
@@ -13,7 +13,7 @@ import { meta } from '#meta';
13
13
  export default Capability.makeModule(() =>
14
14
  Effect.succeed(
15
15
  Capability.contributes(Capabilities.ReactRoot, {
16
- id: meta.id,
16
+ id: meta.profile.key,
17
17
  root: () => {
18
18
  return <SimpleLayout />;
19
19
  },
@@ -7,17 +7,12 @@ import React from 'react';
7
7
 
8
8
  import { Capabilities, Capability } from '@dxos/app-framework';
9
9
  import { Surface } from '@dxos/app-framework/ui';
10
- import { NOT_FOUND_PATH } from '@dxos/app-toolkit';
11
- import { NotFoundArticle } from '@dxos/app-toolkit/ui';
10
+ import { NotFound } from '@dxos/app-toolkit';
11
+ import { AppSurface, NotFoundArticle } from '@dxos/app-toolkit/ui';
12
12
  import { Node } from '@dxos/plugin-graph';
13
13
 
14
14
  import { Home, NavBranch } from '#components';
15
15
 
16
- type SurfaceData = {
17
- attendableId: string;
18
- properties: Record<string, any>;
19
- };
20
-
21
16
  const ALLOWED_DISPOSITIONS = ['workspace', 'user-account', 'pin-end'];
22
17
 
23
18
  export default Capability.makeModule(() =>
@@ -25,24 +20,21 @@ export default Capability.makeModule(() =>
25
20
  Capability.contributes(Capabilities.ReactSurface, [
26
21
  Surface.create({
27
22
  id: 'home',
28
- role: 'article',
29
- filter: (data): data is SurfaceData => data.attendableId === Node.RootId,
23
+ filter: Surface.makeFilter(AppSurface.Article, (data) => data.attendableId === Node.RootId),
30
24
  component: () => <Home />,
31
25
  }),
32
26
  Surface.create({
33
27
  id: 'notFound',
34
- role: 'article',
35
- filter: (data): data is SurfaceData => data.attendableId === NOT_FOUND_PATH,
28
+ filter: Surface.makeFilter(AppSurface.Article, (data) => data.attendableId === NotFound.NOT_FOUND_PATH),
36
29
  component: () => <NotFoundArticle />,
37
30
  }),
38
31
  Surface.create({
39
32
  id: 'navBranch',
40
- role: 'article',
41
33
  position: 'last',
42
- filter: (data): data is SurfaceData => {
43
- const props = data.properties as Record<string, any>;
44
- return ALLOWED_DISPOSITIONS.includes(props?.disposition) || props?.role === 'branch';
45
- },
34
+ filter: Surface.makeFilter(
35
+ AppSurface.Article,
36
+ (data) => ALLOWED_DISPOSITIONS.includes(data.properties?.disposition) || data.properties?.role === 'branch',
37
+ ),
46
38
  component: ({ data }) => <NavBranch id={data.attendableId} />,
47
39
  }),
48
40
  ]),
@@ -5,7 +5,7 @@
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { Capabilities, Capability } from '@dxos/app-framework';
8
- import { AppCapabilities, LayoutOperation, fromUrlPath, getWorkspaceFromPath, toUrlPath } from '@dxos/app-toolkit';
8
+ import { AppCapabilities, LayoutOperation, Paths } from '@dxos/app-toolkit';
9
9
  import { EffectEx } from '@dxos/effect';
10
10
  import { log } from '@dxos/log';
11
11
  import { isTauri } from '@dxos/util';
@@ -48,8 +48,8 @@ export default Capability.makeModule(
48
48
  pathname = '/';
49
49
  }
50
50
 
51
- const qualifiedId = fromUrlPath(pathname);
52
- const workspace = getWorkspaceFromPath(qualifiedId);
51
+ const qualifiedId = Paths.fromUrlPath(pathname);
52
+ const workspace = Paths.getWorkspaceFromPath(qualifiedId);
53
53
 
54
54
  void invokePromise(LayoutOperation.SwitchWorkspace, { subject: workspace });
55
55
 
@@ -108,7 +108,7 @@ export default Capability.makeModule(
108
108
  lastWorkspace = workspace;
109
109
  lastActive = active;
110
110
 
111
- const path = active ? toUrlPath(active) : toUrlPath(workspace);
111
+ const path = active ? Paths.toUrlPath(active) : Paths.toUrlPath(workspace);
112
112
  if (window.location.pathname !== path) {
113
113
  history.pushState(null, '', `${path}${window.location.search}`);
114
114
  }
@@ -25,7 +25,7 @@ export type HomeProps = {};
25
25
  * Home screen.
26
26
  */
27
27
  export const Home = (_: HomeProps) => {
28
- const { t } = useTranslation(meta.id);
28
+ const { t } = useTranslation(meta.profile.key);
29
29
  const userAccountItem = useItemsByDisposition('user-account')[0];
30
30
  const pinnedItems = useItemsByDisposition('pin-end', true);
31
31
  const workspaceItems = useItemsByDisposition('workspace');
@@ -64,7 +64,7 @@ export const Home = (_: HomeProps) => {
64
64
 
65
65
  const WorkspaceTile: MosaicStackTileComponent<Node.Node> = (props) => {
66
66
  const data = props.data;
67
- const { t } = useTranslation(meta.id);
67
+ const { t } = useTranslation(meta.profile.key);
68
68
  const { invokePromise } = useOperationInvoker();
69
69
  const { selectedValue, registerItem, unregisterItem } = useSearchListItem();
70
70
  const name = toLocalizedString(data.properties.label, t);
@@ -104,7 +104,7 @@ const WorkspaceTile: MosaicStackTileComponent<Node.Node> = (props) => {
104
104
  onClick={handleSelect}
105
105
  ref={cardRef}
106
106
  >
107
- <Card.Header density='md'>
107
+ <Card.Header>
108
108
  <Avatar.Root>
109
109
  <Avatar.Content
110
110
  icon={data.properties.icon}
@@ -28,7 +28,7 @@ export type NavBranchProps = {
28
28
  * spaces, collection sections, type sections, and schema nodes.
29
29
  */
30
30
  export const NavBranch = ({ id }: NavBranchProps) => {
31
- const { t } = useTranslation(meta.id);
31
+ const { t } = useTranslation(meta.profile.key);
32
32
  const { graph } = useAppGraph();
33
33
 
34
34
  useExpandPath(id);
@@ -66,7 +66,7 @@ export const NavBranch = ({ id }: NavBranchProps) => {
66
66
 
67
67
  const NavBranchTile: MosaicStackTileComponent<Node.Node> = (props) => {
68
68
  const data = props.data;
69
- const { t } = useTranslation(meta.id);
69
+ const { t } = useTranslation(meta.profile.key);
70
70
  const { invokePromise } = useOperationInvoker();
71
71
  const ref = useRef<HTMLDivElement>(null);
72
72
  const { selectedValue, registerItem, unregisterItem } = useSearchListItem();
@@ -55,7 +55,7 @@ export const PopoverRoot = ({ children }: PropsWithChildren) => {
55
55
  };
56
56
 
57
57
  export const PopoverContent = () => {
58
- const { t } = useTranslation(meta.id);
58
+ const { t } = useTranslation(meta.profile.key);
59
59
  const { state, updateState } = useSimpleLayoutState();
60
60
  const { setOpen } = useLayoutPopoverContext('PopoverContent');
61
61
  const handleClose = useCallback(() => {
@@ -102,9 +102,11 @@ export const PopoverContent = () => {
102
102
  onEscapeKeyDown={handleInteractOutside}
103
103
  >
104
104
  <Popover.Viewport>
105
- {state.popoverKind === 'base' && state.popoverContent && 'component' in state.popoverContent && (
106
- <Surface.Surface type={AppSurface.Popover} data={state.popoverContent} limit={1} />
107
- )}
105
+ {(state.popoverKind === 'base' || state.popoverKind === 'rename') &&
106
+ state.popoverContent &&
107
+ 'component' in state.popoverContent && (
108
+ <Surface.Surface type={AppSurface.Popover} data={state.popoverContent} limit={1} />
109
+ )}
108
110
  {state.popoverKind === 'card' && (
109
111
  <Card.Root border={false} classNames='dx-card-popover'>
110
112
  <Card.Header>
@@ -114,7 +116,7 @@ export const PopoverContent = () => {
114
116
  <Card.ActionIconButton action='close' onClick={handleClose} />
115
117
  </Card.Header>
116
118
  {state.popoverContent && 'subject' in state.popoverContent && (
117
- <Surface.Surface type={AppSurface.Card} data={state.popoverContent} limit={1} />
119
+ <Surface.Surface type={AppSurface.CardContent} data={state.popoverContent} limit={1} />
118
120
  )}
119
121
  </Card.Root>
120
122
  )}
@@ -36,7 +36,7 @@ export type AppBarProps = {
36
36
  */
37
37
  export const AppBar = composable<HTMLDivElement, AppBarProps>(
38
38
  ({ classNames, title, actions, showBackButton, popoverAnchorId, onAction, onBack, ...props }, forwardedRef) => {
39
- const { t } = useTranslation(meta.id);
39
+ const { t } = useTranslation(meta.profile.key);
40
40
  const menuActions = useMenuActions(actions);
41
41
  const actionsValue = useAtomValue(actions);
42
42
  const hasActions = actionsValue.nodes.length > 0;
@@ -73,7 +73,7 @@ export const AppBar = composable<HTMLDivElement, AppBarProps>(
73
73
  <h1 className='text-center truncate font-thin uppercase'>{displayTitle}</h1>
74
74
  {hasActions ? (
75
75
  <AnchorRoot>
76
- <Menu.Root {...menuActions} caller={meta.id} onAction={onAction}>
76
+ <Menu.Root {...menuActions} caller={meta.profile.key} onAction={onAction}>
77
77
  <Menu.Trigger asChild>
78
78
  <IconButton
79
79
  variant='ghost'
@@ -22,7 +22,7 @@ import { SimpleLayoutCapabilities } from '#types';
22
22
  * Derives activeId from state atom. Returns props ready to spread into the AppBar component.
23
23
  */
24
24
  export const useAppBarProps = (): Omit<AppBarProps, 'classNames'> => {
25
- const { t } = useTranslation(meta.id);
25
+ const { t } = useTranslation(meta.profile.key);
26
26
  const stateAtom = useCapability(SimpleLayoutCapabilities.State);
27
27
  const state = useAtomValue(stateAtom);
28
28
  const { graph } = useAppGraph();
@@ -82,7 +82,8 @@ export const useAppBarProps = (): Omit<AppBarProps, 'classNames'> => {
82
82
  }, [graph, invokePromise, state.active, state.history.length]);
83
83
 
84
84
  // Compute popover anchor ID.
85
- const popoverAnchorId = node && state.popoverAnchorId === `${meta.id}:${node.id}` ? state.popoverAnchorId : undefined;
85
+ const popoverAnchorId =
86
+ node && state.popoverAnchorId === `${meta.profile.key}:${node.id}` ? state.popoverAnchorId : undefined;
86
87
 
87
88
  return {
88
89
  title,
@@ -30,7 +30,7 @@ export type DrawerActions = {
30
30
  * Builds the drawer actions including companion tabs and toolbar buttons.
31
31
  */
32
32
  export const useDrawerActions = (consumerName: string): DrawerActions => {
33
- const { t } = useTranslation(meta.id);
33
+ const { t } = useTranslation(meta.profile.key);
34
34
  const stateAtom = useCapability(SimpleLayoutCapabilities.State);
35
35
  const { graph } = useAppGraph();
36
36
  const runAction = useActionRunner();
@@ -36,7 +36,7 @@ export type NavbarActions = {
36
36
  * Derives everything from graph connection atoms for proper reactivity.
37
37
  */
38
38
  export const useNavbarActions = (): NavbarActions => {
39
- const { t } = useTranslation(meta.id);
39
+ const { t } = useTranslation(meta.profile.key);
40
40
  const { graph } = useAppGraph();
41
41
  const runAction = useActionRunner();
42
42
  const stateAtom = useCapability(SimpleLayoutCapabilities.State);
package/src/meta.ts CHANGED
@@ -3,17 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Plugin } from '@dxos/app-framework';
6
- import { DXN } from '@dxos/keys';
7
- import { trim } from '@dxos/util';
8
6
 
9
- export const meta = Plugin.makeMeta({
10
- key: DXN.make('org.dxos.plugin.simpleLayout'),
11
- name: 'Simple Layout',
12
- author: 'DXOS',
13
- description: trim`
14
- Minimal layout plugin for simplified UI contexts like popover windows.
15
- Provides basic content rendering without sidebars or complex navigation.
16
- `,
17
- icon: 'ph--layout--regular',
18
- tags: ['system'],
19
- });
7
+ import config from '../dx.config';
8
+
9
+ export const meta = Plugin.getMetaFromConfig(config);
@@ -3,12 +3,7 @@
3
3
  import * as Effect from 'effect/Effect';
4
4
 
5
5
  import { Capability } from '@dxos/app-framework';
6
- import {
7
- AppCapabilities,
8
- LayoutOperation,
9
- createEdgeExistenceChecker,
10
- validateNavigationTarget,
11
- } from '@dxos/app-toolkit';
6
+ import { AppCapabilities, LayoutOperation, NotFound } from '@dxos/app-toolkit';
12
7
  import { Operation } from '@dxos/compute';
13
8
  import { Context } from '@dxos/context';
14
9
  import { Database, EID } from '@dxos/echo';
@@ -47,13 +42,15 @@ const handler: Operation.WithHandler<typeof LayoutOperation.Open> = LayoutOperat
47
42
  }
48
43
  : undefined;
49
44
  const checkRemoteExistence = client
50
- ? createEdgeExistenceChecker((spaceId, body) => client.edge.http.execQuery(new Context(), spaceId, body))
45
+ ? NotFound.createEdgeExistenceChecker((spaceId, body) =>
46
+ client.edge.http.execQuery(new Context(), spaceId, body),
47
+ )
51
48
  : undefined;
52
49
 
53
50
  const validatedId =
54
51
  input.navigation === 'immediate'
55
52
  ? id
56
- : yield* validateNavigationTarget({
53
+ : yield* NotFound.validateNavigationTarget({
57
54
  graph,
58
55
  subjectId: id,
59
56
  pathResolvers,
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as Effect from 'effect/Effect';
4
4
 
5
- import { isPinnedWorkspace, LayoutOperation } from '@dxos/app-toolkit';
5
+ import { LayoutOperation, Paths } from '@dxos/app-toolkit';
6
6
  import { Operation } from '@dxos/compute';
7
7
 
8
8
  import { layoutStateAccess } from './state-access';
@@ -14,7 +14,7 @@ const handler: Operation.WithHandler<typeof LayoutOperation.SwitchWorkspace> = L
14
14
 
15
15
  updateState((state) => ({
16
16
  ...state,
17
- previousWorkspace: !isPinnedWorkspace(state.workspace) ? state.workspace : state.previousWorkspace,
17
+ previousWorkspace: !Paths.isPinnedWorkspace(state.workspace) ? state.workspace : state.previousWorkspace,
18
18
  workspace: input.subject,
19
19
  active: undefined,
20
20
  history: [],
@@ -11,7 +11,7 @@ export const translations = [
11
11
  ...searchTranslations,
12
12
  {
13
13
  'en-US': {
14
- [meta.id]: {
14
+ [meta.profile.key]: {
15
15
  'plugin.name': 'Simple layout',
16
16
  'settings.title': 'Simple layout settings',
17
17
  'workspaces.heading': 'Workspaces',
@@ -29,7 +29,7 @@ export type SimpleLayoutState = {
29
29
  popoverVariant?: 'virtual' | 'react';
30
30
  popoverAnchor?: HTMLButtonElement;
31
31
  popoverAnchorId?: string;
32
- popoverKind?: 'base' | 'card';
32
+ popoverKind?: 'base' | 'card' | 'rename';
33
33
  popoverTitle?: Label;
34
34
  popoverContent?: { component: string; props?: any } | { subject: any } | null;
35
35
  drawerState: DrawerState;
@@ -38,4 +38,4 @@ export type SimpleLayoutState = {
38
38
  companionVariant?: string;
39
39
  };
40
40
 
41
- export const State = Capability.make<Atom.Writable<SimpleLayoutState>>(`${meta.id}.state`);
41
+ export const State = Capability.make<Atom.Writable<SimpleLayoutState>>(`${meta.profile.key}.state`);
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/types/SimpleLayoutCapabilities.ts", "../../../src/types/SimpleLayoutEvents.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { type Atom } from '@effect-atom/atom-react';\n\nimport { Capability } from '@dxos/app-framework';\nimport { type Label } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\n\nexport type DrawerState = 'closed' | 'open' | 'expanded';\n\nexport type SimpleLayoutState = {\n content?: any;\n previousWorkspace: string;\n workspace: string;\n active?: string;\n dialogOpen: boolean;\n dialogType?: 'default' | 'alert';\n dialogBlockAlign?: 'start' | 'center' | 'end';\n dialogOverlayClasses?: string;\n dialogOverlayStyle?: Record<string, any>;\n dialogContent?: { component: string; props?: any } | null;\n popoverOpen?: boolean;\n popoverSide?: 'top' | 'right' | 'bottom' | 'left';\n popoverVariant?: 'virtual' | 'react';\n popoverAnchor?: HTMLButtonElement;\n popoverAnchorId?: string;\n popoverKind?: 'base' | 'card';\n popoverTitle?: Label;\n popoverContent?: { component: string; props?: any } | { subject: any } | null;\n drawerState: DrawerState;\n history: string[];\n isPopover?: boolean;\n companionVariant?: string;\n};\n\nexport const State = Capability.make<Atom.Writable<SimpleLayoutState>>(`${meta.id}.state`);\n", "//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { type ActivationEvent } from '@dxos/app-framework';\nimport { AppActivationEvents } from '@dxos/app-toolkit';\n\nimport * as SimpleLayoutCapabilities from './SimpleLayoutCapabilities';\n\n/** Fired when State capability is ready. */\nexport const StateReady: ActivationEvent.ActivationEvent = AppActivationEvents.createStateEvent(\n SimpleLayoutCapabilities.State.identifier,\n);\n"],
5
- "mappings": ";;;;;AAAA;;;;AAQA,SAASA,kBAAkB;AAG3B,SAASC,YAAY;AA6Bd,IAAMC,QAAQF,WAAWG,KAAuC,GAAGF,KAAKG,EAAE,QAAQ;;;ACxCzF;;;;AAOA,SAASC,2BAA2B;AAK7B,IAAMC,aAA8CC,oBAAoBC,iBACpDC,MAAMC,UAAU;",
6
- "names": ["Capability", "meta", "State", "make", "id", "AppActivationEvents", "StateReady", "AppActivationEvents", "createStateEvent", "State", "identifier"]
7
- }
@@ -1,8 +0,0 @@
1
- // src/operations/index.ts
2
- import { OperationHandlerSet } from "@dxos/compute";
3
- var SimpleLayoutOperationHandlerSet = OperationHandlerSet.lazy(() => import("./close-WKMURGUB.mjs"), () => import("./open-Y4KNROHW.mjs"), () => import("./revert-workspace-ST6NZUNG.mjs"), () => import("./set-6ZRLWPJS.mjs"), () => import("./set-layout-mode-L22HRCKS.mjs"), () => import("./switch-workspace-PYWPTMFO.mjs"), () => import("./update-complementary-HKWF5OXT.mjs"), () => import("./update-dialog-P4ASXCE7.mjs"), () => import("./update-popover-REAKC2GN.mjs"), () => import("./update-sidebar-O5SQPR6Q.mjs"));
4
-
5
- export {
6
- SimpleLayoutOperationHandlerSet
7
- };
8
- //# sourceMappingURL=chunk-NKAPURKM.mjs.map
@@ -1,22 +0,0 @@
1
- // src/meta.ts
2
- import { Plugin } from "@dxos/app-framework";
3
- import { DXN } from "@dxos/keys";
4
- import { trim } from "@dxos/util";
5
- var meta = Plugin.makeMeta({
6
- key: DXN.make("org.dxos.plugin.simpleLayout"),
7
- name: "Simple Layout",
8
- author: "DXOS",
9
- description: trim`
10
- Minimal layout plugin for simplified UI contexts like popover windows.
11
- Provides basic content rendering without sidebars or complex navigation.
12
- `,
13
- icon: "ph--layout--regular",
14
- tags: [
15
- "system"
16
- ]
17
- });
18
-
19
- export {
20
- meta
21
- };
22
- //# sourceMappingURL=chunk-OK5336TS.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/meta.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\nimport { DXN } from '@dxos/keys';\nimport { trim } from '@dxos/util';\n\nexport const meta = Plugin.makeMeta({\n key: DXN.make('org.dxos.plugin.simpleLayout'),\n name: 'Simple Layout',\n author: 'DXOS',\n description: trim`\n Minimal layout plugin for simplified UI contexts like popover windows.\n Provides basic content rendering without sidebars or complex navigation.\n `,\n icon: 'ph--layout--regular',\n tags: ['system'],\n});\n"],
5
- "mappings": ";AAIA,SAASA,cAAc;AACvB,SAASC,WAAW;AACpB,SAASC,YAAY;AAEd,IAAMC,OAAOH,OAAOI,SAAS;EAClCC,KAAKJ,IAAIK,KAAK,8BAAA;EACdC,MAAM;EACNC,QAAQ;EACRC,aAAaP;;;;EAIbQ,MAAM;EACNC,MAAM;IAAC;;AACT,CAAA;",
6
- "names": ["Plugin", "DXN", "trim", "meta", "makeMeta", "key", "make", "name", "author", "description", "icon", "tags"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/operations/open.ts"],
4
- "sourcesContent": ["// Copyright 2025 DXOS.org\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capability } from '@dxos/app-framework';\nimport {\n AppCapabilities,\n LayoutOperation,\n createEdgeExistenceChecker,\n validateNavigationTarget,\n} from '@dxos/app-toolkit';\nimport { Operation } from '@dxos/compute';\nimport { Context } from '@dxos/context';\nimport { Database, EID } from '@dxos/echo';\nimport { log } from '@dxos/log';\nimport { ClientCapabilities } from '@dxos/plugin-client';\n\nimport { layoutStateAccess } from './state-access';\n\nconst handler: Operation.WithHandler<typeof LayoutOperation.Open> = LayoutOperation.Open.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* (input) {\n log('LayoutOperation.Open handler start');\n const { graph } = yield* Capability.get(AppCapabilities.AppGraph);\n const { updateState } = yield* layoutStateAccess;\n const id = input.subject[0];\n\n // Validate navigation target, redirecting to 404 if not found.\n const capabilities = yield* Capability.Service;\n const pathResolvers = capabilities.getAll(AppCapabilities.NavigationPathResolver);\n const client = yield* Capability.get(ClientCapabilities.Client).pipe(\n Effect.catchAll(() => Effect.succeed(undefined)),\n );\n // Existence checkers for the resolved EID: local (load + catchTag) first, then remote (edge).\n const checkLocalExistence = client\n ? (uri: EID.EID) => {\n const spaceId = EID.getSpaceId(uri);\n const space = spaceId ? client.spaces.get(spaceId) : undefined;\n if (!space) {\n return Effect.succeed(false);\n }\n return Database.load(space.db.makeRef(uri)).pipe(\n Effect.as(true),\n Effect.catchTag('EntityNotFoundError', () => Effect.succeed(false)),\n Effect.catchAll(() => Effect.succeed(false)),\n );\n }\n : undefined;\n const checkRemoteExistence = client\n ? createEdgeExistenceChecker((spaceId, body) => client.edge.http.execQuery(new Context(), spaceId, body))\n : undefined;\n\n const validatedId =\n input.navigation === 'immediate'\n ? id\n : yield* validateNavigationTarget({\n graph,\n subjectId: id,\n pathResolvers,\n checkLocalExistence,\n checkRemoteExistence,\n });\n\n updateState((state) => {\n const newHistory = state.active ? [...state.history, state.active] : state.history;\n const trimmedHistory =\n newHistory.length > MAX_HISTORY_LENGTH ? newHistory.slice(-MAX_HISTORY_LENGTH) : newHistory;\n return {\n ...state,\n active: validatedId,\n history: trimmedHistory,\n };\n });\n\n return [validatedId];\n }),\n ),\n);\n\nexport default handler;\n\nconst MAX_HISTORY_LENGTH = 50;\n"],
5
- "mappings": ";;;;;;;AAEA,YAAYA,YAAY;AAExB,SAASC,kBAAkB;AAC3B,SACEC,iBACAC,iBACAC,4BACAC,gCACK;AACP,SAASC,iBAAiB;AAC1B,SAASC,eAAe;AACxB,SAASC,UAAUC,WAAW;AAC9B,SAASC,WAAW;AACpB,SAASC,0BAA0B;AAInC,IAAA,eAAoEC;IAI9D,UAAQC,gBAAiBC,KAAAA,KAAWC,UAAIC,YAAwB,kBAAA,WAAA,OAAA;AAChE,MAAA,sCAA+BC,QAAAA,EAAAA,YAAAA,YAAAA,GAAAA,cAAAA,GAAAA,IAAAA,GAAAA,KAAAA,CAAAA;AAC/B,QAAMC,EAAAA,MAAKC,IAAMC,OAAQ,WAAE,IAAA,gBAAA,QAAA;AAE3B,QAAA,EAAA,YAAA,IAAA,OAAA;AACA,QAAMC,KAAAA,MAAAA,QAAe,CAAA;AAErB,QAAMC,eAAS,OAAOR,WAAeS;AAGrC,QAAA,gBAAA,aAAA,OAAA,gBAAA,sBAAA;AACA,QAAMC,SAAAA,OAAAA,WAAsBF,IACxB,mBAACG,MAAAA,EAAAA,KAAAA,gBAAAA,MAAAA,eAAAA,MAAAA,CAAAA,CAAAA;QAEC,sBAAwBH,SAAOI,CAAAA,QAAOX;AACtC,UAAKY,UAAO,IAAA,WAAA,GAAA;UACV,QAAOC,UAAOC,OAAQ,OAAA,IAAA,OAAA,IAAA;AACxB,QAAA,CAAA,OAAA;AACA,aAAgBC,eAAKH,KAASI;IAMhCC;AACJ,WAAMC,SAAAA,KAAAA,MAAuBX,GAAAA,QACzBY,GAAAA,CAAAA,EAAAA,KAAAA,UAAAA,IAA4BC,GAASC,gBAASd,uBAA2B,MAAIe,eAAWF,KAASC,CAAAA,GACjGJ,gBAAAA,MAAAA,eAAAA,KAAAA,CAAAA,CAAAA;EAEJ,IAAA;QAIQnB,uBAAAA,SAAAA,2BAAAA,CAAAA,SAAAA,SAAAA,OAAAA,KAAAA,KAAAA,UAAAA,IAAAA,QAAAA,QAAAA,EAAAA,YAAAA,YAAAA,GAAAA,cAAAA,GAAAA,GAAAA,CAAAA,GAAAA,SAAAA,IAAAA,CAAAA,IAAAA;QACAyB,cAAWpB,MAAAA,eAAAA,cAAAA,KAAAA,OAAAA,yBAAAA;IACXqB;IACAf,WAAAA;IACAS;IACF;IAENO;;cACwCC,CAAAA,UAAMC;UAASD,aAAY,MAAA,SAAA;MAAIA,GAAAA,MAAMC;MAC3E,MAAMC;IAEN,IAAA,MAAO;UACL,iBAAQ,WAAA,SAAA,qBAAA,WAAA,MAAA,CAAA,kBAAA,IAAA;WACRC;MACAF,GAAAA;MACF,QAAA;MACF,SAAA;IAEA;;SAAoB;IACtB;EAIJ;AAEA,CAAA,CAAA,CAAA;;;",
6
- "names": ["Effect", "Capability", "AppCapabilities", "LayoutOperation", "createEdgeExistenceChecker", "validateNavigationTarget", "Operation", "Context", "Database", "EID", "log", "ClientCapabilities", "LayoutOperation", "graph", "Capability", "get", "AppCapabilities", "layoutStateAccess", "id", "input", "subject", "capabilities", "client", "ClientCapabilities", "checkLocalExistence", "uri", "spaces", "space", "Effect", "succeed", "load", "makeRef", "undefined", "checkRemoteExistence", "createEdgeExistenceChecker", "spaceId", "body", "Context", "subjectId", "pathResolvers", "updateState", "state", "history", "trimmedHistory", "active"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/capabilities/react-surface.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport React from 'react';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport { Surface } from '@dxos/app-framework/ui';\nimport { NOT_FOUND_PATH } from '@dxos/app-toolkit';\nimport { NotFoundArticle } from '@dxos/app-toolkit/ui';\nimport { Node } from '@dxos/plugin-graph';\n\nimport { Home, NavBranch } from '#components';\n\ntype SurfaceData = {\n attendableId: string;\n properties: Record<string, any>;\n};\n\nconst ALLOWED_DISPOSITIONS = ['workspace', 'user-account', 'pin-end'];\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contributes(Capabilities.ReactSurface, [\n Surface.create({\n id: 'home',\n role: 'article',\n filter: (data): data is SurfaceData => data.attendableId === Node.RootId,\n component: () => <Home />,\n }),\n Surface.create({\n id: 'notFound',\n role: 'article',\n filter: (data): data is SurfaceData => data.attendableId === NOT_FOUND_PATH,\n component: () => <NotFoundArticle />,\n }),\n Surface.create({\n id: 'navBranch',\n role: 'article',\n position: 'last',\n filter: (data): data is SurfaceData => {\n const props = data.properties as Record<string, any>;\n return ALLOWED_DISPOSITIONS.includes(props?.disposition) || props?.role === 'branch';\n },\n component: ({ data }) => <NavBranch id={data.attendableId} />,\n }),\n ]),\n ),\n);\n"],
5
- "mappings": ";;;AAIA,YAAYA,YAAY;AACxB,OAAOC,WAAW;AAElB,SAASC,cAAcC,kBAAkB;AACzC,SAASC,eAAe;AACxB,SAASC,sBAAsB;AAC/B,SAASC,uBAAuB;AAChC,SAASC,YAAY;AAErB,SAASC,MAAMC,iBAAiB;AAOhC,IAAMC,uBAAuB;EAAC;EAAa;EAAgB;;AAE3D,IAAA,wBAAeP,WAAWQ,WAAW,MAC5BC,eACLT,WAAWU,YAAYX,aAAaY,cAAc;EAChDV,QAAQW,OAAO;IACbC,IAAI;IACJC,MAAM;IACNC,QAAQ,CAACC,SAA8BA,KAAKC,iBAAiBb,KAAKc;IAClEC,WAAW,MAAM,sBAAA,cAACd,MAAAA,IAAAA;EACpB,CAAA;EACAJ,QAAQW,OAAO;IACbC,IAAI;IACJC,MAAM;IACNC,QAAQ,CAACC,SAA8BA,KAAKC,iBAAiBf;IAC7DiB,WAAW,MAAM,sBAAA,cAAChB,iBAAAA,IAAAA;EACpB,CAAA;EACAF,QAAQW,OAAO;IACbC,IAAI;IACJC,MAAM;IACNM,UAAU;IACVL,QAAQ,CAACC,SAAAA;AACP,YAAMK,QAAQL,KAAKM;AACnB,aAAOf,qBAAqBgB,SAASF,OAAOG,WAAAA,KAAgBH,OAAOP,SAAS;IAC9E;IACAK,WAAW,CAAC,EAAEH,KAAI,MAAO,sBAAA,cAACV,WAAAA;MAAUO,IAAIG,KAAKC;;EAC/C,CAAA;CACD,CAAA,CAAA;",
6
- "names": ["Effect", "React", "Capabilities", "Capability", "Surface", "NOT_FOUND_PATH", "NotFoundArticle", "Node", "Home", "NavBranch", "ALLOWED_DISPOSITIONS", "makeModule", "succeed", "contributes", "ReactSurface", "create", "id", "role", "filter", "data", "attendableId", "RootId", "component", "position", "props", "properties", "includes", "disposition"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/operations/switch-workspace.ts"],
4
- "sourcesContent": ["// Copyright 2025 DXOS.org\n\nimport * as Effect from 'effect/Effect';\n\nimport { isPinnedWorkspace, LayoutOperation } from '@dxos/app-toolkit';\nimport { Operation } from '@dxos/compute';\n\nimport { layoutStateAccess } from './state-access';\n\nconst handler: Operation.WithHandler<typeof LayoutOperation.SwitchWorkspace> = LayoutOperation.SwitchWorkspace.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* (input) {\n const { updateState } = yield* layoutStateAccess;\n\n updateState((state) => ({\n ...state,\n previousWorkspace: !isPinnedWorkspace(state.workspace) ? state.workspace : state.previousWorkspace,\n workspace: input.subject,\n active: undefined,\n history: [],\n }));\n }),\n ),\n);\n\nexport default handler;\n"],
5
- "mappings": ";;;;;;;AAEA,YAAYA,YAAY;AAExB,SAASC,mBAAmBC,uBAAuB;AACnD,SAASC,iBAAiB;AAI1B,IAAMC,UAAyEC,gBAAgBC,gBAAgBC,KAC7GC,UAAUC,YACDC,kBAAW,WAAWC,OAAK;AAChC,QAAM,EAAEC,YAAW,IAAK,OAAOC;AAE/BD,cAAY,CAACE,WAAW;IACtB,GAAGA;IACHC,mBAAmB,CAACC,kBAAkBF,MAAMG,SAAS,IAAIH,MAAMG,YAAYH,MAAMC;IACjFE,WAAWN,MAAMO;IACjBC,QAAQC;IACRC,SAAS,CAAA;EACX,EAAA;AACF,CAAA,CAAA,CAAA;AAIJ,IAAA,2BAAejB;",
6
- "names": ["Effect", "isPinnedWorkspace", "LayoutOperation", "Operation", "handler", "LayoutOperation", "SwitchWorkspace", "pipe", "Operation", "withHandler", "fnUntraced", "input", "updateState", "layoutStateAccess", "state", "previousWorkspace", "isPinnedWorkspace", "workspace", "subject", "active", "undefined", "history"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/capabilities/url-handler.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport { AppCapabilities, LayoutOperation, fromUrlPath, getWorkspaceFromPath, toUrlPath } from '@dxos/app-toolkit';\nimport { EffectEx } from '@dxos/effect';\nimport { log } from '@dxos/log';\nimport { isTauri } from '@dxos/util';\n\nimport { SimpleLayoutCapabilities } from '#types';\n\n/**\n * URL handler for simple layout that syncs browser URL with layout state.\n * URL paths map directly to qualified graph IDs with the leading `root` segment stripped.\n * Root is represented as `/`.\n *\n * On Tauri, also listens for deep links via the deep-link plugin.\n */\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const { invokePromise } = yield* Capability.get(Capabilities.OperationInvoker);\n const capabilities = yield* Capability.Service;\n\n /** Dispatch all NavigationHandler contributions with a given URL. */\n const dispatchNavigationHandlers = (url: URL) =>\n Effect.gen(function* () {\n const handlers = yield* Capability.getAll(AppCapabilities.NavigationHandler);\n yield* Effect.all(\n handlers.map((handler) => handler(url)),\n { concurrency: 'unbounded' },\n );\n }).pipe(Effect.provideService(Capability.Service, capabilities), EffectEx.runAndForwardErrors);\n\n /**\n * Handle navigation from a URL.\n * Dispatches to NavigationHandler contributions, then handles pathname routing.\n */\n const handlePathNavigation = (url?: URL) => {\n const resolvedUrl = url ?? new URL(window.location.href);\n void dispatchNavigationHandlers(resolvedUrl);\n\n let pathname = resolvedUrl.pathname;\n if (isFilePath(pathname)) {\n log.info('[UrlHandler] Skipping file path (not a graph node)', { pathname });\n pathname = '/';\n }\n\n const qualifiedId = fromUrlPath(pathname);\n const workspace = getWorkspaceFromPath(qualifiedId);\n\n void invokePromise(LayoutOperation.SwitchWorkspace, { subject: workspace });\n\n const activeId = qualifiedId !== workspace ? qualifiedId : undefined;\n if (activeId) {\n void invokePromise(LayoutOperation.Open, { subject: [activeId] });\n }\n };\n\n const onPopState = () => {\n handlePathNavigation();\n };\n\n // Initial navigation.\n yield* Effect.sync(() => handlePathNavigation());\n window.addEventListener('popstate', onPopState);\n\n // Tauri deep link support.\n let unlistenDeepLink: (() => void) | undefined;\n if (isTauri()) {\n yield* Effect.tryPromise({\n try: async () => {\n const { getCurrent, onOpenUrl } = await import('@tauri-apps/plugin-deep-link');\n\n const launchUrls = await getCurrent();\n if (launchUrls && launchUrls.length > 0) {\n log.info('[UrlHandler] App launched with deep links', { urls: launchUrls });\n for (const urlString of launchUrls) {\n handleDeepLink(urlString, handlePathNavigation);\n }\n }\n\n unlistenDeepLink = await onOpenUrl((urls) => {\n log.info('[UrlHandler] Deep links received', { urls });\n for (const urlString of urls) {\n handleDeepLink(urlString, handlePathNavigation);\n }\n });\n },\n catch: (error) => {\n log.warn('[UrlHandler] Failed to initialize deep link listener', { error });\n return error;\n },\n }).pipe(Effect.catchAll(() => Effect.void));\n }\n\n // Sync URL with layout state changes.\n let lastWorkspace: string | undefined;\n let lastActive: string | undefined;\n const unsubscribe = yield* Capabilities.subscribeAtom(\n SimpleLayoutCapabilities.State,\n (state: SimpleLayoutCapabilities.SimpleLayoutState) => {\n const { workspace, active } = state;\n\n if (workspace !== lastWorkspace || active !== lastActive) {\n lastWorkspace = workspace;\n lastActive = active;\n\n const path = active ? toUrlPath(active) : toUrlPath(workspace);\n if (window.location.pathname !== path) {\n history.pushState(null, '', `${path}${window.location.search}`);\n }\n }\n },\n );\n\n return Capability.contributes(Capabilities.Null, null, () =>\n Effect.sync(() => {\n window.removeEventListener('popstate', onPopState);\n unsubscribe();\n unlistenDeepLink?.();\n }),\n );\n }),\n);\n\n/** Check if a path is a redirect path handled elsewhere (e.g., OAuth). */\nconst isRedirectPath = (pathname: string): boolean => pathname.startsWith('/redirect/');\n\n/** Paths with file extensions are not graph node paths. */\nconst isFilePath = (pathname: string): boolean => /\\.[a-z]+$/i.test(pathname);\n\n/** Handle a deep link URL string. Merges query params into window.location and navigates. */\nconst handleDeepLink = (urlString: string, navigate: (url?: URL) => void): void => {\n log.info('[UrlHandler] Deep link received', { url: urlString });\n try {\n const deepLinkUrl = new URL(urlString);\n\n // For custom schemes (e.g., composer://a/b/c), new URL() treats the first segment as the\n // hostname. Reconstruct the full path from hostname + pathname.\n const fullPath =\n deepLinkUrl.protocol !== 'https:' && deepLinkUrl.protocol !== 'http:' && deepLinkUrl.hostname\n ? '/' + deepLinkUrl.hostname + deepLinkUrl.pathname\n : deepLinkUrl.pathname;\n\n if (isRedirectPath(fullPath)) {\n return;\n }\n\n // Merge deep link query params into the current window URL so handlers can read them.\n const current = new URL(window.location.href);\n if (deepLinkUrl.search) {\n deepLinkUrl.searchParams.forEach((value, key) => current.searchParams.set(key, value));\n }\n current.pathname = fullPath;\n history.replaceState(null, '', current.pathname + current.search);\n\n navigate(current);\n } catch (error) {\n log.warn('[UrlHandler] Failed to parse deep link URL', { urlString, error });\n }\n};\n"],
5
- "mappings": "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",
6
- "names": ["Effect", "Capabilities", "Capability", "AppCapabilities", "LayoutOperation", "fromUrlPath", "getWorkspaceFromPath", "toUrlPath", "EffectEx", "log", "isTauri", "SimpleLayoutCapabilities", "capabilities", "Service", "dispatchNavigationHandlers", "handlers", "concurrency", "provideService", "resolvedUrl", "pathname", "isFilePath", "qualifiedId", "invokePromise", "SwitchWorkspace", "subject", "workspace", "activeId", "unlistenDeepLink", "try", "getCurrent", "launchUrls", "length", "handleDeepLink", "urlString", "handlePathNavigation", "urls", "catch", "error", "catchAll", "lastWorkspace", "active", "lastActive", "location", "history", "pushState", "path", "window", "search", "unsubscribe", "navigate", "deepLinkUrl", "searchParams", "forEach", "value", "current", "fullPath"]
7
- }