@dxos/plugin-simple-layout 0.0.0 → 0.8.4-main.03d5cd7b56
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/neutral/SimpleLayoutPlugin.mjs +52 -0
- package/dist/lib/neutral/SimpleLayoutPlugin.mjs.map +7 -0
- package/dist/lib/neutral/app-graph-builder-EYQKLRRP.mjs +21 -0
- package/dist/lib/neutral/app-graph-builder-EYQKLRRP.mjs.map +7 -0
- package/dist/lib/neutral/capabilities/index.mjs +21 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/neutral/chunk-7UDV3JDT.mjs +22 -0
- package/dist/lib/neutral/chunk-7UDV3JDT.mjs.map +7 -0
- package/dist/lib/neutral/chunk-AMTEDJHG.mjs +19 -0
- package/dist/lib/neutral/chunk-AMTEDJHG.mjs.map +7 -0
- package/dist/lib/neutral/chunk-FD2CAY4Q.mjs +26 -0
- package/dist/lib/neutral/chunk-FD2CAY4Q.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/neutral/chunk-XVUAQHKU.mjs +8 -0
- package/dist/lib/neutral/chunk-XVUAQHKU.mjs.map +7 -0
- package/dist/lib/neutral/close-WKMURGUB.mjs +35 -0
- package/dist/lib/neutral/close-WKMURGUB.mjs.map +7 -0
- package/dist/lib/neutral/components/index.mjs +924 -0
- package/dist/lib/neutral/components/index.mjs.map +7 -0
- package/dist/lib/neutral/hooks/index.mjs +332 -0
- package/dist/lib/neutral/hooks/index.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +14 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +8 -0
- package/dist/lib/neutral/meta.mjs.map +7 -0
- package/dist/lib/neutral/open-XI2T7D5O.mjs +49 -0
- package/dist/lib/neutral/open-XI2T7D5O.mjs.map +7 -0
- package/dist/lib/neutral/operation-handler-EAIE7KPR.mjs +13 -0
- package/dist/lib/neutral/operation-handler-EAIE7KPR.mjs.map +7 -0
- package/dist/lib/neutral/operations/index.mjs +8 -0
- package/dist/lib/neutral/operations/index.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +16 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/react-root-VE265VX4.mjs +18 -0
- package/dist/lib/neutral/react-root-VE265VX4.mjs.map +7 -0
- package/dist/lib/neutral/react-surface-REZMYKQV.mjs +46 -0
- package/dist/lib/neutral/react-surface-REZMYKQV.mjs.map +7 -0
- package/dist/lib/neutral/revert-workspace-ST6NZUNG.mjs +22 -0
- package/dist/lib/neutral/revert-workspace-ST6NZUNG.mjs.map +7 -0
- package/dist/lib/neutral/set-6ZRLWPJS.mjs +22 -0
- package/dist/lib/neutral/set-6ZRLWPJS.mjs.map +7 -0
- package/dist/lib/neutral/set-layout-mode-L22HRCKS.mjs +13 -0
- package/dist/lib/neutral/set-layout-mode-L22HRCKS.mjs.map +7 -0
- package/dist/lib/neutral/spotlight-dismiss-EIYW5E7M.mjs +58 -0
- package/dist/lib/neutral/spotlight-dismiss-EIYW5E7M.mjs.map +7 -0
- package/dist/lib/neutral/state-7NXKBLPY.mjs +47 -0
- package/dist/lib/neutral/state-7NXKBLPY.mjs.map +7 -0
- package/dist/lib/neutral/switch-workspace-PYWPTMFO.mjs +25 -0
- package/dist/lib/neutral/switch-workspace-PYWPTMFO.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +36 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +10 -0
- package/dist/lib/neutral/types/index.mjs.map +7 -0
- package/dist/lib/neutral/update-complementary-HKWF5OXT.mjs +33 -0
- package/dist/lib/neutral/update-complementary-HKWF5OXT.mjs.map +7 -0
- package/dist/lib/neutral/update-dialog-P4ASXCE7.mjs +30 -0
- package/dist/lib/neutral/update-dialog-P4ASXCE7.mjs.map +7 -0
- package/dist/lib/neutral/update-popover-REAKC2GN.mjs +34 -0
- package/dist/lib/neutral/update-popover-REAKC2GN.mjs.map +7 -0
- package/dist/lib/neutral/update-sidebar-O5SQPR6Q.mjs +12 -0
- package/dist/lib/neutral/update-sidebar-O5SQPR6Q.mjs.map +7 -0
- package/dist/lib/neutral/url-handler-GZXUUAHD.mjs +129 -0
- package/dist/lib/neutral/url-handler-GZXUUAHD.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 +3 -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/plugin.d.ts +4 -0
- package/dist/types/src/plugin.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/SimpleLayoutCapabilities.d.ts +38 -0
- package/dist/types/src/types/SimpleLayoutCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/SimpleLayoutEvents.d.ts +4 -0
- package/dist/types/src/types/SimpleLayoutEvents.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 +94 -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 +51 -0
- package/src/capabilities/url-handler.ts +164 -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 +372 -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 +46 -63
- 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 +86 -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 +32 -0
- package/src/index.ts +2 -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 +21 -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/plugin.ts +11 -0
- package/src/translations.ts +21 -13
- package/src/types/{capabilities.ts → SimpleLayoutCapabilities.ts} +19 -15
- package/src/types/SimpleLayoutEvents.ts +15 -0
- package/src/types/index.ts +2 -1
- 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
|
@@ -0,0 +1,164 @@
|
|
|
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 { SimpleLayoutCapabilities } 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(
|
|
103
|
+
SimpleLayoutCapabilities.State,
|
|
104
|
+
(state: SimpleLayoutCapabilities.SimpleLayoutState) => {
|
|
105
|
+
const { workspace, active } = state;
|
|
106
|
+
|
|
107
|
+
if (workspace !== lastWorkspace || active !== lastActive) {
|
|
108
|
+
lastWorkspace = workspace;
|
|
109
|
+
lastActive = active;
|
|
110
|
+
|
|
111
|
+
const path = active ? toUrlPath(active) : toUrlPath(workspace);
|
|
112
|
+
if (window.location.pathname !== path) {
|
|
113
|
+
history.pushState(null, '', `${path}${window.location.search}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return Capability.contributes(Capabilities.Null, null, () =>
|
|
120
|
+
Effect.sync(() => {
|
|
121
|
+
window.removeEventListener('popstate', onPopState);
|
|
122
|
+
unsubscribe();
|
|
123
|
+
unlistenDeepLink?.();
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
/** Check if a path is a redirect path handled elsewhere (e.g., OAuth). */
|
|
130
|
+
const isRedirectPath = (pathname: string): boolean => pathname.startsWith('/redirect/');
|
|
131
|
+
|
|
132
|
+
/** Paths with file extensions are not graph node paths. */
|
|
133
|
+
const isFilePath = (pathname: string): boolean => /\.[a-z]+$/i.test(pathname);
|
|
134
|
+
|
|
135
|
+
/** Handle a deep link URL string. Merges query params into window.location and navigates. */
|
|
136
|
+
const handleDeepLink = (urlString: string, navigate: (url?: URL) => void): void => {
|
|
137
|
+
log.info('[UrlHandler] Deep link received', { url: urlString });
|
|
138
|
+
try {
|
|
139
|
+
const deepLinkUrl = new URL(urlString);
|
|
140
|
+
|
|
141
|
+
// For custom schemes (e.g., composer://a/b/c), new URL() treats the first segment as the
|
|
142
|
+
// hostname. Reconstruct the full path from hostname + pathname.
|
|
143
|
+
const fullPath =
|
|
144
|
+
deepLinkUrl.protocol !== 'https:' && deepLinkUrl.protocol !== 'http:' && deepLinkUrl.hostname
|
|
145
|
+
? '/' + deepLinkUrl.hostname + deepLinkUrl.pathname
|
|
146
|
+
: deepLinkUrl.pathname;
|
|
147
|
+
|
|
148
|
+
if (isRedirectPath(fullPath)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Merge deep link query params into the current window URL so handlers can read them.
|
|
153
|
+
const current = new URL(window.location.href);
|
|
154
|
+
if (deepLinkUrl.search) {
|
|
155
|
+
deepLinkUrl.searchParams.forEach((value, key) => current.searchParams.set(key, value));
|
|
156
|
+
}
|
|
157
|
+
current.pathname = fullPath;
|
|
158
|
+
history.replaceState(null, '', current.pathname + current.search);
|
|
159
|
+
|
|
160
|
+
navigate(current);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
log.warn('[UrlHandler] Failed to parse deep link URL', { urlString, error });
|
|
163
|
+
}
|
|
164
|
+
};
|
|
@@ -4,28 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
|
|
7
|
+
import { ErrorFallback } from '@dxos/react-ui';
|
|
7
8
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
8
9
|
|
|
9
|
-
import { translations } from '
|
|
10
|
-
|
|
11
|
-
import { ContentError } from './ContentError';
|
|
10
|
+
import { translations } from '#translations';
|
|
12
11
|
|
|
13
12
|
const meta = {
|
|
14
|
-
title: 'plugins/plugin-simple-layout/
|
|
15
|
-
component:
|
|
16
|
-
decorators: [withTheme],
|
|
13
|
+
title: 'plugins/plugin-simple-layout/components/ErrorFallback',
|
|
14
|
+
component: ErrorFallback,
|
|
15
|
+
decorators: [withTheme()],
|
|
17
16
|
parameters: {
|
|
18
17
|
layout: 'centered',
|
|
19
18
|
translations,
|
|
20
19
|
},
|
|
21
|
-
} satisfies Meta<typeof
|
|
20
|
+
} satisfies Meta<typeof ErrorFallback>;
|
|
22
21
|
|
|
23
22
|
export default meta;
|
|
24
23
|
|
|
25
24
|
type Story = StoryObj<typeof meta>;
|
|
26
25
|
|
|
27
26
|
export const Default: Story = {
|
|
28
|
-
args: {
|
|
27
|
+
args: {
|
|
28
|
+
error: new Error('An unexpected error occurred'),
|
|
29
|
+
},
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
export const WithError: Story = {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createContext } from '@radix-ui/react-context';
|
|
6
|
+
import React, { type PropsWithChildren, useCallback, useRef } from 'react';
|
|
7
|
+
|
|
8
|
+
const DEBUG_OVERLAY_NAME = 'DebugOverlay';
|
|
9
|
+
|
|
10
|
+
//
|
|
11
|
+
// Context
|
|
12
|
+
//
|
|
13
|
+
|
|
14
|
+
type DebugOverlayContextValue = {
|
|
15
|
+
/** Log a timestamped message to the on-screen debug overlay. */
|
|
16
|
+
dbg: (msg: string) => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Default to a no-op so hooks can call useDebugLog() safely outside of a provider.
|
|
20
|
+
const [DebugOverlayProvider, useDebugLog] = createContext<DebugOverlayContextValue>(DEBUG_OVERLAY_NAME, {
|
|
21
|
+
dbg: () => {},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
//
|
|
25
|
+
// Root
|
|
26
|
+
//
|
|
27
|
+
|
|
28
|
+
type DebugOverlayRootProps = PropsWithChildren<{
|
|
29
|
+
/**
|
|
30
|
+
* When true (default), renders the on-screen log panel.
|
|
31
|
+
* Set to false to suppress the overlay while keeping the context available.
|
|
32
|
+
*/
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
maxLines?: number;
|
|
35
|
+
}>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Establishes a debug overlay context.
|
|
39
|
+
*
|
|
40
|
+
* When enabled, renders an on-screen monospaced log panel anchored just above
|
|
41
|
+
* the keyboard (via --kb-height CSS variable). Descendants can call
|
|
42
|
+
* useDebugLog() to obtain the dbg() function for logging.
|
|
43
|
+
*
|
|
44
|
+
* Intended for transient mobile debugging in the iOS Simulator where DevTools
|
|
45
|
+
* console output may not be accessible.
|
|
46
|
+
*/
|
|
47
|
+
const DebugOverlayRoot = ({ children, enabled = true, maxLines = 10 }: DebugOverlayRootProps) => {
|
|
48
|
+
const overlayRef = useRef<HTMLDivElement>(null);
|
|
49
|
+
|
|
50
|
+
const dbg = useCallback((msg: string) => {
|
|
51
|
+
if (!overlayRef.current) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const line = document.createElement('pre');
|
|
55
|
+
line.textContent = `${(performance.now() / 1000).toFixed(2).padStart(8, ' ')} ${msg}`;
|
|
56
|
+
overlayRef.current.prepend(line);
|
|
57
|
+
while (overlayRef.current.children.length > maxLines) {
|
|
58
|
+
overlayRef.current.lastChild?.remove();
|
|
59
|
+
}
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<DebugOverlayProvider dbg={dbg}>
|
|
64
|
+
{children}
|
|
65
|
+
{enabled && (
|
|
66
|
+
<div
|
|
67
|
+
ref={overlayRef}
|
|
68
|
+
style={{
|
|
69
|
+
position: 'fixed',
|
|
70
|
+
bottom: 'calc(var(--kb-height, 0px) + 8px)',
|
|
71
|
+
left: 8,
|
|
72
|
+
right: 8,
|
|
73
|
+
background: 'rgba(0,0,0,0.8)',
|
|
74
|
+
color: '#0f0',
|
|
75
|
+
fontSize: 10,
|
|
76
|
+
fontFamily: 'monospace',
|
|
77
|
+
padding: 6,
|
|
78
|
+
borderRadius: 4,
|
|
79
|
+
zIndex: 9999,
|
|
80
|
+
pointerEvents: 'none',
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
</DebugOverlayProvider>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
//
|
|
89
|
+
// Exports
|
|
90
|
+
//
|
|
91
|
+
|
|
92
|
+
export const DebugOverlay = {
|
|
93
|
+
Root: DebugOverlayRoot,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export { useDebugLog };
|
|
@@ -4,33 +4,44 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { Surface
|
|
7
|
+
import { Surface } from '@dxos/app-framework/ui';
|
|
8
|
+
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
8
9
|
import { AlertDialog, Dialog as NaturalDialog } from '@dxos/react-ui';
|
|
10
|
+
import { ErrorFallback } from '@dxos/react-ui';
|
|
9
11
|
|
|
10
|
-
import {
|
|
11
|
-
import { ContentError } from '../ContentError';
|
|
12
|
+
import { useSimpleLayoutState } from '#hooks';
|
|
12
13
|
|
|
13
14
|
export const Dialog = () => {
|
|
14
|
-
const
|
|
15
|
+
const { state, updateState } = useSimpleLayoutState();
|
|
15
16
|
|
|
16
|
-
const DialogRoot =
|
|
17
|
-
const DialogOverlay =
|
|
17
|
+
const DialogRoot = state.dialogType === 'alert' ? AlertDialog.Root : NaturalDialog.Root;
|
|
18
|
+
const DialogOverlay = state.dialogType === 'alert' ? AlertDialog.Overlay : NaturalDialog.Overlay;
|
|
18
19
|
|
|
19
20
|
return (
|
|
20
21
|
<DialogRoot
|
|
21
|
-
modal={
|
|
22
|
-
open={
|
|
23
|
-
onOpenChange={(nextOpen) => (
|
|
22
|
+
modal={state.dialogBlockAlign !== 'end'}
|
|
23
|
+
open={state.dialogOpen}
|
|
24
|
+
onOpenChange={(nextOpen) => updateState((state) => ({ ...state, dialogOpen: nextOpen }))}
|
|
24
25
|
>
|
|
25
|
-
{
|
|
26
|
-
<Surface
|
|
26
|
+
{state.dialogBlockAlign === 'end' ? (
|
|
27
|
+
<Surface.Surface
|
|
28
|
+
type={AppSurface.Dialog}
|
|
29
|
+
data={state.dialogContent ?? undefined}
|
|
30
|
+
limit={1}
|
|
31
|
+
fallback={ErrorFallback}
|
|
32
|
+
/>
|
|
27
33
|
) : (
|
|
28
34
|
<DialogOverlay
|
|
29
|
-
blockAlign={
|
|
30
|
-
classNames={
|
|
31
|
-
style={
|
|
35
|
+
blockAlign={state.dialogBlockAlign}
|
|
36
|
+
classNames={state.dialogOverlayClasses}
|
|
37
|
+
style={state.dialogOverlayStyle}
|
|
32
38
|
>
|
|
33
|
-
<Surface
|
|
39
|
+
<Surface.Surface
|
|
40
|
+
type={AppSurface.Dialog}
|
|
41
|
+
data={state.dialogContent ?? undefined}
|
|
42
|
+
limit={1}
|
|
43
|
+
fallback={ErrorFallback}
|
|
44
|
+
/>
|
|
34
45
|
</DialogOverlay>
|
|
35
46
|
)}
|
|
36
47
|
</DialogRoot>
|
|
@@ -4,135 +4,135 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
7
|
+
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
8
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
9
|
+
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
10
|
+
import { Node, useConnections } from '@dxos/plugin-graph';
|
|
11
|
+
import { Avatar, Icon, ScrollArea, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { Card } from '@dxos/react-ui';
|
|
13
|
+
import { Mosaic, type MosaicStackTileComponent } from '@dxos/react-ui-mosaic';
|
|
14
|
+
import { SearchPanel, useSearchListItem, useSearchListResults } from '@dxos/react-ui-search';
|
|
13
15
|
import { mx } from '@dxos/ui-theme';
|
|
16
|
+
import { byPosition, getHostPlatform, isTauri } from '@dxos/util';
|
|
14
17
|
|
|
15
|
-
import { meta } from '
|
|
18
|
+
import { meta } from '#meta';
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
import { useExpandPath } from '../hooks';
|
|
18
21
|
|
|
19
|
-
export
|
|
22
|
+
export type HomeProps = {};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Home screen.
|
|
26
|
+
*/
|
|
27
|
+
export const Home = (_: HomeProps) => {
|
|
20
28
|
const { t } = useTranslation(meta.id);
|
|
21
|
-
const
|
|
22
|
-
|
|
29
|
+
const userAccountItem = useItemsByDisposition('user-account')[0];
|
|
30
|
+
const pinnedItems = useItemsByDisposition('pin-end', true);
|
|
31
|
+
const workspaceItems = useItemsByDisposition('workspace');
|
|
32
|
+
useExpandPath(Node.RootId);
|
|
33
|
+
|
|
34
|
+
const items = useMemo(
|
|
35
|
+
() => [...(userAccountItem ? [userAccountItem] : []), ...pinnedItems, ...workspaceItems],
|
|
36
|
+
[userAccountItem, pinnedItems, workspaceItems],
|
|
37
|
+
);
|
|
23
38
|
|
|
24
39
|
const { results, handleSearch } = useSearchListResults({
|
|
25
|
-
items
|
|
40
|
+
items,
|
|
26
41
|
extract: (node) => toLocalizedString(node.properties.label, t),
|
|
27
42
|
});
|
|
28
43
|
|
|
44
|
+
const autoFocus = !isTauri() || getHostPlatform() !== 'ios';
|
|
45
|
+
|
|
29
46
|
return (
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
</
|
|
42
|
-
</
|
|
43
|
-
</
|
|
44
|
-
</
|
|
47
|
+
<SearchPanel onSearch={handleSearch}>
|
|
48
|
+
<Mosaic.Container asChild>
|
|
49
|
+
<ScrollArea.Root centered padding thin>
|
|
50
|
+
<ScrollArea.Viewport>
|
|
51
|
+
<Mosaic.Stack
|
|
52
|
+
classNames='gap-1'
|
|
53
|
+
draggable={false}
|
|
54
|
+
items={results}
|
|
55
|
+
getId={(item) => item.id}
|
|
56
|
+
Tile={WorkspaceTile}
|
|
57
|
+
/>
|
|
58
|
+
</ScrollArea.Viewport>
|
|
59
|
+
</ScrollArea.Root>
|
|
60
|
+
</Mosaic.Container>
|
|
61
|
+
</SearchPanel>
|
|
45
62
|
);
|
|
46
63
|
};
|
|
47
64
|
|
|
48
|
-
const
|
|
65
|
+
const WorkspaceTile: MosaicStackTileComponent<Node.Node> = (props) => {
|
|
66
|
+
const data = props.data;
|
|
49
67
|
const { t } = useTranslation(meta.id);
|
|
50
68
|
const { invokePromise } = useOperationInvoker();
|
|
51
69
|
const { selectedValue, registerItem, unregisterItem } = useSearchListItem();
|
|
52
|
-
const
|
|
70
|
+
const name = toLocalizedString(data.properties.label, t);
|
|
71
|
+
const isSelected = selectedValue === data.id;
|
|
72
|
+
const cardRef = useRef<HTMLDivElement>(null);
|
|
73
|
+
|
|
74
|
+
useExpandPath(data.id);
|
|
53
75
|
|
|
54
76
|
const handleSelect = useCallback(
|
|
55
|
-
() => invokePromise(
|
|
56
|
-
[invokePromise,
|
|
77
|
+
() => invokePromise(LayoutOperation.SwitchWorkspace, { subject: data.id }),
|
|
78
|
+
[invokePromise, data.id],
|
|
57
79
|
);
|
|
58
80
|
|
|
59
|
-
useLoadDescendents(node.id);
|
|
60
|
-
|
|
61
|
-
const name = toLocalizedString(node.properties.label, t);
|
|
62
|
-
const isSelected = selectedValue === node.id;
|
|
63
|
-
|
|
64
81
|
// Register this workspace with the search context.
|
|
65
82
|
useEffect(() => {
|
|
66
|
-
if (
|
|
67
|
-
registerItem(
|
|
83
|
+
if (cardRef.current) {
|
|
84
|
+
registerItem(data.id, cardRef.current, handleSelect);
|
|
68
85
|
}
|
|
69
86
|
|
|
70
|
-
return () => unregisterItem(
|
|
71
|
-
}, [
|
|
87
|
+
return () => unregisterItem(data.id);
|
|
88
|
+
}, [data.id, handleSelect, registerItem, unregisterItem]);
|
|
72
89
|
|
|
73
90
|
// Scroll into view when selected.
|
|
74
91
|
useEffect(() => {
|
|
75
|
-
if (isSelected &&
|
|
76
|
-
|
|
92
|
+
if (isSelected && cardRef.current) {
|
|
93
|
+
cardRef.current.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
77
94
|
}
|
|
78
95
|
}, [isSelected]);
|
|
79
96
|
|
|
80
97
|
return (
|
|
81
98
|
<Card.Root
|
|
82
|
-
ref={ref}
|
|
83
99
|
role='button'
|
|
84
|
-
|
|
100
|
+
fullWidth
|
|
101
|
+
tabIndex={-1} // TODO(burdon): Use Mosaic.Focus.
|
|
85
102
|
data-selected={isSelected}
|
|
86
|
-
classNames={mx('dx-focus-ring', isSelected && 'bg-
|
|
103
|
+
classNames={mx('dx-focus-ring', isSelected && 'bg-hover-overlay')}
|
|
87
104
|
onClick={handleSelect}
|
|
105
|
+
ref={cardRef}
|
|
88
106
|
>
|
|
89
|
-
<Card.
|
|
107
|
+
<Card.Toolbar density='fine'>
|
|
90
108
|
<Avatar.Root>
|
|
91
109
|
<Avatar.Content
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
hueVariant='
|
|
110
|
+
icon={data.properties.icon}
|
|
111
|
+
hue={data.properties.hue}
|
|
112
|
+
hueVariant='transparent'
|
|
95
113
|
variant='square'
|
|
96
|
-
size={
|
|
114
|
+
size={8}
|
|
97
115
|
fallback={name}
|
|
98
116
|
/>
|
|
99
|
-
<Avatar.Label>{name}</Avatar.Label>
|
|
117
|
+
<Avatar.Label classNames='cursor-pointer'>{name}</Avatar.Label>
|
|
100
118
|
<Icon icon='ph--caret-right--regular' />
|
|
101
119
|
</Avatar.Root>
|
|
102
|
-
</Card.
|
|
120
|
+
</Card.Toolbar>
|
|
103
121
|
</Card.Root>
|
|
104
122
|
);
|
|
105
123
|
};
|
|
106
124
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
const frame = requestAnimationFrame(() => {
|
|
112
|
-
if (nodeId) {
|
|
113
|
-
Graph.expand(graph, nodeId, 'outbound');
|
|
114
|
-
Graph.getConnections(graph, nodeId, 'outbound').forEach((child) => {
|
|
115
|
-
Graph.expand(graph, child.id, 'outbound');
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
return () => cancelAnimationFrame(frame);
|
|
121
|
-
}, [nodeId, graph]);
|
|
125
|
+
/** Filters nodes by disposition. */
|
|
126
|
+
const filterItems = (node: Node.Node, disposition: string) => {
|
|
127
|
+
return node.properties.disposition === disposition;
|
|
122
128
|
};
|
|
123
129
|
|
|
124
|
-
|
|
130
|
+
/** Returns root-level items filtered by disposition. */
|
|
131
|
+
const useItemsByDisposition = (disposition: string, sort = false) => {
|
|
125
132
|
const { graph } = useAppGraph();
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
[rootConnections],
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
// Get first collection's children as workspaces.
|
|
135
|
-
// TODO(wittjosiah): Support multiple collections or nested workspaces if needed.
|
|
136
|
-
const firstCollection = collections[0];
|
|
137
|
-
return useConnections(graph, firstCollection?.id);
|
|
133
|
+
const connections = useConnections(graph, Node.RootId, 'child');
|
|
134
|
+
return useMemo(() => {
|
|
135
|
+
const filtered = connections.filter((node) => filterItems(node, disposition));
|
|
136
|
+
return sort ? filtered.toSorted((a, b) => byPosition(a.properties, b.properties)) : filtered;
|
|
137
|
+
}, [connections, disposition, sort]);
|
|
138
138
|
};
|
|
@@ -6,16 +6,16 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
|
|
7
7
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { Loading } from './Loading';
|
|
10
10
|
|
|
11
11
|
const meta = {
|
|
12
|
-
title: 'plugins/plugin-simple-layout/
|
|
13
|
-
component:
|
|
14
|
-
decorators: [withTheme],
|
|
12
|
+
title: 'plugins/plugin-simple-layout/components/Loading',
|
|
13
|
+
component: Loading,
|
|
14
|
+
decorators: [withTheme()],
|
|
15
15
|
parameters: {
|
|
16
16
|
layout: 'centered',
|
|
17
17
|
},
|
|
18
|
-
} satisfies Meta<typeof
|
|
18
|
+
} satisfies Meta<typeof Loading>;
|
|
19
19
|
|
|
20
20
|
export default meta;
|
|
21
21
|
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
// TODO(burdon): Show skeleton: https://github.com/dxos/dxos/issues/8259
|
|
8
|
-
export const
|
|
9
|
-
return <div
|
|
8
|
+
export const Loading = () => {
|
|
9
|
+
return <div className='grid place-items-center dx-attention-surface' />;
|
|
10
10
|
};
|