@vuu-ui/vuu-shell 4.0.0-alpha.3 → 4.0.0-alpha.30

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 (43) hide show
  1. package/package.json +20 -19
  2. package/src/app-header/AppHeader.css.js +1 -1
  3. package/src/persistence-manager/PersistenceProvider.js +5 -7
  4. package/src/persistence-manager/index.js +1 -3
  5. package/src/shell.css.js +1 -1
  6. package/src/shell.js +2 -2
  7. package/src/workspace-management/LayoutList.js +1 -1
  8. package/src/workspace-management/index.js +1 -8
  9. package/src/workspace-management/useWorkspaceContextMenuItems.js +1 -2
  10. package/types/app-header/AppHeader.d.ts +1 -1
  11. package/types/feature/Feature.d.ts +1 -1
  12. package/types/feature/Loader.d.ts +1 -1
  13. package/types/feature-list/FeatureList.d.ts +1 -1
  14. package/types/left-nav/LeftNav.d.ts +1 -1
  15. package/types/login/LoginPanel.d.ts +1 -1
  16. package/types/login/VuuLogo.d.ts +1 -1
  17. package/types/persistence-manager/PersistenceProvider.d.ts +2 -3
  18. package/types/persistence-manager/index.d.ts +1 -3
  19. package/types/shell-layout-templates/context-panel/ApplicationSettingsContextPanel.d.ts +1 -1
  20. package/types/shell-layout-templates/context-panel/ContextPanel.d.ts +1 -1
  21. package/types/shell-layout-templates/side-panel/SidePanel.d.ts +1 -1
  22. package/types/shell.d.ts +2 -2
  23. package/types/theme-switch/ThemeSwitch.d.ts +1 -1
  24. package/types/workspace-management/LayoutList.d.ts +1 -1
  25. package/types/workspace-management/LayoutTile.d.ts +1 -1
  26. package/types/workspace-management/SaveLayoutPanel.d.ts +1 -1
  27. package/types/workspace-management/WorkspaceHost.d.ts +1 -1
  28. package/types/workspace-management/WorkspaceProvider.d.ts +50 -61
  29. package/types/workspace-management/WorkspaceStartPanel.d.ts +1 -1
  30. package/types/workspace-management/index.d.ts +5 -8
  31. package/types/workspace-management/shell-workspace-components.d.ts +1 -1
  32. package/src/persistence-manager/LocalWorkspacePersistenceService.js +0 -129
  33. package/src/persistence-manager/RemoteWorkspacePersistenceService.js +0 -112
  34. package/src/persistence-manager/WorkspacePersistenceService.js +0 -9
  35. package/src/workspace-management/WorkspaceController.js +0 -73
  36. package/src/workspace-management/WorkspaceHost.js +0 -194
  37. package/src/workspace-management/WorkspacePersistentState.js +0 -67
  38. package/src/workspace-management/WorkspaceProvider.js +0 -730
  39. package/src/workspace-management/WorkspaceRepository.js +0 -127
  40. package/src/workspace-management/WorkspaceStartPanel.css.js +0 -44
  41. package/src/workspace-management/WorkspaceStartPanel.js +0 -34
  42. package/src/workspace-management/workspace-component-registry.js +0 -30
  43. package/src/workspace-management/workspace-schemas.js +0 -257
