@onerjs/shared-ui-components 8.48.4 → 8.48.5

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 (104) hide show
  1. package/fluent/hoc/buttonLine.js +2 -1
  2. package/fluent/hoc/buttonLine.js.map +1 -1
  3. package/fluent/primitives/contextMenu.js.map +1 -1
  4. package/fluent/primitives/popover.js.map +1 -1
  5. package/modularTool/components/errorBoundary.d.ts +31 -0
  6. package/modularTool/components/errorBoundary.js +91 -0
  7. package/modularTool/components/errorBoundary.js.map +1 -0
  8. package/modularTool/components/extensibleAccordion.d.ts +67 -0
  9. package/modularTool/components/extensibleAccordion.js +148 -0
  10. package/modularTool/components/extensibleAccordion.js.map +1 -0
  11. package/modularTool/components/pane.d.ts +4 -0
  12. package/modularTool/components/pane.js +20 -0
  13. package/modularTool/components/pane.js.map +1 -0
  14. package/modularTool/components/teachingMoment.d.ts +20 -0
  15. package/modularTool/components/teachingMoment.js +17 -0
  16. package/modularTool/components/teachingMoment.js.map +1 -0
  17. package/modularTool/components/theme.d.ts +10 -0
  18. package/modularTool/components/theme.js +24 -0
  19. package/modularTool/components/theme.js.map +1 -0
  20. package/modularTool/components/uxContextProvider.d.ts +2 -0
  21. package/modularTool/components/uxContextProvider.js +19 -0
  22. package/modularTool/components/uxContextProvider.js.map +1 -0
  23. package/modularTool/contexts/extensionManagerContext.d.ts +6 -0
  24. package/modularTool/contexts/extensionManagerContext.js +6 -0
  25. package/modularTool/contexts/extensionManagerContext.js.map +1 -0
  26. package/modularTool/contexts/settingsContext.d.ts +3 -0
  27. package/modularTool/contexts/settingsContext.js +6 -0
  28. package/modularTool/contexts/settingsContext.js.map +1 -0
  29. package/modularTool/extensibility/builtInsExtensionFeed.d.ts +21 -0
  30. package/modularTool/extensibility/builtInsExtensionFeed.js +26 -0
  31. package/modularTool/extensibility/builtInsExtensionFeed.js.map +1 -0
  32. package/modularTool/extensibility/extensionFeed.d.ts +113 -0
  33. package/modularTool/extensibility/extensionFeed.js +2 -0
  34. package/modularTool/extensibility/extensionFeed.js.map +1 -0
  35. package/modularTool/extensibility/extensionManager.d.ts +111 -0
  36. package/modularTool/extensibility/extensionManager.js +277 -0
  37. package/modularTool/extensibility/extensionManager.js.map +1 -0
  38. package/modularTool/hooks/observableHooks.d.ts +35 -0
  39. package/modularTool/hooks/observableHooks.js +84 -0
  40. package/modularTool/hooks/observableHooks.js.map +1 -0
  41. package/modularTool/hooks/resourceHooks.d.ts +20 -0
  42. package/modularTool/hooks/resourceHooks.js +101 -0
  43. package/modularTool/hooks/resourceHooks.js.map +1 -0
  44. package/modularTool/hooks/settingsHooks.d.ts +8 -0
  45. package/modularTool/hooks/settingsHooks.js +40 -0
  46. package/modularTool/hooks/settingsHooks.js.map +1 -0
  47. package/modularTool/hooks/teachingMomentHooks.d.ts +34 -0
  48. package/modularTool/hooks/teachingMomentHooks.js +89 -0
  49. package/modularTool/hooks/teachingMomentHooks.js.map +1 -0
  50. package/modularTool/hooks/themeHooks.d.ts +17 -0
  51. package/modularTool/hooks/themeHooks.js +38 -0
  52. package/modularTool/hooks/themeHooks.js.map +1 -0
  53. package/modularTool/hooks/useResizeHandle.d.ts +35 -0
  54. package/modularTool/hooks/useResizeHandle.js +75 -0
  55. package/modularTool/hooks/useResizeHandle.js.map +1 -0
  56. package/modularTool/misc/assert.d.ts +5 -0
  57. package/modularTool/misc/assert.js +10 -0
  58. package/modularTool/misc/assert.js.map +1 -0
  59. package/modularTool/misc/graphUtils.d.ts +44 -0
  60. package/modularTool/misc/graphUtils.js +90 -0
  61. package/modularTool/misc/graphUtils.js.map +1 -0
  62. package/modularTool/misc/observableCollection.d.ts +23 -0
  63. package/modularTool/misc/observableCollection.js +43 -0
  64. package/modularTool/misc/observableCollection.js.map +1 -0
  65. package/modularTool/modularTool.d.ts +42 -0
  66. package/modularTool/modularTool.js +223 -0
  67. package/modularTool/modularTool.js.map +1 -0
  68. package/modularTool/modularity/serviceContainer.d.ts +64 -0
  69. package/modularTool/modularity/serviceContainer.js +181 -0
  70. package/modularTool/modularity/serviceContainer.js.map +1 -0
  71. package/modularTool/modularity/serviceDefinition.d.ts +64 -0
  72. package/modularTool/modularity/serviceDefinition.js +11 -0
  73. package/modularTool/modularity/serviceDefinition.js.map +1 -0
  74. package/modularTool/services/extensionsListService.d.ts +3 -0
  75. package/modularTool/services/extensionsListService.js +202 -0
  76. package/modularTool/services/extensionsListService.js.map +1 -0
  77. package/modularTool/services/globalSettings.d.ts +3 -0
  78. package/modularTool/services/globalSettings.js +9 -0
  79. package/modularTool/services/globalSettings.js.map +1 -0
  80. package/modularTool/services/reactContextService.d.ts +18 -0
  81. package/modularTool/services/reactContextService.js +5 -0
  82. package/modularTool/services/reactContextService.js.map +1 -0
  83. package/modularTool/services/settingsService.d.ts +24 -0
  84. package/modularTool/services/settingsService.js +41 -0
  85. package/modularTool/services/settingsService.js.map +1 -0
  86. package/modularTool/services/settingsStore.d.ts +55 -0
  87. package/modularTool/services/settingsStore.js +35 -0
  88. package/modularTool/services/settingsStore.js.map +1 -0
  89. package/modularTool/services/shellService.d.ts +256 -0
  90. package/modularTool/services/shellService.js +729 -0
  91. package/modularTool/services/shellService.js.map +1 -0
  92. package/modularTool/services/shellSettingsService.d.ts +3 -0
  93. package/modularTool/services/shellSettingsService.js +35 -0
  94. package/modularTool/services/shellSettingsService.js.map +1 -0
  95. package/modularTool/services/themeSelectorService.d.ts +3 -0
  96. package/modularTool/services/themeSelectorService.js +42 -0
  97. package/modularTool/services/themeSelectorService.js.map +1 -0
  98. package/modularTool/services/themeService.d.ts +60 -0
  99. package/modularTool/services/themeService.js +69 -0
  100. package/modularTool/services/themeService.js.map +1 -0
  101. package/modularTool/themes/babylonTheme.d.ts +3 -0
  102. package/modularTool/themes/babylonTheme.js +36 -0
  103. package/modularTool/themes/babylonTheme.js.map +1 -0
  104. package/package.json +1 -1
