@dxos/plugin-simple-layout 0.8.4-main.21d9917 → 0.8.4-main.52d7546f51

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 (162) hide show
  1. package/dist/lib/browser/{chunk-CLPGTNWJ.mjs → chunk-7VLT3S46.mjs} +3 -3
  2. package/dist/lib/browser/chunk-7VLT3S46.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-O3BQBYMW.mjs +1165 -0
  4. package/dist/lib/browser/chunk-O3BQBYMW.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +27 -20
  6. package/dist/lib/browser/index.mjs.map +3 -3
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/{operation-resolver-LTB63NKP.mjs → operation-resolver-BYRIQOQT.mjs} +66 -29
  9. package/dist/lib/browser/operation-resolver-BYRIQOQT.mjs.map +7 -0
  10. package/dist/lib/browser/{react-root-6ARAPH3O.mjs → react-root-GPTKI5H2.mjs} +5 -5
  11. package/dist/lib/browser/react-root-GPTKI5H2.mjs.map +7 -0
  12. package/dist/lib/browser/{react-surface-SO7B23GS.mjs → react-surface-LT5JJTPR.mjs} +10 -8
  13. package/dist/lib/browser/react-surface-LT5JJTPR.mjs.map +7 -0
  14. package/dist/lib/browser/{spotlight-dismiss-VSNOPETH.mjs → spotlight-dismiss-67PHYS5B.mjs} +3 -3
  15. package/dist/lib/browser/spotlight-dismiss-67PHYS5B.mjs.map +7 -0
  16. package/dist/lib/browser/{state-H4IGICBB.mjs → state-A3PGDWWZ.mjs} +8 -5
  17. package/dist/lib/browser/state-A3PGDWWZ.mjs.map +7 -0
  18. package/dist/lib/browser/url-handler-HTIUY6WL.mjs +152 -0
  19. package/dist/lib/browser/url-handler-HTIUY6WL.mjs.map +7 -0
  20. package/dist/lib/node-esm/chunk-UAWM4B2S.mjs +1166 -0
  21. package/dist/lib/node-esm/chunk-UAWM4B2S.mjs.map +7 -0
  22. package/dist/lib/node-esm/{chunk-MUVVYBUE.mjs → chunk-VIDE5UMB.mjs} +3 -3
  23. package/dist/lib/node-esm/chunk-VIDE5UMB.mjs.map +7 -0
  24. package/dist/lib/node-esm/index.mjs +27 -20
  25. package/dist/lib/node-esm/index.mjs.map +3 -3
  26. package/dist/lib/node-esm/meta.json +1 -1
  27. package/dist/lib/node-esm/{operation-resolver-7O6O7T4Q.mjs → operation-resolver-BDTFNCS2.mjs} +66 -29
  28. package/dist/lib/node-esm/operation-resolver-BDTFNCS2.mjs.map +7 -0
  29. package/dist/lib/node-esm/{react-root-2CPA2ZUS.mjs → react-root-GRG2OAI2.mjs} +5 -5
  30. package/dist/lib/node-esm/react-root-GRG2OAI2.mjs.map +7 -0
  31. package/dist/lib/node-esm/{react-surface-FKAV56MO.mjs → react-surface-TCUSDIN2.mjs} +10 -8
  32. package/dist/lib/node-esm/react-surface-TCUSDIN2.mjs.map +7 -0
  33. package/dist/lib/node-esm/{spotlight-dismiss-L5PCWIJG.mjs → spotlight-dismiss-RMLRZUVY.mjs} +3 -3
  34. package/dist/lib/node-esm/spotlight-dismiss-RMLRZUVY.mjs.map +7 -0
  35. package/dist/lib/node-esm/{state-QIU2LMLT.mjs → state-ZCFZTTPL.mjs} +8 -5
  36. package/dist/lib/node-esm/state-ZCFZTTPL.mjs.map +7 -0
  37. package/dist/lib/node-esm/url-handler-WBVVKVPC.mjs +153 -0
  38. package/dist/lib/node-esm/url-handler-WBVVKVPC.mjs.map +7 -0
  39. package/dist/types/src/SimpleLayoutPlugin.d.ts.map +1 -1
  40. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +2 -2
  41. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -1
  42. package/dist/types/src/capabilities/react-root/react-root.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/react-surface/index.d.ts +1 -1
  44. package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -1
  45. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +2 -2
  46. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -1
  47. package/dist/types/src/capabilities/spotlight-dismiss/spotlight-dismiss.d.ts.map +1 -1
  48. package/dist/types/src/capabilities/state/state.d.ts.map +1 -1
  49. package/dist/types/src/capabilities/url-handler/url-handler.d.ts +2 -0
  50. package/dist/types/src/capabilities/url-handler/url-handler.d.ts.map +1 -1
  51. package/dist/types/src/components/ContentError.stories.d.ts +6 -0
  52. package/dist/types/src/components/ContentError.stories.d.ts.map +1 -1
  53. package/dist/types/src/components/Home/Home.d.ts.map +1 -1
  54. package/dist/types/src/components/MobileLayout/MobileLayout.d.ts +35 -0
  55. package/dist/types/src/components/MobileLayout/MobileLayout.d.ts.map +1 -0
  56. package/dist/types/src/components/MobileLayout/MobileLayout.stories.d.ts +7 -0
  57. package/dist/types/src/components/MobileLayout/MobileLayout.stories.d.ts.map +1 -0
  58. package/dist/types/src/components/MobileLayout/index.d.ts +2 -0
  59. package/dist/types/src/components/MobileLayout/index.d.ts.map +1 -0
  60. package/dist/types/src/components/SimpleLayout/AppBar.d.ts +26 -0
  61. package/dist/types/src/components/SimpleLayout/AppBar.d.ts.map +1 -0
  62. package/dist/types/src/components/SimpleLayout/AppBar.stories.d.ts +47 -0
  63. package/dist/types/src/components/SimpleLayout/AppBar.stories.d.ts.map +1 -0
  64. package/dist/types/src/components/SimpleLayout/Drawer.d.ts +9 -0
  65. package/dist/types/src/components/SimpleLayout/Drawer.d.ts.map +1 -0
  66. package/dist/types/src/components/SimpleLayout/Main.d.ts +1 -1
  67. package/dist/types/src/components/SimpleLayout/Main.d.ts.map +1 -1
  68. package/dist/types/src/components/SimpleLayout/NavBar.d.ts +13 -3
  69. package/dist/types/src/components/SimpleLayout/NavBar.d.ts.map +1 -1
  70. package/dist/types/src/components/SimpleLayout/NavBar.stories.d.ts +13 -9
  71. package/dist/types/src/components/SimpleLayout/NavBar.stories.d.ts.map +1 -1
  72. package/dist/types/src/components/SimpleLayout/SimpleLayout.d.ts.map +1 -1
  73. package/dist/types/src/components/SimpleLayout/SimpleLayout.stories.d.ts +10 -0
  74. package/dist/types/src/components/SimpleLayout/SimpleLayout.stories.d.ts.map +1 -1
  75. package/dist/types/src/components/SimpleLayout/index.d.ts +3 -0
  76. package/dist/types/src/components/SimpleLayout/index.d.ts.map +1 -1
  77. package/dist/types/src/components/Workspace/Workspace.d.ts +3 -1
  78. package/dist/types/src/components/Workspace/Workspace.d.ts.map +1 -1
  79. package/dist/types/src/components/hooks.d.ts.map +1 -1
  80. package/dist/types/src/components/index.d.ts +1 -0
  81. package/dist/types/src/components/index.d.ts.map +1 -1
  82. package/dist/types/src/hooks/actions.d.ts +20 -0
  83. package/dist/types/src/hooks/actions.d.ts.map +1 -0
  84. package/dist/types/src/hooks/index.d.ts +5 -0
  85. package/dist/types/src/hooks/index.d.ts.map +1 -1
  86. package/dist/types/src/hooks/useAppBarProps.d.ts +7 -0
  87. package/dist/types/src/hooks/useAppBarProps.d.ts.map +1 -0
  88. package/dist/types/src/hooks/useCompanions.d.ts +12 -0
  89. package/dist/types/src/hooks/useCompanions.d.ts.map +1 -0
  90. package/dist/types/src/hooks/useDrawerActions.d.ts +13 -0
  91. package/dist/types/src/hooks/useDrawerActions.d.ts.map +1 -0
  92. package/dist/types/src/hooks/useNavbarActions.d.ts +14 -0
  93. package/dist/types/src/hooks/useNavbarActions.d.ts.map +1 -0
  94. package/dist/types/src/hooks/useSimpleLayoutState.d.ts +3 -3
  95. package/dist/types/src/hooks/useSimpleLayoutState.d.ts.map +1 -1
  96. package/dist/types/src/translations.d.ts +6 -0
  97. package/dist/types/src/translations.d.ts.map +1 -1
  98. package/dist/types/src/types/capabilities.d.ts +8 -3
  99. package/dist/types/src/types/capabilities.d.ts.map +1 -1
  100. package/dist/types/src/types/events.d.ts.map +1 -1
  101. package/dist/types/tsconfig.tsbuildinfo +1 -1
  102. package/package.json +35 -30
  103. package/src/SimpleLayoutPlugin.ts +10 -9
  104. package/src/capabilities/operation-resolver/operation-resolver.ts +65 -26
  105. package/src/capabilities/react-root/react-root.tsx +2 -2
  106. package/src/capabilities/react-surface/react-surface.tsx +6 -5
  107. package/src/capabilities/spotlight-dismiss/spotlight-dismiss.ts +2 -2
  108. package/src/capabilities/state/state.tsx +6 -3
  109. package/src/capabilities/url-handler/url-handler.ts +111 -34
  110. package/src/components/ContentError.stories.tsx +1 -1
  111. package/src/components/ContentLoading.stories.tsx +1 -1
  112. package/src/components/Dialog/Dialog.tsx +4 -4
  113. package/src/components/Home/Home.tsx +21 -19
  114. package/src/components/MobileLayout/MobileLayout.stories.tsx +125 -0
  115. package/src/components/MobileLayout/MobileLayout.tsx +305 -0
  116. package/src/components/MobileLayout/index.ts +5 -0
  117. package/src/components/Popover/Popover.tsx +4 -4
  118. package/src/components/SimpleLayout/AppBar.stories.tsx +144 -0
  119. package/src/components/SimpleLayout/AppBar.tsx +101 -0
  120. package/src/components/SimpleLayout/Drawer.tsx +102 -0
  121. package/src/components/SimpleLayout/Main.tsx +44 -44
  122. package/src/components/SimpleLayout/NavBar.stories.tsx +129 -24
  123. package/src/components/SimpleLayout/NavBar.tsx +24 -58
  124. package/src/components/SimpleLayout/SimpleLayout.stories.tsx +22 -9
  125. package/src/components/SimpleLayout/SimpleLayout.tsx +45 -6
  126. package/src/components/SimpleLayout/index.ts +3 -0
  127. package/src/components/Workspace/Workspace.tsx +14 -10
  128. package/src/components/hooks.ts +9 -13
  129. package/src/components/index.ts +1 -0
  130. package/src/hooks/actions.ts +85 -0
  131. package/src/hooks/index.ts +5 -0
  132. package/src/hooks/useAppBarProps.ts +112 -0
  133. package/src/hooks/useCompanions.ts +22 -0
  134. package/src/hooks/useDrawerActions.ts +98 -0
  135. package/src/hooks/useNavbarActions.ts +86 -0
  136. package/src/hooks/useSimpleLayoutState.ts +5 -5
  137. package/src/translations.ts +6 -0
  138. package/src/types/capabilities.ts +12 -3
  139. package/src/types/events.ts +3 -2
  140. package/dist/lib/browser/chunk-CLPGTNWJ.mjs.map +0 -7
  141. package/dist/lib/browser/chunk-FK4M7GJV.mjs +0 -613
  142. package/dist/lib/browser/chunk-FK4M7GJV.mjs.map +0 -7
  143. package/dist/lib/browser/operation-resolver-LTB63NKP.mjs.map +0 -7
  144. package/dist/lib/browser/react-root-6ARAPH3O.mjs.map +0 -7
  145. package/dist/lib/browser/react-surface-SO7B23GS.mjs.map +0 -7
  146. package/dist/lib/browser/spotlight-dismiss-VSNOPETH.mjs.map +0 -7
  147. package/dist/lib/browser/state-H4IGICBB.mjs.map +0 -7
  148. package/dist/lib/browser/url-handler-7CFGTLNG.mjs +0 -54
  149. package/dist/lib/browser/url-handler-7CFGTLNG.mjs.map +0 -7
  150. package/dist/lib/node-esm/chunk-EGFZAVBD.mjs +0 -614
  151. package/dist/lib/node-esm/chunk-EGFZAVBD.mjs.map +0 -7
  152. package/dist/lib/node-esm/chunk-MUVVYBUE.mjs.map +0 -7
  153. package/dist/lib/node-esm/operation-resolver-7O6O7T4Q.mjs.map +0 -7
  154. package/dist/lib/node-esm/react-root-2CPA2ZUS.mjs.map +0 -7
  155. package/dist/lib/node-esm/react-surface-FKAV56MO.mjs.map +0 -7
  156. package/dist/lib/node-esm/spotlight-dismiss-L5PCWIJG.mjs.map +0 -7
  157. package/dist/lib/node-esm/state-QIU2LMLT.mjs.map +0 -7
  158. package/dist/lib/node-esm/url-handler-4LYP3JM7.mjs +0 -55
  159. package/dist/lib/node-esm/url-handler-4LYP3JM7.mjs.map +0 -7
  160. package/dist/types/src/components/SimpleLayout/Banner.d.ts +0 -8
  161. package/dist/types/src/components/SimpleLayout/Banner.d.ts.map +0 -1
  162. package/src/components/SimpleLayout/Banner.tsx +0 -82
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-simple-layout",
3
- "version": "0.8.4-main.21d9917",
3
+ "version": "0.8.4-main.52d7546f51",
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",
@@ -14,10 +14,10 @@
14
14
  "type": "module",
