@dxos/plugin-deck 0.7.5-main.9d26e3a → 0.7.5-main.b19bfc8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist/lib/browser/app-graph-builder-IYHAGFA3.mjs +151 -0
  2. package/dist/lib/browser/app-graph-builder-IYHAGFA3.mjs.map +7 -0
  3. package/dist/lib/browser/check-app-scheme-S3EYUPMF.mjs +33 -0
  4. package/dist/lib/browser/check-app-scheme-S3EYUPMF.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-22AQ5IVX.mjs +17 -0
  6. package/dist/lib/browser/chunk-22AQ5IVX.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-CYE6QZBQ.mjs +128 -0
  8. package/dist/lib/browser/chunk-CYE6QZBQ.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-DIM5INBX.mjs +24 -0
  10. package/dist/lib/browser/chunk-DIM5INBX.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-DIXE74SK.mjs +1097 -0
  12. package/dist/lib/browser/chunk-DIXE74SK.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-KANJBSIX.mjs +97 -0
  14. package/dist/lib/browser/chunk-KANJBSIX.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-GVOGPULO.mjs → chunk-N7TEPFVR.mjs} +5 -4
  16. package/dist/lib/browser/chunk-N7TEPFVR.mjs.map +7 -0
  17. package/dist/lib/browser/index.mjs +104 -1776
  18. package/dist/lib/browser/index.mjs.map +4 -4
  19. package/dist/lib/browser/intent-resolver-MWUADUNI.mjs +488 -0
  20. package/dist/lib/browser/intent-resolver-MWUADUNI.mjs.map +7 -0
  21. package/dist/lib/browser/meta.json +1 -1
  22. package/dist/lib/browser/react-root-IELFERPV.mjs +45 -0
  23. package/dist/lib/browser/react-root-IELFERPV.mjs.map +7 -0
  24. package/dist/lib/browser/react-surface-WL45R43W.mjs +39 -0
  25. package/dist/lib/browser/react-surface-WL45R43W.mjs.map +7 -0
  26. package/dist/lib/browser/settings-YONG3QB7.mjs +28 -0
  27. package/dist/lib/browser/settings-YONG3QB7.mjs.map +7 -0
  28. package/dist/lib/browser/state-MZZL5S2D.mjs +124 -0
  29. package/dist/lib/browser/state-MZZL5S2D.mjs.map +7 -0
  30. package/dist/lib/browser/tools-5LDJNU56.mjs +51 -0
  31. package/dist/lib/browser/tools-5LDJNU56.mjs.map +7 -0
  32. package/dist/lib/browser/types.mjs +16 -4
  33. package/dist/lib/browser/url-handler-MVHTKUYA.mjs +72 -0
  34. package/dist/lib/browser/url-handler-MVHTKUYA.mjs.map +7 -0
  35. package/dist/types/src/DeckPlugin.d.ts +1 -5
  36. package/dist/types/src/DeckPlugin.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/app-graph-builder.d.ts +181 -0
  38. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  39. package/dist/types/src/capabilities/capabilities.d.ts +142 -0
  40. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  41. package/dist/types/src/capabilities/check-app-scheme.d.ts +4 -0
  42. package/dist/types/src/capabilities/check-app-scheme.d.ts.map +1 -0
  43. package/dist/types/src/capabilities/index.d.ts +190 -0
  44. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  45. package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
  46. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/react-root.d.ts +7 -0
  48. package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
  49. package/dist/types/src/capabilities/react-surface.d.ts +4 -0
  50. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  51. package/dist/types/src/capabilities/settings.d.ts +4 -0
  52. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  53. package/dist/types/src/capabilities/state.d.ts +79 -0
  54. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  55. package/dist/types/src/capabilities/tools.d.ts +10 -0
  56. package/dist/types/src/capabilities/tools.d.ts.map +1 -0
  57. package/dist/types/src/capabilities/url-handler.d.ts +4 -0
  58. package/dist/types/src/capabilities/url-handler.d.ts.map +1 -0
  59. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts +1 -2
  60. package/dist/types/src/components/DeckLayout/ActiveNode.d.ts.map +1 -1
  61. package/dist/types/src/components/DeckLayout/Banner.d.ts +5 -0
  62. package/dist/types/src/components/DeckLayout/Banner.d.ts.map +1 -0
  63. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts +1 -4
  64. package/dist/types/src/components/DeckLayout/ComplementarySidebar.d.ts.map +1 -1
  65. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts +1 -2
  66. package/dist/types/src/components/DeckLayout/ContentEmpty.d.ts.map +1 -1
  67. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts +2 -7
  68. package/dist/types/src/components/DeckLayout/DeckLayout.d.ts.map +1 -1
  69. package/dist/types/src/components/DeckLayout/Fallback.d.ts +1 -2
  70. package/dist/types/src/components/DeckLayout/Fallback.d.ts.map +1 -1
  71. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts +1 -2
  72. package/dist/types/src/components/DeckLayout/Fullscreen.d.ts.map +1 -1
  73. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts +3 -3
  74. package/dist/types/src/components/DeckLayout/NodePlankHeading.d.ts.map +1 -1
  75. package/dist/types/src/components/DeckLayout/Plank.d.ts +8 -6
  76. package/dist/types/src/components/DeckLayout/Plank.d.ts.map +1 -1
  77. package/dist/types/src/components/DeckLayout/PlankControls.d.ts +2 -2
  78. package/dist/types/src/components/DeckLayout/PlankControls.d.ts.map +1 -1
  79. package/dist/types/src/components/DeckLayout/PlankError.d.ts +6 -6
  80. package/dist/types/src/components/DeckLayout/PlankError.d.ts.map +1 -1
  81. package/dist/types/src/components/DeckLayout/PlankLoading.d.ts +1 -2
  82. package/dist/types/src/components/DeckLayout/PlankLoading.d.ts.map +1 -1
  83. package/dist/types/src/components/DeckLayout/Sidebar.d.ts +1 -6
  84. package/dist/types/src/components/DeckLayout/Sidebar.d.ts.map +1 -1
  85. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts +7 -0
  86. package/dist/types/src/components/DeckLayout/SidebarButton.d.ts.map +1 -0
  87. package/dist/types/src/components/DeckLayout/StatusBar.d.ts +1 -2
  88. package/dist/types/src/components/DeckLayout/StatusBar.d.ts.map +1 -1
  89. package/dist/types/src/components/DeckLayout/Toast.d.ts +2 -3
  90. package/dist/types/src/components/DeckLayout/Toast.d.ts.map +1 -1
  91. package/dist/types/src/components/DeckLayout/Topbar.d.ts +2 -0
  92. package/dist/types/src/components/DeckLayout/Topbar.d.ts.map +1 -0
  93. package/dist/types/src/components/LayoutSettings.d.ts +1 -2
  94. package/dist/types/src/components/LayoutSettings.d.ts.map +1 -1
  95. package/dist/types/src/components/fragments.d.ts +4 -0
  96. package/dist/types/src/components/fragments.d.ts.map +1 -0
  97. package/dist/types/src/components/index.d.ts +0 -2
  98. package/dist/types/src/components/index.d.ts.map +1 -1
  99. package/dist/types/src/events.d.ts +5 -0
  100. package/dist/types/src/events.d.ts.map +1 -0
  101. package/dist/types/src/hooks/useMainSize.d.ts +2 -2
  102. package/dist/types/src/hooks/useNode.d.ts.map +1 -1
  103. package/dist/types/src/index.d.ts +3 -2
  104. package/dist/types/src/index.d.ts.map +1 -1
  105. package/dist/types/src/layout.d.ts +5 -19
  106. package/dist/types/src/layout.d.ts.map +1 -1
  107. package/dist/types/src/meta.d.ts +4 -4
  108. package/dist/types/src/meta.d.ts.map +1 -1
  109. package/dist/types/src/translations.d.ts +4 -2
  110. package/dist/types/src/translations.d.ts.map +1 -1
  111. package/dist/types/src/types.d.ts +121 -20
  112. package/dist/types/src/types.d.ts.map +1 -1
  113. package/dist/types/src/util/index.d.ts +4 -2
  114. package/dist/types/src/util/index.d.ts.map +1 -1
  115. package/dist/types/src/util/layoutAppliesTopbar.d.ts +2 -0
  116. package/dist/types/src/util/layoutAppliesTopbar.d.ts.map +1 -0
  117. package/dist/types/src/util/set-active.d.ts +9 -0
  118. package/dist/types/src/util/set-active.d.ts.map +1 -0
  119. package/dist/types/src/util/useBreakpoints.d.ts +2 -0
  120. package/dist/types/src/util/useBreakpoints.d.ts.map +1 -0
  121. package/dist/types/src/util/useHoistStatusbar.d.ts +2 -0
  122. package/dist/types/src/util/useHoistStatusbar.d.ts.map +1 -0
  123. package/dist/types/tsconfig.tsbuildinfo +1 -1
  124. package/package.json +32 -37
  125. package/src/DeckPlugin.ts +87 -0
  126. package/src/capabilities/app-graph-builder.ts +113 -0
  127. package/src/capabilities/capabilities.ts +15 -0
  128. package/src/capabilities/check-app-scheme.ts +44 -0
  129. package/src/capabilities/index.ts +17 -0
  130. package/src/capabilities/intent-resolver.ts +368 -0
  131. package/src/capabilities/react-root.tsx +46 -0
  132. package/src/capabilities/react-surface.tsx +31 -0
  133. package/src/capabilities/settings.ts +21 -0
  134. package/src/capabilities/state.ts +107 -0
  135. package/src/capabilities/tools.ts +61 -0
  136. package/src/capabilities/url-handler.ts +65 -0
  137. package/src/components/DeckLayout/ActiveNode.tsx +2 -3
  138. package/src/components/DeckLayout/Banner.tsx +37 -0
  139. package/src/components/DeckLayout/ComplementarySidebar.tsx +142 -59
  140. package/src/components/DeckLayout/ContentEmpty.tsx +9 -4
  141. package/src/components/DeckLayout/DeckLayout.tsx +151 -99
  142. package/src/components/DeckLayout/Fullscreen.tsx +2 -3
  143. package/src/components/DeckLayout/NodePlankHeading.tsx +64 -77
  144. package/src/components/DeckLayout/Plank.tsx +35 -43
  145. package/src/components/DeckLayout/PlankControls.tsx +12 -11
  146. package/src/components/DeckLayout/PlankError.tsx +6 -5
  147. package/src/components/DeckLayout/Sidebar.tsx +19 -9
  148. package/src/components/DeckLayout/SidebarButton.tsx +68 -0
  149. package/src/components/DeckLayout/StatusBar.tsx +6 -12
  150. package/src/components/DeckLayout/Toast.tsx +2 -2
  151. package/src/components/DeckLayout/Topbar.tsx +11 -0
  152. package/src/components/LayoutSettings.tsx +8 -8
  153. package/src/components/fragments.ts +14 -0
  154. package/src/components/index.ts +0 -2
  155. package/src/events.ts +12 -0
  156. package/src/hooks/useMainSize.ts +3 -3
  157. package/src/hooks/useNode.ts +3 -1
  158. package/src/index.ts +3 -4
  159. package/src/layout.ts +43 -212
  160. package/src/meta.ts +3 -2
  161. package/src/translations.ts +8 -6
  162. package/src/types.ts +104 -36
  163. package/src/util/index.ts +4 -2
  164. package/src/util/layoutAppliesTopbar.ts +7 -0
  165. package/src/util/set-active.ts +47 -0
  166. package/src/util/useBreakpoints.ts +11 -0
  167. package/src/util/useHoistStatusbar.ts +20 -0
  168. package/dist/lib/browser/chunk-GVOGPULO.mjs.map +0 -7
  169. package/dist/lib/browser/chunk-ZC3K6C2W.mjs +0 -37
  170. package/dist/lib/browser/chunk-ZC3K6C2W.mjs.map +0 -7
  171. package/dist/lib/browser/meta.mjs +0 -9
  172. package/dist/lib/browser/meta.mjs.map +0 -7
  173. package/dist/types/src/components/DeckContext.d.ts +0 -8
  174. package/dist/types/src/components/DeckContext.d.ts.map +0 -1
  175. package/dist/types/src/components/LayoutContext.d.ts +0 -5
  176. package/dist/types/src/components/LayoutContext.d.ts.map +0 -1
  177. package/dist/types/src/layout.test.d.ts +0 -2
  178. package/dist/types/src/layout.test.d.ts.map +0 -1
  179. package/dist/types/src/util/check-app-scheme.d.ts +0 -2
  180. package/dist/types/src/util/check-app-scheme.d.ts.map +0 -1
  181. package/dist/types/src/util/layout-parts.d.ts +0 -7
  182. package/dist/types/src/util/layout-parts.d.ts.map +0 -1
  183. package/src/DeckPlugin.tsx +0 -623
  184. package/src/components/DeckContext.ts +0 -14
  185. package/src/components/LayoutContext.ts +0 -12
  186. package/src/layout.test.ts +0 -380
  187. package/src/util/check-app-scheme.ts +0 -21
  188. package/src/util/layout-parts.ts +0 -12
