@dxos/plugin-deck 0.7.4 → 0.7.5-labs.071a3e2
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/app-graph-builder-CI6ZFMNL.mjs +147 -0
- package/dist/lib/browser/app-graph-builder-CI6ZFMNL.mjs.map +7 -0
- package/dist/lib/browser/check-app-scheme-S3EYUPMF.mjs +33 -0
- package/dist/lib/browser/check-app-scheme-S3EYUPMF.mjs.map +7 -0
- package/dist/lib/browser/chunk-BTDY6SES.mjs +1119 -0
- package/dist/lib/browser/chunk-BTDY6SES.mjs.map +7 -0
- package/dist/lib/browser/chunk-FZOBKOA2.mjs +24 -0
- package/dist/lib/browser/chunk-FZOBKOA2.mjs.map +7 -0
- package/dist/lib/browser/chunk-M2L53AIH.mjs +126 -0
- package/dist/lib/browser/chunk-M2L53AIH.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GVOGPULO.mjs → chunk-N7TEPFVR.mjs} +5 -4
- package/dist/lib/browser/chunk-N7TEPFVR.mjs.map +7 -0
- package/dist/lib/browser/chunk-YQ2GWTDU.mjs +17 -0
- package/dist/lib/browser/chunk-YQ2GWTDU.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +100 -1807
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs +494 -0
- package/dist/lib/browser/intent-resolver-CSXFDKTC.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react-root-HIHLRMCW.mjs +46 -0
- package/dist/lib/browser/react-root-HIHLRMCW.mjs.map +7 -0
- package/dist/lib/browser/react-surface-4QVWKQYY.mjs +38 -0
- package/dist/lib/browser/react-surface-4QVWKQYY.mjs.map +7 -0
- package/dist/lib/browser/settings-WACNLCPB.mjs +28 -0
- package/dist/lib/browser/settings-WACNLCPB.mjs.map +7 -0
- package/dist/lib/browser/state-VPOYUKK6.mjs +117 -0
- package/dist/lib/browser/state-VPOYUKK6.mjs.map +7 -0
- package/dist/lib/browser/tools-5LDJNU56.mjs +51 -0
- package/dist/lib/browser/tools-5LDJNU56.mjs.map +7 -0
- package/dist/lib/browser/types.mjs +20 -3
- package/dist/lib/browser/url-handler-HLF42IHP.mjs +70 -0
- package/dist/lib/browser/url-handler-HLF42IHP.mjs.map +7 -0
- package/dist/types/src/DeckPlugin.d.ts +1 -14
- package/dist/types/src/DeckPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +181 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/capabilities.d.ts +142 -0
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
- package/dist/types/src/capabilities/check-app-scheme.d.ts +4 -0
- package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +190 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-root.d.ts +7 -0
- package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +4 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/set-active.d.ts +9 -0
- package/dist/types/src/capabilities/set-active.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +4 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/capabilities/state.d.ts +76 -0
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/capabilities/tools.d.ts +10 -0
- package/dist/types/src/capabilities/tools.d.ts.map +1 -0
- package/dist/types/src/capabilities/url-handler.d.ts +4 -0
- package/dist/types/src/capabilities/url-handler.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Banner.d.ts +6 -0
- package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +1 -4
- package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +3 -3
- package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Plank.d.ts +8 -6
- package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
- package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/PlankError.d.ts +4 -3
- package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts +1 -5
- package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +8 -0
- package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -0
- package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -2
- package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
- package/dist/types/src/components/DeckLayout/Topbar.d.ts +3 -0
- package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +1 -0
- package/dist/types/src/components/fragments.d.ts +4 -0
- package/dist/types/src/components/fragments.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +0 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/events.d.ts +4 -0
- package/dist/types/src/events.d.ts.map +1 -0
- package/dist/types/src/hooks/useMainSize.d.ts +2 -2
- package/dist/types/src/index.d.ts +3 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/layout.d.ts +5 -19
- package/dist/types/src/layout.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +4 -4
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +7 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +130 -17
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +3 -2
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/layoutAppliesTopbar.d.ts +2 -0
- package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -0
- package/dist/types/src/util/useBreakpoints.d.ts +2 -0
- package/dist/types/src/util/useBreakpoints.d.ts.map +1 -0
- package/dist/types/src/util/useHoistStatusbar.d.ts +2 -0
- package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +32 -36
- package/src/DeckPlugin.ts +83 -0
- package/src/capabilities/app-graph-builder.ts +109 -0
- package/src/capabilities/capabilities.ts +18 -0
- package/src/capabilities/check-app-scheme.ts +44 -0
- package/src/capabilities/index.ts +17 -0
- package/src/capabilities/intent-resolver.ts +350 -0
- package/src/capabilities/react-root.tsx +48 -0
- package/src/capabilities/react-surface.tsx +31 -0
- package/src/capabilities/set-active.ts +43 -0
- package/src/capabilities/settings.ts +21 -0
- package/src/capabilities/state.ts +102 -0
- package/src/capabilities/tools.ts +61 -0
- package/src/capabilities/url-handler.ts +63 -0
- package/src/components/DeckLayout/ActiveNode.tsx +3 -4
- package/src/components/DeckLayout/Banner.tsx +37 -0
- package/src/components/DeckLayout/ComplementarySidebar.tsx +130 -56
- package/src/components/DeckLayout/ContentEmpty.tsx +9 -4
- package/src/components/DeckLayout/DeckLayout.tsx +116 -83
- package/src/components/DeckLayout/Fullscreen.tsx +3 -4
- package/src/components/DeckLayout/NodePlankHeading.tsx +66 -93
- package/src/components/DeckLayout/Plank.tsx +36 -43
- package/src/components/DeckLayout/PlankControls.tsx +12 -13
- package/src/components/DeckLayout/PlankError.tsx +6 -5
- package/src/components/DeckLayout/Sidebar.tsx +19 -26
- package/src/components/DeckLayout/SidebarButton.tsx +68 -0
- package/src/components/DeckLayout/StatusBar.tsx +6 -12
- package/src/components/DeckLayout/Toast.tsx +21 -8
- package/src/components/DeckLayout/Topbar.tsx +11 -0
- package/src/components/LayoutSettings.tsx +8 -8
- package/src/components/fragments.ts +14 -0
- package/src/components/index.ts +0 -2
- package/src/events.ts +11 -0
- package/src/hooks/useMainSize.ts +3 -3
- package/src/index.ts +3 -4
- package/src/layout.ts +43 -212
- package/src/meta.ts +3 -2
- package/src/translations.ts +11 -6
- package/src/types.ts +110 -34
- package/src/util/index.ts +3 -2
- package/src/util/layoutAppliesTopbar.ts +7 -0
- package/src/util/useBreakpoints.ts +11 -0
- package/src/util/useHoistStatusbar.ts +24 -0
- package/dist/lib/browser/chunk-GVOGPULO.mjs.map +0 -7
- package/dist/lib/browser/chunk-NIRHDTX4.mjs +0 -17
- package/dist/lib/browser/chunk-NIRHDTX4.mjs.map +0 -7
- package/dist/lib/browser/meta.mjs +0 -9
- package/dist/lib/browser/meta.mjs.map +0 -7
- package/dist/types/src/components/DeckContext.d.ts +0 -8
- package/dist/types/src/components/DeckContext.d.ts.map +0 -1
- package/dist/types/src/components/LayoutContext.d.ts +0 -5
- package/dist/types/src/components/LayoutContext.d.ts.map +0 -1
- package/dist/types/src/layout.test.d.ts +0 -2
- package/dist/types/src/layout.test.d.ts.map +0 -1
- package/dist/types/src/util/check-app-scheme.d.ts +0 -2
- package/dist/types/src/util/check-app-scheme.d.ts.map +0 -1
- package/dist/types/src/util/layout-parts.d.ts +0 -7
- package/dist/types/src/util/layout-parts.d.ts.map +0 -1
- package/src/DeckPlugin.tsx +0 -657
- package/src/components/DeckContext.ts +0 -14
- package/src/components/LayoutContext.ts +0 -12
- package/src/layout.test.ts +0 -380
- package/src/util/check-app-scheme.ts +0 -21
- package/src/util/layout-parts.ts +0 -12
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { defineCapability, type Label } from '@dxos/app-framework';
|
|
6
|
+
import { type DeepReadonly } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
import { DECK_PLUGIN } from '../meta';
|
|
9
|
+
import { type DeckState } from '../types';
|
|
10
|
+
|
|
11
|
+
export namespace DeckCapabilities {
|
|
12
|
+
export const DeckState = defineCapability<DeepReadonly<DeckState>>(`${DECK_PLUGIN}/capability/state`);
|
|
13
|
+
export const MutableDeckState = defineCapability<DeckState>(`${DECK_PLUGIN}/capability/state`);
|
|
14
|
+
|
|
15
|
+
export const ComplementaryPanel = defineCapability<{ id: string; label: Label; icon: string }>(
|
|
16
|
+
`${DECK_PLUGIN}/capability/complementary-panel`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Copyright 2025 DXOS.org
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import { Capabilities, contributes, type PluginsContext } from '@dxos/app-framework';
|
|
9
|
+
|
|
10
|
+
import { DECK_PLUGIN } from '../meta';
|
|
11
|
+
import { type DeckSettingsProps } from '../types';
|
|
12
|
+
|
|
13
|
+
const isSocket = !!(globalThis as any).__args;
|
|
14
|
+
|
|
15
|
+
// TODO(mjamesderocher): Can we get this directly from Socket?
|
|
16
|
+
const appScheme = 'composer://';
|
|
17
|
+
|
|
18
|
+
// TODO(mjamesderocher): Factor out as part of NavigationPlugin.
|
|
19
|
+
const checkAppScheme = (url: string) => {
|
|
20
|
+
const iframe = document.createElement('iframe');
|
|
21
|
+
iframe.style.display = 'none';
|
|
22
|
+
document.body.appendChild(iframe);
|
|
23
|
+
|
|
24
|
+
iframe.src = url + window.location.pathname.replace(/^\/+/, '') + window.location.search;
|
|
25
|
+
|
|
26
|
+
const timer = setTimeout(() => {
|
|
27
|
+
document.body.removeChild(iframe);
|
|
28
|
+
}, 3000);
|
|
29
|
+
|
|
30
|
+
window.addEventListener('pagehide', (event) => {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
document.body.removeChild(iframe);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default async (context: PluginsContext) => {
|
|
37
|
+
const settingsStore = context.requestCapability(Capabilities.SettingsStore);
|
|
38
|
+
const settings = settingsStore.getStore<DeckSettingsProps>(DECK_PLUGIN)?.value;
|
|
39
|
+
if (!isSocket && settings?.enableNativeRedirect) {
|
|
40
|
+
checkAppScheme(appScheme);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return contributes(Capabilities.Null, null);
|
|
44
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { lazy } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
export const AppGraphBuilder = lazy(() => import('./app-graph-builder'));
|
|
8
|
+
export const CheckAppScheme = lazy(() => import('./check-app-scheme'));
|
|
9
|
+
export const LayoutIntentResolver = lazy(() => import('./intent-resolver'));
|
|
10
|
+
export const ReactRoot = lazy(() => import('./react-root'));
|
|
11
|
+
export const ReactSurface = lazy(() => import('./react-surface'));
|
|
12
|
+
export const DeckSettings = lazy(() => import('./settings'));
|
|
13
|
+
export const DeckState = lazy(() => import('./state'));
|
|
14
|
+
export const Tools = lazy(() => import('./tools'));
|
|
15
|
+
export const UrlHandler = lazy(() => import('./url-handler'));
|
|
16
|
+
|
|
17
|
+
export * from './capabilities';
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { batch } from '@preact/signals-core';
|
|
6
|
+
import { pipe } from 'effect';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Capabilities,
|
|
10
|
+
createResolver,
|
|
11
|
+
contributes,
|
|
12
|
+
IntentAction,
|
|
13
|
+
LayoutAction,
|
|
14
|
+
type PluginsContext,
|
|
15
|
+
createIntent,
|
|
16
|
+
chain,
|
|
17
|
+
} from '@dxos/app-framework';
|
|
18
|
+
import { getTypename, S } from '@dxos/echo-schema';
|
|
19
|
+
import { isReactiveObject } from '@dxos/live-object';
|
|
20
|
+
import { log } from '@dxos/log';
|
|
21
|
+
import { AttentionCapabilities } from '@dxos/plugin-attention';
|
|
22
|
+
import { ObservabilityAction } from '@dxos/plugin-observability/types';
|
|
23
|
+
import { nonNullable } from '@dxos/util';
|
|
24
|
+
|
|
25
|
+
import { DeckCapabilities } from './capabilities';
|
|
26
|
+
import { setActive } from './set-active';
|
|
27
|
+
import { closeEntry, incrementPlank, openEntry } from '../layout';
|
|
28
|
+
import { DECK_PLUGIN } from '../meta';
|
|
29
|
+
import { DeckAction, type LayoutMode, type DeckSettingsProps, isLayoutMode, getMode } from '../types';
|
|
30
|
+
|
|
31
|
+
export default (context: PluginsContext) =>
|
|
32
|
+
contributes(Capabilities.IntentResolver, [
|
|
33
|
+
createResolver({
|
|
34
|
+
intent: IntentAction.ShowUndo,
|
|
35
|
+
resolve: (data) => {
|
|
36
|
+
const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
37
|
+
const { undoPromise: undo } = context.requestCapability(Capabilities.IntentDispatcher);
|
|
38
|
+
|
|
39
|
+
// TODO(wittjosiah): Support undoing further back than the last action.
|
|
40
|
+
if (layout.currentUndoId) {
|
|
41
|
+
layout.toasts = layout.toasts.filter((toast) => toast.id !== layout.currentUndoId);
|
|
42
|
+
}
|
|
43
|
+
layout.currentUndoId = `${IntentAction.ShowUndo._tag}-${Date.now()}`;
|
|
44
|
+
layout.toasts = [
|
|
45
|
+
...layout.toasts,
|
|
46
|
+
{
|
|
47
|
+
id: layout.currentUndoId,
|
|
48
|
+
title: data.message ?? ['undo available label', { ns: DECK_PLUGIN }],
|
|
49
|
+
duration: 10_000,
|
|
50
|
+
actionLabel: ['undo action label', { ns: DECK_PLUGIN }],
|
|
51
|
+
actionAlt: ['undo action alt', { ns: DECK_PLUGIN }],
|
|
52
|
+
closeLabel: ['undo close label', { ns: DECK_PLUGIN }],
|
|
53
|
+
onAction: () => undo(),
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
createResolver({
|
|
59
|
+
intent: LayoutAction.UpdateLayout,
|
|
60
|
+
// TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdateSidebar.fields.input)`
|
|
61
|
+
// but the filter is not being applied correctly.
|
|
62
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdateSidebar.fields.input> =>
|
|
63
|
+
S.is(LayoutAction.UpdateSidebar.fields.input)(data),
|
|
64
|
+
resolve: ({ options }) => {
|
|
65
|
+
const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
66
|
+
const next = options?.state ?? layout.sidebarState;
|
|
67
|
+
if (next !== layout.sidebarState) {
|
|
68
|
+
layout.sidebarState = next;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
createResolver({
|
|
73
|
+
intent: LayoutAction.UpdateLayout,
|
|
74
|
+
// TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdateComplementary.fields.input)`
|
|
75
|
+
// but the filter is not being applied correctly.
|
|
76
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdateComplementary.fields.input> =>
|
|
77
|
+
S.is(LayoutAction.UpdateComplementary.fields.input)(data),
|
|
78
|
+
resolve: ({ subject, options }) => {
|
|
79
|
+
const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
80
|
+
|
|
81
|
+
if (layout.complementarySidebarPanel !== subject) {
|
|
82
|
+
layout.complementarySidebarPanel = subject;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const next = subject ? 'expanded' : options?.state ?? layout.complementarySidebarState;
|
|
86
|
+
if (next !== layout.complementarySidebarState) {
|
|
87
|
+
layout.complementarySidebarState = next;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
createResolver({
|
|
92
|
+
intent: LayoutAction.UpdateLayout,
|
|
93
|
+
// TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdateDialog.fields.input)`
|
|
94
|
+
// but the filter is not being applied correctly.
|
|
95
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdateDialog.fields.input> =>
|
|
96
|
+
S.is(LayoutAction.UpdateDialog.fields.input)(data),
|
|
97
|
+
resolve: ({ subject, options }) => {
|
|
98
|
+
const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
99
|
+
layout.dialogOpen = options.state ?? Boolean(subject);
|
|
100
|
+
layout.dialogContent = subject ? { component: subject, props: options.props } : null;
|
|
101
|
+
layout.dialogBlockAlign = options.blockAlign ?? 'center';
|
|
102
|
+
layout.dialogType = options.type ?? 'default';
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
createResolver({
|
|
106
|
+
intent: LayoutAction.UpdateLayout,
|
|
107
|
+
// TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.UpdatePopover.fields.input)`
|
|
108
|
+
// but the filter is not being applied correctly.
|
|
109
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.UpdatePopover.fields.input> =>
|
|
110
|
+
S.is(LayoutAction.UpdatePopover.fields.input)(data),
|
|
111
|
+
resolve: ({ subject, options }) => {
|
|
112
|
+
const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
113
|
+
layout.popoverOpen = options.state ?? Boolean(subject);
|
|
114
|
+
layout.popoverContent = subject ? { component: subject, props: options.props } : null;
|
|
115
|
+
layout.popoverAnchorId = options.anchorId;
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
createResolver({
|
|
119
|
+
intent: LayoutAction.UpdateLayout,
|
|
120
|
+
// TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.AddToast.fields.input)`
|
|
121
|
+
// but the filter is not being applied correctly.
|
|
122
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.AddToast.fields.input> =>
|
|
123
|
+
S.is(LayoutAction.AddToast.fields.input)(data),
|
|
124
|
+
resolve: ({ subject }) => {
|
|
125
|
+
const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
126
|
+
layout.toasts.push(subject);
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
createResolver({
|
|
130
|
+
intent: LayoutAction.UpdateLayout,
|
|
131
|
+
// TODO(wittjosiah): This should be able to just be `S.is(LayoutAction.SetLayoutMode.fields.input)`
|
|
132
|
+
// but the filter is not being applied correctly.
|
|
133
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.SetLayoutMode.fields.input> => {
|
|
134
|
+
if (!S.is(LayoutAction.SetLayoutMode.fields.input)(data)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if ('mode' in data.options) {
|
|
139
|
+
return isLayoutMode(data.options.mode);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return true;
|
|
143
|
+
},
|
|
144
|
+
resolve: ({ subject, options }) => {
|
|
145
|
+
const state = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
146
|
+
|
|
147
|
+
const setMode = (mode: LayoutMode) => {
|
|
148
|
+
const deck = state.deck;
|
|
149
|
+
const current = deck.solo ? [deck.solo] : deck.active;
|
|
150
|
+
// When un-soloing, the solo entry is added to the deck.
|
|
151
|
+
const next = (
|
|
152
|
+
mode === 'solo' ? [subject ?? deck.solo ?? deck.active[0]] : [...deck.active, deck.solo]
|
|
153
|
+
).filter(nonNullable);
|
|
154
|
+
|
|
155
|
+
const removed = current.filter((id) => !next.includes(id));
|
|
156
|
+
const closed = Array.from(new Set([...deck.inactive.filter((id) => !next.includes(id)), ...removed]));
|
|
157
|
+
deck.inactive = closed;
|
|
158
|
+
|
|
159
|
+
if (mode === 'solo' && next[0]) {
|
|
160
|
+
deck.solo = next[0];
|
|
161
|
+
} else if (mode !== 'solo' && deck.solo) {
|
|
162
|
+
deck.solo = undefined;
|
|
163
|
+
deck.initialized = true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (mode === 'fullscreen' && !deck.fullscreen) {
|
|
167
|
+
deck.fullscreen = true;
|
|
168
|
+
} else if (mode !== 'fullscreen' && deck.fullscreen) {
|
|
169
|
+
deck.fullscreen = false;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return batch(() => {
|
|
174
|
+
if ('mode' in options) {
|
|
175
|
+
const current = getMode(state.deck);
|
|
176
|
+
if (current !== options.mode) {
|
|
177
|
+
state.previousMode[state.activeDeck] = current;
|
|
178
|
+
}
|
|
179
|
+
setMode(options.mode as LayoutMode);
|
|
180
|
+
} else if ('revert' in options) {
|
|
181
|
+
const last = state.previousMode[state.activeDeck];
|
|
182
|
+
setMode(last ?? 'solo');
|
|
183
|
+
} else {
|
|
184
|
+
log.warn('Invalid layout mode', options);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
}),
|
|
189
|
+
createResolver({
|
|
190
|
+
intent: LayoutAction.UpdateLayout,
|
|
191
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
|
|
192
|
+
S.is(LayoutAction.SwitchWorkspace.fields.input)(data),
|
|
193
|
+
resolve: ({ subject }) => {
|
|
194
|
+
const state = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
195
|
+
batch(() => {
|
|
196
|
+
state.activeDeck = subject;
|
|
197
|
+
if (!state.decks[subject]) {
|
|
198
|
+
state.decks[subject] = { initialized: false, active: [], inactive: [], fullscreen: false, plankSizing: {} };
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const first = state.decks[state.activeDeck].active[0];
|
|
203
|
+
if (first) {
|
|
204
|
+
return {
|
|
205
|
+
intents: [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: first })],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
}),
|
|
210
|
+
createResolver({
|
|
211
|
+
intent: LayoutAction.UpdateLayout,
|
|
212
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.Open.fields.input> =>
|
|
213
|
+
S.is(LayoutAction.Open.fields.input)(data),
|
|
214
|
+
resolve: ({ subject, options }) => {
|
|
215
|
+
const { graph } = context.requestCapability(Capabilities.AppGraph);
|
|
216
|
+
const state = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
217
|
+
const attention = context.requestCapability(AttentionCapabilities.Attention);
|
|
218
|
+
const settings = context
|
|
219
|
+
.requestCapabilities(Capabilities.SettingsStore)[0]
|
|
220
|
+
?.getStore<DeckSettingsProps>(DECK_PLUGIN)?.value;
|
|
221
|
+
|
|
222
|
+
const previouslyOpenIds = new Set<string>(state.deck.solo ? [state.deck.solo] : state.deck.active);
|
|
223
|
+
batch(() => {
|
|
224
|
+
const next = state.deck.solo
|
|
225
|
+
? (subject as string[])
|
|
226
|
+
: subject.reduce(
|
|
227
|
+
(acc, entryId) =>
|
|
228
|
+
openEntry(acc, entryId, {
|
|
229
|
+
key: options?.key,
|
|
230
|
+
positioning: options?.positioning ?? settings?.newPlankPositioning,
|
|
231
|
+
pivotId: options?.pivotId,
|
|
232
|
+
}),
|
|
233
|
+
state.deck.active,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
return setActive({ next, state, attention });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const ids = state.deck.solo ? [state.deck.solo] : state.deck.active;
|
|
240
|
+
const newlyOpen = ids.filter((i) => !previouslyOpenIds.has(i));
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
intents: [
|
|
244
|
+
...(options?.scrollIntoView !== false
|
|
245
|
+
? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: newlyOpen[0] ?? subject[0] })]
|
|
246
|
+
: []),
|
|
247
|
+
createIntent(LayoutAction.Expose, { part: 'navigation', subject: newlyOpen[0] ?? subject[0] }),
|
|
248
|
+
...newlyOpen.map((id) => {
|
|
249
|
+
const active = graph?.findNode(id)?.data;
|
|
250
|
+
const typename = isReactiveObject(active) ? getTypename(active) : undefined;
|
|
251
|
+
return createIntent(ObservabilityAction.SendEvent, {
|
|
252
|
+
name: 'navigation.activate',
|
|
253
|
+
properties: {
|
|
254
|
+
id,
|
|
255
|
+
typename,
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
}),
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
},
|
|
262
|
+
}),
|
|
263
|
+
createResolver({
|
|
264
|
+
intent: LayoutAction.UpdateLayout,
|
|
265
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.Close.fields.input> =>
|
|
266
|
+
S.is(LayoutAction.Close.fields.input)(data),
|
|
267
|
+
resolve: ({ subject }) => {
|
|
268
|
+
const state = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
269
|
+
const attention = context.requestCapability(AttentionCapabilities.Attention);
|
|
270
|
+
const next = subject.reduce((acc, id) => closeEntry(acc, id), state.deck.active);
|
|
271
|
+
const toAttend = setActive({ next, state, attention });
|
|
272
|
+
return {
|
|
273
|
+
intents: toAttend ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: toAttend })] : [],
|
|
274
|
+
};
|
|
275
|
+
},
|
|
276
|
+
}),
|
|
277
|
+
createResolver({
|
|
278
|
+
intent: LayoutAction.UpdateLayout,
|
|
279
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.Set.fields.input> =>
|
|
280
|
+
S.is(LayoutAction.Set.fields.input)(data),
|
|
281
|
+
resolve: ({ subject }) => {
|
|
282
|
+
const state = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
283
|
+
const attention = context.requestCapability(AttentionCapabilities.Attention);
|
|
284
|
+
const toAttend = setActive({ next: subject as string[], state, attention });
|
|
285
|
+
return {
|
|
286
|
+
intents: toAttend ? [createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: toAttend })] : [],
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
}),
|
|
290
|
+
createResolver({
|
|
291
|
+
intent: LayoutAction.UpdateLayout,
|
|
292
|
+
filter: (data): data is S.Schema.Type<typeof LayoutAction.ScrollIntoView.fields.input> =>
|
|
293
|
+
S.is(LayoutAction.ScrollIntoView.fields.input)(data),
|
|
294
|
+
resolve: ({ subject }) => {
|
|
295
|
+
const layout = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
296
|
+
layout.scrollIntoView = subject;
|
|
297
|
+
},
|
|
298
|
+
}),
|
|
299
|
+
createResolver({
|
|
300
|
+
intent: DeckAction.UpdatePlankSize,
|
|
301
|
+
resolve: (data) => {
|
|
302
|
+
const state = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
303
|
+
state.deck.plankSizing[data.id] = data.size;
|
|
304
|
+
},
|
|
305
|
+
}),
|
|
306
|
+
createResolver({
|
|
307
|
+
intent: DeckAction.Adjust,
|
|
308
|
+
resolve: (adjustment) => {
|
|
309
|
+
const state = context.requestCapability(DeckCapabilities.MutableDeckState);
|
|
310
|
+
const attention = context.requestCapability(AttentionCapabilities.Attention);
|
|
311
|
+
|
|
312
|
+
return batch(() => {
|
|
313
|
+
if (adjustment.type === 'increment-end' || adjustment.type === 'increment-start') {
|
|
314
|
+
setActive({
|
|
315
|
+
next: incrementPlank(state.deck.active, adjustment),
|
|
316
|
+
state,
|
|
317
|
+
attention,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (adjustment.type === 'solo') {
|
|
322
|
+
const entryId = adjustment.id;
|
|
323
|
+
if (!state.deck.solo) {
|
|
324
|
+
// Solo the entry.
|
|
325
|
+
return {
|
|
326
|
+
intents: [
|
|
327
|
+
createIntent(LayoutAction.SetLayoutMode, {
|
|
328
|
+
part: 'mode',
|
|
329
|
+
subject: entryId,
|
|
330
|
+
options: { mode: 'solo' },
|
|
331
|
+
}),
|
|
332
|
+
],
|
|
333
|
+
};
|
|
334
|
+
} else {
|
|
335
|
+
// Un-solo the current entry.
|
|
336
|
+
return {
|
|
337
|
+
intents: [
|
|
338
|
+
// NOTE: The order of these is important.
|
|
339
|
+
pipe(
|
|
340
|
+
createIntent(LayoutAction.SetLayoutMode, { part: 'mode', options: { mode: 'deck' } }),
|
|
341
|
+
chain(LayoutAction.Open, { part: 'main', subject: [entryId] }),
|
|
342
|
+
),
|
|
343
|
+
],
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
},
|
|
349
|
+
}),
|
|
350
|
+
]);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, contributes, useCapabilities, useCapability } from '@dxos/app-framework';
|
|
8
|
+
|
|
9
|
+
import { DeckCapabilities } from './capabilities';
|
|
10
|
+
import { DeckLayout } from '../components';
|
|
11
|
+
import { DECK_PLUGIN } from '../meta';
|
|
12
|
+
import { type DeckSettingsProps } from '../types';
|
|
13
|
+
|
|
14
|
+
export default () =>
|
|
15
|
+
contributes(Capabilities.ReactRoot, {
|
|
16
|
+
id: DECK_PLUGIN,
|
|
17
|
+
root: () => {
|
|
18
|
+
const layout = useCapability(DeckCapabilities.MutableDeckState);
|
|
19
|
+
const settings = useCapability(Capabilities.SettingsStore).getStore<DeckSettingsProps>(DECK_PLUGIN)!.value;
|
|
20
|
+
const panels = useCapabilities(DeckCapabilities.ComplementaryPanel);
|
|
21
|
+
|
|
22
|
+
const handleDismissToast = useCallback(
|
|
23
|
+
(id: string) => {
|
|
24
|
+
const index = layout.toasts.findIndex((toast) => toast.id === id);
|
|
25
|
+
if (index !== -1) {
|
|
26
|
+
// Allow time for the toast to animate out.
|
|
27
|
+
// TODO(burdon): Factor out and unregister timeout.
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
if (layout.toasts[index].id === layout.currentUndoId) {
|
|
30
|
+
layout.currentUndoId = undefined;
|
|
31
|
+
}
|
|
32
|
+
layout.toasts.splice(index, 1);
|
|
33
|
+
}, 1_000);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
[layout.toasts],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<DeckLayout
|
|
41
|
+
showHints={settings.showHints}
|
|
42
|
+
overscroll={settings.overscroll}
|
|
43
|
+
panels={panels}
|
|
44
|
+
onDismissToast={handleDismissToast}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
|
|
8
|
+
import { SettingsStore } from '@dxos/local-storage';
|
|
9
|
+
|
|
10
|
+
import { LayoutSettings } from '../components';
|
|
11
|
+
import { Banner } from '../components/DeckLayout/Banner';
|
|
12
|
+
import { DECK_PLUGIN } from '../meta';
|
|
13
|
+
import { type DeckSettingsProps } from '../types';
|
|
14
|
+
|
|
15
|
+
export default () =>
|
|
16
|
+
contributes(Capabilities.ReactSurface, [
|
|
17
|
+
createSurface({
|
|
18
|
+
id: `${DECK_PLUGIN}/settings`,
|
|
19
|
+
role: 'article',
|
|
20
|
+
filter: (data): data is { subject: SettingsStore<DeckSettingsProps> } =>
|
|
21
|
+
data.subject instanceof SettingsStore && data.subject.prefix === DECK_PLUGIN,
|
|
22
|
+
component: ({ data: { subject } }) => <LayoutSettings settings={subject.value} />,
|
|
23
|
+
}),
|
|
24
|
+
createSurface({
|
|
25
|
+
id: `${DECK_PLUGIN}/banner`,
|
|
26
|
+
role: 'banner',
|
|
27
|
+
component: ({ data }) => {
|
|
28
|
+
return <Banner variant={data.variant} />;
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
]);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { batch } from '@preact/signals-core';
|
|
6
|
+
|
|
7
|
+
import { type AttentionManager } from '@dxos/plugin-attention';
|
|
8
|
+
|
|
9
|
+
import { type DeckState } from '../types';
|
|
10
|
+
|
|
11
|
+
export type SetActiveOptions = {
|
|
12
|
+
next: string[];
|
|
13
|
+
state: DeckState;
|
|
14
|
+
attention?: AttentionManager;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const setActive = ({ next, state, attention }: SetActiveOptions) => {
|
|
18
|
+
return batch(() => {
|
|
19
|
+
const active = state.deck.solo ? [state.deck.solo] : state.deck.active;
|
|
20
|
+
const removed = active.filter((id) => !next.includes(id));
|
|
21
|
+
const closed = Array.from(new Set([...state.deck.inactive.filter((id) => !next.includes(id)), ...removed]));
|
|
22
|
+
|
|
23
|
+
state.deck.inactive = closed;
|
|
24
|
+
|
|
25
|
+
if (state.deck.solo || !state.deck.initialized) {
|
|
26
|
+
state.deck.solo = next[0];
|
|
27
|
+
} else {
|
|
28
|
+
state.deck.active = next;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (attention) {
|
|
32
|
+
const attended = attention.current;
|
|
33
|
+
const [attendedId] = Array.from(attended);
|
|
34
|
+
const isAttendedAvailable = !!attendedId && next.includes(attendedId);
|
|
35
|
+
if (!isAttendedAvailable) {
|
|
36
|
+
const attendedIndex = active.indexOf(attendedId);
|
|
37
|
+
// If outside of bounds, focus on the first/last plank, otherwise focus on the new plank in the same position.
|
|
38
|
+
const index = attendedIndex === -1 ? 0 : attendedIndex >= next.length ? next.length - 1 : attendedIndex;
|
|
39
|
+
return next[index];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capabilities, contributes } from '@dxos/app-framework';
|
|
6
|
+
import { create } from '@dxos/live-object';
|
|
7
|
+
|
|
8
|
+
import { DECK_PLUGIN } from '../meta';
|
|
9
|
+
import { DeckSettingsSchema, type DeckSettingsProps } from '../types';
|
|
10
|
+
|
|
11
|
+
export default () => {
|
|
12
|
+
const settings = create<DeckSettingsProps>({
|
|
13
|
+
showHints: false,
|
|
14
|
+
enableNativeRedirect: false,
|
|
15
|
+
enableIdeStyleStatusbar: true,
|
|
16
|
+
newPlankPositioning: 'start',
|
|
17
|
+
overscroll: 'none',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return contributes(Capabilities.Settings, { schema: DeckSettingsSchema, prefix: DECK_PLUGIN, value: settings });
|
|
21
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Capabilities, contributes } from '@dxos/app-framework';
|
|
6
|
+
import { invariant } from '@dxos/invariant';
|
|
7
|
+
import { create } from '@dxos/live-object';
|
|
8
|
+
import { LocalStorageStore } from '@dxos/local-storage';
|
|
9
|
+
import { type SidebarState } from '@dxos/react-ui';
|
|
10
|
+
|
|
11
|
+
import { DeckCapabilities } from './capabilities';
|
|
12
|
+
import { DECK_PLUGIN } from '../meta';
|
|
13
|
+
import { getMode, type Deck, type DeckState } from '../types';
|
|
14
|
+
|
|
15
|
+
const boolean = /true|false/;
|
|
16
|
+
|
|
17
|
+
// TODO(thure, 18 Feb 2025): Remove after the next release.
|
|
18
|
+
|
|
19
|
+
const migrateSidebarStateDefaults = {
|
|
20
|
+
[`${DECK_PLUGIN}/complementary-sidebar-state`]: 'expanded',
|
|
21
|
+
[`${DECK_PLUGIN}/sidebar-state`]: 'collapsed',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const migrateSidebarState = () => {
|
|
25
|
+
Object.entries(migrateSidebarStateDefaults).forEach(([key, defaultValue]) => {
|
|
26
|
+
if (boolean.test(localStorage.getItem(key) ?? 'never')) {
|
|
27
|
+
localStorage.setItem(key, defaultValue);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default () => {
|
|
33
|
+
migrateSidebarState();
|
|
34
|
+
|
|
35
|
+
const state = new LocalStorageStore<DeckState>(DECK_PLUGIN, {
|
|
36
|
+
sidebarState: 'expanded',
|
|
37
|
+
complementarySidebarState: 'collapsed',
|
|
38
|
+
complementarySidebarPanel: undefined,
|
|
39
|
+
dialogContent: null,
|
|
40
|
+
dialogOpen: false,
|
|
41
|
+
dialogBlockAlign: undefined,
|
|
42
|
+
dialogType: undefined,
|
|
43
|
+
popoverContent: null,
|
|
44
|
+
popoverAnchorId: undefined,
|
|
45
|
+
popoverOpen: false,
|
|
46
|
+
toasts: [],
|
|
47
|
+
currentUndoId: undefined,
|
|
48
|
+
activeDeck: 'default',
|
|
49
|
+
decks: {
|
|
50
|
+
default: {
|
|
51
|
+
initialized: false,
|
|
52
|
+
active: [],
|
|
53
|
+
inactive: [],
|
|
54
|
+
fullscreen: false,
|
|
55
|
+
solo: undefined,
|
|
56
|
+
plankSizing: {},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
get deck() {
|
|
60
|
+
const deck = this.decks[this.activeDeck];
|
|
61
|
+
invariant(deck, `Deck not found: ${this.activeDeck}`);
|
|
62
|
+
return deck;
|
|
63
|
+
},
|
|
64
|
+
previousMode: {},
|
|
65
|
+
scrollIntoView: undefined,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
state
|
|
69
|
+
.prop({ key: 'sidebarState', type: LocalStorageStore.enum<SidebarState>() })
|
|
70
|
+
.prop({ key: 'complementarySidebarState', type: LocalStorageStore.enum<SidebarState>() })
|
|
71
|
+
.prop({ key: 'decks', type: LocalStorageStore.json<Record<string, Deck>>() })
|
|
72
|
+
.prop({ key: 'activeDeck', type: LocalStorageStore.string() });
|
|
73
|
+
|
|
74
|
+
const layout = create<Capabilities.Layout>({
|
|
75
|
+
get mode() {
|
|
76
|
+
return getMode(state.values.deck);
|
|
77
|
+
},
|
|
78
|
+
get dialogOpen() {
|
|
79
|
+
return state.values.dialogOpen;
|
|
80
|
+
},
|
|
81
|
+
get sidebarOpen() {
|
|
82
|
+
return state.values.sidebarState === 'expanded';
|
|
83
|
+
},
|
|
84
|
+
get complementarySidebarOpen() {
|
|
85
|
+
return state.values.complementarySidebarState === 'expanded';
|
|
86
|
+
},
|
|
87
|
+
get active() {
|
|
88
|
+
return state.values.deck.solo ? [state.values.deck.solo] : state.values.deck.active;
|
|
89
|
+
},
|
|
90
|
+
get inactive() {
|
|
91
|
+
return state.values.deck.inactive;
|
|
92
|
+
},
|
|
93
|
+
get scrollIntoView() {
|
|
94
|
+
return state.values.scrollIntoView;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return [
|
|
99
|
+
contributes(DeckCapabilities.DeckState, state.values, () => state.close()),
|
|
100
|
+
contributes(Capabilities.Layout, layout),
|
|
101
|
+
];
|
|
102
|
+
};
|