@@ -0,0 +1,35 @@
1
+ export type GrowDirection = "end" | "start" | "up" | "down";
2
+ export type UseResizeHandleParams = {
3
+ /**
4
+ * The direction in which the element is considered growing in size ('end', 'start', 'up', or 'down').
5
+ */
6
+ growDirection: GrowDirection;
7
+ /**
8
+ * The name of the CSS variable that will be set on the wrapper element to reflect the current size of the element.
9
+ */
10
+ variableName: string;
11
+ /**
12
+ * A callback that will be called when the element is resized.
13
+ *
14
+ * @remarks The passed function should be memoized for better performance.
15
+ */
16
+ onChange?: (value: number) => void;
17
+ /**
18
+ * The minimum change allowed (e.g. the smallest negative number allowed).
19
+ */
20
+ minValue?: number;
21
+ /**
22
+ * The maximum change allowed (e.g. the largest positive number allowed).
23
+ */
24
+ maxValue?: number;
25
+ };
26
+ /**
27
+ * A custom hook that helps with element resizing.
28
+ * @param params The parameters for the resize handle.
29
+ * @returns An object containing refs and a function to set the value.
30
+ */
31
+ export declare function useResizeHandle(params: UseResizeHandleParams): {
32
+ elementRef: import("react").Dispatch<import("react").SetStateAction<HTMLElement | null>>;
33
+ handleRef: import("react").Dispatch<import("react").SetStateAction<HTMLElement | null>>;
34
+ setValue: (value: number) => void;
35
+ };
@@ -0,0 +1,75 @@
1
+ // NOTE: This is basically a super simplified version of https://github.com/microsoft/fluentui-contrib/blob/main/packages/react-resize-handle/src/hooks
2
+ // This version does not support keyboard interactions, absolute values, automatically clamping to the actual adjusted size, accessibility, and various other features.
3
+ // We can switch back to Fluent's implementation once some known bugs are fixed:
4
+ // 1. https://github.com/microsoft/fluentui-contrib/issues/523
5
+ // 2. React 19 compatibility
6
+ import { useCallback, useEffect, useRef, useState } from "react";
7
+ import { Clamp } from "@onerjs/core/Maths/math.scalar.functions.js";
8
+ /**
9
+ * A custom hook that helps with element resizing.
10
+ * @param params The parameters for the resize handle.
11
+ * @returns An object containing refs and a function to set the value.
12
+ */
13
+ export function useResizeHandle(params) {
14
+ const { growDirection, variableName, onChange } = params;
15
+ const valueRef = useRef(0);
16
+ const [elementRef, setElementRef] = useState(null);
17
+ const [handleRef, setHandleRef] = useState(null);
18
+ const updateElementStyle = useCallback((value) => {
19
+ if (elementRef) {
20
+ elementRef.style.setProperty(variableName, `${value}px`);
21
+ }
22
+ }, [elementRef, variableName]);
23
+ const setValue = useCallback((value) => {
24
+ valueRef.current = value;
25
+ updateElementStyle(value);
26
+ onChange?.(value);
27
+ }, [updateElementStyle, onChange]);
28
+ useEffect(() => {
29
+ updateElementStyle(valueRef.current);
30
+ }, [updateElementStyle]);
31
+ useEffect(() => {
32
+ if (handleRef) {
33
+ let delta = 0;
34
+ const coerceDelta = (delta) => Clamp(delta, params.minValue ?? -Infinity, params.maxValue ?? Infinity);
35
+ const onPointerMove = (event) => {
36
+ event.preventDefault();
37
+ switch (growDirection) {
38
+ case "up":
39
+ delta -= event.movementY;
40
+ break;
41
+ case "down":
42
+ delta += event.movementY;
43
+ break;
44
+ case "start":
45
+ delta -= event.movementX;
46
+ break;
47
+ case "end":
48
+ delta += event.movementX;
49
+ break;
50
+ }
51
+ setValue(coerceDelta(delta));
52
+ };
53
+ const onPointerDown = (event) => {
54
+ event.preventDefault();
55
+ delta = valueRef.current;
56
+ handleRef.setPointerCapture(event.pointerId);
57
+ handleRef.addEventListener("pointermove", onPointerMove);
58
+ };
59
+ const onPointerUp = (event) => {
60
+ event.preventDefault();
61
+ handleRef.releasePointerCapture(event.pointerId);
62
+ handleRef.removeEventListener("pointermove", onPointerMove);
63
+ valueRef.current = coerceDelta(valueRef.current);
64
+ };
65
+ handleRef.addEventListener("pointerdown", onPointerDown);
66
+ handleRef.addEventListener("pointerup", onPointerUp);
67
+ }
68
+ }, [handleRef, setValue]);
69
+ return {
70
+ elementRef: setElementRef,
71
+ handleRef: setHandleRef,
72
+ setValue,
73
+ };
74
+ }
75
+ //# sourceMappingURL=useResizeHandle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResizeHandle.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/useResizeHandle.ts"],"names":[],"mappings":"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","sourcesContent":["// NOTE: This is basically a super simplified version of https://github.com/microsoft/fluentui-contrib/blob/main/packages/react-resize-handle/src/hooks\r\n// This version does not support keyboard interactions, absolute values, automatically clamping to the actual adjusted size, accessibility, and various other features.\r\n// We can switch back to Fluent's implementation once some known bugs are fixed:\r\n// 1. https://github.com/microsoft/fluentui-contrib/issues/523\r\n// 2. React 19 compatibility\r\n\r\nimport { useCallback, useEffect, useRef, useState } from \"react\";\r\n\r\nimport { Clamp } from \"core/Maths/math.scalar.functions\";\r\n\r\nexport type GrowDirection = \"end\" | \"start\" | \"up\" | \"down\";\r\n\r\nexport type UseResizeHandleParams = {\r\n /**\r\n * The direction in which the element is considered growing in size ('end', 'start', 'up', or 'down').\r\n */\r\n growDirection: GrowDirection;\r\n /**\r\n * The name of the CSS variable that will be set on the wrapper element to reflect the current size of the element.\r\n */\r\n variableName: string;\r\n /**\r\n * A callback that will be called when the element is resized.\r\n *\r\n * @remarks The passed function should be memoized for better performance.\r\n */\r\n onChange?: (value: number) => void;\r\n /**\r\n * The minimum change allowed (e.g. the smallest negative number allowed).\r\n */\r\n minValue?: number;\r\n /**\r\n * The maximum change allowed (e.g. the largest positive number allowed).\r\n */\r\n maxValue?: number;\r\n};\r\n\r\n/**\r\n * A custom hook that helps with element resizing.\r\n * @param params The parameters for the resize handle.\r\n * @returns An object containing refs and a function to set the value.\r\n */\r\nexport function useResizeHandle(params: UseResizeHandleParams) {\r\n const { growDirection, variableName, onChange } = params;\r\n\r\n const valueRef = useRef(0);\r\n const [elementRef, setElementRef] = useState<HTMLElement | null>(null);\r\n const [handleRef, setHandleRef] = useState<HTMLElement | null>(null);\r\n\r\n const updateElementStyle = useCallback(\r\n (value: number) => {\r\n if (elementRef) {\r\n elementRef.style.setProperty(variableName, `${value}px`);\r\n }\r\n },\r\n [elementRef, variableName]\r\n );\r\n\r\n const setValue = useCallback(\r\n (value: number) => {\r\n valueRef.current = value;\r\n updateElementStyle(value);\r\n onChange?.(value);\r\n },\r\n [updateElementStyle, onChange]\r\n );\r\n\r\n useEffect(() => {\r\n updateElementStyle(valueRef.current);\r\n }, [updateElementStyle]);\r\n\r\n useEffect(() => {\r\n if (handleRef) {\r\n let delta = 0;\r\n\r\n const coerceDelta = (delta: number) => Clamp(delta, params.minValue ?? -Infinity, params.maxValue ?? Infinity);\r\n\r\n const onPointerMove = (event: PointerEvent) => {\r\n event.preventDefault();\r\n switch (growDirection) {\r\n case \"up\":\r\n delta -= event.movementY;\r\n break;\r\n case \"down\":\r\n delta += event.movementY;\r\n break;\r\n case \"start\":\r\n delta -= event.movementX;\r\n break;\r\n case \"end\":\r\n delta += event.movementX;\r\n break;\r\n }\r\n setValue(coerceDelta(delta));\r\n };\r\n\r\n const onPointerDown = (event: PointerEvent) => {\r\n event.preventDefault();\r\n delta = valueRef.current;\r\n handleRef.setPointerCapture(event.pointerId);\r\n handleRef.addEventListener(\"pointermove\", onPointerMove);\r\n };\r\n\r\n const onPointerUp = (event: PointerEvent) => {\r\n event.preventDefault();\r\n handleRef.releasePointerCapture(event.pointerId);\r\n handleRef.removeEventListener(\"pointermove\", onPointerMove);\r\n valueRef.current = coerceDelta(valueRef.current);\r\n };\r\n\r\n handleRef.addEventListener(\"pointerdown\", onPointerDown);\r\n handleRef.addEventListener(\"pointerup\", onPointerUp);\r\n }\r\n }, [handleRef, setValue]);\r\n\r\n return {\r\n elementRef: setElementRef,\r\n handleRef: setHandleRef,\r\n setValue,\r\n };\r\n}\r\n"]}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Asserts that the given value is truthy.
3
+ * @param value The value to check.
4
+ */
5
+ export declare function Assert(value: unknown): asserts value;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Asserts that the given value is truthy.
3
+ * @param value The value to check.
4
+ */
5
+ export function Assert(value) {
6
+ if (!value) {
7
+ throw new Error("assertion failed");
8
+ }
9
+ }
10
+ //# sourceMappingURL=assert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/misc/assert.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,KAAc;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACxC,CAAC;AACL,CAAC","sourcesContent":["/**\r\n * Asserts that the given value is truthy.\r\n * @param value The value to check.\r\n */\r\nexport function Assert(value: unknown): asserts value {\r\n if (!value) {\r\n throw new Error(\"assertion failed\");\r\n }\r\n}\r\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Performs a topological sort on a graph.
3
+ * @param graph The set of nodes that make up the graph.
4
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
5
+ * @param onSortedNode A function that is called for each node in the sorted order.
6
+ * @remarks
7
+ * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
8
+ */
9
+ export declare function SortGraph<NodeT>(graph: Iterable<NodeT>, getAdjacentNodes: (node: NodeT) => Iterable<NodeT>, onSortedNode: (node: NodeT) => void): void;
10
+ /**
11
+ * Traverses a graph.
12
+ * @param graph The set of nodes that make up the graph.
13
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
14
+ * @param onBeforeTraverse A function that is called before traversing each node.
15
+ * @param onAfterTraverse A function that is called after traversing each node.
16
+ * @remarks
17
+ * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
18
+ */
19
+ export declare function TraverseGraph<NodeT>(graph: Iterable<NodeT>, getAdjacentNodes: (node: NodeT) => Iterable<NodeT> | null | undefined, onBeforeTraverse?: (node: NodeT) => void, onAfterTraverse?: (node: NodeT) => void): void;
20
+ /**
21
+ * A utility class for performing graph operations.
22
+ * @remarks
23
+ * The class allocates new objects, but each operation (e.g. sort, traverse) is allocation free. This is useful when used in the hot path.
24
+ */
25
+ export declare class GraphUtils<DefaultNodeT = unknown> {
26
+ private readonly _traversalState;
27
+ private _isTraversing;
28
+ /**
29
+ * Performs a topological sort on a graph.
30
+ * @param graph The set of nodes that make up the graph.
31
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
32
+ * @param onSortedNode A function that is called for each node in the sorted order.
33
+ */
34
+ sort<NodeT extends DefaultNodeT>(graph: Iterable<NodeT>, getAdjacentNodes: (node: NodeT) => Iterable<NodeT>, onSortedNode: (node: NodeT) => void): void;
35
+ /**
36
+ * Traverses a graph.
37
+ * @param graph The set of nodes that make up the graph.
38
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
39
+ * @param onBeforeTraverse A function that is called before traversing each node.
40
+ * @param onAfterTraverse A function that is called after traversing each node.
41
+ */
42
+ traverse<NodeT extends DefaultNodeT>(graph: Iterable<NodeT>, getAdjacentNodes: (node: NodeT) => Iterable<NodeT> | null | undefined, onBeforeTraverse?: (node: NodeT) => void, onAfterTraverse?: (node: NodeT) => void): void;
43
+ private _traverseCore;
44
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Performs a topological sort on a graph.
3
+ * @param graph The set of nodes that make up the graph.
4
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
5
+ * @param onSortedNode A function that is called for each node in the sorted order.
6
+ * @remarks
7
+ * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
8
+ */
9
+ export function SortGraph(graph, getAdjacentNodes, onSortedNode) {
10
+ const sorter = new GraphUtils();
11
+ sorter.sort(graph, getAdjacentNodes, onSortedNode);
12
+ }
13
+ /**
14
+ * Traverses a graph.
15
+ * @param graph The set of nodes that make up the graph.
16
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
17
+ * @param onBeforeTraverse A function that is called before traversing each node.
18
+ * @param onAfterTraverse A function that is called after traversing each node.
19
+ * @remarks
20
+ * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.
21
+ */
22
+ export function TraverseGraph(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
23
+ const traverser = new GraphUtils();
24
+ traverser.traverse(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
25
+ }
26
+ /**
27
+ * A utility class for performing graph operations.
28
+ * @remarks
29
+ * The class allocates new objects, but each operation (e.g. sort, traverse) is allocation free. This is useful when used in the hot path.
30
+ */
31
+ export class GraphUtils {
32
+ constructor() {
33
+ // Tracks three states:
34
+ // 1. No entry for the node - this means the node has not been encountered yet during any traversal
35
+ // 2. Entry with value false - this means the node is currently being traversed (needed to detect cycles)
36
+ // 3. Entry with value true - this means the node has already been fully traversed (and cycles were not detected)
37
+ this._traversalState = new Map();
38
+ this._isTraversing = false;
39
+ }
40
+ /**
41
+ * Performs a topological sort on a graph.
42
+ * @param graph The set of nodes that make up the graph.
43
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
44
+ * @param onSortedNode A function that is called for each node in the sorted order.
45
+ */
46
+ sort(graph, getAdjacentNodes, onSortedNode) {
47
+ this.traverse(graph, getAdjacentNodes, undefined, onSortedNode);
48
+ }
49
+ /**
50
+ * Traverses a graph.
51
+ * @param graph The set of nodes that make up the graph.
52
+ * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.
53
+ * @param onBeforeTraverse A function that is called before traversing each node.
54
+ * @param onAfterTraverse A function that is called after traversing each node.
55
+ */
56
+ traverse(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
57
+ // Since the traversal state is re-used, disallow re-entrancy through the getAdjacentNodes or onBeforeTraverse or onAfterTraverse callbacks.
58
+ if (this._isTraversing) {
59
+ throw new Error("This TopologicalSorter instance is already traversing.");
60
+ }
61
+ this._isTraversing = true;
62
+ try {
63
+ for (const node of graph) {
64
+ this._traverseCore(node, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
65
+ }
66
+ }
67
+ finally {
68
+ this._isTraversing = false;
69
+ this._traversalState.clear();
70
+ }
71
+ }
72
+ _traverseCore(node, getAdjacentNodes, onBeforeTraverse, onAfterTraverse) {
73
+ if (this._traversalState.get(node) !== true) {
74
+ if (this._traversalState.get(node) === false) {
75
+ throw new Error("Graph has cycle.");
76
+ }
77
+ this._traversalState.set(node, false);
78
+ onBeforeTraverse?.(node);
79
+ const adjacentNodes = getAdjacentNodes(node);
80
+ if (adjacentNodes) {
81
+ for (const adjacentNode of adjacentNodes) {
82
+ this._traverseCore(adjacentNode, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);
83
+ }
84
+ }
85
+ this._traversalState.set(node, true);
86
+ onAfterTraverse?.(node);
87
+ }
88
+ }
89
+ }
90
+ //# sourceMappingURL=graphUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphUtils.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/misc/graphUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAQ,KAAsB,EAAE,gBAAkD,EAAE,YAAmC;IAC5I,MAAM,MAAM,GAAG,IAAI,UAAU,EAAS,CAAC;IACvC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,gBAAgB,EAAE,YAAY,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CACzB,KAAsB,EACtB,gBAAqE,EACrE,gBAAwC,EACxC,eAAuC;IAEvC,MAAM,SAAS,GAAG,IAAI,UAAU,EAAS,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,CAAC,CAAC;AACnF,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,UAAU;IAAvB;QACI,uBAAuB;QACvB,mGAAmG;QACnG,yGAAyG;QACzG,iHAAiH;QAChG,oBAAe,GAAG,IAAI,GAAG,EAAyB,CAAC;QAC5D,kBAAa,GAAG,KAAK,CAAC;IAmElC,CAAC;IAjEG;;;;;OAKG;IACI,IAAI,CAA6B,KAAsB,EAAE,gBAAkD,EAAE,YAAmC;QACnJ,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;OAMG;IACI,QAAQ,CACX,KAAsB,EACtB,gBAAqE,EACrE,gBAAwC,EACxC,eAAuC;QAEvC,4IAA4I;QAC5I,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC9E,CAAC;QAED,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAE1B,IAAI,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACvB,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,CAAC,CAAC;YAClF,CAAC;QACL,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QACjC,CAAC;IACL,CAAC;IAEO,aAAa,CACjB,IAAW,EACX,gBAAqE,EACrE,gBAAwC,EACxC,eAAuC;QAEvC,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;gBAC3C,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACxC,CAAC;YAED,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACtC,gBAAgB,EAAE,CAAC,IAAI,CAAC,CAAC;YAEzB,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,aAAa,EAAE,CAAC;gBAChB,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;oBACvC,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,CAAC,CAAC;gBAC1F,CAAC;YACL,CAAC;YAED,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACrC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;CACJ","sourcesContent":["/**\r\n * Performs a topological sort on a graph.\r\n * @param graph The set of nodes that make up the graph.\r\n * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.\r\n * @param onSortedNode A function that is called for each node in the sorted order.\r\n * @remarks\r\n * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.\r\n */\r\nexport function SortGraph<NodeT>(graph: Iterable<NodeT>, getAdjacentNodes: (node: NodeT) => Iterable<NodeT>, onSortedNode: (node: NodeT) => void) {\r\n const sorter = new GraphUtils<NodeT>();\r\n sorter.sort(graph, getAdjacentNodes, onSortedNode);\r\n}\r\n\r\n/**\r\n * Traverses a graph.\r\n * @param graph The set of nodes that make up the graph.\r\n * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.\r\n * @param onBeforeTraverse A function that is called before traversing each node.\r\n * @param onAfterTraverse A function that is called after traversing each node.\r\n * @remarks\r\n * This function allocates. Do not use it in the hot path. Instead use an instance of GraphUtils.\r\n */\r\nexport function TraverseGraph<NodeT>(\r\n graph: Iterable<NodeT>,\r\n getAdjacentNodes: (node: NodeT) => Iterable<NodeT> | null | undefined,\r\n onBeforeTraverse?: (node: NodeT) => void,\r\n onAfterTraverse?: (node: NodeT) => void\r\n) {\r\n const traverser = new GraphUtils<NodeT>();\r\n traverser.traverse(graph, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);\r\n}\r\n\r\n/**\r\n * A utility class for performing graph operations.\r\n * @remarks\r\n * The class allocates new objects, but each operation (e.g. sort, traverse) is allocation free. This is useful when used in the hot path.\r\n */\r\nexport class GraphUtils<DefaultNodeT = unknown> {\r\n // Tracks three states:\r\n // 1. No entry for the node - this means the node has not been encountered yet during any traversal\r\n // 2. Entry with value false - this means the node is currently being traversed (needed to detect cycles)\r\n // 3. Entry with value true - this means the node has already been fully traversed (and cycles were not detected)\r\n private readonly _traversalState = new Map<DefaultNodeT, boolean>();\r\n private _isTraversing = false;\r\n\r\n /**\r\n * Performs a topological sort on a graph.\r\n * @param graph The set of nodes that make up the graph.\r\n * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.\r\n * @param onSortedNode A function that is called for each node in the sorted order.\r\n */\r\n public sort<NodeT extends DefaultNodeT>(graph: Iterable<NodeT>, getAdjacentNodes: (node: NodeT) => Iterable<NodeT>, onSortedNode: (node: NodeT) => void) {\r\n this.traverse(graph, getAdjacentNodes, undefined, onSortedNode);\r\n }\r\n\r\n /**\r\n * Traverses a graph.\r\n * @param graph The set of nodes that make up the graph.\r\n * @param getAdjacentNodes A function that returns the adjacent nodes for a given node.\r\n * @param onBeforeTraverse A function that is called before traversing each node.\r\n * @param onAfterTraverse A function that is called after traversing each node.\r\n */\r\n public traverse<NodeT extends DefaultNodeT>(\r\n graph: Iterable<NodeT>,\r\n getAdjacentNodes: (node: NodeT) => Iterable<NodeT> | null | undefined,\r\n onBeforeTraverse?: (node: NodeT) => void,\r\n onAfterTraverse?: (node: NodeT) => void\r\n ) {\r\n // Since the traversal state is re-used, disallow re-entrancy through the getAdjacentNodes or onBeforeTraverse or onAfterTraverse callbacks.\r\n if (this._isTraversing) {\r\n throw new Error(\"This TopologicalSorter instance is already traversing.\");\r\n }\r\n\r\n this._isTraversing = true;\r\n\r\n try {\r\n for (const node of graph) {\r\n this._traverseCore(node, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);\r\n }\r\n } finally {\r\n this._isTraversing = false;\r\n this._traversalState.clear();\r\n }\r\n }\r\n\r\n private _traverseCore<NodeT extends DefaultNodeT>(\r\n node: NodeT,\r\n getAdjacentNodes: (node: NodeT) => Iterable<NodeT> | null | undefined,\r\n onBeforeTraverse?: (node: NodeT) => void,\r\n onAfterTraverse?: (node: NodeT) => void\r\n ) {\r\n if (this._traversalState.get(node) !== true) {\r\n if (this._traversalState.get(node) === false) {\r\n throw new Error(\"Graph has cycle.\");\r\n }\r\n\r\n this._traversalState.set(node, false);\r\n onBeforeTraverse?.(node);\r\n\r\n const adjacentNodes = getAdjacentNodes(node);\r\n if (adjacentNodes) {\r\n for (const adjacentNode of adjacentNodes) {\r\n this._traverseCore(adjacentNode, getAdjacentNodes, onBeforeTraverse, onAfterTraverse);\r\n }\r\n }\r\n\r\n this._traversalState.set(node, true);\r\n onAfterTraverse?.(node);\r\n }\r\n }\r\n}\r\n"]}
@@ -0,0 +1,23 @@
1
+ import { type IDisposable, type IReadonlyObservable } from "@onerjs/core/index.js";
2
+ /**
3
+ * A collection of items that can be observed for changes.
4
+ */
5
+ export declare class ObservableCollection<T> {
6
+ private readonly _items;
7
+ private readonly _keys;
8
+ private readonly _observable;
9
+ /**
10
+ * An observable that notifies observers when the collection changes.
11
+ */
12
+ get observable(): IReadonlyObservable<void>;
13
+ /**
14
+ * The items in the collection.
15
+ */
16
+ get items(): readonly T[];
17
+ /**
18
+ * Adds an item to the collection.
19
+ * @param item The item to add.
20
+ * @returns A disposable that removes the item from the collection when disposed.
21
+ */
22
+ add(item: T): IDisposable;
23
+ }
@@ -0,0 +1,43 @@
1
+ import { Observable } from "@onerjs/core/Misc/observable.js";
2
+ /**
3
+ * A collection of items that can be observed for changes.
4
+ */
5
+ export class ObservableCollection {
6
+ constructor() {
7
+ this._items = [];
8
+ this._keys = [];
9
+ this._observable = new Observable();
10
+ }
11
+ /**
12
+ * An observable that notifies observers when the collection changes.
13
+ */
14
+ get observable() {
15
+ return this._observable;
16
+ }
17
+ /**
18
+ * The items in the collection.
19
+ */
20
+ get items() {
21
+ return this._items;
22
+ }
23
+ /**
24
+ * Adds an item to the collection.
25
+ * @param item The item to add.
26
+ * @returns A disposable that removes the item from the collection when disposed.
27
+ */
28
+ add(item) {
29
+ const key = Symbol();
30
+ this._items.push(item);
31
+ this._keys.push(key);
32
+ this._observable.notifyObservers();
33
+ return {
34
+ dispose: () => {
35
+ const index = this._keys.indexOf(key);
36
+ this._items.splice(index, 1);
37
+ this._keys.splice(index, 1);
38
+ this._observable.notifyObservers();
39
+ },
40
+ };
41
+ }
42
+ }
43
+ //# sourceMappingURL=observableCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observableCollection.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/misc/observableCollection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD;;GAEG;AACH,MAAM,OAAO,oBAAoB;IAAjC;QACqB,WAAM,GAAQ,EAAE,CAAC;QACjB,UAAK,GAAa,EAAE,CAAC;QACrB,gBAAW,GAAG,IAAI,UAAU,EAAQ,CAAC;IAoC1D,CAAC;IAlCG;;OAEG;IACH,IAAW,UAAU;QACjB,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,IAAW,KAAK;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,IAAO;QACd,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;QACrB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC;QAEnC,OAAO;YACH,OAAO,EAAE,GAAG,EAAE;gBACV,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACtC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAC7B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAC5B,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC;YACvC,CAAC;SACJ,CAAC;IACN,CAAC;CACJ","sourcesContent":["import { type IDisposable, type IReadonlyObservable } from \"core/index\";\r\n\r\nimport { Observable } from \"core/Misc/observable\";\r\n\r\n/**\r\n * A collection of items that can be observed for changes.\r\n */\r\nexport class ObservableCollection<T> {\r\n private readonly _items: T[] = [];\r\n private readonly _keys: symbol[] = [];\r\n private readonly _observable = new Observable<void>();\r\n\r\n /**\r\n * An observable that notifies observers when the collection changes.\r\n */\r\n public get observable(): IReadonlyObservable<void> {\r\n return this._observable;\r\n }\r\n\r\n /**\r\n * The items in the collection.\r\n */\r\n public get items(): readonly T[] {\r\n return this._items;\r\n }\r\n\r\n /**\r\n * Adds an item to the collection.\r\n * @param item The item to add.\r\n * @returns A disposable that removes the item from the collection when disposed.\r\n */\r\n public add(item: T): IDisposable {\r\n const key = Symbol();\r\n this._items.push(item);\r\n this._keys.push(key);\r\n this._observable.notifyObservers();\r\n\r\n return {\r\n dispose: () => {\r\n const index = this._keys.indexOf(key);\r\n this._items.splice(index, 1);\r\n this._keys.splice(index, 1);\r\n this._observable.notifyObservers();\r\n },\r\n };\r\n }\r\n}\r\n"]}
@@ -0,0 +1,42 @@
1
+ import { type IDisposable } from "@onerjs/core/index.js";
2
+ import { type IExtensionFeed } from "./extensibility/extensionFeed.js";
3
+ import { type WeaklyTypedServiceDefinition, ServiceContainer } from "./modularity/serviceContainer.js";
4
+ import { type ShellServiceOptions } from "./services/shellService.js";
5
+ import { type ThemeMode } from "./services/themeService.js";
6
+ export type ModularToolOptions = {
7
+ /**
8
+ * The namespace for the tool, used for scoping persisted settings and other storage.
9
+ */
10
+ namespace: string;
11
+ /**
12
+ * The container element where the tool will be rendered.
13
+ */
14
+ containerElement: HTMLElement;
15
+ /**
16
+ * The service definitions to be registered with the tool.
17
+ */
18
+ serviceDefinitions: readonly WeaklyTypedServiceDefinition[];
19
+ /**
20
+ * The theme mode to use. If not specified, the default is "system", which uses the system/browser preference, and the last used mode is persisted.
21
+ */
22
+ themeMode?: ThemeMode;
23
+ /**
24
+ * Whether to show the theme selector in the toolbar. Default is true.
25
+ */
26
+ showThemeSelector?: boolean;
27
+ /**
28
+ * The extension feeds that provide optional extensions the user can install.
29
+ */
30
+ extensionFeeds?: readonly IExtensionFeed[];
31
+ /**
32
+ * An optional parent ServiceContainer. Dependencies not found in the tool's own container
33
+ * will be resolved from this parent.
34
+ */
35
+ parentContainer?: ServiceContainer;
36
+ } & ShellServiceOptions;
37
+ /**
38
+ * Creates a modular tool with a base set of common tool services, including the toolbar/side pane basic UI layout.
39
+ * @param options The options for the tool.
40
+ * @returns A token that can be used to dispose of the tool.
41
+ */
42
+ export declare function MakeModularTool(options: ModularToolOptions): IDisposable;
@@ -0,0 +1,223 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createElement, Suspense, useCallback, useEffect, useReducer, useState, } from "react";
3
+ import { ExtensionManager } from "./extensibility/extensionManager.js";
4
+ import { ServiceContainer } from "./modularity/serviceContainer.js";
5
+ import { SettingsStore, SettingsStoreIdentity } from "./services/settingsStore.js";
6
+ import { MakeShellServiceDefinition, RootComponentServiceIdentity } from "./services/shellService.js";
7
+ import { ThemeModeSettingDescriptor, ThemeServiceDefinition } from "./services/themeService.js";
8
+ import { Body1, Button, Dialog, DialogActions, DialogBody, DialogContent, DialogSurface, DialogTitle, List, ListItem, makeStyles, Spinner, tokens, } from "@fluentui/react-components";
9
+ import { ErrorCircleRegular } from "@fluentui/react-icons";
10
+ import { createRoot } from "react-dom/client";
11
+ import { Deferred } from "@onerjs/core/Misc/deferred.js";
12
+ import { Logger } from "@onerjs/core/Misc/logger.js";
13
+ import { ToastProvider } from "../fluent/primitives/toast.js";
14
+ import { Theme } from "./components/theme.js";
15
+ import { ExtensionManagerContext } from "./contexts/extensionManagerContext.js";
16
+ import { SettingsStoreContext } from "./contexts/settingsContext.js";
17
+ import { ReactContextServiceIdentity } from "./services/reactContextService.js";
18
+ import { ThemeSelectorServiceDefinition } from "./services/themeSelectorService.js";
19
+ const useStyles = makeStyles({
20
+ app: {
21
+ colorScheme: "light dark",
22
+ flexGrow: 1,
23
+ display: "flex",
24
+ flexDirection: "column",
25
+ backgroundColor: tokens.colorTransparentBackground,
26
+ },
27
+ spinner: {
28
+ flexGrow: 1,
29
+ animationDuration: "1s",
30
+ animationName: {
31
+ from: { opacity: 0 },
32
+ to: { opacity: 1 },
33
+ },
34
+ },
35
+ extensionErrorTitleDiv: {
36
+ display: "flex",
37
+ flexDirection: "row",
38
+ alignItems: "center",
39
+ gap: tokens.spacingHorizontalS,
40
+ },
41
+ extensionErrorIcon: {
42
+ color: tokens.colorPaletteRedForeground1,
43
+ },
44
+ });
45
+ const ReactContextsWrapper = ({ contexts, children }) => {
46
+ return _jsx(_Fragment, { children: contexts.reduceRight((acc, entry) => createElement(entry.provider, { value: entry.value }, acc), children) });
47
+ };
48
+ /**
49
+ * Creates a modular tool with a base set of common tool services, including the toolbar/side pane basic UI layout.
50
+ * @param options The options for the tool.
51
+ * @returns A token that can be used to dispose of the tool.
52
+ */
53
+ export function MakeModularTool(options) {
54
+ const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer } = options;
55
+ // Create the settings store immediately as it will be exposed to services and through React context.
56
+ const settingsStore = new SettingsStore(namespace);
57
+ // If a theme mode is provided, just write the setting so it is the active theme.
58
+ if (themeMode) {
59
+ settingsStore.writeSetting(ThemeModeSettingDescriptor, themeMode);
60
+ }
61
+ const modularToolRootComponent = () => {
62
+ const classes = useStyles();
63
+ const [extensionManagerContext, setExtensionManagerContext] = useState();
64
+ const [requiredExtensions, setRequiredExtensions] = useState();
65
+ const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState();
66
+ const [extensionInstallError, setExtensionInstallError] = useState();
67
+ const [rootComponentService, setRootComponentService] = useState();
68
+ const [contexts, updateContexts] = useReducer((state, action) => {
69
+ switch (action.type) {
70
+ case "add":
71
+ return [...state, action.entry].sort((a, b) => a.order - b.order);
72
+ case "remove":
73
+ return state.filter((e) => e.provider !== action.provider);
74
+ case "update":
75
+ return state.map((e) => (e.provider === action.provider ? { ...e, value: action.value } : e));
76
+ }
77
+ }, []);
78
+ // This is the main async initialization.
79
+ useEffect(() => {
80
+ const initializeExtensionManagerAsync = async () => {
81
+ const serviceContainer = new ServiceContainer("ModularToolContainer", parentContainer);
82
+ // Expose the settings store as a service so other services can read/write settings.
83
+ await serviceContainer.addServiceAsync({
84
+ friendlyName: "Settings Store",
85
+ produces: [SettingsStoreIdentity],
86
+ factory: () => settingsStore,
87
+ });
88
+ // Expose the react context service so other services can add React context providers.
89
+ await serviceContainer.addServiceAsync({
90
+ friendlyName: "React Context Service",
91
+ produces: [ReactContextServiceIdentity],
92
+ factory: () => ({
93
+ addContext(provider, initialValue, options) {
94
+ const typedProvider = provider;
95
+ updateContexts({ type: "add", entry: { provider: typedProvider, value: initialValue, order: options?.order ?? 0 } });
96
+ return {
97
+ updateValue: (newValue) => {
98
+ updateContexts({ type: "update", provider: typedProvider, value: newValue });
99
+ },
100
+ dispose: () => {
101
+ updateContexts({ type: "remove", provider: typedProvider });
102
+ },
103
+ };
104
+ },
105
+ }),
106
+ });
107
+ // Register the shell service (top level toolbar/side pane UI layout).
108
+ await serviceContainer.addServiceAsync(MakeShellServiceDefinition(options));
109
+ // Register a service that simply consumes the services we need before first render.
110
+ await serviceContainer.addServiceAsync({
111
+ friendlyName: "Service Bootstrapper",
112
+ consumes: [RootComponentServiceIdentity],
113
+ factory: (rootComponent) => {
114
+ // Use function syntax for the state setter since the root component may be a function component.
115
+ setRootComponentService(() => rootComponent);
116
+ return {
117
+ dispose: () => setRootComponentService(undefined),
118
+ };
119
+ },
120
+ });
121
+ // Register the theme service (exposes the current theme to other services).
122
+ await serviceContainer.addServiceAsync(ThemeServiceDefinition);
123
+ // Register the theme selector service (for selecting the theme) if theming is configured.
124
+ if (showThemeSelector) {
125
+ await serviceContainer.addServiceAsync(ThemeSelectorServiceDefinition);
126
+ }
127
+ // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.
128
+ if (extensionFeeds.length > 0) {
129
+ const { ExtensionListServiceDefinition } = await import("./services/extensionsListService.js");
130
+ await serviceContainer.addServiceAsync(ExtensionListServiceDefinition);
131
+ }
132
+ // Register all external services (that make up a unique tool).
133
+ await serviceContainer.addServicesAsync(...serviceDefinitions);
134
+ // Create the extension manager, passing along the registry for runtime changes to the registered services.
135
+ const extensionManager = await ExtensionManager.CreateAsync(namespace, serviceContainer, extensionFeeds, setExtensionInstallError);
136
+ // Check query params for required extensions. This lets users share links with sets of extensions.
137
+ const queryParams = new URLSearchParams(window.location.search);
138
+ const requiredExtensions = queryParams.getAll("babylon.requiredExtension");
139
+ const uninstalledExtensions = [];
140
+ for (const requiredExtension of requiredExtensions) {
141
+ // These could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.
142
+ // eslint-disable-next-line no-await-in-loop
143
+ const query = await extensionManager.queryExtensionsAsync(requiredExtension);
144
+ // eslint-disable-next-line no-await-in-loop
145
+ const extensions = await query.getExtensionsAsync(0, query.totalCount);
146
+ for (const extension of extensions) {
147
+ if (!extension.isInstalled) {
148
+ uninstalledExtensions.push(extension);
149
+ }
150
+ }
151
+ }
152
+ // Check if any required extensions are uninstalled or disabled. If so, show a dialog to the user.
153
+ if (uninstalledExtensions.length > 0) {
154
+ setRequiredExtensions(uninstalledExtensions.map((extension) => extension.metadata.name));
155
+ const deferred = new Deferred();
156
+ setRequiredExtensionsDeferred(deferred);
157
+ if (await deferred.promise) {
158
+ for (const extension of uninstalledExtensions) {
159
+ // This could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.
160
+ // eslint-disable-next-line no-await-in-loop
161
+ await extension.installAsync();
162
+ }
163
+ }
164
+ }
165
+ // Set the contexts.
166
+ setExtensionManagerContext({ extensionManager });
167
+ return () => {
168
+ extensionManager.dispose();
169
+ serviceContainer.dispose();
170
+ serviceContainer.dispose();
171
+ };
172
+ };
173
+ const disposePromise = initializeExtensionManagerAsync();
174
+ return () => {
175
+ disposePromise
176
+ // eslint-disable-next-line github/no-then
177
+ .then((dispose) => dispose())
178
+ // eslint-disable-next-line github/no-then
179
+ .catch((error) => {
180
+ Logger.Error(`Failed to dispose of the modular tool: ${error}`);
181
+ });
182
+ };
183
+ }, []);
184
+ const onAcceptRequiredExtensions = useCallback(() => {
185
+ setRequiredExtensions(undefined);
186
+ requiredExtensionsDeferred?.resolve(true);
187
+ }, [setRequiredExtensions, requiredExtensionsDeferred]);
188
+ const onRejectRequiredExtensions = useCallback(() => {
189
+ setRequiredExtensions(undefined);
190
+ requiredExtensionsDeferred?.resolve(false);
191
+ }, [setRequiredExtensions, requiredExtensionsDeferred]);
192
+ const onAcknowledgedExtensionInstallError = useCallback(() => {
193
+ setExtensionInstallError(undefined);
194
+ }, [setExtensionInstallError]);
195
+ // Show a spinner until a main view has been set.
196
+ if (!rootComponentService) {
197
+ return (_jsx(ReactContextsWrapper, { contexts: contexts, children: _jsx(SettingsStoreContext.Provider, { value: settingsStore, children: _jsx(Theme, { className: classes.app, children: _jsx(Spinner, { className: classes.spinner }) }) }) }));
198
+ }
199
+ else {
200
+ // eslint-disable-next-line @typescript-eslint/naming-convention
201
+ const Content = rootComponentService.rootComponent;
202
+ return (_jsx(ReactContextsWrapper, { contexts: contexts, children: _jsx(SettingsStoreContext.Provider, { value: settingsStore, children: _jsx(ExtensionManagerContext.Provider, { value: extensionManagerContext, children: _jsx(Theme, { className: classes.app, children: _jsxs(ToastProvider, { children: [_jsx(Dialog, { open: !!requiredExtensions, modalType: "alert", children: _jsx(DialogSurface, { children: _jsxs(DialogBody, { children: [_jsx(DialogTitle, { children: "Required Extensions" }), _jsxs(DialogContent, { children: ["Opening this URL requires the following extensions to be installed and enabled:", _jsx("ul", { children: requiredExtensions?.map((name) => (_jsx("li", { children: name }, name))) })] }), _jsxs(DialogActions, { children: [_jsx(Button, { appearance: "primary", onClick: onAcceptRequiredExtensions, children: "Install & Enable" }), _jsx(Button, { appearance: "secondary", onClick: onRejectRequiredExtensions, children: "No Thanks" })] })] }) }) }), _jsx(Dialog, { open: !!extensionInstallError, modalType: "alert", children: _jsx(DialogSurface, { children: _jsxs(DialogBody, { children: [_jsx(DialogTitle, { children: _jsxs("div", { className: classes.extensionErrorTitleDiv, children: ["Extension Install Error", _jsx(ErrorCircleRegular, { className: classes.extensionErrorIcon })] }) }), _jsx(DialogContent, { children: _jsxs(List, { children: [_jsx(ListItem, { children: _jsx(Body1, { children: `Extension "${extensionInstallError?.extension.name}" failed to install and was removed.` }) }), _jsx(ListItem, { children: _jsx(Body1, { children: `${extensionInstallError?.error}` }) })] }) }), _jsx(DialogActions, { children: _jsx(Button, { appearance: "primary", onClick: onAcknowledgedExtensionInstallError, children: "Close" }) })] }) }) }), _jsx(Suspense, { fallback: _jsx(Spinner, { className: classes.spinner }), children: _jsx(Content, {}) })] }) }) }) }) }));
203
+ }
204
+ };
205
+ // Set the container element to be a flex container so that the tool can be displayed properly.
206
+ const originalContainerElementDisplay = containerElement.style.display;
207
+ containerElement.style.display = "flex";
208
+ // Create and render the react root component.
209
+ const reactRoot = createRoot(containerElement);
210
+ reactRoot.render(createElement(modularToolRootComponent));
211
+ let disposed = false;
212
+ return {
213
+ dispose: () => {
214
+ // Unmount and restore the original container element display.
215
+ if (!disposed) {
216
+ disposed = true;
217
+ reactRoot.unmount();
218
+ containerElement.style.display = originalContainerElementDisplay;
219
+ }
220
+ },
221
+ };
222
+ }
223
+ //# sourceMappingURL=modularTool.js.map