@vuu-ui/vuu-shell 4.0.0-alpha.9 → 4.0.0-beta.1

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 (143) hide show
  1. package/package.json +19 -20
  2. package/src/app-header/AppHeader.css.js +1 -1
  3. package/src/app-header/AppHeader.js +26 -36
  4. package/src/application-provider/ApplicationContext.js +5 -2
  5. package/src/application-provider/ApplicationProvider.js +59 -8
  6. package/src/feature/Feature.js +34 -3
  7. package/src/feature/FeatureErrorBoundary.js +33 -0
  8. package/src/feature/index.js +0 -1
  9. package/src/feature-list/FeatureList.css.js +5 -27
  10. package/src/feature-list/FeatureList.js +96 -117
  11. package/src/index.js +3 -3
  12. package/src/left-nav/LeftNav.css.js +4 -2
  13. package/src/left-nav/LeftNav.js +100 -139
  14. package/src/persistence-manager/PersistenceProvider.js +3 -10
  15. package/src/persistence-manager/index.js +0 -3
  16. package/src/shell-layout-templates/context-panel/ContextPanel.css.js +8 -8
  17. package/src/shell-layout-templates/context-panel/ContextPanel.js +35 -17
  18. package/src/shell-layout-templates/context-panel/index.js +0 -1
  19. package/src/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.js +40 -54
  20. package/src/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.js +64 -54
  21. package/src/shell-layout-templates/left-main-tabs/useLeftMainTabs.js +28 -49
  22. package/src/shell-layout-templates/side-panel/SidePanel.css.js +1 -3
  23. package/src/shell-layout-templates/simple-content-pane/useSimpleContentPane.js +35 -43
  24. package/src/shell.css.js +3 -86
  25. package/src/shell.js +70 -148
  26. package/src/user-settings/SettingsForm.css.js +10 -0
  27. package/src/user-settings/SettingsForm.js +141 -0
  28. package/src/user-settings/UserSettingsPanel.css.js +9 -0
  29. package/src/user-settings/UserSettingsPanel.js +29 -0
  30. package/src/user-settings/index.js +2 -0
  31. package/src/workspace-management/LayoutList.js +99 -59
  32. package/src/workspace-management/SaveLayoutPanel.js +3 -3
  33. package/src/workspace-management/WorkspaceProvider.js +178 -699
  34. package/src/workspace-management/index.js +0 -8
  35. package/src/workspace-management/useWorkspaceContextMenuItems.js +6 -12
  36. package/types/app-header/AppHeader.d.ts +2 -2
  37. package/types/application-provider/ApplicationContext.d.ts +6 -1
  38. package/types/application-provider/ApplicationProvider.d.ts +14 -5
  39. package/types/feature/Feature.d.ts +10 -2
  40. package/types/feature/FeatureErrorBoundary.d.ts +1 -1
  41. package/types/feature/Loader.d.ts +1 -1
  42. package/types/feature/index.d.ts +1 -2
  43. package/types/feature-list/FeatureList.d.ts +4 -4
  44. package/types/index.d.ts +3 -3
  45. package/types/left-nav/LeftNav.d.ts +1 -2
  46. package/types/login/LoginPanel.d.ts +1 -1
  47. package/types/login/VuuLogo.d.ts +1 -1
  48. package/types/persistence-manager/PersistenceProvider.d.ts +3 -7
  49. package/types/persistence-manager/index.d.ts +0 -3
  50. package/types/shell-layout-templates/context-panel/ContextPanel.d.ts +11 -9
  51. package/types/shell-layout-templates/context-panel/index.d.ts +0 -1
  52. package/types/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.d.ts +1 -1
  53. package/types/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.d.ts +1 -1
  54. package/types/shell-layout-templates/left-main-tabs/useLeftMainTabs.d.ts +1 -1
  55. package/types/shell-layout-templates/side-panel/SidePanel.d.ts +1 -1
  56. package/types/shell-layout-templates/simple-content-pane/useSimpleContentPane.d.ts +1 -1
  57. package/types/shell-layout-templates/useShellLayout.d.ts +2 -4
  58. package/types/shell.d.ts +15 -19
  59. package/types/theme-switch/ThemeSwitch.d.ts +1 -1
  60. package/types/workspace-management/LayoutList.d.ts +2 -2
  61. package/types/workspace-management/LayoutTile.d.ts +1 -1
  62. package/types/workspace-management/SaveLayoutPanel.d.ts +2 -2
  63. package/types/workspace-management/WorkspaceProvider.d.ts +50 -61
  64. package/types/workspace-management/defaultWorkspaceJSON.d.ts +6 -7
  65. package/types/workspace-management/index.d.ts +0 -8
  66. package/types/workspace-management/useWorkspaceContextMenuItems.d.ts +1 -1
  67. package/src/module-registry/getRegisteredModules.js +0 -12
  68. package/src/persistence-manager/LocalWorkspacePersistenceService.js +0 -129
  69. package/src/persistence-manager/RemoteWorkspacePersistenceService.js +0 -112
  70. package/src/persistence-manager/WorkspacePersistenceService.js +0 -9
  71. package/src/shell-layout-templates/context-panel/ApplicationSettingsContextPanel.js +0 -24
  72. package/src/workspace-management/WorkspaceController.js +0 -73
  73. package/src/workspace-management/WorkspaceHost.js +0 -194
  74. package/src/workspace-management/WorkspacePersistentState.js +0 -67
  75. package/src/workspace-management/WorkspaceRepository.js +0 -127
  76. package/src/workspace-management/WorkspaceStartPanel.css.js +0 -44
  77. package/src/workspace-management/WorkspaceStartPanel.js +0 -34
  78. package/src/workspace-management/shell-workspace-components.js +0 -59
  79. package/src/workspace-management/workspace-component-registry.js +0 -30
  80. package/src/workspace-management/workspace-schemas.js +0 -257
  81. package/types/module-registry/getRegisteredModules.d.ts +0 -5
  82. package/types/persistence-manager/LocalWorkspacePersistenceService.d.ts +0 -18
  83. package/types/persistence-manager/RemoteWorkspacePersistenceService.d.ts +0 -25
  84. package/types/persistence-manager/WorkspacePersistenceService.d.ts +0 -27
  85. package/types/shell-layout-templates/context-panel/ApplicationSettingsContextPanel.d.ts +0 -1
  86. package/types/src/ShellContextProvider.d.ts +0 -7
  87. package/types/src/app-header/AppHeader.d.ts +0 -6
  88. package/types/src/app-header/index.d.ts +0 -1
  89. package/types/src/application-provider/ApplicationContext.d.ts +0 -11
  90. package/types/src/application-provider/ApplicationProvider.d.ts +0 -17
  91. package/types/src/application-provider/index.d.ts +0 -1
  92. package/types/src/feature/Feature.d.ts +0 -10
  93. package/types/src/feature/FeatureErrorBoundary.d.ts +0 -13
  94. package/types/src/feature/Loader.d.ts +0 -1
  95. package/types/src/feature/index.d.ts +0 -1
  96. package/types/src/feature-and-layout-provider/FeatureAndLayoutProvider.d.ts +0 -19
  97. package/types/src/feature-and-layout-provider/index.d.ts +0 -1
  98. package/types/src/feature-list/FeatureList.d.ts +0 -7
  99. package/types/src/feature-list/index.d.ts +0 -1
  100. package/types/src/get-layout-history.d.ts +0 -8
  101. package/types/src/index.d.ts +0 -14
  102. package/types/src/left-nav/LeftNav.d.ts +0 -14
  103. package/types/src/left-nav/index.d.ts +0 -1
  104. package/types/src/login/LoginPanel.d.ts +0 -6
  105. package/types/src/login/VuuLogo.d.ts +0 -1
  106. package/types/src/persistence-manager/LocalPersistenceManager.d.ts +0 -20
  107. package/types/src/persistence-manager/PersistenceManager.d.ts +0 -71
  108. package/types/src/persistence-manager/PersistenceProvider.d.ts +0 -11
  109. package/types/src/persistence-manager/RemotePersistenceManager.d.ts +0 -25
  110. package/types/src/persistence-manager/StaticPersistenceManager.d.ts +0 -25
  111. package/types/src/persistence-manager/index.d.ts +0 -5
  112. package/types/src/shell-layout-templates/context-panel/ContextPanel.d.ts +0 -13
  113. package/types/src/shell-layout-templates/context-panel/index.d.ts +0 -1
  114. package/types/src/shell-layout-templates/full-height-left-panel/useFullHeightLeftPanel.d.ts +0 -2
  115. package/types/src/shell-layout-templates/index.d.ts +0 -3
  116. package/types/src/shell-layout-templates/inlay-left-panel/useInlayLeftPanel.d.ts +0 -2
  117. package/types/src/shell-layout-templates/left-main-tabs/useLeftMainTabs.d.ts +0 -2
  118. package/types/src/shell-layout-templates/side-panel/SidePanel.d.ts +0 -8
  119. package/types/src/shell-layout-templates/side-panel/index.d.ts +0 -1
  120. package/types/src/shell-layout-templates/simple-content-pane/useSimpleContentPane.d.ts +0 -2
  121. package/types/src/shell-layout-templates/useShellLayout.d.ts +0 -51
  122. package/types/src/shell.d.ts +0 -17
  123. package/types/src/theme-switch/ThemeSwitch.d.ts +0 -8
  124. package/types/src/theme-switch/index.d.ts +0 -1
  125. package/types/src/user-settings/SettingsForm.d.ts +0 -46
  126. package/types/src/user-settings/UserSettingsPanel.d.ts +0 -3
  127. package/types/src/user-settings/index.d.ts +0 -2
  128. package/types/src/workspace-management/LayoutList.d.ts +0 -2
  129. package/types/src/workspace-management/LayoutTile.d.ts +0 -7
  130. package/types/src/workspace-management/SaveLayoutPanel.d.ts +0 -9
  131. package/types/src/workspace-management/WorkspaceProvider.d.ts +0 -52
  132. package/types/src/workspace-management/defaultWorkspaceJSON.d.ts +0 -10
  133. package/types/src/workspace-management/index.d.ts +0 -5
  134. package/types/src/workspace-management/screenshot-utils.d.ts +0 -6
  135. package/types/src/workspace-management/useWorkspaceContextMenuItems.d.ts +0 -5
  136. package/types/workspace-management/WorkspaceController.d.ts +0 -32
  137. package/types/workspace-management/WorkspaceHost.d.ts +0 -1
  138. package/types/workspace-management/WorkspacePersistentState.d.ts +0 -19
  139. package/types/workspace-management/WorkspaceRepository.d.ts +0 -13
  140. package/types/workspace-management/WorkspaceStartPanel.d.ts +0 -1
  141. package/types/workspace-management/shell-workspace-components.d.ts +0 -2
  142. package/types/workspace-management/workspace-component-registry.d.ts +0 -12
  143. package/types/workspace-management/workspace-schemas.d.ts +0 -69