@@ -1,623 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { batch } from '@preact/signals-core';
6
- import { pipe } from 'effect';
7
- import { setAutoFreeze } from 'immer';
8
- import React, { type PropsWithChildren } from 'react';
9
-
10
- import {
11
- chain,
12
- createIntent,
13
- createResolver,
14
- createSurface,
15
- filterPlugins,
16
- IntentAction,
17
- type IntentPluginProvides,
18
- type Layout,
19
- LayoutAction,
20
- type LayoutEntry,
21
- type LayoutMode,
22
- type LayoutPart,
23
- type LayoutParts,
24
- NavigationAction,
25
- openIds,
26
- parseGraphPlugin,
27
- parseIntentPlugin,
28
- type Plugin,
29
- type PluginDefinition,
30
- resolvePlugin,
31
- SLUG_PATH_SEPARATOR,
32
- Toast as ToastSchema,
33
- } from '@dxos/app-framework';
34
- import { type UnsubscribeCallback } from '@dxos/async';
35
- import { getTypename, S } from '@dxos/echo-schema';
36
- import { scheduledEffect } from '@dxos/echo-signals/core';
37
- import { invariant } from '@dxos/invariant';
38
- import { create, isReactiveObject } from '@dxos/live-object';
39
- import { LocalStorageStore } from '@dxos/local-storage';
40
- import { log } from '@dxos/log';
41
- import { type AttentionPluginProvides, parseAttentionPlugin } from '@dxos/plugin-attention';
42
- import { createExtension, ROOT_ID, type Node } from '@dxos/plugin-graph';
43
- import { ObservabilityAction } from '@dxos/plugin-observability/types';
44
- import { translations as stackTranslations } from '@dxos/react-ui-stack';
45
-
46
- import { DeckContext, type DeckContextType, DeckLayout, LayoutContext, LayoutSettings, NAV_ID } from './components';
47
- import {
48
- closeEntry,
49
- incrementPlank,
50
- mergeLayoutParts,
51
- openEntry,
52
- removePart,
53
- soloPartToUri,
54
- uriToSoloPart,
55
- } from './layout';
56
- import meta, { DECK_PLUGIN } from './meta';
57
- import translations from './translations';
58
- import {
59
- DeckAction,
60
- type DeckPluginProvides,
61
- type DeckSettingsProps,
62
- type NewPlankPositioning,
63
- type Overscroll,
64
- type Panel,
65
- parsePanelPlugin,
66
- } from './types';
67
- import { checkAppScheme, getEffectivePart } from './util';
68
-
69
- const isSocket = !!(globalThis as any).__args;
70
-
71
- // TODO(mjamesderocher): Can we get this directly from Socket?
72
- const appScheme = 'composer://';
73
-
74
- // NOTE(Zan): When producing values with immer, we shouldn't auto-freeze them because
75
- // our signal implementation needs to add some hidden properties to the produced values.
76
- // TODO(Zan): Move this to a more global location if we use immer more broadly.
77
- setAutoFreeze(false);
78
-
79
- export const DeckPlugin = ({
80
- observability,
81
- }: {
82
- observability?: boolean;
83
- } = {}): PluginDefinition<DeckPluginProvides> => {
84
- let intentPlugin: Plugin<IntentPluginProvides> | undefined;
85
- let attentionPlugin: Plugin<AttentionPluginProvides> | undefined;
86
- const unsubscriptionCallbacks = [] as (UnsubscribeCallback | undefined)[];
87
- let currentUndoId: string | undefined;
88
- let handleNavigation: () => Promise<void> | undefined;
89
- const panels: Panel[] = [];
90
-
91
- const settings = new LocalStorageStore<DeckSettingsProps>('dxos.org/settings/layout', {
92
- showHints: false,
93
- customSlots: false,
94
- flatDeck: false,
95
- enableNativeRedirect: false,
96
- disableDeck: false,
97
- newPlankPositioning: 'start',
98
- overscroll: 'centering',
99
- });
100
-
101
- const layout = new LocalStorageStore<Layout>('dxos.org/settings/layout', {
102
- layoutMode: 'solo',
103
- sidebarOpen: true,
104
- complementarySidebarOpen: false,
105
- dialogContent: null,
106
- dialogOpen: false,
107
- dialogBlockAlign: undefined,
108
- dialogType: undefined,
109
- popoverContent: null,
110
- popoverAnchorId: undefined,
111
- popoverOpen: false,
112
- toasts: [],
113
- });
114
-
115
- const deck = new LocalStorageStore<DeckContextType>('dxos.org/settings/deck', {
116
- plankSizing: {},
117
- });
118
-
119
- // TODO(wittjosiah): This active state is not a generic navigation state but quite deck specific.
120
- // It is also closely tied to the layout mode state (which also seems quite deck specific).
121
- // The layout and navigation interfaces need to be revisited and cleaned up.
122
- // Doing this cleanup should also help simplify some of the convoluted logic for managing it.
123
- const location = new LocalStorageStore<{ active: LayoutParts; closed: string[] }>('dxos.org/state/layout', {
124
- active: { sidebar: [{ id: NAV_ID }] },
125
- closed: [],
126
- });
127
-
128
- // TODO(Zan): Cap depth!
129
- const layoutModeHistory = create({ values: [] as LayoutMode[] });
130
-
131
- const handleSetLayout = ({
132
- element,
133
- state,
134
- component,
135
- subject,
136
- anchorId,
137
- dialogBlockAlign,
138
- dialogType,
139
- }: LayoutAction.SetLayout['input']) => {
140
- switch (element) {
141
- case 'sidebar': {
142
- layout.values.sidebarOpen = state ?? !layout.values.sidebarOpen;
143
- break;
144
- }
145
-
146
- case 'complementary': {
147
- layout.values.complementarySidebarOpen = !!state;
148
- // TODO(thure): Hoist content into the c11y sidebar of Deck.
149
- // layout.values.complementarySidebarContent = component || subject ? { component, subject } : null;
150
- break;
151
- }
152
-
153
- case 'dialog': {
154
- layout.values.dialogOpen = state ?? Boolean(component);
155
- layout.values.dialogContent = component ? { component, subject } : null;
156
- layout.values.dialogBlockAlign = dialogBlockAlign ?? 'center';
157
- layout.values.dialogType = dialogType;
158
- break;
159
- }
160
-
161
- case 'popover': {
162
- layout.values.popoverOpen = state ?? Boolean(component);
163
- layout.values.popoverContent = component ? { component, subject } : null;
164
- layout.values.popoverAnchorId = anchorId;
165
- break;
166
- }
167
-
168
- case 'toast': {
169
- if (S.is(ToastSchema)(subject)) {
170
- layout.values.toasts = [...layout.values.toasts, subject];
171
- }
172
- break;
173
- }
174
- }
175
- };
176
-
177
- /**
178
- * Update the active state and returns the id of the next attended plank.
179
- */
180
- const handleSetLocation = (next: LayoutParts) => {
181
- const part = layout.values.layoutMode === 'solo' ? 'solo' : 'main';
182
- const ids = openIds(next, [part]);
183
-
184
- const current = openIds(location.values.active, [part]);
185
- const removed = current.filter((id) => !ids.includes(id));
186
- const closed = Array.from(new Set([...location.values.closed.filter((id) => !ids.includes(id)), ...removed]));
187
-
188
- location.values.closed = closed;
189
- location.values.active = next;
190
-
191
- if (attentionPlugin) {
192
- const attended = attentionPlugin.provides.attention.attended;
193
- const [attendedId] = Array.from(attended);
194
- const isAttendedAvailable = !!attendedId && ids.includes(attendedId);
195
- if (!isAttendedAvailable) {
196
- const currentIds = location.values.active[part]?.map(({ id }) => id) ?? [];
197
- const attendedIndex = currentIds.indexOf(attendedId);
198
- // If outside of bounds, focus on the first/last plank, otherwise focus on the new plank in the same position.
199
- const index = attendedIndex === -1 ? 0 : attendedIndex >= ids.length ? ids.length - 1 : attendedIndex;
200
- return next[part]?.[index].id;
201
- }
202
- }
203
- };
204
-
205
- return {
206
- meta,
207
- ready: async ({ plugins }) => {
208
- attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
209
- intentPlugin = resolvePlugin(plugins, parseIntentPlugin);
210
- const dispatch = intentPlugin?.provides.intent.dispatchPromise;
211
-
212
- layout
213
- .prop({ key: 'layoutMode', type: LocalStorageStore.enum<LayoutMode>() })
214
- .prop({ key: 'sidebarOpen', type: LocalStorageStore.bool() })
215
- .prop({ key: 'complementarySidebarOpen', type: LocalStorageStore.bool() });
216
-
217
- deck.prop({ key: 'plankSizing', type: LocalStorageStore.json<Record<string, number>>() });
218
-
219
- location
220
- .prop({ key: 'active', type: LocalStorageStore.json<LayoutParts>() })
221
- .prop({ key: 'closed', type: LocalStorageStore.json<string[]>() });
222
-
223
- panels.push(
224
- ...filterPlugins(plugins, parsePanelPlugin).flatMap((plugin) => plugin.provides.complementary.panels),
225
- );
226
-
227
- settings
228
- .prop({ key: 'showHints', type: LocalStorageStore.bool() })
229
- .prop({ key: 'customSlots', type: LocalStorageStore.bool() })
230
- .prop({ key: 'flatDeck', type: LocalStorageStore.bool() })
231
- .prop({ key: 'enableNativeRedirect', type: LocalStorageStore.bool() })
232
- .prop({ key: 'disableDeck', type: LocalStorageStore.bool() }) // Deprecated.
233
- .prop({ key: 'newPlankPositioning', type: LocalStorageStore.enum<NewPlankPositioning>() })
234
- .prop({ key: 'overscroll', type: LocalStorageStore.enum<Overscroll>() });
235
-
236
- if (!isSocket && settings.values.enableNativeRedirect) {
237
- checkAppScheme(appScheme);
238
- }
239
-
240
- handleNavigation = async () => {
241
- const pathname = window.location.pathname;
242
- if (pathname === '/reset') {
243
- handleSetLocation({ sidebar: [{ id: NAV_ID }] });
244
- location.values.closed = [];
245
- layout.values.layoutMode = 'solo';
246
- window.location.pathname = '/';
247
- return;
248
- }
249
-
250
- const startingLayout = removePart(location.values.active, 'solo');
251
- const layoutFromUri = uriToSoloPart(pathname);
252
- if (!layoutFromUri) {
253
- const toAttend = handleSetLocation(startingLayout);
254
- layout.values.layoutMode = 'deck';
255
- await dispatch?.(createIntent(LayoutAction.ScrollIntoView, { id: toAttend }));
256
- return;
257
- }
258
-
259
- const toAttend = handleSetLocation(mergeLayoutParts(layoutFromUri, startingLayout));
260
- layout.values.layoutMode = 'solo';
261
- await dispatch?.(createIntent(LayoutAction.ScrollIntoView, { id: toAttend }));
262
- };
263
-
264
- await handleNavigation();
265
- window.addEventListener('popstate', handleNavigation);
266
-
267
- unsubscriptionCallbacks.push(
268
- scheduledEffect(
269
- () => ({ selectedPath: soloPartToUri(location.values.active) }),
270
- ({ selectedPath }) => {
271
- // TODO(thure): In some browsers, this only preserves the most recent state change, even though this is not `history.replace`…
272
- history.pushState(null, '', `/${selectedPath}${window.location.search}`);
273
- },
274
- ),
275
- );
276
-
277
- layoutModeHistory.values.push(`${layout.values.layoutMode}`);
278
- },
279
- unload: async () => {
280
- layout.close();
281
- location.close();
282
- unsubscriptionCallbacks.forEach((unsubscribe) => unsubscribe?.());
283
- window.removeEventListener('popstate', handleNavigation);
284
- },
285
- provides: {
286
- settings: settings.values,
287
- layout: layout.values,
288
- location: location.values,
289
- translations: [...translations, ...stackTranslations],
290
- graph: {
291
- builder: (plugins) => {
292
- const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatchPromise;
293
- const attention = resolvePlugin(plugins, parseAttentionPlugin)?.provides.attention;
294
-
295
- invariant(dispatch, 'Intent plugin is required for deck plugin.');
296
- invariant(attention, 'Attention plugin is required for deck plugin.');
297
-
298
- // TODO(burdon): Root menu isn't visible so nothing bound.
299
- return createExtension({
300
- id: DECK_PLUGIN,
301
- filter: (node): node is Node<null> => node.id === ROOT_ID,
302
- actions: () => {
303
- // NOTE(Zan): This is currently disabled.
304
- // TODO(Zan): Fullscreen needs to know the active node and provide that to the layout part.
305
- const _fullscreen = {
306
- id: `${LayoutAction.SetLayoutMode._tag}/fullscreen`,
307
- data: async () => {
308
- await dispatch(createIntent(LayoutAction.SetLayoutMode, { layoutMode: 'fullscreen' }));
309
- },
310
- properties: {
311
- label: ['toggle fullscreen label', { ns: DECK_PLUGIN }],
312
- icon: 'ph--arrows-out--regular',
313
- keyBinding: {
314
- macos: 'ctrl+meta+f',
315
- windows: 'shift+ctrl+f',
316
- },
317
- },
318
- };
319
-
320
- const closeCurrent = {
321
- id: `${NavigationAction.Close._tag}/current`,
322
- data: async () => {
323
- const attended = attention.attended.at(-1);
324
- if (attended) {
325
- await dispatch(createIntent(NavigationAction.Close, { activeParts: { main: [attended] } }));
326
- }
327
- },
328
- properties: {
329
- label: ['close current label', { ns: DECK_PLUGIN }],
330
- icon: 'ph--x--regular',
331
- },
332
- };
333
-
334
- const closeOthers = {
335
- id: `${NavigationAction.Close._tag}/others`,
336
- data: async () => {
337
- const attended = attention.attended.at(-1);
338
- const ids = openIds(location.values.active, ['main']).filter((id) => id !== attended);
339
- await dispatch(
340
- createIntent(NavigationAction.Close, {
341
- activeParts: { main: ids },
342
- }),
343
- );
344
- },
345
- properties: {
346
- label: ['close others label', { ns: DECK_PLUGIN }],
347
- icon: 'ph--x-square--regular',
348
- },
349
- };
350
-
351
- const closeAll = {
352
- id: `${NavigationAction.Close._tag}/all`,
353
- data: async () => {
354
- await dispatch(
355
- createIntent(NavigationAction.Close, {
356
- activeParts: { main: openIds(location.values.active, ['main']) },
357
- }),
358
- );
359
- },
360
- properties: {
361
- label: ['close all label', { ns: DECK_PLUGIN }],
362
- icon: 'ph--x-circle--regular',
363
- },
364
- };
365
-
366
- return layout.values.layoutMode === 'deck' ? [closeCurrent, closeOthers, closeAll] : [];
367
- },
368
- });
369
- },
370
- },
371
- context: (props: PropsWithChildren) => (
372
- <LayoutContext.Provider value={layout.values}>
373
- <DeckContext.Provider value={deck.values}>{props.children}</DeckContext.Provider>
374
- </LayoutContext.Provider>
375
- ),
376
- root: () => {
377
- return (
378
- <DeckLayout
379
- layoutParts={location.values.active}
380
- showHints={settings.values.showHints}
381
- overscroll={settings.values.overscroll}
382
- toasts={layout.values.toasts}
383
- panels={panels}
384
- onDismissToast={(id) => {
385
- const index = layout.values.toasts.findIndex((toast) => toast.id === id);
386
- if (index !== -1) {
387
- // Allow time for the toast to animate out.
388
- // TODO(burdon): Factor out and unregister timeout.
389
- setTimeout(() => {
390
- if (layout.values.toasts[index].id === currentUndoId) {
391
- currentUndoId = undefined;
392
- }
393
- layout.values.toasts.splice(index, 1);
394
- }, 1_000);
395
- }
396
- }}
397
- />
398
- );
399
- },
400
- surface: {
401
- definitions: () =>
402
- createSurface({
403
- id: DECK_PLUGIN,
404
- role: 'settings',
405
- filter: (data): data is any => data.subject === DECK_PLUGIN,
406
- component: () => <LayoutSettings settings={settings.values} />,
407
- }),
408
- },
409
- intent: {
410
- resolvers: ({ plugins }) => {
411
- const graph = resolvePlugin(plugins, parseGraphPlugin)?.provides.graph;
412
-
413
- return [
414
- createResolver(DeckAction.UpdatePlankSize, (data) => {
415
- deck.values.plankSizing[data.id] = data.size;
416
- }),
417
- createResolver(IntentAction.ShowUndo, (data) => {
418
- // TODO(wittjosiah): Support undoing further back than the last action.
419
- if (currentUndoId) {
420
- layout.values.toasts = layout.values.toasts.filter((toast) => toast.id !== currentUndoId);
421
- }
422
- currentUndoId = `${IntentAction.ShowUndo._tag}-${Date.now()}`;
423
- layout.values.toasts = [
424
- ...layout.values.toasts,
425
- {
426
- id: currentUndoId,
427
- title: data.message ?? ['undo available label', { ns: DECK_PLUGIN }],
428
- duration: 10_000,
429
- actionLabel: ['undo action label', { ns: DECK_PLUGIN }],
430
- actionAlt: ['undo action alt', { ns: DECK_PLUGIN }],
431
- closeLabel: ['undo close label', { ns: DECK_PLUGIN }],
432
- onAction: () => intentPlugin?.provides.intent.undoPromise?.(),
433
- },
434
- ];
435
- }),
436
- createResolver(LayoutAction.SetLayout, handleSetLayout),
437
- createResolver(LayoutAction.SetLayoutMode, (data) => {
438
- const setMode = (mode: LayoutMode) => {
439
- const main = openIds(location.values.active, ['main']);
440
- const solo = openIds(location.values.active, ['solo']);
441
- const current = layout.values.layoutMode === 'solo' ? solo : main;
442
- // When un-soloing, the solo entry is added to the deck.
443
- const next = mode === 'solo' ? solo : [...main, ...solo];
444
- const removed = current.filter((id) => !next.includes(id));
445
- const closed = Array.from(
446
- new Set([...location.values.closed.filter((id) => !next.includes(id)), ...removed]),
447
- );
448
-
449
- location.values.closed = closed;
450
- layout.values.layoutMode = mode;
451
- };
452
-
453
- return batch(() => {
454
- if ('layoutMode' in data) {
455
- layoutModeHistory.values.push(layout.values.layoutMode);
456
- setMode(data.layoutMode);
457
- } else if (data.revert) {
458
- setMode(layoutModeHistory.values.pop() ?? 'solo');
459
- } else {
460
- log.warn('Invalid layout mode', data);
461
- }
462
- });
463
- }),
464
- createResolver(LayoutAction.ScrollIntoView, ({ id }) => {
465
- layout.values.scrollIntoView = id;
466
- }),
467
- // TODO(wittjosiah): Factor out navgiation from deck plugin.
468
- createResolver(NavigationAction.Open, (data) => {
469
- const previouslyOpenIds = new Set<string>(openIds(location.values.active));
470
- const layoutMode = layout.values.layoutMode;
471
- const toAttend = batch(() => {
472
- const processLayoutEntry = (partName: string, entryString: string, currentLayout: any) => {
473
- // TODO(burdon): Option to toggle?
474
- const toggle = false;
475
- const [id, path] = entryString.split(SLUG_PATH_SEPARATOR);
476
- const layoutEntry: LayoutEntry = { id, ...(path ? { path } : {}) };
477
- const effectivePart = getEffectivePart(partName as LayoutPart, layoutMode);
478
- if (
479
- toggle &&
480
- layoutMode === 'deck' &&
481
- effectivePart === 'main' &&
482
- currentLayout[effectivePart]?.some((entry: LayoutEntry) => entry.id === id) &&
483
- !data?.noToggle
484
- ) {
485
- // If we're in deck mode and the main part is already open, toggle it closed.
486
- return closeEntry(currentLayout, { part: effectivePart as LayoutPart, entryId: id });
487
- } else {
488
- return openEntry(currentLayout, effectivePart, layoutEntry, {
489
- positioning: settings.values.newPlankPositioning,
490
- });
491
- }
492
- };
493
-
494
- let newLayout = location.values.active;
495
- Object.entries(data.activeParts).forEach(([partName, layoutEntries]) => {
496
- if (Array.isArray(layoutEntries)) {
497
- layoutEntries.forEach((activePartEntry: string) => {
498
- newLayout = processLayoutEntry(partName, activePartEntry, newLayout);
499
- });
500
- } else if (typeof layoutEntries === 'string') {
501
- // Legacy single string entry.
502
- newLayout = processLayoutEntry(partName, layoutEntries, newLayout);
503
- }
504
- });
505
-
506
- return handleSetLocation(newLayout);
507
- });
508
-
509
- const ids = openIds(location.values.active);
510
- const newlyOpen = ids.filter((i) => !previouslyOpenIds.has(i));
511
-
512
- return {
513
- data: { open: ids },
514
- intents: [
515
- createIntent(LayoutAction.ScrollIntoView, { id: newlyOpen[0] ?? toAttend }),
516
- ...(toAttend ? [createIntent(NavigationAction.Expose, { id: toAttend })] : []),
517
- ...(observability
518
- ? newlyOpen.map((id) => {
519
- const active = graph?.findNode(id)?.data;
520
- const typename = isReactiveObject(active) ? getTypename(active) : undefined;
521
- return createIntent(ObservabilityAction.SendEvent, {
522
- name: 'navigation.activate',
523
- properties: {
524
- id,
525
- typename,
526
- },
527
- });
528
- })
529
- : []),
530
- ],
531
- };
532
- }),
533
- createResolver(NavigationAction.AddToActive, (data) => {
534
- const layoutEntry = { id: data.id };
535
- const effectivePart = getEffectivePart(data.part, layout.values.layoutMode);
536
-
537
- handleSetLocation(
538
- openEntry(location.values.active, effectivePart, layoutEntry, {
539
- positioning: data.positioning ?? settings.values.newPlankPositioning,
540
- pivotId: data.pivotId,
541
- }),
542
- );
543
-
544
- const intents = [];
545
- if (data.scrollIntoView && layout.values.layoutMode === 'deck') {
546
- intents.push(createIntent(LayoutAction.ScrollIntoView, { id: data.id }));
547
- }
548
-
549
- return { intents };
550
- }),
551
- createResolver(NavigationAction.Close, (data) => {
552
- let newLayout = location.values.active;
553
- const layoutMode = layout.values.layoutMode;
554
- const intentParts = data.activeParts;
555
- Object.keys(intentParts).forEach((partName: string) => {
556
- const effectivePart = getEffectivePart(partName as LayoutPart, layoutMode);
557
- const ids = intentParts[partName];
558
- if (Array.isArray(ids)) {
559
- ids.forEach((id: string) => {
560
- newLayout = closeEntry(newLayout, { part: effectivePart, entryId: id });
561
- });
562
- } else {
563
- // Legacy single string entry
564
- newLayout = closeEntry(newLayout, { part: effectivePart, entryId: ids });
565
- }
566
- });
567
-
568
- const toAttend = handleSetLocation(newLayout);
569
- return { intents: [createIntent(LayoutAction.ScrollIntoView, { id: toAttend })] };
570
- }),
571
- createResolver(NavigationAction.Set, (data) => {
572
- return batch(() => {
573
- const toAttend = handleSetLocation(data.activeParts);
574
- return { intents: [createIntent(LayoutAction.ScrollIntoView, { id: toAttend })] };
575
- });
576
- }),
577
- createResolver(NavigationAction.Adjust, (adjustment) => {
578
- return batch(() => {
579
- if (adjustment.type === 'increment-end' || adjustment.type === 'increment-start') {
580
- handleSetLocation(
581
- incrementPlank(location.values.active, {
582
- type: adjustment.type,
583
- layoutCoordinate: adjustment.layoutCoordinate,
584
- }),
585
- );
586
- }
587
-
588
- if (adjustment.type === 'solo') {
589
- const entryId = adjustment.layoutCoordinate.entryId;
590
- if (layout.values.layoutMode !== 'solo') {
591
- // Solo the entry.
592
- return {
593
- intents: [
594
- // NOTE: The order of these is important.
595
- pipe(
596
- createIntent(NavigationAction.Open, { activeParts: { solo: [entryId] } }),
597
- chain(LayoutAction.SetLayoutMode, { layoutMode: 'solo' }),
598
- ),
599
- ],
600
- };
601
- } else {
602
- // Un-solo the current entry.
603
- return {
604
- intents: [
605
- // NOTE: The order of these is important.
606
- pipe(
607
- createIntent(LayoutAction.SetLayoutMode, { layoutMode: 'deck' }),
608
- chain(NavigationAction.Close, { activeParts: { solo: [entryId] } }),
609
- chain(NavigationAction.Open, { activeParts: { main: [entryId] }, noToggle: true }),
610
- chain(LayoutAction.ScrollIntoView, { id: entryId }),
611
- ),
612
- ],
613
- };
614
- }
615
- }
616
- });
617
- }),
618
- ];
619
- },
620
- },
621
- },
622
- };
623
- };
@@ -1,14 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { type Context, createContext, useContext } from 'react';
6
-
7
- import { raise } from '@dxos/debug';
8
-
9
- export type PlankSizing = Record<string, number>;
10
- export type DeckContextType = { plankSizing: PlankSizing };
11
-
12
- export const DeckContext: Context<DeckContextType | null> = createContext<DeckContextType | null>(null);
13
-
14
- export const useDeckContext = (): DeckContextType => useContext(DeckContext) ?? raise(new Error('Missing DeckContext'));
@@ -1,12 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { type Context, createContext, useContext } from 'react';
6
-
7
- import type { Layout } from '@dxos/app-framework';
8
- import { raise } from '@dxos/debug';
9
-
10
- export const LayoutContext: Context<Layout | null> = createContext<Layout | null>(null);
11
-
12
- export const useLayout = (): Layout => useContext(LayoutContext) ?? raise(new Error('Missing LayoutContext'));