15
15
  "exports": {
16
16
  ".": {
17
- "source": "./src/index.ts",
18
- "types": "./dist/types/src/index.d.ts",
19
17
  "browser": "./dist/lib/browser/index.mjs",
20
- "node": "./dist/lib/node-esm/index.mjs"
18
+ "node": "./dist/lib/node-esm/index.mjs",
19
+ "source": "./src/index.ts",
20
+ "types": "./dist/types/src/index.d.ts"
21
21
  }
22
22
  },
23
23
  "types": "dist/types/src/index.d.ts",
@@ -26,44 +26,49 @@
26
26
  "src"
27
27
  ],
28
28
  "dependencies": {
29
- "@effect-atom/atom": "^0.4.13",
30
- "@effect-atom/atom-react": "^0.4.6",
29
+ "@effect-atom/atom": "^0.5.1",
30
+ "@effect-atom/atom-react": "^0.5.0",
31
31
  "@radix-ui/react-context": "1.1.1",
32
- "@dxos/app-framework": "0.8.4-main.21d9917",
33
- "@dxos/log": "0.8.4-main.21d9917",
34
- "@dxos/operation": "0.8.4-main.21d9917",
35
- "@dxos/react-ui-attention": "0.8.4-main.21d9917",
36
- "@dxos/plugin-graph": "0.8.4-main.21d9917",
37
- "@dxos/react-ui-menu": "0.8.4-main.21d9917",
38
- "@dxos/react-ui-mosaic": "0.8.4-main.21d9917",
39
- "@dxos/react-ui-searchlist": "0.8.4-main.21d9917",
40
- "@dxos/react-ui-stack": "0.8.4-main.21d9917",
41
- "@dxos/schema": "0.8.4-main.21d9917",
42
- "@dxos/util": "0.8.4-main.21d9917"
32
+ "@tauri-apps/plugin-deep-link": "^2.2.0",
33
+ "@tauri-apps/plugin-haptics": "^2.3.2",
34
+ "@dxos/app-framework": "0.8.4-main.52d7546f51",
35
+ "@dxos/async": "0.8.4-main.52d7546f51",
36
+ "@dxos/log": "0.8.4-main.52d7546f51",
37
+ "@dxos/operation": "0.8.4-main.52d7546f51",
38
+ "@dxos/plugin-graph": "0.8.4-main.52d7546f51",
39
+ "@dxos/react-ui-attention": "0.8.4-main.52d7546f51",
40
+ "@dxos/react-ui-menu": "0.8.4-main.52d7546f51",
41
+ "@dxos/react-ui-mosaic": "0.8.4-main.52d7546f51",
42
+ "@dxos/react-ui-searchlist": "0.8.4-main.52d7546f51",
43
+ "@dxos/schema": "0.8.4-main.52d7546f51",
44
+ "@dxos/util": "0.8.4-main.52d7546f51",
45
+ "@dxos/react-ui-stack": "0.8.4-main.52d7546f51",
46
+ "@dxos/app-toolkit": "0.8.4-main.52d7546f51"
43
47
  },