@@ -1,129 +0,0 @@
1
- import { WorkspaceValidationError, cloneWorkspaceValue, validateApplicationSessionV1, validateNamedWorkspaceDefinitionV1, validateNamedWorkspaceMetadataV1, validateWorkspaceSnapshotRecordV1, workspaceMetadata } from "../workspace-management/workspace-schemas.js";
2
- import { WorkspacePersistenceError } from "./WorkspacePersistenceService.js";
3
- const validateScope = (scope)=>{
4
- for (const [name, value] of Object.entries(scope))if (!value) throw new WorkspacePersistenceError(`Persistence scope ${name} must not be empty`, "construct");
5
- };
6
- const requireId = (id, operation)=>{
7
- if (!id) throw new WorkspacePersistenceError("Entity id must not be empty", operation);
8
- return id;
9
- };
10
- class LocalWorkspacePersistenceService {
11
- scope;
12
- #storage;
13
- #prefix;
14
- constructor(scope, storage = localStorage){
15
- validateScope(scope);
16
- this.scope = Object.freeze({
17
- ...scope
18
- });
19
- this.#storage = storage;
20
- this.#prefix = [
21
- "vuu-workspace",
22
- scope.applicationNamespace,
23
- scope.applicationId,
24
- scope.userId
25
- ].map(encodeURIComponent).join(":");
26
- }
27
- async loadApplicationSession() {
28
- return this.#read("application-session", void 0, validateApplicationSessionV1);
29
- }
30
- async saveApplicationSession(session) {
31
- this.#write("application-session", void 0, validateApplicationSessionV1(session));
32
- }
33
- async deleteApplicationSession() {
34
- this.#remove("application-session");
35
- }
36
- async listWorkspaceSnapshots() {
37
- return this.#ids("snapshot").map((id)=>{
38
- const snapshot = this.#read("snapshot", id, validateWorkspaceSnapshotRecordV1);
39
- if (!snapshot) throw new WorkspacePersistenceError(`Snapshot index references missing workspace "${id}"`, "listWorkspaceSnapshots");
40
- return snapshot;
41
- });
42
- }
43
- async loadWorkspaceSnapshot(workspaceInstanceId) {
44
- return this.#read("snapshot", requireId(workspaceInstanceId, "loadWorkspaceSnapshot"), validateWorkspaceSnapshotRecordV1);
45
- }
46
- async saveWorkspaceSnapshot(snapshot) {
47
- const validated = validateWorkspaceSnapshotRecordV1(snapshot);
48
- this.#write("snapshot", validated.workspaceInstanceId, validated);
49
- this.#addId("snapshot", validated.workspaceInstanceId);
50
- }
51
- async deleteWorkspaceSnapshot(workspaceInstanceId) {
52
- const id = requireId(workspaceInstanceId, "deleteWorkspaceSnapshot");
53
- this.#remove("snapshot", id);
54
- this.#deleteId("snapshot", id);
55
- }
56
- async listNamedWorkspaceMetadata() {
57
- return this.#ids("definition").map((id)=>{
58
- const metadata = this.#read("definition-metadata", id, validateNamedWorkspaceMetadataV1);
59
- if (!metadata) throw new WorkspacePersistenceError(`Definition index references missing metadata "${id}"`, "listNamedWorkspaceMetadata");
60
- return metadata;
61
- });
62
- }
63
- async loadNamedWorkspaceDefinition(id) {
64
- return this.#read("definition", requireId(id, "loadNamedWorkspaceDefinition"), validateNamedWorkspaceDefinitionV1);
65
- }
66
- async saveNamedWorkspaceDefinition(definition) {
67
- const validated = validateNamedWorkspaceDefinitionV1(definition);
68
- this.#write("definition", validated.id, validated);
69
- this.#write("definition-metadata", validated.id, workspaceMetadata(validated));
70
- this.#addId("definition", validated.id);
71
- }
72
- async deleteNamedWorkspaceDefinition(id) {
73
- const validatedId = requireId(id, "deleteNamedWorkspaceDefinition");
74
- this.#remove("definition", validatedId);
75
- this.#remove("definition-metadata", validatedId);
76
- this.#deleteId("definition", validatedId);
77
- }
78
- #key(entity, id) {
79
- return `${this.#prefix}:${encodeURIComponent(entity)}${void 0 === id ? "" : `:${encodeURIComponent(id)}`}`;
80
- }
81
- #read(entity, id, validator) {
82
- const stored = this.#storage.getItem(this.#key(entity, id));
83
- if (null === stored) return;
84
- let parsed;
85
- try {
86
- parsed = JSON.parse(stored);
87
- } catch (error) {
88
- if (!(error instanceof SyntaxError)) throw error;
89
- throw new WorkspacePersistenceError(`Stored ${entity} is not valid JSON: ${error.message}`, `load:${entity}`);
90
- }
91
- try {
92
- return cloneWorkspaceValue(validator(parsed));
93
- } catch (error) {
94
- if (!(error instanceof WorkspaceValidationError)) throw error;
95
- throw new WorkspacePersistenceError(`Stored ${entity} is invalid: ${error.message}`, `load:${entity}`);
96
- }
97
- }
98
- #write(entity, id, value) {
99
- this.#storage.setItem(this.#key(entity, id), JSON.stringify(cloneWorkspaceValue(value)));
100
- }
101
- #remove(entity, id) {
102
- this.#storage.removeItem(this.#key(entity, id));
103
- }
104
- #ids(entity) {
105
- const stored = this.#storage.getItem(this.#key(`${entity}-index`));
106
- if (null === stored) return [];
107
- let parsed;
108
- try {
109
- parsed = JSON.parse(stored);
110
- } catch (error) {
111
- if (!(error instanceof SyntaxError)) throw error;
112
- throw new WorkspacePersistenceError(`Stored ${entity} index is not valid JSON: ${error.message}`, `list:${entity}`);
113
- }
114
- if (!Array.isArray(parsed) || parsed.some((id)=>"string" != typeof id || !id) || new Set(parsed).size !== parsed.length) throw new WorkspacePersistenceError(`Stored ${entity} index is invalid`, `list:${entity}`);
115
- return parsed;
116
- }
117
- #addId(entity, id) {
118
- const ids = this.#ids(entity);
119
- if (!ids.includes(id)) this.#storage.setItem(this.#key(`${entity}-index`), JSON.stringify([
120
- ...ids,
121
- id
122
- ]));
123
- }
124
- #deleteId(entity, id) {
125
- const ids = this.#ids(entity).filter((existing)=>existing !== id);
126
- this.#storage.setItem(this.#key(`${entity}-index`), JSON.stringify(ids));
127
- }
128
- }
129
- export { LocalWorkspacePersistenceService };
@@ -1,112 +0,0 @@
1
- import { cloneWorkspaceValue, validateApplicationSessionV1, validateNamedWorkspaceDefinitionV1, validateNamedWorkspaceMetadataV1, validateWorkspaceSnapshotRecordV1 } from "../workspace-management/workspace-schemas.js";
2
- import { WorkspacePersistenceError } from "./WorkspacePersistenceService.js";
3
- const trimSlash = (value)=>value.replace(/\/+$/, "");
4
- const requireId = (id, operation)=>{
5
- if (!id) throw new WorkspacePersistenceError("Entity id must not be empty", operation);
6
- return encodeURIComponent(id);
7
- };
8
- class RemoteWorkspacePersistenceService {
9
- scope;
10
- #baseUrl;
11
- #requestInit;
12
- #requestInitProvider;
13
- #fetch;
14
- constructor(scope, { baseUrl, requestInit, requestInitProvider, fetch: fetchImplementation = globalThis.fetch }){
15
- if (!scope.applicationNamespace || !scope.applicationId || !scope.userId || !baseUrl) throw new WorkspacePersistenceError("Remote persistence scope and base URL must not be empty", "construct");
16
- this.scope = Object.freeze({
17
- ...scope
18
- });
19
- this.#baseUrl = trimSlash(baseUrl);
20
- this.#requestInit = requestInit;
21
- this.#requestInitProvider = requestInitProvider;
22
- this.#fetch = fetchImplementation;
23
- }
24
- async loadApplicationSession() {
25
- return this.#optional("loadApplicationSession", "application-session", validateApplicationSessionV1);
26
- }
27
- async saveApplicationSession(session) {
28
- await this.#void("saveApplicationSession", "application-session", "PUT", validateApplicationSessionV1(session));
29
- }
30
- async deleteApplicationSession() {
31
- await this.#void("deleteApplicationSession", "application-session", "DELETE");
32
- }
33
- async listWorkspaceSnapshots() {
34
- const value = await this.#json("listWorkspaceSnapshots", "snapshots");
35
- if (!Array.isArray(value)) throw new WorkspacePersistenceError("Remote snapshot list response must be an array", "listWorkspaceSnapshots");
36
- return value.map(validateWorkspaceSnapshotRecordV1);
37
- }
38
- async loadWorkspaceSnapshot(workspaceInstanceId) {
39
- return this.#optional("loadWorkspaceSnapshot", `snapshots/${requireId(workspaceInstanceId, "loadWorkspaceSnapshot")}`, validateWorkspaceSnapshotRecordV1);
40
- }
41
- async saveWorkspaceSnapshot(snapshot) {
42
- const validated = validateWorkspaceSnapshotRecordV1(snapshot);
43
- await this.#void("saveWorkspaceSnapshot", `snapshots/${encodeURIComponent(validated.workspaceInstanceId)}`, "PUT", validated);
44
- }
45
- async deleteWorkspaceSnapshot(workspaceInstanceId) {
46
- await this.#void("deleteWorkspaceSnapshot", `snapshots/${requireId(workspaceInstanceId, "deleteWorkspaceSnapshot")}`, "DELETE");
47
- }
48
- async listNamedWorkspaceMetadata() {
49
- const value = await this.#json("listNamedWorkspaceMetadata", "named-workspaces");
50
- if (!Array.isArray(value)) throw new WorkspacePersistenceError("Remote workspace metadata response must be an array", "listNamedWorkspaceMetadata");
51
- return value.map(validateNamedWorkspaceMetadataV1);
52
- }
53
- async loadNamedWorkspaceDefinition(id) {
54
- return this.#optional("loadNamedWorkspaceDefinition", `named-workspaces/${requireId(id, "loadNamedWorkspaceDefinition")}`, validateNamedWorkspaceDefinitionV1);
55
- }
56
- async saveNamedWorkspaceDefinition(definition) {
57
- const validated = validateNamedWorkspaceDefinitionV1(definition);
58
- await this.#void("saveNamedWorkspaceDefinition", `named-workspaces/${encodeURIComponent(validated.id)}`, "PUT", validated);
59
- }
60
- async deleteNamedWorkspaceDefinition(id) {
61
- await this.#void("deleteNamedWorkspaceDefinition", `named-workspaces/${requireId(id, "deleteNamedWorkspaceDefinition")}`, "DELETE");
62
- }
63
- async #optional(operation, path, validator) {
64
- const response = await this.#request(operation, path, "GET");
65
- if (404 === response.status) return;
66
- await this.#assertOk(response, operation);
67
- return cloneWorkspaceValue(validator(await response.json()));
68
- }
69
- async #json(operation, path) {
70
- const response = await this.#request(operation, path, "GET");
71
- await this.#assertOk(response, operation);
72
- return response.json();
73
- }
74
- async #void(operation, path, method, body) {
75
- const response = await this.#request(operation, path, method, body);
76
- await this.#assertOk(response, operation);
77
- }
78
- async #request(operation, path, method, body) {
79
- const provided = this.#requestInitProvider ? await this.#requestInitProvider() : {};
80
- const configured = this.#requestInit ?? {};
81
- const headers = new Headers(configured.headers);
82
- new Headers(provided.headers).forEach((value, key)=>{
83
- headers.set(key, value);
84
- });
85
- headers.set("Accept", "application/json");
86
- headers.set("X-Vuu-Application-Namespace", this.scope.applicationNamespace);
87
- headers.set("X-Vuu-Application-Id", this.scope.applicationId);
88
- headers.set("X-Vuu-User-Id", this.scope.userId);
89
- if (void 0 !== body) headers.set("Content-Type", "application/json");
90
- const scopePath = [
91
- this.scope.applicationNamespace,
92
- this.scope.applicationId,
93
- this.scope.userId
94
- ].map(encodeURIComponent).join("/");
95
- try {
96
- return await this.#fetch(`${this.#baseUrl}/${scopePath}/${path}`, {
97
- ...configured,
98
- ...provided,
99
- headers,
100
- method,
101
- body: void 0 === body ? void 0 : JSON.stringify(cloneWorkspaceValue(body))
102
- });
103
- } catch (error) {
104
- if (!(error instanceof Error)) throw error;
105
- throw new WorkspacePersistenceError(error.message, operation);
106
- }
107
- }
108
- async #assertOk(response, operation) {
109
- if (!response.ok) throw new WorkspacePersistenceError(response.statusText || `Remote persistence request failed (${response.status})`, operation, response.status);
110
- }
111
- }
112
- export { RemoteWorkspacePersistenceService };
@@ -1,9 +0,0 @@
1
- class WorkspacePersistenceError extends Error {
2
- operation;
3
- status;
4
- constructor(message, operation, status){
5
- super(message), this.operation = operation, this.status = status;
6
- this.name = "WorkspacePersistenceError";
7
- }
8
- }
9
- export { WorkspacePersistenceError };
@@ -1,24 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { useCallback } from "react";
3
- import { useWorkspace } from "../../workspace-management/index.js";
4
- import { ContextPanel } from "./ContextPanel.js";
5
- const ApplicationSettingsContextPanel = ()=>{
6
- const { getApplicationSetting, setApplicationSetting } = useWorkspace();
7
- const expanded = true === getApplicationSetting("applicationSettings.panelOpen");
8
- const handleClose = useCallback(()=>{
9
- setApplicationSetting("applicationSettings.panelOpen", false);
10
- }, [
11
- setApplicationSetting
12
- ]);
13
- return /*#__PURE__*/ jsx(ContextPanel, {
14
- content: /*#__PURE__*/ jsx("div", {
15
- children: "Application settings"
16
- }),
17
- expanded: expanded,
18
- id: "vuu-context-panel",
19
- onClose: handleClose,
20
- overlay: true,
21
- title: "Settings"
22
- });
23
- };
24
- export { ApplicationSettingsContextPanel };
@@ -1,73 +0,0 @@
1
- import { GridLayoutDocumentController } from "@vuu-ui/grid-layout";
2
- import { WorkspacePersistentStateStore } from "./WorkspacePersistentState.js";
3
- import { WorkspaceRepository } from "./WorkspaceRepository.js";
4
- import { WORKSPACE_SNAPSHOT_VERSION } from "./workspace-schemas.js";
5
- class WorkspaceController {
6
- definitionId;
7
- documentController;
8
- instanceId;
9
- name;
10
- persistentState;
11
- renderers;
12
- repository;
13
- settingsCodecs;
14
- #disposed = false;
15
- #savingStructure = false;
16
- #onError;
17
- #unsubscribe;
18
- constructor(options){
19
- this.definitionId = options.definitionId;
20
- this.instanceId = options.instanceId;
21
- this.name = options.name;
22
- this.renderers = options.renderers;
23
- this.settingsCodecs = options.settingsCodecs;
24
- this.#onError = options.onError;
25
- this.repository = new WorkspaceRepository(options.service, options.instanceId, void 0 === options.revision ? void 0 : {
26
- revision: options.revision,
27
- snapshot: options.snapshot,
28
- version: 1,
29
- workspaceInstanceId: options.instanceId
30
- });
31
- this.persistentState = new WorkspacePersistentStateStore(options.snapshot.componentState);
32
- this.documentController = new GridLayoutDocumentController(options.snapshot.layout);
33
- this.#unsubscribe = this.persistentState.subscribe(()=>{
34
- if (!this.#savingStructure) this.#save();
35
- });
36
- }
37
- get snapshot() {
38
- const layout = this.documentController.getSnapshot();
39
- if (!layout) throw new Error(`Workspace "${this.instanceId}" has no layout document`);
40
- return {
41
- version: WORKSPACE_SNAPSHOT_VERSION,
42
- layout,
43
- componentState: this.persistentState.getSnapshot()
44
- };
45
- }
46
- handleDocumentChange = (document, change)=>{
47
- if (this.#disposed) return;
48
- this.documentController.replace(document);
49
- this.#savingStructure = true;
50
- try {
51
- for (const componentId of change.removedComponentInstanceIds)this.persistentState.purge(componentId);
52
- } finally{
53
- this.#savingStructure = false;
54
- }
55
- this.#save();
56
- };
57
- rename(name) {
58
- this.name = name;
59
- }
60
- dispose() {
61
- if (!this.#disposed) {
62
- this.#disposed = true;
63
- this.#unsubscribe();
64
- }
65
- return this.repository.dispose();
66
- }
67
- #save() {
68
- if (!this.#disposed) this.repository.save(this.snapshot).catch((cause)=>{
69
- if (!this.#disposed) this.#onError(cause instanceof Error ? cause : new Error("Workspace snapshot persistence failed"));
70
- });
71
- }
72
- }
73
- export { WorkspaceController };
@@ -1,194 +0,0 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { GridLayout, GridLayoutProvider } from "@vuu-ui/grid-layout";
3
- import { Tab, Tabstrip } from "@vuu-ui/vuu-ui-controls";
4
- import { useCallback, useRef, useState } from "react";
5
- import { WorkspacePersistentStateProvider, WorkspacePersistentStateStore } from "./WorkspacePersistentState.js";
6
- import { emptyWorkspaceSnapshot, useWorkspace } from "./WorkspaceProvider.js";
7
- import { WorkspaceStartPanel } from "./WorkspaceStartPanel.js";
8
- import { WORKSPACE_SNAPSHOT_VERSION } from "./workspace-schemas.js";
9
- const WorkspaceGrid = ({ active, controller })=>{
10
- const [isEmpty, setIsEmpty] = useState(0 === controller.snapshot.layout.components.length);
11
- const handleDocumentError = useCallback((error)=>{
12
- throw new Error(`Invalid workspace document at ${error.path}: ${error.message}`);
13
- }, []);
14
- const handleDocumentChange = useCallback((document, change)=>{
15
- setIsEmpty(0 === document.components.length);
16
- controller.handleDocumentChange(document, change);
17
- }, [
18
- controller
19
- ]);
20
- return /*#__PURE__*/ jsx("section", {
21
- "aria-hidden": !active,
22
- "aria-labelledby": `${controller.instanceId}-tab`,
23
- className: "vuuWorkspaceHost-workspace",
24
- "data-workspace-instance-id": controller.instanceId,
25
- hidden: !active,
26
- id: controller.instanceId,
27
- role: "tabpanel",
28
- children: /*#__PURE__*/ jsx(WorkspacePersistentStateProvider, {
29
- store: controller.persistentState,
30
- children: /*#__PURE__*/ jsx(GridLayoutProvider, {
31
- componentRenderers: controller.renderers,
32
- document: controller.snapshot.layout,
33
- documentController: controller.documentController,
34
- onDocumentChange: handleDocumentChange,
35
- onDocumentError: handleDocumentError,
36
- settingsCodecs: controller.settingsCodecs,
37
- children: /*#__PURE__*/ jsx(GridLayout, {
38
- id: controller.snapshot.layout.layout.id,
39
- placeholderContent: isEmpty ? /*#__PURE__*/ jsx(WorkspaceStartPanel, {}) : void 0
40
- })
41
- })
42
- })
43
- });
44
- };
45
- const EmptyWorkspaceDropTarget = ()=>{
46
- const { componentRenderers, createWorkspaceFromSnapshot, settingsCodecs } = useWorkspace();
47
- const creating = useRef(false);
48
- const [creationPending, setCreationPending] = useState(false);
49
- const [generation, setGeneration] = useState(0);
50
- const [persistentState, setPersistentState] = useState(()=>new WorkspacePersistentStateStore());
51
- const handleDocumentError = useCallback((error)=>{
52
- throw new Error(`Invalid empty workspace document at ${error.path}: ${error.message}`);
53
- }, []);
54
- const handleDocumentChange = useCallback((document)=>{
55
- if (creating.current || 0 === document.components.length) return;
56
- creating.current = true;
57
- setCreationPending(true);
58
- createWorkspaceFromSnapshot({
59
- componentState: persistentState.getSnapshot(),
60
- layout: document,
61
- version: WORKSPACE_SNAPSHOT_VERSION
62
- }, "Untitled").catch(()=>{
63
- creating.current = false;
64
- setCreationPending(false);
65
- setPersistentState(new WorkspacePersistentStateStore());
66
- setGeneration((current)=>current + 1);
67
- });
68
- }, [
69
- createWorkspaceFromSnapshot,
70
- persistentState
71
- ]);
72
- if (!componentRenderers || !settingsCodecs) return /*#__PURE__*/ jsx("div", {
73
- className: "vuuWorkspaceHost-state",
74
- role: "alert",
75
- children: "Workspace component registries are not configured."
76
- });
77
- if (creationPending) return /*#__PURE__*/ jsx("div", {
78
- className: "vuuWorkspaceHost-empty",
79
- children: /*#__PURE__*/ jsx("div", {
80
- className: "vuuWorkspaceHost-state",
81
- role: "status",
82
- children: "Creating workspace…"
83
- })
84
- });
85
- return /*#__PURE__*/ jsx("div", {
86
- className: "vuuWorkspaceHost-empty",
87
- children: /*#__PURE__*/ jsx(WorkspacePersistentStateProvider, {
88
- store: persistentState,
89
- children: /*#__PURE__*/ jsx(GridLayoutProvider, {
90
- componentRenderers: componentRenderers,
91
- onDocumentChange: handleDocumentChange,
92
- onDocumentError: handleDocumentError,
93
- settingsCodecs: settingsCodecs,
94
- children: /*#__PURE__*/ jsx(GridLayout, {
95
- className: "vuuWorkspaceHost-emptyGrid",
96
- colsAndRows: {
97
- cols: [
98
- "1fr"
99
- ],
100
- rows: [
101
- "1fr"
102
- ]
103
- },
104
- id: emptyWorkspaceSnapshot.layout.layout.id,
105
- placeholderContent: /*#__PURE__*/ jsx(WorkspaceStartPanel, {})
106
- })
107
- }, generation)
108
- })
109
- });
110
- };
111
- const WorkspaceHost = ()=>{
112
- const { activeWorkspaceInstanceId, closeWorkspace, controllers, createWorkspaceFromSnapshot, error, renameWorkspace, selectWorkspace, status } = useWorkspace();
113
- const [tabRevision, setTabRevision] = useState(0);
114
- const activeWorkspaceIndex = controllers.findIndex(({ instanceId })=>instanceId === activeWorkspaceInstanceId);
115
- const handleActiveChange = useCallback((tabIndex)=>{
116
- const controller = controllers[tabIndex];
117
- if (controller) selectWorkspace(controller.instanceId);
118
- }, [
119
- controllers,
120
- selectWorkspace
121
- ]);
122
- const handleCloseTab = useCallback((tabIndex)=>{
123
- const controller = controllers[tabIndex];
124
- if (controller) closeWorkspace(controller.instanceId);
125
- }, [
126
- closeWorkspace,
127
- controllers
128
- ]);
129
- const handleAddTab = useCallback(()=>{
130
- const titles = new Set(controllers.map(({ name })=>name));
131
- let suffix = 1;
132
- let title = "Untitled";
133
- while(titles.has(title)){
134
- suffix += 1;
135
- title = `Untitled ${suffix}`;
136
- }
137
- createWorkspaceFromSnapshot(emptyWorkspaceSnapshot, title).catch(()=>void 0);
138
- }, [
139
- controllers,
140
- createWorkspaceFromSnapshot
141
- ]);
142
- const handleExitEditMode = useCallback((originalTitle, title, _allowDeactivation, tabIndex)=>{
143
- const controller = controllers[tabIndex];
144
- if (controller && title !== originalTitle) renameWorkspace(controller.instanceId, title).catch(()=>{
145
- setTabRevision((revision)=>revision + 1);
146
- });
147
- }, [
148
- controllers,
149
- renameWorkspace
150
- ]);
151
- if ("loading" === status) return /*#__PURE__*/ jsx("div", {
152
- className: "vuuWorkspaceHost-state",
153
- role: "status",
154
- children: "Loading workspaces…"
155
- });
156
- if ("error" === status) return /*#__PURE__*/ jsx("div", {
157
- className: "vuuWorkspaceHost-state",
158
- role: "alert",
159
- children: error?.message ?? "Unable to load workspaces"
160
- });
161
- return /*#__PURE__*/ jsxs("div", {
162
- className: "vuuWorkspaceHost vuu-workspace-tabs",
163
- children: [
164
- /*#__PURE__*/ jsx(Tabstrip, {
165
- activeTabIndex: activeWorkspaceIndex,
166
- allowAddTab: true,
167
- allowCloseTab: true,
168
- allowRenameTab: true,
169
- animateSelectionThumb: false,
170
- "aria-label": "Workspace Tabs",
171
- className: "vuuTabHeader",
172
- onActiveChange: handleActiveChange,
173
- onAddTab: handleAddTab,
174
- onCloseTab: handleCloseTab,
175
- onExitEditMode: handleExitEditMode,
176
- variant: "primary",
177
- children: controllers.map((controller)=>/*#__PURE__*/ jsx(Tab, {
178
- ariaControls: controller.instanceId,
179
- id: `${controller.instanceId}-tab`,
180
- label: controller.name,
181
- onClose: ()=>void closeWorkspace(controller.instanceId)
182
- }, `${controller.instanceId}-${tabRevision}`))
183
- }),
184
- /*#__PURE__*/ jsx("div", {
185
- className: "vuuWorkspaceHost-content",
186
- children: "empty" === status ? /*#__PURE__*/ jsx(EmptyWorkspaceDropTarget, {}) : controllers.map((controller)=>/*#__PURE__*/ jsx(WorkspaceGrid, {
187
- active: controller.instanceId === activeWorkspaceInstanceId,
188
- controller: controller
189
- }, controller.instanceId))
190
- })
191
- ]
192
- });
193
- };
194
- export { WorkspaceHost };
@@ -1,67 +0,0 @@
1
- import { createContext, createElement, useContext, useSyncExternalStore } from "react";
2
- import { cloneWorkspaceValue } from "./workspace-schemas.js";
3
- class WorkspacePersistentStateStore {
4
- #state;
5
- #listeners = new Set();
6
- constructor(initialState = {}){
7
- this.#state = Object.freeze(cloneWorkspaceValue(initialState));
8
- }
9
- getSnapshot = ()=>this.#state;
10
- subscribe = (listener)=>{
11
- this.#listeners.add(listener);
12
- return ()=>this.#listeners.delete(listener);
13
- };
14
- get(componentInstanceId) {
15
- const value = this.#state[componentInstanceId];
16
- return void 0 === value ? void 0 : cloneWorkspaceValue(value);
17
- }
18
- update(componentInstanceId, update) {
19
- if (!componentInstanceId) throw new Error("componentInstanceId must not be empty");
20
- const next = "function" == typeof update ? update(this.get(componentInstanceId)) : update;
21
- const captured = cloneWorkspaceValue(next);
22
- if (!captured.componentType || !Number.isInteger(captured.schemaVersion) || captured.schemaVersion < 1) throw new Error("Component persistent state metadata is invalid");
23
- this.#state = Object.freeze({
24
- ...this.#state,
25
- [componentInstanceId]: captured
26
- });
27
- this.#emit();
28
- }
29
- purge(componentInstanceId) {
30
- if (void 0 === componentInstanceId) {
31
- if (Object.keys(this.#state).length > 0) {
32
- this.#state = Object.freeze({});
33
- this.#emit();
34
- }
35
- return;
36
- }
37
- if (void 0 !== this.#state[componentInstanceId]) {
38
- const { [componentInstanceId]: removed, ...remaining } = this.#state;
39
- this.#state = Object.freeze(remaining);
40
- this.#emit();
41
- }
42
- }
43
- #emit() {
44
- this.#listeners.forEach((listener)=>{
45
- listener();
46
- });
47
- }
48
- }
49
- const WorkspacePersistentStateContext = /*#__PURE__*/ createContext(null);
50
- const WorkspacePersistentStateProvider = ({ children, store })=>/*#__PURE__*/ createElement(WorkspacePersistentStateContext.Provider, {
51
- value: store
52
- }, children);
53
- const useWorkspacePersistentStateStore = ()=>{
54
- const store = useContext(WorkspacePersistentStateContext);
55
- if (!store) throw new Error("useWorkspacePersistentStateStore requires WorkspacePersistentStateProvider");
56
- return store;
57
- };
58
- const useComponentPersistentState = (componentInstanceId)=>{
59
- const store = useWorkspacePersistentStateStore();
60
- const state = useSyncExternalStore(store.subscribe, ()=>store.getSnapshot()[componentInstanceId], ()=>store.getSnapshot()[componentInstanceId]);
61
- return [
62
- state,
63
- (update)=>store.update(componentInstanceId, update),
64
- ()=>store.purge(componentInstanceId)
65
- ];
66
- };
67
- export { WorkspacePersistentStateProvider, WorkspacePersistentStateStore, useComponentPersistentState, useWorkspacePersistentStateStore };