@@ -1,730 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { GRID_LAYOUT_DOCUMENT_KIND, GRID_LAYOUT_DOCUMENT_VERSION, createSequentialGridLayoutIdAllocator, remapGridLayoutDocumentIds } from "@vuu-ui/grid-layout";
3
- import { useNotifications } from "@vuu-ui/vuu-notifications";
4
- import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
5
- import { useOptionalWorkspacePersistenceService } from "../persistence-manager/index.js";
6
- import { WorkspaceController } from "./WorkspaceController.js";
7
- import { APPLICATION_SESSION_VERSION, NAMED_WORKSPACE_DEFINITION_VERSION, WORKSPACE_SNAPSHOT_VERSION, cloneWorkspaceValue } from "./workspace-schemas.js";
8
- const emptyLayout = {
9
- components: [],
10
- kind: GRID_LAYOUT_DOCUMENT_KIND,
11
- layout: {
12
- columns: [
13
- "1fr"
14
- ],
15
- id: "empty-workspace",
16
- items: [
17
- {
18
- column: {
19
- span: 1,
20
- start: 1
21
- },
22
- id: "empty-workspace-placeholder",
23
- row: {
24
- span: 1,
25
- start: 1
26
- }
27
- }
28
- ],
29
- placeholderIds: [
30
- "empty-workspace-placeholder"
31
- ],
32
- rows: [
33
- "1fr"
34
- ],
35
- stacks: []
36
- },
37
- version: GRID_LAYOUT_DOCUMENT_VERSION
38
- };
39
- const emptyWorkspaceSnapshot = {
40
- componentState: {},
41
- layout: emptyLayout,
42
- version: WORKSPACE_SNAPSHOT_VERSION
43
- };
44
- const missing = async ()=>{
45
- throw new Error("WorkspaceProvider is not mounted");
46
- };
47
- const WorkspaceContext = /*#__PURE__*/ createContext({
48
- activeWorkspaceInstanceId: null,
49
- closeWorkspace: missing,
50
- createWorkspaceFromSnapshot: async ()=>{
51
- throw new Error("WorkspaceProvider is not mounted");
52
- },
53
- controllers: [],
54
- deleteNamedWorkspace: missing,
55
- getApplicationSetting: ()=>void 0,
56
- getApplicationSettings: ()=>void 0,
57
- namedWorkspaces: [],
58
- openNamedWorkspace: missing,
59
- openSystemWorkspace: missing,
60
- renameWorkspace: missing,
61
- saveActiveWorkspaceAs: missing,
62
- saveApplicationSettings: missing,
63
- selectWorkspace: missing,
64
- setApplicationSetting: missing,
65
- settings: {},
66
- scopeIdentity: "missing",
67
- status: "loading",
68
- systemWorkspaces: [],
69
- resetApplication: missing
70
- });
71
- let nextWorkspaceInstance = 1;
72
- const createInstanceId = ()=>`workspace-${Date.now().toString(36)}-${nextWorkspaceInstance++}`;
73
- const errorFrom = (cause, message)=>cause instanceof Error ? cause : new Error(message);
74
- class StaleWorkspaceScopeError extends Error {
75
- constructor(){
76
- super("Workspace operation belongs to an inactive persistence scope");
77
- this.name = "StaleWorkspaceScopeError";
78
- }
79
- }
80
- const remapSnapshot = (source, instanceId)=>{
81
- const { document, mappings } = remapGridLayoutDocumentIds(source.layout, createSequentialGridLayoutIdAllocator(instanceId));
82
- const componentIds = new Map(mappings.filter(({ kind })=>"component" === kind).map(({ newId, oldId })=>[
83
- oldId,
84
- newId
85
- ]));
86
- return {
87
- version: WORKSPACE_SNAPSHOT_VERSION,
88
- layout: document,
89
- componentState: Object.fromEntries(Object.entries(source.componentState).map(([id, state])=>[
90
- componentIds.get(id) ?? id,
91
- cloneWorkspaceValue(state)
92
- ]))
93
- };
94
- };
95
- const WorkspaceProvider = ({ activeLayoutIndex, children, componentRenderers, defaultWorkspace, layoutJSON, layoutPlaceholderJSON, persistenceService: serviceProp, settingsCodecs, showTabs, systemWorkspaces = [], TabstripProps, userId, workspaceJSON })=>{
96
- const inheritedService = useOptionalWorkspacePersistenceService();
97
- const service = serviceProp ?? inheritedService;
98
- const { showNotification } = useNotifications();
99
- const [controllers, setControllers] = useState([]);
100
- const controllersRef = useRef([]);
101
- const [session, setSession] = useState();
102
- const sessionRef = useRef(void 0);
103
- const [namedWorkspaces, setNamedWorkspaces] = useState([]);
104
- const [loadedScope, setLoadedScope] = useState();
105
- const [loadError, setLoadError] = useState();
106
- const generation = useRef(0);
107
- const operationQueue = useRef(Promise.resolve());
108
- const resetGeneration = useRef(0);
109
- const resetPending = useRef(false);
110
- const scopeKey = service ? `${service.scope.applicationNamespace}/${service.scope.applicationId}/${service.scope.userId}/authenticated:${userId ?? service.scope.userId}` : "missing";
111
- const scopeIsValid = void 0 !== service && (!userId || service.scope.userId === userId);
112
- const activeScope = useRef({
113
- generation: 0,
114
- scopeKey,
115
- service
116
- });
117
- if (activeScope.current.scopeKey !== scopeKey || activeScope.current.service !== service) activeScope.current = {
118
- generation: ++generation.current,
119
- scopeKey,
120
- service
121
- };
122
- const status = loadedScope !== scopeKey ? "loading" : loadError ? "error" : 0 === controllers.length ? "empty" : "ready";
123
- const notifyError = useCallback((header, cause)=>{
124
- const error = errorFrom(cause, header);
125
- showNotification({
126
- content: error.message,
127
- header,
128
- status: "error",
129
- type: "toast"
130
- });
131
- return error;
132
- }, [
133
- showNotification
134
- ]);
135
- const captureScope = useCallback((expectedService = service, expectedScopeKey = scopeKey)=>{
136
- const active = activeScope.current;
137
- if (!expectedService || active.service !== expectedService || active.scopeKey !== expectedScopeKey || active.generation !== generation.current) throw new StaleWorkspaceScopeError();
138
- return {
139
- generation: active.generation,
140
- scopeKey: active.scopeKey,
141
- service: expectedService
142
- };
143
- }, [
144
- scopeKey,
145
- service
146
- ]);
147
- const assertScope = useCallback((guard)=>{
148
- const active = activeScope.current;
149
- if (generation.current !== guard.generation || active.generation !== guard.generation || active.scopeKey !== guard.scopeKey || active.service !== guard.service) throw new StaleWorkspaceScopeError();
150
- }, []);
151
- const replaceControllers = useCallback((next)=>{
152
- controllersRef.current = next;
153
- setControllers(next);
154
- }, []);
155
- useEffect(()=>{
156
- for (const controller of controllersRef.current)controller.dispose();
157
- controllersRef.current = [];
158
- setControllers([]);
159
- setSession(void 0);
160
- sessionRef.current = void 0;
161
- setNamedWorkspaces([]);
162
- setLoadError(void 0);
163
- setLoadedScope(void 0);
164
- operationQueue.current = Promise.resolve();
165
- resetGeneration.current = 0;
166
- resetPending.current = false;
167
- if (!service) {
168
- const error = notifyError("Workspace Persistence Is Not Configured", new Error("Shell requires an injected WorkspacePersistenceService"));
169
- setLoadError(error);
170
- setLoadedScope(scopeKey);
171
- return;
172
- }
173
- const legacyProps = [
174
- void 0 === activeLayoutIndex ? void 0 : "activeLayoutIndex",
175
- void 0 === layoutJSON ? void 0 : "layoutJSON",
176
- void 0 === layoutPlaceholderJSON ? void 0 : "layoutPlaceholderJSON",
177
- void 0 === showTabs ? void 0 : "showTabs",
178
- void 0 === TabstripProps ? void 0 : "TabstripProps",
179
- void 0 === workspaceJSON ? void 0 : "workspaceJSON"
180
- ].filter((name)=>void 0 !== name);
181
- if (legacyProps.length > 0) {
182
- const error = notifyError("Legacy Workspace Configuration Is Not Supported", new Error(`WorkspaceProvider no longer accepts ${legacyProps.join(", ")}; inject a WorkspacePersistenceService and provide defaultWorkspace as a versioned snapshot`));
183
- setLoadError(error);
184
- setLoadedScope(scopeKey);
185
- return;
186
- }
187
- if (!scopeIsValid) {
188
- const error = notifyError("Workspace Scope Does Not Match", new Error(`Workspace persistence scope "${service.scope.userId}" does not match authenticated user "${userId}"`));
189
- setLoadError(error);
190
- setLoadedScope(scopeKey);
191
- return;
192
- }
193
- const guard = captureScope();
194
- const load = async ()=>{
195
- const restored = [];
196
- try {
197
- assertScope(guard);
198
- const [loadedSession, metadata] = await Promise.all([
199
- guard.service.loadApplicationSession(),
200
- guard.service.listNamedWorkspaceMetadata()
201
- ]);
202
- assertScope(guard);
203
- if (loadedSession) {
204
- const descriptors = new Map(loadedSession.openWorkspaces.map((entry)=>[
205
- entry.instanceId,
206
- entry
207
- ]));
208
- for (const instanceId of loadedSession.workspaceOrder){
209
- const descriptor = descriptors.get(instanceId);
210
- if (!descriptor) throw new Error(`Session references missing workspace "${instanceId}"`);
211
- assertScope(guard);
212
- const record = await guard.service.loadWorkspaceSnapshot(descriptor.snapshotId);
213
- assertScope(guard);
214
- if (!record) throw new Error(`Snapshot for workspace "${instanceId}" was not found`);
215
- const definitionMetadata = metadata.find(({ id })=>id === descriptor.definitionId);
216
- restored.push(new WorkspaceController({
217
- definitionId: descriptor.definitionId,
218
- instanceId,
219
- name: descriptor.title ?? definitionMetadata?.name ?? instanceId,
220
- onError: (error)=>{
221
- try {
222
- assertScope(guard);
223
- notifyError("Failed to Save Workspace", error);
224
- } catch {}
225
- },
226
- renderers: componentRenderers ?? (()=>{
227
- throw new Error("Workspace component renderers are required");
228
- })(),
229
- service: guard.service,
230
- revision: record.revision,
231
- settingsCodecs: settingsCodecs ?? (()=>{
232
- throw new Error("Workspace settings codecs are required");
233
- })(),
234
- snapshot: record.snapshot
235
- }));
236
- }
237
- }
238
- let resolvedSession = loadedSession ?? {
239
- activeWorkspaceInstanceId: null,
240
- openWorkspaces: [],
241
- settings: {},
242
- version: APPLICATION_SESSION_VERSION,
243
- workspaceOrder: []
244
- };
245
- if (!loadedSession && defaultWorkspace) {
246
- if (!componentRenderers || !settingsCodecs) throw new Error("Default workspace requires component renderers and settings codecs");
247
- const instanceId = createInstanceId();
248
- const initialSnapshot = remapSnapshot(defaultWorkspace, instanceId);
249
- const controller = new WorkspaceController({
250
- instanceId,
251
- name: "Workspace",
252
- onError: (error)=>{
253
- try {
254
- assertScope(guard);
255
- notifyError("Failed to Save Workspace", error);
256
- } catch {}
257
- },
258
- renderers: componentRenderers,
259
- service: guard.service,
260
- settingsCodecs,
261
- snapshot: initialSnapshot
262
- });
263
- assertScope(guard);
264
- await controller.repository.save(initialSnapshot);
265
- assertScope(guard);
266
- restored.push(controller);
267
- resolvedSession = {
268
- activeWorkspaceInstanceId: instanceId,
269
- openWorkspaces: [
270
- {
271
- instanceId,
272
- snapshotId: instanceId,
273
- title: "Workspace"
274
- }
275
- ],
276
- settings: {},
277
- version: APPLICATION_SESSION_VERSION,
278
- workspaceOrder: [
279
- instanceId
280
- ]
281
- };
282
- await guard.service.saveApplicationSession(resolvedSession);
283
- assertScope(guard);
284
- }
285
- assertScope(guard);
286
- replaceControllers(restored);
287
- sessionRef.current = resolvedSession;
288
- setSession(resolvedSession);
289
- setNamedWorkspaces(metadata);
290
- setLoadedScope(scopeKey);
291
- } catch (cause) {
292
- restored.forEach((controller)=>{
293
- controller.dispose();
294
- });
295
- if (!(cause instanceof StaleWorkspaceScopeError)) try {
296
- assertScope(guard);
297
- const error = notifyError("Failed to Load Workspaces", cause);
298
- setLoadError(error);
299
- setLoadedScope(scopeKey);
300
- } catch {}
301
- }
302
- };
303
- load();
304
- return ()=>{
305
- if (activeScope.current.generation === guard.generation) activeScope.current = {
306
- ...activeScope.current,
307
- generation: ++generation.current
308
- };
309
- for (const controller of controllersRef.current)controller.dispose();
310
- controllersRef.current = [];
311
- };
312
- }, [
313
- componentRenderers,
314
- activeLayoutIndex,
315
- assertScope,
316
- captureScope,
317
- defaultWorkspace,
318
- layoutJSON,
319
- layoutPlaceholderJSON,
320
- notifyError,
321
- replaceControllers,
322
- scopeIsValid,
323
- scopeKey,
324
- service,
325
- settingsCodecs,
326
- showTabs,
327
- TabstripProps,
328
- userId,
329
- workspaceJSON
330
- ]);
331
- const writeSession = useCallback(async (next, guard)=>{
332
- assertScope(guard);
333
- await guard.service.saveApplicationSession(next);
334
- assertScope(guard);
335
- sessionRef.current = next;
336
- setSession(next);
337
- }, [
338
- assertScope
339
- ]);
340
- const enqueueOperation = useCallback((guard, operation)=>{
341
- assertScope(guard);
342
- if (resetPending.current) return Promise.reject(new Error("Workspace operation was blocked by application reset"));
343
- const expectedResetGeneration = resetGeneration.current;
344
- const result = operationQueue.current.catch(()=>void 0).then(async ()=>{
345
- assertScope(guard);
346
- if (resetGeneration.current !== expectedResetGeneration) throw new Error("Workspace operation was invalidated by application reset");
347
- return operation();
348
- });
349
- operationQueue.current = result.then(()=>void 0, ()=>void 0);
350
- return result;
351
- }, [
352
- assertScope
353
- ]);
354
- const addWorkspace = useCallback(async (sourceSnapshot, name, definitionId, operationGuard)=>{
355
- if (!service || !componentRenderers || !settingsCodecs) throw new Error("Workspace runtime is not configured");
356
- const guard = operationGuard ?? captureScope();
357
- return enqueueOperation(guard, async ()=>{
358
- assertScope(guard);
359
- const instanceId = createInstanceId();
360
- const snapshot = remapSnapshot(sourceSnapshot, instanceId);
361
- const controller = new WorkspaceController({
362
- definitionId,
363
- instanceId,
364
- name,
365
- onError: (error)=>void notifyError("Failed to Save Workspace", error),
366
- renderers: componentRenderers,
367
- service: guard.service,
368
- settingsCodecs,
369
- snapshot
370
- });
371
- await controller.repository.save(snapshot);
372
- assertScope(guard);
373
- const current = sessionRef.current ?? {
374
- activeWorkspaceInstanceId: null,
375
- openWorkspaces: [],
376
- settings: {},
377
- version: APPLICATION_SESSION_VERSION,
378
- workspaceOrder: []
379
- };
380
- try {
381
- await writeSession({
382
- ...current,
383
- activeWorkspaceInstanceId: instanceId,
384
- openWorkspaces: [
385
- ...current.openWorkspaces,
386
- {
387
- ...void 0 === definitionId ? {} : {
388
- definitionId
389
- },
390
- instanceId,
391
- snapshotId: instanceId,
392
- title: name
393
- }
394
- ],
395
- workspaceOrder: [
396
- ...current.workspaceOrder,
397
- instanceId
398
- ]
399
- }, guard);
400
- assertScope(guard);
401
- replaceControllers([
402
- ...controllersRef.current,
403
- controller
404
- ]);
405
- } catch (cause) {
406
- await controller.dispose();
407
- assertScope(guard);
408
- await guard.service.deleteWorkspaceSnapshot(instanceId);
409
- assertScope(guard);
410
- throw errorFrom(cause, "Failed to save application session");
411
- }
412
- return instanceId;
413
- });
414
- }, [
415
- componentRenderers,
416
- assertScope,
417
- captureScope,
418
- enqueueOperation,
419
- notifyError,
420
- replaceControllers,
421
- service,
422
- settingsCodecs,
423
- writeSession
424
- ]);
425
- const addDefinition = useCallback((definition, operationGuard)=>addWorkspace(definition.snapshot, definition.name, definition.id, operationGuard), [
426
- addWorkspace
427
- ]);
428
- const openSystemWorkspace = useCallback(async (definition)=>{
429
- await addDefinition(definition);
430
- }, [
431
- addDefinition
432
- ]);
433
- const createWorkspaceFromSnapshot = useCallback(async (snapshot, title = "Untitled")=>{
434
- try {
435
- return await addWorkspace(snapshot, title);
436
- } catch (cause) {
437
- if (!(cause instanceof StaleWorkspaceScopeError)) notifyError("Failed to Create Workspace", cause);
438
- throw cause;
439
- }
440
- }, [
441
- addWorkspace,
442
- notifyError
443
- ]);
444
- const openNamedWorkspace = useCallback(async (definitionId)=>{
445
- if (!service) throw new Error("Workspace persistence service is not configured");
446
- const guard = captureScope();
447
- const expectedResetGeneration = resetGeneration.current;
448
- try {
449
- assertScope(guard);
450
- const definition = await guard.service.loadNamedWorkspaceDefinition(definitionId);
451
- assertScope(guard);
452
- if (resetGeneration.current !== expectedResetGeneration) throw new Error("Workspace operation was invalidated by application reset");
453
- if (!definition) throw new Error(`Workspace definition "${definitionId}" was not found`);
454
- await addDefinition(definition, guard);
455
- } catch (cause) {
456
- if (!(cause instanceof StaleWorkspaceScopeError)) notifyError("Failed to Open Workspace", cause);
457
- }
458
- }, [
459
- addDefinition,
460
- assertScope,
461
- captureScope,
462
- notifyError,
463
- service
464
- ]);
465
- const selectWorkspace = useCallback(async (instanceId)=>{
466
- const guard = captureScope();
467
- await enqueueOperation(guard, async ()=>{
468
- const current = sessionRef.current;
469
- if (!current || !current.workspaceOrder.includes(instanceId)) throw new Error(`Workspace "${instanceId}" is not open`);
470
- await writeSession({
471
- ...current,
472
- activeWorkspaceInstanceId: instanceId
473
- }, guard);
474
- });
475
- }, [
476
- captureScope,
477
- enqueueOperation,
478
- writeSession
479
- ]);
480
- const renameWorkspace = useCallback(async (instanceId, title)=>{
481
- const trimmedTitle = title.trim();
482
- if (!trimmedTitle) throw new Error("Workspace title must not be empty");
483
- const guard = captureScope();
484
- try {
485
- await enqueueOperation(guard, async ()=>{
486
- const current = sessionRef.current;
487
- const controller = controllersRef.current.find((candidate)=>candidate.instanceId === instanceId);
488
- if (!current || !controller) throw new Error(`Workspace "${instanceId}" is not open`);
489
- await writeSession({
490
- ...current,
491
- openWorkspaces: current.openWorkspaces.map((workspace)=>workspace.instanceId === instanceId ? {
492
- ...workspace,
493
- title: trimmedTitle
494
- } : workspace)
495
- }, guard);
496
- assertScope(guard);
497
- controller.rename(trimmedTitle);
498
- replaceControllers([
499
- ...controllersRef.current
500
- ]);
501
- });
502
- } catch (cause) {
503
- if (!(cause instanceof StaleWorkspaceScopeError)) notifyError("Failed to Rename Workspace", cause);
504
- throw cause;
505
- }
506
- }, [
507
- assertScope,
508
- captureScope,
509
- enqueueOperation,
510
- notifyError,
511
- replaceControllers,
512
- writeSession
513
- ]);
514
- const closeWorkspace = useCallback(async (instanceId)=>{
515
- if (!service) throw new Error("Workspace persistence service is not configured");
516
- const guard = captureScope();
517
- await enqueueOperation(guard, async ()=>{
518
- const current = sessionRef.current;
519
- const controller = controllersRef.current.find((candidate)=>candidate.instanceId === instanceId);
520
- if (!current || !controller) return;
521
- const nextControllers = controllersRef.current.filter((candidate)=>candidate !== controller);
522
- const closedIndex = current.workspaceOrder.indexOf(instanceId);
523
- const order = current.workspaceOrder.filter((id)=>id !== instanceId);
524
- const active = current.activeWorkspaceInstanceId === instanceId ? order[Math.min(closedIndex, order.length - 1)] ?? null : current.activeWorkspaceInstanceId;
525
- await writeSession({
526
- ...current,
527
- activeWorkspaceInstanceId: active,
528
- openWorkspaces: current.openWorkspaces.filter(({ instanceId: id })=>id !== instanceId),
529
- workspaceOrder: order
530
- }, guard);
531
- assertScope(guard);
532
- await controller.dispose();
533
- replaceControllers(nextControllers);
534
- await guard.service.deleteWorkspaceSnapshot(instanceId);
535
- assertScope(guard);
536
- });
537
- }, [
538
- assertScope,
539
- captureScope,
540
- enqueueOperation,
541
- replaceControllers,
542
- service,
543
- writeSession
544
- ]);
545
- const deleteNamedWorkspace = useCallback(async (definitionId)=>{
546
- if (!service) throw new Error("Workspace persistence service is not configured");
547
- const guard = captureScope();
548
- assertScope(guard);
549
- await guard.service.deleteNamedWorkspaceDefinition(definitionId);
550
- assertScope(guard);
551
- setNamedWorkspaces((current)=>current.filter(({ id })=>id !== definitionId));
552
- }, [
553
- assertScope,
554
- captureScope,
555
- service
556
- ]);
557
- const saveActiveWorkspaceAs = useCallback(async (name, description)=>{
558
- if (!service) throw new Error("Workspace persistence service is not configured");
559
- const guard = captureScope();
560
- assertScope(guard);
561
- const activeId = sessionRef.current?.activeWorkspaceInstanceId;
562
- const active = controllersRef.current.find(({ instanceId })=>instanceId === activeId);
563
- if (!active) throw new Error("There is no active workspace to save");
564
- const now = new Date().toISOString();
565
- const id = createInstanceId();
566
- const definition = {
567
- createdAt: now,
568
- ...void 0 === description ? {} : {
569
- description
570
- },
571
- id,
572
- name,
573
- snapshot: active.snapshot,
574
- updatedAt: now,
575
- version: NAMED_WORKSPACE_DEFINITION_VERSION
576
- };
577
- await guard.service.saveNamedWorkspaceDefinition(definition);
578
- assertScope(guard);
579
- setNamedWorkspaces((current)=>[
580
- ...current,
581
- {
582
- createdAt: now,
583
- ...void 0 === description ? {} : {
584
- description
585
- },
586
- id,
587
- name,
588
- updatedAt: now,
589
- version: NAMED_WORKSPACE_DEFINITION_VERSION
590
- }
591
- ]);
592
- }, [
593
- assertScope,
594
- captureScope,
595
- service
596
- ]);
597
- const setApplicationSetting = useCallback(async (key, value)=>{
598
- const guard = captureScope();
599
- await enqueueOperation(guard, async ()=>{
600
- const current = sessionRef.current;
601
- if (!current) throw new Error("Application session has not loaded");
602
- await writeSession({
603
- ...current,
604
- settings: {
605
- ...current.settings,
606
- [key]: value
607
- }
608
- }, guard);
609
- });
610
- }, [
611
- captureScope,
612
- enqueueOperation,
613
- writeSession
614
- ]);
615
- const saveApplicationSettings = useCallback(async (value, key)=>{
616
- if (void 0 !== key) return void await setApplicationSetting(key, value);
617
- if ("object" != typeof value || null === value || Array.isArray(value)) throw new Error("Application settings must be a JSON object");
618
- const guard = captureScope();
619
- await enqueueOperation(guard, async ()=>{
620
- const current = sessionRef.current;
621
- if (!current) throw new Error("Application session has not loaded");
622
- await writeSession({
623
- ...current,
624
- settings: value
625
- }, guard);
626
- });
627
- }, [
628
- captureScope,
629
- enqueueOperation,
630
- setApplicationSetting,
631
- writeSession
632
- ]);
633
- const resetApplication = useCallback(async ()=>{
634
- const guard = captureScope();
635
- assertScope(guard);
636
- if (resetPending.current) throw new Error("Application reset is already in progress");
637
- resetPending.current = true;
638
- resetGeneration.current += 1;
639
- const reset = operationQueue.current.catch(()=>void 0).then(async ()=>{
640
- assertScope(guard);
641
- const instanceIds = sessionRef.current?.openWorkspaces.map(({ snapshotId })=>snapshotId) ?? [];
642
- await guard.service.deleteApplicationSession();
643
- assertScope(guard);
644
- await Promise.all(controllersRef.current.map((controller)=>controller.dispose()));
645
- replaceControllers([]);
646
- const emptySession = {
647
- activeWorkspaceInstanceId: null,
648
- openWorkspaces: [],
649
- settings: {},
650
- version: APPLICATION_SESSION_VERSION,
651
- workspaceOrder: []
652
- };
653
- sessionRef.current = emptySession;
654
- setSession(emptySession);
655
- for (const snapshotId of instanceIds){
656
- assertScope(guard);
657
- await guard.service.deleteWorkspaceSnapshot(snapshotId);
658
- assertScope(guard);
659
- }
660
- });
661
- operationQueue.current = reset.then(()=>void 0, ()=>void 0);
662
- try {
663
- await reset;
664
- } finally{
665
- if (activeScope.current.generation === guard.generation && activeScope.current.scopeKey === guard.scopeKey) resetPending.current = false;
666
- }
667
- }, [
668
- assertScope,
669
- captureScope,
670
- replaceControllers
671
- ]);
672
- const value = useMemo(()=>{
673
- const settings = session?.settings ?? {};
674
- const visibleControllers = "loading" === status ? [] : controllers;
675
- const activeWorkspace = visibleControllers.find(({ instanceId })=>instanceId === session?.activeWorkspaceInstanceId);
676
- return {
677
- activeWorkspace,
678
- activeWorkspaceInstanceId: session?.activeWorkspaceInstanceId ?? null,
679
- closeWorkspace,
680
- componentRenderers,
681
- controllers: visibleControllers,
682
- createWorkspaceFromSnapshot,
683
- deleteNamedWorkspace,
684
- error: loadError,
685
- getApplicationSetting: (key)=>settings[key],
686
- getApplicationSettings: (key)=>void 0 === key ? settings : settings[key],
687
- namedWorkspaces,
688
- openNamedWorkspace,
689
- openSystemWorkspace,
690
- renameWorkspace,
691
- saveActiveWorkspaceAs,
692
- saveApplicationSettings,
693
- selectWorkspace,
694
- setApplicationSetting,
695
- settings,
696
- settingsCodecs,
697
- scopeIdentity: scopeKey,
698
- status,
699
- systemWorkspaces,
700
- resetApplication
701
- };
702
- }, [
703
- closeWorkspace,
704
- componentRenderers,
705
- controllers,
706
- createWorkspaceFromSnapshot,
707
- deleteNamedWorkspace,
708
- loadError,
709
- namedWorkspaces,
710
- openNamedWorkspace,
711
- openSystemWorkspace,
712
- renameWorkspace,
713
- saveActiveWorkspaceAs,
714
- saveApplicationSettings,
715
- selectWorkspace,
716
- session,
717
- setApplicationSetting,
718
- settingsCodecs,
719
- scopeKey,
720
- status,
721
- systemWorkspaces,
722
- resetApplication
723
- ]);
724
- return /*#__PURE__*/ jsx(WorkspaceContext.Provider, {
725
- value: value,
726
- children: children
727
- });
728
- };
729
- const useWorkspace = ()=>useContext(WorkspaceContext);
730
- export { StaleWorkspaceScopeError, WorkspaceProvider, emptyWorkspaceSnapshot, useWorkspace };