44
48
  "devDependencies": {
45
49
  "@types/react": "~19.2.7",
46
50
  "@types/react-dom": "~19.2.3",
47
- "effect": "3.19.11",
51
+ "effect": "3.19.16",
48
52
  "react": "~19.2.3",
49
53
  "react-dom": "~19.2.3",
50
54
  "vite": "7.1.9",
51
- "@dxos/plugin-client": "0.8.4-main.21d9917",
52
- "@dxos/plugin-preview": "0.8.4-main.21d9917",
53
- "@dxos/plugin-search": "0.8.4-main.21d9917",
54
- "@dxos/plugin-space": "0.8.4-main.21d9917",
55
- "@dxos/plugin-testing": "0.8.4-main.21d9917",
56
- "@dxos/react-ui": "0.8.4-main.21d9917",
57
- "@dxos/schema": "0.8.4-main.21d9917",
58
- "@dxos/storybook-utils": "0.8.4-main.21d9917",
59
- "@dxos/ui-theme": "0.8.4-main.21d9917"
55
+ "@dxos/app-graph": "0.8.4-main.52d7546f51",
56
+ "@dxos/plugin-preview": "0.8.4-main.52d7546f51",
57
+ "@dxos/plugin-client": "0.8.4-main.52d7546f51",
58
+ "@dxos/plugin-search": "0.8.4-main.52d7546f51",
59
+ "@dxos/plugin-space": "0.8.4-main.52d7546f51",
60
+ "@dxos/plugin-testing": "0.8.4-main.52d7546f51",
61
+ "@dxos/react-ui": "0.8.4-main.52d7546f51",
62
+ "@dxos/storybook-utils": "0.8.4-main.52d7546f51",
63
+ "@dxos/schema": "0.8.4-main.52d7546f51",
64
+ "@dxos/ui-theme": "0.8.4-main.52d7546f51"
60
65
  },
