@dxos/plugin-simple-layout 0.0.0 → 0.8.4-main.1c7ec43d41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/SimpleLayoutPlugin-Q5BZE6KD.mjs +50 -0
- package/dist/lib/browser/SimpleLayoutPlugin-Q5BZE6KD.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +25 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/browser/translations.mjs +34 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/SimpleLayoutPlugin-OD45TNPO.mjs +52 -0
- package/dist/lib/node-esm/SimpleLayoutPlugin-OD45TNPO.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +27 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/translations.mjs +36 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/SimpleLayoutPlugin.d.ts +8 -0
- package/dist/types/src/SimpleLayoutPlugin.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +6 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +22 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-root.d.ts +9 -0
- package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/spotlight-dismiss.d.ts +14 -0
- package/dist/types/src/capabilities/spotlight-dismiss.d.ts.map +1 -0
- package/dist/types/src/capabilities/state.d.ts +19 -0
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/capabilities/url-handler.d.ts +12 -0
- package/dist/types/src/capabilities/url-handler.d.ts.map +1 -0
- package/dist/types/src/components/ContentError.stories.d.ts +46 -0
- package/dist/types/src/components/ContentError.stories.d.ts.map +1 -0
- package/dist/types/src/components/DebugOverlay/DebugOverlay.d.ts +19 -0
- package/dist/types/src/components/DebugOverlay/DebugOverlay.d.ts.map +1 -0
- package/dist/types/src/components/DebugOverlay/index.d.ts +2 -0
- package/dist/types/src/components/DebugOverlay/index.d.ts.map +1 -0
- package/dist/types/src/components/Dialog/Dialog.d.ts +3 -0
- package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -0
- package/dist/types/src/components/Dialog/index.d.ts +2 -0
- package/dist/types/src/components/Dialog/index.d.ts.map +1 -0
- package/dist/types/src/components/Home/Home.d.ts +7 -0
- package/dist/types/src/components/Home/Home.d.ts.map +1 -0
- package/dist/types/src/components/Home/index.d.ts +2 -0
- package/dist/types/src/components/Home/index.d.ts.map +1 -0
- package/dist/types/src/components/Loading/Loading.d.ts +3 -0
- package/dist/types/src/components/Loading/Loading.d.ts.map +1 -0
- package/dist/types/src/components/Loading/Loading.stories.d.ts +13 -0
- package/dist/types/src/components/Loading/Loading.stories.d.ts.map +1 -0
- package/dist/types/src/components/Loading/index.d.ts +2 -0
- package/dist/types/src/components/Loading/index.d.ts.map +1 -0
- package/dist/types/src/components/MobileLayout/MobileLayout.d.ts +35 -0
- package/dist/types/src/components/MobileLayout/MobileLayout.d.ts.map +1 -0
- package/dist/types/src/components/MobileLayout/MobileLayout.stories.d.ts +7 -0
- package/dist/types/src/components/MobileLayout/MobileLayout.stories.d.ts.map +1 -0
- package/dist/types/src/components/MobileLayout/index.d.ts +2 -0
- package/dist/types/src/components/MobileLayout/index.d.ts.map +1 -0
- package/dist/types/src/components/NavBranch/NavBranch.d.ts +11 -0
- package/dist/types/src/components/NavBranch/NavBranch.d.ts.map +1 -0
- package/dist/types/src/components/NavBranch/index.d.ts +2 -0
- package/dist/types/src/components/NavBranch/index.d.ts.map +1 -0
- package/dist/types/src/components/Popover/Popover.d.ts +4 -0
- package/dist/types/src/components/Popover/Popover.d.ts.map +1 -0
- package/dist/types/src/components/Popover/index.d.ts +2 -0
- package/dist/types/src/components/Popover/index.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/AppBar.d.ts +24 -0
- package/dist/types/src/components/SimpleLayout/AppBar.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/AppBar.stories.d.ts +54 -0
- package/dist/types/src/components/SimpleLayout/AppBar.stories.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/Drawer.d.ts +6 -0
- package/dist/types/src/components/SimpleLayout/Drawer.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/Main.d.ts +6 -0
- package/dist/types/src/components/SimpleLayout/Main.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/NavBar.d.ts +16 -0
- package/dist/types/src/components/SimpleLayout/NavBar.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/NavBar.stories.d.ts +49 -0
- package/dist/types/src/components/SimpleLayout/NavBar.stories.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/SimpleLayout.d.ts +3 -0
- package/dist/types/src/components/SimpleLayout/SimpleLayout.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/SimpleLayout.stories.d.ts +48 -0
- package/dist/types/src/components/SimpleLayout/SimpleLayout.stories.d.ts.map +1 -0
- package/dist/types/src/components/SimpleLayout/index.d.ts +5 -0
- package/dist/types/src/components/SimpleLayout/index.d.ts.map +1 -0
- package/dist/types/src/components/hooks.d.ts +7 -0
- package/dist/types/src/components/hooks.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +8 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/hooks/actions.d.ts +19 -0
- package/dist/types/src/hooks/actions.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +7 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useAppBarProps.d.ts +7 -0
- package/dist/types/src/hooks/useAppBarProps.d.ts.map +1 -0
- package/dist/types/src/hooks/useCompanions.d.ts +12 -0
- package/dist/types/src/hooks/useCompanions.d.ts.map +1 -0
- package/dist/types/src/hooks/useDrawerActions.d.ts +13 -0
- package/dist/types/src/hooks/useDrawerActions.d.ts.map +1 -0
- package/dist/types/src/hooks/useNavbarActions.d.ts +14 -0
- package/dist/types/src/hooks/useNavbarActions.d.ts.map +1 -0
- package/dist/types/src/hooks/useSimpleLayoutState.d.ts +7 -0
- package/dist/types/src/hooks/useSimpleLayoutState.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +3 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/operations/close.d.ts +5 -0
- package/dist/types/src/operations/close.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/open.d.ts +5 -0
- package/dist/types/src/operations/open.d.ts.map +1 -0
- package/dist/types/src/operations/revert-workspace.d.ts +5 -0
- package/dist/types/src/operations/revert-workspace.d.ts.map +1 -0
- package/dist/types/src/operations/set-layout-mode.d.ts +5 -0
- package/dist/types/src/operations/set-layout-mode.d.ts.map +1 -0
- package/dist/types/src/operations/set.d.ts +5 -0
- package/dist/types/src/operations/set.d.ts.map +1 -0
- package/dist/types/src/operations/state-access.d.ts +8 -0
- package/dist/types/src/operations/state-access.d.ts.map +1 -0
- package/dist/types/src/operations/switch-workspace.d.ts +5 -0
- package/dist/types/src/operations/switch-workspace.d.ts.map +1 -0
- package/dist/types/src/operations/update-complementary.d.ts +5 -0
- package/dist/types/src/operations/update-complementary.d.ts.map +1 -0
- package/dist/types/src/operations/update-dialog.d.ts +5 -0
- package/dist/types/src/operations/update-dialog.d.ts.map +1 -0
- package/dist/types/src/operations/update-popover.d.ts +5 -0
- package/dist/types/src/operations/update-popover.d.ts.map +1 -0
- package/dist/types/src/operations/update-sidebar.d.ts +5 -0
- package/dist/types/src/operations/update-sidebar.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +32 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/types/capabilities.d.ts +44 -0
- package/dist/types/src/types/capabilities.d.ts.map +1 -0
- package/dist/types/src/types/events.d.ts +6 -0
- package/dist/types/src/types/events.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +3 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +58 -29
- package/src/SimpleLayoutPlugin.ts +39 -11
- package/src/capabilities/app-graph-builder.ts +21 -0
- package/src/capabilities/index.ts +13 -3
- package/src/capabilities/operation-handler.ts +14 -0
- package/src/capabilities/{react-root/react-root.tsx → react-root.tsx} +4 -4
- package/src/capabilities/react-surface.tsx +50 -0
- package/src/{hooks/useSpotlightDismiss.ts → capabilities/spotlight-dismiss.ts} +31 -40
- package/src/capabilities/state.tsx +52 -0
- package/src/capabilities/url-handler.ts +161 -0
- package/src/components/ContentError.stories.tsx +9 -8
- package/src/components/DebugOverlay/DebugOverlay.tsx +96 -0
- package/src/components/DebugOverlay/index.ts +5 -0
- package/src/components/Dialog/Dialog.tsx +26 -15
- package/src/components/Home/Home.tsx +80 -80
- package/src/components/{ContentLoading.stories.tsx → Loading/Loading.stories.tsx} +5 -5
- package/src/components/{ContentLoading.tsx → Loading/Loading.tsx} +2 -2
- package/src/components/Loading/index.ts +5 -0
- package/src/components/MobileLayout/MobileLayout.stories.tsx +133 -0
- package/src/components/MobileLayout/MobileLayout.tsx +374 -0
- package/src/components/MobileLayout/index.ts +5 -0
- package/src/components/NavBranch/NavBranch.tsx +124 -0
- package/src/components/NavBranch/index.ts +5 -0
- package/src/components/Popover/Popover.tsx +49 -27
- package/src/components/SimpleLayout/AppBar.stories.tsx +144 -0
- package/src/components/SimpleLayout/AppBar.tsx +94 -0
- package/src/components/SimpleLayout/Drawer.tsx +104 -0
- package/src/components/SimpleLayout/Main.tsx +54 -61
- package/src/components/SimpleLayout/NavBar.stories.tsx +164 -0
- package/src/components/SimpleLayout/NavBar.tsx +19 -83
- package/src/components/SimpleLayout/SimpleLayout.stories.tsx +44 -61
- package/src/components/SimpleLayout/SimpleLayout.tsx +45 -8
- package/src/components/SimpleLayout/index.ts +3 -0
- package/src/components/hooks.ts +26 -0
- package/src/components/index.ts +4 -1
- package/src/hooks/actions.ts +84 -0
- package/src/hooks/index.ts +6 -1
- package/src/hooks/useAppBarProps.ts +95 -0
- package/src/hooks/useCompanions.ts +22 -0
- package/src/hooks/useDrawerActions.ts +100 -0
- package/src/hooks/useNavbarActions.ts +87 -0
- package/src/hooks/useSimpleLayoutState.ts +30 -0
- package/src/index.ts +7 -1
- package/src/meta.ts +2 -1
- package/src/operations/close.ts +34 -0
- package/src/operations/index.ts +16 -0
- package/src/operations/open.ts +63 -0
- package/src/operations/revert-workspace.ts +22 -0
- package/src/operations/set-layout-mode.ts +12 -0
- package/src/operations/set.ts +23 -0
- package/src/operations/state-access.ts +19 -0
- package/src/operations/switch-workspace.ts +26 -0
- package/src/operations/update-complementary.ts +35 -0
- package/src/operations/update-dialog.ts +28 -0
- package/src/operations/update-popover.ts +35 -0
- package/src/operations/update-sidebar.ts +12 -0
- package/src/translations.ts +21 -13
- package/src/types/capabilities.ts +23 -7
- package/src/types/events.ts +15 -0
- package/src/types/index.ts +1 -0
- package/src/capabilities/operation-resolver/index.ts +0 -10
- package/src/capabilities/operation-resolver/operation-resolver.ts +0 -135
- package/src/capabilities/react-root/index.ts +0 -7
- package/src/capabilities/state/index.ts +0 -9
- package/src/capabilities/state/state.tsx +0 -60
- package/src/components/ContentError.tsx +0 -23
- package/src/components/SimpleLayout/Banner.tsx +0 -60
- package/src/components/SimpleLayout/NavBarstories.tsx +0 -59
package/package.json
CHANGED
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-simple-layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.4-main.1c7ec43d41",
|
|
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",
|
|
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,
|
|
10
14
|
"type": "module",
|
|
15
|
+
"imports": {
|
|
16
|
+
"#capabilities": "./src/capabilities/index.ts",
|
|
17
|
+
"#components": "./src/components/index.ts",
|
|
18
|
+
"#hooks": "./src/hooks/index.ts",
|
|
19
|
+
"#meta": "./src/meta.ts",
|
|
20
|
+
"#operations": "./src/operations/index.ts",
|
|
21
|
+
"#translations": "./src/translations.ts",
|
|
22
|
+
"#types": "./src/types/index.ts"
|
|
23
|
+
},
|
|
11
24
|
"exports": {
|
|
12
25
|
".": {
|
|
13
26
|
"source": "./src/index.ts",
|
|
14
|
-
"types": "./dist/types/src/index.d.ts",
|
|
15
27
|
"browser": "./dist/lib/browser/index.mjs",
|
|
16
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
28
|
+
"node": "./dist/lib/node-esm/index.mjs",
|
|
29
|
+
"types": "./dist/types/src/index.d.ts"
|
|
30
|
+
},
|
|
31
|
+
"./translations": {
|
|
32
|
+
"source": "./src/translations.ts",
|
|
33
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
34
|
+
"browser": "./dist/lib/browser/translations.mjs",
|
|
35
|
+
"node": "./dist/lib/node-esm/translations.mjs"
|
|
17
36
|
}
|
|
18
37
|
},
|
|
19
38
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -22,43 +41,53 @@
|
|
|
22
41
|
"src"
|
|
23
42
|
],
|
|
24
43
|
"dependencies": {
|
|
25
|
-
"@
|
|
44
|
+
"@effect-atom/atom": "^0.5.1",
|
|
45
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
26
46
|
"@radix-ui/react-context": "1.1.1",
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@dxos/
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
47
|
+
"@tauri-apps/plugin-deep-link": "^2.2.0",
|
|
48
|
+
"@tauri-apps/plugin-haptics": "^2.3.2",
|
|
49
|
+
"@dxos/app-framework": "0.8.4-main.1c7ec43d41",
|
|
50
|
+
"@dxos/app-toolkit": "0.8.4-main.1c7ec43d41",
|
|
51
|
+
"@dxos/async": "0.8.4-main.1c7ec43d41",
|
|
52
|
+
"@dxos/context": "0.8.4-main.1c7ec43d41",
|
|
53
|
+
"@dxos/compute": "0.8.4-main.1c7ec43d41",
|
|
54
|
+
"@dxos/echo": "0.8.4-main.1c7ec43d41",
|
|
55
|
+
"@dxos/log": "0.8.4-main.1c7ec43d41",
|
|
56
|
+
"@dxos/effect": "0.8.4-main.1c7ec43d41",
|
|
57
|
+
"@dxos/plugin-client": "0.8.4-main.1c7ec43d41",
|
|
58
|
+
"@dxos/plugin-graph": "0.8.4-main.1c7ec43d41",
|
|
59
|
+
"@dxos/react-ui-list": "0.8.4-main.1c7ec43d41",
|
|
60
|
+
"@dxos/react-ui-menu": "0.8.4-main.1c7ec43d41",
|
|
61
|
+
"@dxos/react-ui-mosaic": "0.8.4-main.1c7ec43d41",
|
|
62
|
+
"@dxos/react-ui-attention": "0.8.4-main.1c7ec43d41",
|
|
63
|
+
"@dxos/react-ui-search": "0.8.4-main.1c7ec43d41",
|
|
64
|
+
"@dxos/react-ui-stack": "0.8.4-main.1c7ec43d41",
|
|
65
|
+
"@dxos/schema": "0.8.4-main.1c7ec43d41",
|
|
66
|
+
"@dxos/util": "0.8.4-main.1c7ec43d41"
|
|
39
67
|
},
|
|
40
68
|
"devDependencies": {
|
|
41
69
|
"@types/react": "~19.2.7",
|
|
42
70
|
"@types/react-dom": "~19.2.3",
|
|
43
|
-
"effect": "3.
|
|
71
|
+
"effect": "3.20.0",
|
|
44
72
|
"react": "~19.2.3",
|
|
45
73
|
"react-dom": "~19.2.3",
|
|
46
|
-
"vite": "
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/plugin-
|
|
50
|
-
"@dxos/plugin-space": "0.8.
|
|
51
|
-
"@dxos/plugin-testing": "0.
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/react-ui": "0.8.
|
|
54
|
-
"@dxos/storybook-utils": "0.8.
|
|
74
|
+
"vite": "^8.0.10",
|
|
75
|
+
"@dxos/plugin-search": "0.8.4-main.1c7ec43d41",
|
|
76
|
+
"@dxos/app-graph": "0.8.4-main.1c7ec43d41",
|
|
77
|
+
"@dxos/plugin-preview": "0.8.4-main.1c7ec43d41",
|
|
78
|
+
"@dxos/plugin-space": "0.8.4-main.1c7ec43d41",
|
|
79
|
+
"@dxos/plugin-testing": "0.8.4-main.1c7ec43d41",
|
|
80
|
+
"@dxos/schema": "0.8.4-main.1c7ec43d41",
|
|
81
|
+
"@dxos/react-ui": "0.8.4-main.1c7ec43d41",
|
|
82
|
+
"@dxos/storybook-utils": "0.8.4-main.1c7ec43d41",
|
|
83
|
+
"@dxos/ui-theme": "0.8.4-main.1c7ec43d41"
|
|
55
84
|
},
|
|
56
85
|
"peerDependencies": {
|
|
57
|
-
"effect": "3.
|
|
86
|
+
"effect": "3.20.0",
|
|
58
87
|
"react": "~19.2.3",
|
|
59
88
|
"react-dom": "~19.2.3",
|
|
60
|
-
"@dxos/react-ui": "0.8.
|
|
61
|
-
"@dxos/ui-theme": "0.
|
|
89
|
+
"@dxos/react-ui": "0.8.4-main.1c7ec43d41",
|
|
90
|
+
"@dxos/ui-theme": "0.8.4-main.1c7ec43d41"
|
|
62
91
|
},
|
|
63
92
|
"publishConfig": {
|
|
64
93
|
"access": "public"
|
|
@@ -2,30 +2,58 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ActivationEvent, ActivationEvents, Capability, Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { AppActivationEvents, AppPlugin } from '@dxos/app-toolkit';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
AppGraphBuilder,
|
|
10
|
+
OperationHandler,
|
|
11
|
+
ReactRoot,
|
|
12
|
+
ReactSurface,
|
|
13
|
+
SpotlightDismiss,
|
|
14
|
+
State,
|
|
15
|
+
UrlHandler,
|
|
16
|
+
} from '#capabilities';
|
|
17
|
+
import { meta } from '#meta';
|
|
18
|
+
import { translations } from '#translations';
|
|
19
|
+
import { SimpleLayoutEvents } from '#types';
|
|
10
20
|
|
|
11
21
|
export type SimpleLayoutPluginOptions = {
|
|
12
|
-
/**
|
|
22
|
+
/** Determines if running in popover window context (hides mobile-specific UI). */
|
|
13
23
|
isPopover?: boolean;
|
|
14
24
|
};
|
|
15
25
|
|
|
16
26
|
export const SimpleLayoutPlugin = Plugin.define<SimpleLayoutPluginOptions>(meta).pipe(
|
|
27
|
+
AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),
|
|
28
|
+
AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),
|
|
29
|
+
AppPlugin.addTranslationsModule({ translations }),
|
|
17
30
|
Plugin.addModule(({ isPopover = false }) => ({
|
|
18
31
|
id: Capability.getModuleTag(State),
|
|
19
|
-
activatesOn:
|
|
20
|
-
|
|
21
|
-
activate: () => State({ initialState: { isPopover } }
|
|
32
|
+
activatesOn: ActivationEvents.Startup,
|
|
33
|
+
firesAfterActivation: [SimpleLayoutEvents.StateReady, AppActivationEvents.LayoutReady],
|
|
34
|
+
activate: () => State({ initialState: { isPopover } }),
|
|
35
|
+
})),
|
|
36
|
+
Plugin.addModule(({ isPopover = false }) => ({
|
|
37
|
+
id: Capability.getModuleTag(SpotlightDismiss),
|
|
38
|
+
activatesOn: ActivationEvents.Startup,
|
|
39
|
+
activate: () => SpotlightDismiss({ isPopover }),
|
|
22
40
|
})),
|
|
23
41
|
Plugin.addModule({
|
|
24
42
|
id: Capability.getModuleTag(ReactRoot),
|
|
25
|
-
activatesOn:
|
|
43
|
+
activatesOn: ActivationEvents.Startup,
|
|
26
44
|
activate: ReactRoot,
|
|
27
45
|
}),
|
|
28
|
-
|
|
29
|
-
|
|
46
|
+
Plugin.addModule({
|
|
47
|
+
id: Capability.getModuleTag(ReactSurface),
|
|
48
|
+
activatesOn: ActivationEvents.Startup,
|
|
49
|
+
activate: ReactSurface,
|
|
50
|
+
}),
|
|
51
|
+
Plugin.addModule({
|
|
52
|
+
id: Capability.getModuleTag(UrlHandler),
|
|
53
|
+
activatesOn: ActivationEvent.allOf(ActivationEvents.OperationInvokerReady, SimpleLayoutEvents.StateReady),
|
|
54
|
+
activate: UrlHandler,
|
|
55
|
+
}),
|
|
30
56
|
Plugin.make,
|
|
31
57
|
);
|
|
58
|
+
|
|
59
|
+
export default SimpleLayoutPlugin;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities, AppNode } from '@dxos/app-toolkit';
|
|
9
|
+
import { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';
|
|
10
|
+
|
|
11
|
+
export default Capability.makeModule(
|
|
12
|
+
Effect.fnUntraced(function* () {
|
|
13
|
+
const extensions = yield* GraphBuilder.createExtension({
|
|
14
|
+
id: 'org.dxos.plugin.simpleLayout.not-found',
|
|
15
|
+
match: NodeMatcher.whenRoot,
|
|
16
|
+
connector: () => Effect.succeed([AppNode.makeNotFound()]),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { Capability } from '@dxos/app-framework';
|
|
6
|
+
import { OperationHandlerSet } from '@dxos/compute';
|
|
7
|
+
|
|
8
|
+
export const AppGraphBuilder = Capability.lazy('AppGraphBuilder', () => import('./app-graph-builder'));
|
|
9
|
+
export const OperationHandler = Capability.lazy<OperationHandlerSet.OperationHandlerSet>(
|
|
10
|
+
'OperationHandler',
|
|
11
|
+
() => import('./operation-handler'),
|
|
12
|
+
);
|
|
13
|
+
export const ReactRoot = Capability.lazy('ReactRoot', () => import('./react-root'));
|
|
14
|
+
export const ReactSurface = Capability.lazy('ReactSurface', () => import('./react-surface'));
|
|
15
|
+
export const SpotlightDismiss = Capability.lazy('SpotlightDismiss', () => import('./spotlight-dismiss'));
|
|
16
|
+
export const State = Capability.lazy('State', () => import('./state'));
|
|
17
|
+
export const UrlHandler = Capability.lazy('UrlHandler', () => import('./url-handler'));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright 2025 DXOS.org
|
|
2
|
+
|
|
3
|
+
import * as Effect from 'effect/Effect';
|
|
4
|
+
|
|
5
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
6
|
+
import type { OperationHandlerSet } from '@dxos/compute';
|
|
7
|
+
|
|
8
|
+
import { SimpleLayoutOperationHandlerSet } from '#operations';
|
|
9
|
+
|
|
10
|
+
export default Capability.makeModule<OperationHandlerSet.OperationHandlerSet>(
|
|
11
|
+
Effect.fnUntraced(function* () {
|
|
12
|
+
return Capability.contributes(Capabilities.OperationHandler, SimpleLayoutOperationHandlerSet);
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
9
9
|
|
|
10
|
-
import { SimpleLayout } from '
|
|
11
|
-
import { meta } from '
|
|
10
|
+
import { SimpleLayout } from '#components';
|
|
11
|
+
import { meta } from '#meta';
|
|
12
12
|
|
|
13
13
|
export default Capability.makeModule(() =>
|
|
14
14
|
Effect.succeed(
|
|
15
|
-
Capability.contributes(
|
|
15
|
+
Capability.contributes(Capabilities.ReactRoot, {
|
|
16
16
|
id: meta.id,
|
|
17
17
|
root: () => {
|
|
18
18
|
return <SimpleLayout />;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
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';
|
|
12
|
+
import { Node } from '@dxos/plugin-graph';
|
|
13
|
+
|
|
14
|
+
import { Home, NavBranch } from '#components';
|
|
15
|
+
|
|
16
|
+
type SurfaceData = {
|
|
17
|
+
attendableId: string;
|
|
18
|
+
properties: Record<string, any>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const ALLOWED_DISPOSITIONS = ['workspace', 'user-account', 'pin-end'];
|
|
22
|
+
|
|
23
|
+
export default Capability.makeModule(() =>
|
|
24
|
+
Effect.succeed(
|
|
25
|
+
Capability.contributes(Capabilities.ReactSurface, [
|
|
26
|
+
Surface.create({
|
|
27
|
+
id: 'home',
|
|
28
|
+
role: 'article',
|
|
29
|
+
filter: (data): data is SurfaceData => data.attendableId === Node.RootId,
|
|
30
|
+
component: () => <Home />,
|
|
31
|
+
}),
|
|
32
|
+
Surface.create({
|
|
33
|
+
id: 'not-found',
|
|
34
|
+
role: 'article',
|
|
35
|
+
filter: (data): data is SurfaceData => data.attendableId === NOT_FOUND_PATH,
|
|
36
|
+
component: () => <NotFoundArticle />,
|
|
37
|
+
}),
|
|
38
|
+
Surface.create({
|
|
39
|
+
id: 'nav-branch',
|
|
40
|
+
role: 'article',
|
|
41
|
+
position: 'fallback',
|
|
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
|
+
},
|
|
46
|
+
component: ({ data }) => <NavBranch id={data.attendableId} />,
|
|
47
|
+
}),
|
|
48
|
+
]),
|
|
49
|
+
),
|
|
50
|
+
);
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
// Based on the frontend-driven dismiss pattern from:
|
|
6
6
|
// https://github.com/Jedliu/tauri-template-demo
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
9
|
|
|
10
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
10
11
|
import { log } from '@dxos/log';
|
|
11
12
|
import { isTauri } from '@dxos/util';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Get the Tauri window API from the global object.
|
|
15
|
-
* Returns undefined if not running in Tauri.
|
|
16
16
|
*/
|
|
17
17
|
const getTauriWindow = (): any => {
|
|
18
18
|
const tauri = (globalThis as any).__TAURI__;
|
|
@@ -21,60 +21,46 @@ const getTauriWindow = (): any => {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Get the Tauri core API (invoke) from the global object.
|
|
24
|
-
* Returns undefined if not running in Tauri.
|
|
25
24
|
*/
|
|
26
25
|
const getTauriCore = (): any => {
|
|
27
26
|
const tauri = (globalThis as any).__TAURI__;
|
|
28
27
|
return tauri?.core;
|
|
29
28
|
};
|
|
30
29
|
|
|
30
|
+
export type SpotlightDismissOptions = {
|
|
31
|
+
/** Whether running in popover window context. */
|
|
32
|
+
isPopover?: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
31
35
|
/**
|
|
32
|
-
*
|
|
36
|
+
* Capability that sets up spotlight panel dismiss behavior.
|
|
33
37
|
* When running in Tauri popover mode, listens for focus loss and Escape key
|
|
34
|
-
* to dismiss the spotlight panel.
|
|
38
|
+
* to dismiss the spotlight panel. Runs at startup before React renders.
|
|
35
39
|
*/
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
40
|
+
export default Capability.makeModule(({ isPopover = false }: SpotlightDismissOptions = {}) =>
|
|
41
|
+
Effect.promise(async () => {
|
|
39
42
|
if (!isPopover || !isTauri()) {
|
|
40
|
-
return;
|
|
43
|
+
return [];
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!tauriWindow || !tauriCore) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
46
|
+
// Set up focus listener.
|
|
47
|
+
let focusCleanup: (() => void) | undefined;
|
|
48
|
+
try {
|
|
49
|
+
const tauriWindow = getTauriWindow();
|
|
50
|
+
const tauriCore = getTauriCore();
|
|
51
|
+
if (tauriWindow && tauriCore) {
|
|
53
52
|
const win = tauriWindow.getCurrentWindow();
|
|
54
|
-
|
|
53
|
+
focusCleanup = await win.onFocusChanged(async ({ payload }: { payload: boolean }) => {
|
|
55
54
|
if (!payload) {
|
|
56
55
|
await tauriCore.invoke('hide_spotlight');
|
|
57
56
|
}
|
|
58
57
|
});
|
|
59
|
-
cleanup = unlisten;
|
|
60
|
-
} catch (err) {
|
|
61
|
-
log.catch(err);
|
|
62
58
|
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
void setup();
|
|
66
|
-
|
|
67
|
-
return () => {
|
|
68
|
-
cleanup?.();
|
|
69
|
-
};
|
|
70
|
-
}, [isPopover]);
|
|
71
|
-
|
|
72
|
-
// Handle Escape key to dismiss spotlight.
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
if (!isPopover || !isTauri()) {
|
|
75
|
-
return;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
log.catch(err);
|
|
76
61
|
}
|
|
77
62
|
|
|
63
|
+
// Set up Escape key listener.
|
|
78
64
|
const handleKeyDown = async (event: KeyboardEvent) => {
|
|
79
65
|
if (event.key === 'Escape') {
|
|
80
66
|
event.preventDefault();
|
|
@@ -88,8 +74,13 @@ export const useSpotlightDismiss = (isPopover: boolean | undefined) => {
|
|
|
88
74
|
}
|
|
89
75
|
}
|
|
90
76
|
};
|
|
91
|
-
|
|
92
77
|
window.addEventListener('keydown', handleKeyDown);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
|
|
79
|
+
return Capability.contributes(Capabilities.Null, null, () =>
|
|
80
|
+
Effect.sync(() => {
|
|
81
|
+
focusCleanup?.();
|
|
82
|
+
window.removeEventListener('keydown', handleKeyDown);
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
|
|
8
|
+
import { Capability } from '@dxos/app-framework';
|
|
9
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
10
|
+
import { Node } from '@dxos/plugin-graph';
|
|
11
|
+
|
|
12
|
+
import { type SimpleLayoutState } from '#types';
|
|
13
|
+
import { SimpleLayoutState as SimpleLayoutStateCapability } from '#types';
|
|
14
|
+
|
|
15
|
+
const defaultState: SimpleLayoutState = {
|
|
16
|
+
dialogOpen: false,
|
|
17
|
+
workspace: Node.RootId,
|
|
18
|
+
previousWorkspace: Node.RootId,
|
|
19
|
+
history: [],
|
|
20
|
+
isPopover: false,
|
|
21
|
+
companionVariant: undefined,
|
|
22
|
+
drawerState: 'closed',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type SimpleLayoutStateOptions = {
|
|
26
|
+
initialState?: Partial<SimpleLayoutState>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default Capability.makeModule(({ initialState }: SimpleLayoutStateOptions = {}) =>
|
|
30
|
+
Effect.sync(() => {
|
|
31
|
+
const stateAtom = Atom.make<SimpleLayoutState>({ ...defaultState, ...initialState });
|
|
32
|
+
|
|
33
|
+
const layoutAtom = Atom.make((get): AppCapabilities.Layout => {
|
|
34
|
+
const state = get(stateAtom);
|
|
35
|
+
return {
|
|
36
|
+
mode: 'simple',
|
|
37
|
+
dialogOpen: state.dialogOpen,
|
|
38
|
+
sidebarOpen: false,
|
|
39
|
+
complementarySidebarOpen: false,
|
|
40
|
+
workspace: state.workspace,
|
|
41
|
+
active: state.active ? [state.active] : [],
|
|
42
|
+
inactive: [],
|
|
43
|
+
scrollIntoView: undefined,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return [
|
|
48
|
+
Capability.contributes(SimpleLayoutStateCapability, stateAtom),
|
|
49
|
+
Capability.contributes(AppCapabilities.Layout, layoutAtom),
|
|
50
|
+
];
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities, LayoutOperation, fromUrlPath, getWorkspaceFromPath, toUrlPath } from '@dxos/app-toolkit';
|
|
9
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
11
|
+
import { isTauri } from '@dxos/util';
|
|
12
|
+
|
|
13
|
+
import { type SimpleLayoutState, SimpleLayoutState as SimpleLayoutStateCapability } from '#types';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* URL handler for simple layout that syncs browser URL with layout state.
|
|
17
|
+
* URL paths map directly to qualified graph IDs with the leading `root` segment stripped.
|
|
18
|
+
* Root is represented as `/`.
|
|
19
|
+
*
|
|
20
|
+
* On Tauri, also listens for deep links via the deep-link plugin.
|
|
21
|
+
*/
|
|
22
|
+
export default Capability.makeModule(
|
|
23
|
+
Effect.fnUntraced(function* () {
|
|
24
|
+
const { invokePromise } = yield* Capability.get(Capabilities.OperationInvoker);
|
|
25
|
+
const capabilities = yield* Capability.Service;
|
|
26
|
+
|
|
27
|
+
/** Dispatch all NavigationHandler contributions with a given URL. */
|
|
28
|
+
const dispatchNavigationHandlers = (url: URL) =>
|
|
29
|
+
Effect.gen(function* () {
|
|
30
|
+
const handlers = yield* Capability.getAll(AppCapabilities.NavigationHandler);
|
|
31
|
+
yield* Effect.all(
|
|
32
|
+
handlers.map((handler) => handler(url)),
|
|
33
|
+
{ concurrency: 'unbounded' },
|
|
34
|
+
);
|
|
35
|
+
}).pipe(Effect.provideService(Capability.Service, capabilities), runAndForwardErrors);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Handle navigation from a URL.
|
|
39
|
+
* Dispatches to NavigationHandler contributions, then handles pathname routing.
|
|
40
|
+
*/
|
|
41
|
+
const handlePathNavigation = (url?: URL) => {
|
|
42
|
+
const resolvedUrl = url ?? new URL(window.location.href);
|
|
43
|
+
void dispatchNavigationHandlers(resolvedUrl);
|
|
44
|
+
|
|
45
|
+
let pathname = resolvedUrl.pathname;
|
|
46
|
+
if (isFilePath(pathname)) {
|
|
47
|
+
log.info('[UrlHandler] Skipping file path (not a graph node)', { pathname });
|
|
48
|
+
pathname = '/';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const qualifiedId = fromUrlPath(pathname);
|
|
52
|
+
const workspace = getWorkspaceFromPath(qualifiedId);
|
|
53
|
+
|
|
54
|
+
void invokePromise(LayoutOperation.SwitchWorkspace, { subject: workspace });
|
|
55
|
+
|
|
56
|
+
const activeId = qualifiedId !== workspace ? qualifiedId : undefined;
|
|
57
|
+
if (activeId) {
|
|
58
|
+
void invokePromise(LayoutOperation.Open, { subject: [activeId] });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const onPopState = () => {
|
|
63
|
+
handlePathNavigation();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Initial navigation.
|
|
67
|
+
yield* Effect.sync(() => handlePathNavigation());
|
|
68
|
+
window.addEventListener('popstate', onPopState);
|
|
69
|
+
|
|
70
|
+
// Tauri deep link support.
|
|
71
|
+
let unlistenDeepLink: (() => void) | undefined;
|
|
72
|
+
if (isTauri()) {
|
|
73
|
+
yield* Effect.tryPromise({
|
|
74
|
+
try: async () => {
|
|
75
|
+
const { getCurrent, onOpenUrl } = await import('@tauri-apps/plugin-deep-link');
|
|
76
|
+
|
|
77
|
+
const launchUrls = await getCurrent();
|
|
78
|
+
if (launchUrls && launchUrls.length > 0) {
|
|
79
|
+
log.info('[UrlHandler] App launched with deep links', { urls: launchUrls });
|
|
80
|
+
for (const urlString of launchUrls) {
|
|
81
|
+
handleDeepLink(urlString, handlePathNavigation);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
unlistenDeepLink = await onOpenUrl((urls) => {
|
|
86
|
+
log.info('[UrlHandler] Deep links received', { urls });
|
|
87
|
+
for (const urlString of urls) {
|
|
88
|
+
handleDeepLink(urlString, handlePathNavigation);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
catch: (error) => {
|
|
93
|
+
log.warn('[UrlHandler] Failed to initialize deep link listener', { error });
|
|
94
|
+
return error;
|
|
95
|
+
},
|
|
96
|
+
}).pipe(Effect.catchAll(() => Effect.void));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Sync URL with layout state changes.
|
|
100
|
+
let lastWorkspace: string | undefined;
|
|
101
|
+
let lastActive: string | undefined;
|
|
102
|
+
const unsubscribe = yield* Capabilities.subscribeAtom(SimpleLayoutStateCapability, (state: SimpleLayoutState) => {
|
|
103
|
+
const { workspace, active } = state;
|
|
104
|
+
|
|
105
|
+
if (workspace !== lastWorkspace || active !== lastActive) {
|
|
106
|
+
lastWorkspace = workspace;
|
|
107
|
+
lastActive = active;
|
|
108
|
+
|
|
109
|
+
const path = active ? toUrlPath(active) : toUrlPath(workspace);
|
|
110
|
+
if (window.location.pathname !== path) {
|
|
111
|
+
history.pushState(null, '', `${path}${window.location.search}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return Capability.contributes(Capabilities.Null, null, () =>
|
|
117
|
+
Effect.sync(() => {
|
|
118
|
+
window.removeEventListener('popstate', onPopState);
|
|
119
|
+
unsubscribe();
|
|
120
|
+
unlistenDeepLink?.();
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
/** Check if a path is a redirect path handled elsewhere (e.g., OAuth). */
|
|
127
|
+
const isRedirectPath = (pathname: string): boolean => pathname.startsWith('/redirect/');
|
|
128
|
+
|
|
129
|
+
/** Paths with file extensions are not graph node paths. */
|
|
130
|
+
const isFilePath = (pathname: string): boolean => /\.[a-z]+$/i.test(pathname);
|
|
131
|
+
|
|
132
|
+
/** Handle a deep link URL string. Merges query params into window.location and navigates. */
|
|
133
|
+
const handleDeepLink = (urlString: string, navigate: (url?: URL) => void): void => {
|
|
134
|
+
log.info('[UrlHandler] Deep link received', { url: urlString });
|
|
135
|
+
try {
|
|
136
|
+
const deepLinkUrl = new URL(urlString);
|
|
137
|
+
|
|
138
|
+
// For custom schemes (e.g., composer://a/b/c), new URL() treats the first segment as the
|
|
139
|
+
// hostname. Reconstruct the full path from hostname + pathname.
|
|
140
|
+
const fullPath =
|
|
141
|
+
deepLinkUrl.protocol !== 'https:' && deepLinkUrl.protocol !== 'http:' && deepLinkUrl.hostname
|
|
142
|
+
? '/' + deepLinkUrl.hostname + deepLinkUrl.pathname
|
|
143
|
+
: deepLinkUrl.pathname;
|
|
144
|
+
|
|
145
|
+
if (isRedirectPath(fullPath)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Merge deep link query params into the current window URL so handlers can read them.
|
|
150
|
+
const current = new URL(window.location.href);
|
|
151
|
+
if (deepLinkUrl.search) {
|
|
152
|
+
deepLinkUrl.searchParams.forEach((value, key) => current.searchParams.set(key, value));
|
|
153
|
+
}
|
|
154
|
+
current.pathname = fullPath;
|
|
155
|
+
history.replaceState(null, '', current.pathname + current.search);
|
|
156
|
+
|
|
157
|
+
navigate(current);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
log.warn('[UrlHandler] Failed to parse deep link URL', { urlString, error });
|
|
160
|
+
}
|
|
161
|
+
};
|