61
66
  "peerDependencies": {
62
- "effect": "3.19.11",
67
+ "effect": "3.19.16",
63
68
  "react": "~19.2.3",
64
69
  "react-dom": "~19.2.3",
65
- "@dxos/react-ui": "0.8.4-main.21d9917",
66
- "@dxos/ui-theme": "0.8.4-main.21d9917"
70
+ "@dxos/react-ui": "0.8.4-main.52d7546f51",
71
+ "@dxos/ui-theme": "0.8.4-main.52d7546f51"
67
72
  },
68
73
  "publishConfig": {
69
74
  "access": "public"
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { ActivationEvent, Capability, Common, Plugin } from '@dxos/app-framework';
5
+ import { ActivationEvent, ActivationEvents, Capability, Plugin } from '@dxos/app-framework';
6
+ import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
6
7
 
7
8
  import { OperationResolver, ReactRoot, ReactSurface, SpotlightDismiss, State, UrlHandler } from './capabilities';
8
9
  import { meta } from './meta';
@@ -15,33 +16,33 @@ export type SimpleLayoutPluginOptions = {
15
16
  };
16
17
 
17
18
  export const SimpleLayoutPlugin = Plugin.define<SimpleLayoutPluginOptions>(meta).pipe(
19
+ AppPlugin.addOperationResolverModule({ activate: OperationResolver }),
20
+ AppPlugin.addTranslationsModule({ translations }),
18
21
  Plugin.addModule(({ isPopover = false }) => ({
19
22
  id: Capability.getModuleTag(State),
20
- activatesOn: Common.ActivationEvent.Startup,
21
- activatesAfter: [SimpleLayoutEvents.StateReady, Common.ActivationEvent.LayoutReady],
23
+ activatesOn: ActivationEvents.Startup,
24
+ activatesAfter: [SimpleLayoutEvents.StateReady, AppActivationEvents.LayoutReady],
22
25
  activate: () => State({ initialState: { isPopover } }),
23
26
  })),
24
27
  Plugin.addModule(({ isPopover = false }) => ({
25
28
  id: Capability.getModuleTag(SpotlightDismiss),
26
- activatesOn: Common.ActivationEvent.Startup,
29
+ activatesOn: ActivationEvents.Startup,
27
30
  activate: () => SpotlightDismiss({ isPopover }),
28
31
  })),
29
32
  Plugin.addModule({
30
33
  id: Capability.getModuleTag(ReactRoot),
31
- activatesOn: Common.ActivationEvent.Startup,
34
+ activatesOn: ActivationEvents.Startup,
32
35
  activate: ReactRoot,
33
36
  }),
34
37
  Plugin.addModule({
35
38
  id: Capability.getModuleTag(ReactSurface),
36
- activatesOn: Common.ActivationEvent.Startup,
39
+ activatesOn: ActivationEvents.Startup,
37
40
  activate: ReactSurface,
38
41
  }),
39
42
  Plugin.addModule({
40
43
  id: Capability.getModuleTag(UrlHandler),
41
- activatesOn: ActivationEvent.allOf(Common.ActivationEvent.OperationInvokerReady, SimpleLayoutEvents.StateReady),
44
+ activatesOn: ActivationEvent.allOf(ActivationEvents.OperationInvokerReady, SimpleLayoutEvents.StateReady),
42
45
  activate: UrlHandler,
43
46
  }),
44
- Common.Plugin.addOperationResolverModule({ activate: OperationResolver }),
45
- Common.Plugin.addTranslationsModule({ translations }),
46
47
  Plugin.make,
47
48
  );
@@ -4,17 +4,25 @@
4
4
 
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
- import { Capability, Common } from '@dxos/app-framework';
7
+ import { Capabilities, Capability } from '@dxos/app-framework';
8
+ import { LayoutOperation } from '@dxos/app-toolkit';
8
9
  import { Operation, OperationResolver } from '@dxos/operation';
10
+ import { ATTENDABLE_PATH_SEPARATOR } from '@dxos/react-ui-attention';
9
11
 
10
12
  import { type SimpleLayoutState, SimpleLayoutState as SimpleLayoutStateCapability } from '../../types';
11
13
 
12
14
  /** Maximum number of items to keep in navigation history. */
13
15
  const MAX_HISTORY_LENGTH = 50;
14
16
 
17
+ /** Parse entry ID to extract primary ID and variant. */
18
+ const parseEntryId = (entryId: string) => {
19
+ const [id, variant] = entryId.split(ATTENDABLE_PATH_SEPARATOR);
20
+ return { id, variant };
21
+ };
22
+
15
23
  export default Capability.makeModule(
16
24
  Effect.fnUntraced(function* () {
17
- const registry = yield* Capability.get(Common.Capability.AtomRegistry);
25
+ const registry = yield* Capability.get(Capabilities.AtomRegistry);
18
26
  const stateAtom = yield* Capability.get(SimpleLayoutStateCapability);
19
27
 
20
28
  const getState = () => registry.get(stateAtom);
@@ -22,28 +30,44 @@ export default Capability.makeModule(
22
30
  registry.set(stateAtom, fn(getState()));
23
31
  };
24
32
 
25
- return Capability.contributes(Common.Capability.OperationResolver, [
33
+ return Capability.contributes(Capabilities.OperationResolver, [
34
+ //
35
+ // SetLayoutMode
36
+ //
37
+ // TODO(burdon): No-op for to fix startup bug?
38
+ OperationResolver.make({
39
+ operation: LayoutOperation.SetLayoutMode,
40
+ handler: Effect.fnUntraced(function* () {}),
41
+ }),
42
+
26
43
  //
27
44
  // UpdateSidebar - No-op for simple layout.
28
45
  //
29
46
  OperationResolver.make({
30
- operation: Common.LayoutOperation.UpdateSidebar,
47
+ operation: LayoutOperation.UpdateSidebar,
31
48
  handler: () => Effect.void,
32
49
  }),
33
50
 
34
51
  //
35
- // UpdateComplementary - No-op for simple layout.
52
+ // UpdateComplementary - Controls companion drawer.
36
53
  //
37
54
  OperationResolver.make({
38
- operation: Common.LayoutOperation.UpdateComplementary,
39
- handler: () => Effect.void,
55
+ operation: LayoutOperation.UpdateComplementary,
56
+ handler: Effect.fnUntraced(function* (input) {
57
+ if (input.state === 'closed') {
58
+ updateState((state) => ({
59
+ ...state,
60
+ drawerState: 'closed',
61
+ }));
62
+ }
63
+ }),
40
64
  }),
41
65
 
42
66
  //
43
67
  // UpdateDialog
44
68
  //
45
69
  OperationResolver.make({
46
- operation: Common.LayoutOperation.UpdateDialog,
70
+ operation: LayoutOperation.UpdateDialog,
47
71
  handler: Effect.fnUntraced(function* (input) {
48
72
  updateState((state) => ({
49
73
  ...state,
@@ -61,7 +85,7 @@ export default Capability.makeModule(
61
85
  // UpdatePopover
62
86
  //
63
87
  OperationResolver.make({
64
- operation: Common.LayoutOperation.UpdatePopover,
88
+ operation: LayoutOperation.UpdatePopover,
65
89
  handler: Effect.fnUntraced(function* (input) {
66
90
  updateState((state) => ({
67
91
  ...state,
@@ -86,7 +110,7 @@ export default Capability.makeModule(
86
110
  // SwitchWorkspace
87
111
  //
88
112
  OperationResolver.make({
89
- operation: Common.LayoutOperation.SwitchWorkspace,
113
+ operation: LayoutOperation.SwitchWorkspace,
90
114
  handler: Effect.fnUntraced(function* (input) {
91
115
  updateState((state) => ({
92
116
  ...state,
@@ -105,10 +129,10 @@ export default Capability.makeModule(
105
129
  // RevertWorkspace
106
130
  //
107
131
  OperationResolver.make({
108
- operation: Common.LayoutOperation.RevertWorkspace,
132
+ operation: LayoutOperation.RevertWorkspace,
109
133
  handler: Effect.fnUntraced(function* () {
110
134
  const state = getState();
111
- yield* Operation.invoke(Common.LayoutOperation.SwitchWorkspace, {
135
+ yield* Operation.invoke(LayoutOperation.SwitchWorkspace, {
112
136
  subject: state.previousWorkspace,
113
137
  });
114
138
  }),
@@ -118,20 +142,35 @@ export default Capability.makeModule(
118
142
  // Open
119
143
  //
120
144
  OperationResolver.make({
121
- operation: Common.LayoutOperation.Open,
145
+ operation: LayoutOperation.Open,
122
146
  handler: Effect.fnUntraced(function* (input) {
123
- updateState((state) => {
124
- // Push current active to history if it exists.
125
- const newHistory = state.active ? [...state.history, state.active] : state.history;
126
- // Limit history length to prevent memory issues.
127
- const trimmedHistory =
128
- newHistory.length > MAX_HISTORY_LENGTH ? newHistory.slice(-MAX_HISTORY_LENGTH) : newHistory;
129
- return {
147
+ const id = input.subject[0];
148
+ const { id: primaryId, variant } = parseEntryId(id);
149
+ const state = getState();
150
+
151
+ // Only treat as companion when opening a variant of the current workspace/active (e.g. object~comments).
152
+ // IDs like settings~spaceId are alternate-tree nodes and should navigate main content, not open the drawer.
153
+ // TODO(wittjosiah): Factor out the change-companion operation from deck to a common layout operation.
154
+ const isCompanionOfCurrent = variant && (primaryId === state.workspace || primaryId === state.active);
155
+ if (isCompanionOfCurrent) {
156
+ updateState((state) => ({
130
157
  ...state,
131
- active: input.subject[0],
132
- history: trimmedHistory,
133
- };
134
- });
158
+ companionVariant: variant,
159
+ drawerState: state.drawerState === 'closed' || !state.drawerState ? 'open' : state.drawerState,
160
+ }));
161
+ } else {
162
+ // Regular navigation - update active and history (use full id for alternate-tree nodes).
163
+ updateState((state) => {
164
+ const newHistory = state.active ? [...state.history, state.active] : state.history;
165
+ const trimmedHistory =
166
+ newHistory.length > MAX_HISTORY_LENGTH ? newHistory.slice(-MAX_HISTORY_LENGTH) : newHistory;
167
+ return {
168
+ ...state,
169
+ active: id,
170
+ history: trimmedHistory,
171
+ };
172
+ });
173
+ }
135
174
  }),
136
175
  }),
137
176
 
@@ -139,7 +178,7 @@ export default Capability.makeModule(
139
178
  // Close
140
179
  //
141
180
  OperationResolver.make({
142
- operation: Common.LayoutOperation.Close,
181
+ operation: LayoutOperation.Close,
143
182
  handler: Effect.fnUntraced(function* () {
144
183
  updateState((state) => {
145
184
  // Pop from history if available.
@@ -165,7 +204,7 @@ export default Capability.makeModule(
165
204
  // Set
166
205
  //
167
206
  OperationResolver.make({
168
- operation: Common.LayoutOperation.Set,
207
+ operation: LayoutOperation.Set,
169
208
  handler: Effect.fnUntraced(function* (input) {
170
209
  updateState((state) => ({
171
210
  ...state,
@@ -5,14 +5,14 @@
5
5
  import * as Effect from 'effect/Effect';
6
6
  import React from 'react';
7
7
 
8
- import { Capability, Common } from '@dxos/app-framework';
8
+ import { Capabilities, Capability } from '@dxos/app-framework';
9
9
 
10
10
  import { SimpleLayout } from '../../components';
11
11
  import { meta } from '../../meta';
12
12
 
13
13
  export default Capability.makeModule(() =>
14
14
  Effect.succeed(
15
- Capability.contributes(Common.Capability.ReactRoot, {
15
+ Capability.contributes(Capabilities.ReactRoot, {
16
16
  id: meta.id,
17
17
  root: () => {
18
18
  return <SimpleLayout />;
@@ -5,7 +5,8 @@
5
5
  import * as Effect from 'effect/Effect';
6
6
  import React from 'react';
7
7
 
8
- import { Capability, Common } from '@dxos/app-framework';
8
+ import { Capabilities, Capability } from '@dxos/app-framework';
9
+ import { Surface } from '@dxos/app-framework/ui';
9
10
  import { Node } from '@dxos/plugin-graph';
10
11
 
11
12
  import { Home, Workspace } from '../../components';
@@ -16,18 +17,18 @@ type SurfaceData = {
16
17
  properties: Record<string, any>;
17
18
  };
18
19
 
19
- const ALLOWED_DISPOSITIONS = ['workspace', 'user-account', 'pin-end'];
20
+ const ALLOWED_DISPOSITIONS = ['workspace', 'user-account', 'pin-end', 'alternate-tree'];
20
21
 
21
22
  export default Capability.makeModule(() =>
22
23
  Effect.succeed(
23
- Capability.contributes(Common.Capability.ReactSurface, [
24
- Common.createSurface({
24
+ Capability.contributes(Capabilities.ReactSurface, [
25
+ Surface.create({
25
26
  id: `${meta.id}/home`,
26
27
  role: 'article',
27
28
  filter: (data): data is SurfaceData => data.attendableId === Node.RootId,
28
29
  component: () => <Home />,
29
30
  }),
30
- Common.createSurface({
31
+ Surface.create({
31
32
  id: `${meta.id}/workspace-article`,
32
33
  role: 'article',
33
34
  position: 'fallback',
@@ -7,7 +7,7 @@
7
7
 
8
8
  import * as Effect from 'effect/Effect';
9
9
 
10
- import { Capability, Common } from '@dxos/app-framework';
10
+ import { Capabilities, Capability } from '@dxos/app-framework';
11
11
  import { log } from '@dxos/log';
12
12
  import { isTauri } from '@dxos/util';
13
13
 
@@ -76,7 +76,7 @@ export default Capability.makeModule(({ isPopover = false }: SpotlightDismissOpt
76
76
  };
77
77
  window.addEventListener('keydown', handleKeyDown);
78
78
 
79
- return Capability.contributes(Common.Capability.Null, null, () =>
79
+ return Capability.contributes(Capabilities.Null, null, () =>
80
80
  Effect.sync(() => {
81
81
  focusCleanup?.();
82
82
  window.removeEventListener('keydown', handleKeyDown);
@@ -5,7 +5,8 @@
5
5
  import { Atom } from '@effect-atom/atom-react';
6
6
  import * as Effect from 'effect/Effect';
7
7
 
8
- import { Capability, Common } from '@dxos/app-framework';
8
+ import { Capability } from '@dxos/app-framework';
9
+ import { AppCapabilities } from '@dxos/app-toolkit';
9
10
  import { Node } from '@dxos/plugin-graph';
10
11
 
11
12
  import { type SimpleLayoutState } from '../../types';
@@ -17,6 +18,8 @@ const defaultState: SimpleLayoutState = {
17
18
  previousWorkspace: Node.RootId,
18
19
  history: [],
19
20
  isPopover: false,
21
+ companionVariant: undefined,
22
+ drawerState: 'closed',
20
23
  };
21
24
 
22
25
  export type SimpleLayoutStateOptions = {
@@ -27,7 +30,7 @@ export default Capability.makeModule(({ initialState }: SimpleLayoutStateOptions
27
30
  Effect.sync(() => {
28
31
  const stateAtom = Atom.make<SimpleLayoutState>({ ...defaultState, ...initialState });
29
32
 
30
- const layoutAtom = Atom.make((get): Common.Capability.Layout => {
33
+ const layoutAtom = Atom.make((get): AppCapabilities.Layout => {
31
34
  const state = get(stateAtom);
32
35
  return {
33
36
  mode: 'simple',
@@ -43,7 +46,7 @@ export default Capability.makeModule(({ initialState }: SimpleLayoutStateOptions
43
46
 
44
47
  return [
45
48
  Capability.contributes(SimpleLayoutStateCapability, stateAtom),
46
- Capability.contributes(Common.Capability.Layout, layoutAtom),
49
+ Capability.contributes(AppCapabilities.Layout, layoutAtom),
47
50
  ];
48
51
  }),
49
52
  );
@@ -4,8 +4,11 @@
4
4
 
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
- import { Capability, Common } from '@dxos/app-framework';
7
+ import { Capabilities, Capability } from '@dxos/app-framework';
8
+ import { LayoutOperation } from '@dxos/app-toolkit';
9
+ import { log } from '@dxos/log';
8
10
  import { Node } from '@dxos/plugin-graph';
11
+ import { isTauri } from '@dxos/util';
9
12
 
10
13
  import { type SimpleLayoutState, SimpleLayoutState as SimpleLayoutStateCapability } from '../../types';
11
14
 
@@ -13,17 +16,19 @@ import { type SimpleLayoutState, SimpleLayoutState as SimpleLayoutStateCapabilit
13
16
  * URL handler for simple layout that syncs browser URL with layout state.
14
17
  * URL format: /{workspace} or /{workspace}/{active}
15
18
  * Root is represented as / or /root.
19
+ *
20
+ * On mobile Tauri, also listens for deep links via the deep-link plugin.
16
21
  */
17
22
  export default Capability.makeModule(
18
23
  Effect.fnUntraced(function* () {
19
- const { invokeSync } = yield* Capability.get(Common.Capability.OperationInvoker);
24
+ const { invokeSync } = yield* Capability.get(Capabilities.OperationInvoker);
20
25
 
21
26
  /**
22
- * Handle navigation events (initial load and popstate).
23
- * Parses URL and updates state accordingly.
27
+ * Handle navigation from a pathname.
28
+ * Parses path and updates state accordingly.
24
29
  */
25
- const handleNavigation = () => {
26
- const pathname = window.location.pathname;
30
+ const handlePathNavigation = (pathname: string) => {
31
+ log.info('[UrlHandler] Navigating to path', { pathname });
27
32
 
28
33
  // Parse URL segments: /{workspace}/{active}
29
34
  const [_, nextWorkspace, nextActive] = pathname.split('/');
@@ -32,49 +37,121 @@ export default Capability.makeModule(
32
37
  const targetWorkspace = !nextWorkspace || nextWorkspace === 'root' ? Node.RootId : nextWorkspace;
33
38
 
34
39
  // Navigate via operations (they will update state accordingly).
35
- invokeSync(Common.LayoutOperation.SwitchWorkspace, { subject: targetWorkspace });
40
+ invokeSync(LayoutOperation.SwitchWorkspace, { subject: targetWorkspace });
36
41
  if (nextActive) {
37
- invokeSync(Common.LayoutOperation.Open, { subject: [nextActive] });
42
+ invokeSync(LayoutOperation.Open, { subject: [nextActive] });
38
43
  }
39
44
  };
40
45
 
46
+ const onNavigation = handleNavigation(handlePathNavigation);
47
+
41
48
  // Handle initial URL and listen for browser navigation.
42
- yield* Effect.sync(() => handleNavigation());
43
- window.addEventListener('popstate', handleNavigation);
49
+ yield* Effect.sync(() => onNavigation());
50
+ window.addEventListener('popstate', onNavigation);
51
+
52
+ // Set up deep link listener for mobile Tauri.
53
+ let unlistenDeepLink: (() => void) | undefined;
54
+ if (isTauri()) {
55
+ yield* Effect.tryPromise({
56
+ try: async () => {
57
+ const { getCurrent, onOpenUrl } = await import('@tauri-apps/plugin-deep-link');
58
+
59
+ // Check if app was launched via deep link (cold start).
60
+ const launchUrls = await getCurrent();
61
+ if (launchUrls && launchUrls.length > 0) {
62
+ log.info('[UrlHandler] App launched with deep links', { urls: launchUrls });
63
+ for (const url of launchUrls) {
64
+ handleDeepLink(url, handlePathNavigation);
65
+ }
66
+ }
67
+
68
+ // Listen for deep links while app is running.
69
+ unlistenDeepLink = await onOpenUrl((urls) => {
70
+ log.info('[UrlHandler] Deep links received', { urls });
71
+ for (const url of urls) {
72
+ handleDeepLink(url, handlePathNavigation);
73
+ }
74
+ });
75
+
76
+ log.info('[UrlHandler] Deep link listener initialized');
77
+ },
78
+ catch: (error) => {
79
+ log.warn('[UrlHandler] Failed to initialize deep link listener', { error });
80
+ return error;
81
+ },
82
+ }).pipe(Effect.catchAll(() => Effect.void));
83
+ }
44
84
 
45
85
  // Subscribe to state changes to update the URL.
46
86
  let lastWorkspace: string | undefined;
47
87
  let lastActive: string | undefined;
48
- const unsubscribe = yield* Common.Capability.subscribeAtom(
49
- SimpleLayoutStateCapability,
50
- (state: SimpleLayoutState) => {
51
- const { workspace, active } = state;
52
-
53
- // Only update URL if relevant state changed.
54
- if (workspace !== lastWorkspace || active !== lastActive) {
55
- lastWorkspace = workspace;
56
- lastActive = active;
57
-
58
- // Build path: root is represented as /, other workspaces as /{workspace}.
59
- let path: string;
60
- if (workspace === Node.RootId) {
61
- path = active ? `/${Node.RootId}/${active}` : '/';
62
- } else {
63
- path = active ? `/${workspace}/${active}` : `/${workspace}`;
64
- }
88
+ const unsubscribe = yield* Capabilities.subscribeAtom(SimpleLayoutStateCapability, (state: SimpleLayoutState) => {
89
+ const { workspace, active } = state;
65
90
 
66
- if (window.location.pathname !== path) {
67
- history.pushState(null, '', `${path}${window.location.search}`);
68
- }
91
+ // Only update URL if relevant state changed.
92
+ if (workspace !== lastWorkspace || active !== lastActive) {
93
+ lastWorkspace = workspace;
94
+ lastActive = active;
95
+
96
+ const path = pathFromState(workspace, active);
97
+ if (window.location.pathname !== path) {
98
+ history.pushState(null, '', `${path}${window.location.search}`);
69
99
  }
70
- },
71
- );
100
+ }
101
+ });
72
102
 
73
- return Capability.contributes(Common.Capability.Null, null, () =>
103
+ return Capability.contributes(Capabilities.Null, null, () =>
74
104
  Effect.sync(() => {
75
- window.removeEventListener('popstate', handleNavigation);
105
+ window.removeEventListener('popstate', onNavigation);
76
106
  unsubscribe();
107
+ unlistenDeepLink?.();
77
108
  }),
78
109
  );
79
110
  }),
80
111
  );
112
+
113
+ // TODO(wittjosiah): Instead of hardcoding redirect paths, we should either:
114
+ // 1. Validate that the workspace exists in the graph before navigating.
115
+ // 2. Implement more structured routing with explicit route definitions.
116
+ /**
117
+ * Check if a path is a special redirect path that shouldn't be navigated to.
118
+ * These paths are handled by other systems (e.g., OAuth).
119
+ */
120
+ const isRedirectPath = (pathname: string): boolean => pathname.startsWith('/redirect/');
121
+
122
+ /**
123
+ * Build pathname from layout state. Root workspace is / or /root/{active}.
124
+ */
125
+ const pathFromState = (workspace: string, active: string | undefined): string =>
126
+ workspace === Node.RootId
127
+ ? active
128
+ ? `/${Node.RootId}/${active}`
129
+ : '/'
130
+ : active
131
+ ? `/${workspace}/${active}`
132
+ : `/${workspace}`;
133
+
134
+ /**
135
+ * Returns a handler for navigation events (initial load and popstate) that navigates to current pathname.
136
+ */
137
+ const handleNavigation =
138
+ (navigate: (pathname: string) => void): (() => void) =>
139
+ () =>
140
+ navigate(window.location.pathname);
141
+
142
+ /**
143
+ * Handle deep link URL from Tauri. Parses the URL and calls navigate unless it's a redirect path.
144
+ */
145
+ const handleDeepLink = (urlString: string, navigate: (pathname: string) => void): void => {
146
+ log.info('[UrlHandler] Deep link received', { url: urlString });
147
+ try {
148
+ const url = new URL(urlString);
149
+ if (isRedirectPath(url.pathname)) {
150
+ log.info('[UrlHandler] Skipping redirect path (handled elsewhere)', { pathname: url.pathname });
151
+ return;
152
+ }
153
+ navigate(url.pathname);
154
+ } catch (error) {
155
+ log.warn('[UrlHandler] Failed to parse deep link URL', { urlString, error });
156
+ }
157
+ };
@@ -13,7 +13,7 @@ import { ContentError } from './ContentError';
13
13
  const meta = {
14
14
  title: 'plugins/plugin-simple-layout/ContentError',
15
15
  component: ContentError,
16
- decorators: [withTheme],
16
+ decorators: [withTheme()],
17
17
  parameters: {
18
18
  layout: 'centered',
19
19
  translations,
@@ -11,7 +11,7 @@ import { ContentLoading } from './ContentLoading';
11
11
  const meta = {
12
12
  title: 'plugins/plugin-simple-layout/ContentLoading',
13
13
  component: ContentLoading,
14
- decorators: [withTheme],
14
+ decorators: [withTheme()],
15
15
  parameters: {
16
16
  layout: 'centered',
17
17
  },