@babylonjs/shared-ui-components 9.10.0 → 9.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mcp/mcpEditorSessionConnection.d.ts +38 -0
- package/mcp/mcpEditorSessionConnection.js +76 -0
- package/mcp/mcpEditorSessionConnection.js.map +1 -0
- package/modularTool/contexts/teachingMomentsContext.d.ts +15 -0
- package/modularTool/contexts/teachingMomentsContext.js +10 -0
- package/modularTool/contexts/teachingMomentsContext.js.map +1 -0
- package/modularTool/hooks/teachingMomentHooks.js +3 -0
- package/modularTool/hooks/teachingMomentHooks.js.map +1 -1
- package/modularTool/modularTool.d.ts +4 -0
- package/modularTool/modularTool.js +16 -10
- package/modularTool/modularTool.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options used to open an MCP editor session event stream.
|
|
3
|
+
*/
|
|
4
|
+
export interface IMcpEditorSessionEventSourceOptions {
|
|
5
|
+
/** Base session URL, such as `http://localhost:3001/session/<id>`. */
|
|
6
|
+
sessionUrl: string;
|
|
7
|
+
/** Called whenever the server sends a document update. */
|
|
8
|
+
onDocument: (document: unknown) => void;
|
|
9
|
+
/** Called when the server explicitly closes the session. */
|
|
10
|
+
onSessionClosed: (reason: string) => void;
|
|
11
|
+
/** Called when the EventSource reports a connection error. */
|
|
12
|
+
onConnectionError: () => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Normalize a user-provided MCP editor session URL.
|
|
16
|
+
* @param sessionUrl - The URL entered by the user or returned by an MCP tool.
|
|
17
|
+
* @returns The URL without trailing slash characters.
|
|
18
|
+
*/
|
|
19
|
+
export declare function NormalizeMcpEditorSessionUrl(sessionUrl: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Open an EventSource for server-to-editor MCP session updates.
|
|
22
|
+
* @param options - Event stream options and callbacks.
|
|
23
|
+
* @returns The opened EventSource. Call `CloseMcpEditorSessionEventSource` to disconnect it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function OpenMcpEditorSessionEventSource(options: IMcpEditorSessionEventSourceOptions): EventSource;
|
|
26
|
+
/**
|
|
27
|
+
* Close an MCP editor session EventSource if one is active.
|
|
28
|
+
* @param eventSource - EventSource to close.
|
|
29
|
+
*/
|
|
30
|
+
export declare function CloseMcpEditorSessionEventSource(eventSource: EventSource | null | undefined): void;
|
|
31
|
+
/**
|
|
32
|
+
* Post a document JSON payload to an MCP editor session.
|
|
33
|
+
* @param sessionUrl - Base session URL, such as `http://localhost:3001/session/<id>`.
|
|
34
|
+
* @param document - Serialized JSON document to send to the MCP server.
|
|
35
|
+
* @param legacyDocumentRoute - Optional compatibility route to try when `/document` is unavailable.
|
|
36
|
+
* @returns The final fetch response from the standard or compatibility route.
|
|
37
|
+
*/
|
|
38
|
+
export declare function PostMcpEditorSessionDocumentAsync(sessionUrl: string, document: string, legacyDocumentRoute?: string): Promise<Response>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const DocumentRoute = "document";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a user-provided MCP editor session URL.
|
|
4
|
+
* @param sessionUrl - The URL entered by the user or returned by an MCP tool.
|
|
5
|
+
* @returns The URL without trailing slash characters.
|
|
6
|
+
*/
|
|
7
|
+
export function NormalizeMcpEditorSessionUrl(sessionUrl) {
|
|
8
|
+
return sessionUrl.replace(/\/+$/, "");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Open an EventSource for server-to-editor MCP session updates.
|
|
12
|
+
* @param options - Event stream options and callbacks.
|
|
13
|
+
* @returns The opened EventSource. Call `CloseMcpEditorSessionEventSource` to disconnect it.
|
|
14
|
+
*/
|
|
15
|
+
export function OpenMcpEditorSessionEventSource(options) {
|
|
16
|
+
const normalizedUrl = NormalizeMcpEditorSessionUrl(options.sessionUrl);
|
|
17
|
+
const eventSource = new EventSource(`${normalizedUrl}/events`);
|
|
18
|
+
eventSource.onmessage = (event) => {
|
|
19
|
+
try {
|
|
20
|
+
options.onDocument(JSON.parse(event.data));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Ignore malformed document updates and keep the live session open.
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
eventSource.addEventListener("session-closed", (event) => {
|
|
27
|
+
options.onSessionClosed(ReadSessionClosedReason(event));
|
|
28
|
+
eventSource.close();
|
|
29
|
+
});
|
|
30
|
+
eventSource.onerror = () => {
|
|
31
|
+
options.onConnectionError();
|
|
32
|
+
eventSource.close();
|
|
33
|
+
};
|
|
34
|
+
return eventSource;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Close an MCP editor session EventSource if one is active.
|
|
38
|
+
* @param eventSource - EventSource to close.
|
|
39
|
+
*/
|
|
40
|
+
export function CloseMcpEditorSessionEventSource(eventSource) {
|
|
41
|
+
eventSource?.close();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Post a document JSON payload to an MCP editor session.
|
|
45
|
+
* @param sessionUrl - Base session URL, such as `http://localhost:3001/session/<id>`.
|
|
46
|
+
* @param document - Serialized JSON document to send to the MCP server.
|
|
47
|
+
* @param legacyDocumentRoute - Optional compatibility route to try when `/document` is unavailable.
|
|
48
|
+
* @returns The final fetch response from the standard or compatibility route.
|
|
49
|
+
*/
|
|
50
|
+
export async function PostMcpEditorSessionDocumentAsync(sessionUrl, document, legacyDocumentRoute) {
|
|
51
|
+
const normalizedUrl = NormalizeMcpEditorSessionUrl(sessionUrl);
|
|
52
|
+
const response = await PostDocumentToRouteAsync(normalizedUrl, DocumentRoute, document);
|
|
53
|
+
if (response.ok || !legacyDocumentRoute || (response.status !== 404 && response.status !== 405)) {
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
return await PostDocumentToRouteAsync(normalizedUrl, legacyDocumentRoute.replace(/^\//, ""), document);
|
|
57
|
+
}
|
|
58
|
+
async function PostDocumentToRouteAsync(sessionUrl, route, document) {
|
|
59
|
+
const headers = new Headers();
|
|
60
|
+
headers.set("Content-Type", "application/json");
|
|
61
|
+
return await fetch(`${sessionUrl}/${route}`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers,
|
|
64
|
+
body: document,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function ReadSessionClosedReason(event) {
|
|
68
|
+
try {
|
|
69
|
+
const data = JSON.parse(event.data);
|
|
70
|
+
return typeof data.reason === "string" ? data.reason : "Session closed by MCP server";
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return "Session closed by MCP server";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=mcpEditorSessionConnection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpEditorSessionConnection.js","sourceRoot":"","sources":["../../../../dev/sharedUiComponents/src/mcp/mcpEditorSessionConnection.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,UAAU,CAAC;AAgBjC;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,UAAkB;IAC3D,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAAC,OAA4C;IACxF,MAAM,aAAa,GAAG,4BAA4B,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACvE,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,GAAG,aAAa,SAAS,CAAC,CAAC;IAE/D,WAAW,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE;QAC9B,IAAI,CAAC;YACD,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACL,oEAAoE;QACxE,CAAC;IACL,CAAC,CAAC;IAEF,WAAW,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;QACrD,OAAO,CAAC,eAAe,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC;QACxD,WAAW,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,OAAO,GAAG,GAAG,EAAE;QACvB,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAC5B,WAAW,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC,CAAC;IAEF,OAAO,WAAW,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gCAAgC,CAAC,WAA2C;IACxF,WAAW,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CAAC,UAAkB,EAAE,QAAgB,EAAE,mBAA4B;IACtH,MAAM,aAAa,GAAG,4BAA4B,CAAC,UAAU,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAAC,aAAa,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;IACxF,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,mBAAmB,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;QAC9F,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,OAAO,MAAM,wBAAwB,CAAC,aAAa,EAAE,mBAAmB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC3G,CAAC;AAED,KAAK,UAAU,wBAAwB,CAAC,UAAkB,EAAE,KAAa,EAAE,QAAgB;IACvF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;IAEhD,OAAO,MAAM,KAAK,CAAC,GAAG,UAAU,IAAI,KAAK,EAAE,EAAE;QACzC,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,QAAQ;KACjB,CAAC,CAAC;AACP,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAY;IACzC,IAAI,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAE,KAAsB,CAAC,IAAI,CAAC,CAAC;QACtD,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,8BAA8B,CAAC;IAC1F,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,8BAA8B,CAAC;IAC1C,CAAC;AACL,CAAC","sourcesContent":["const DocumentRoute = \"document\";\n\n/**\n * Options used to open an MCP editor session event stream.\n */\nexport interface IMcpEditorSessionEventSourceOptions {\n /** Base session URL, such as `http://localhost:3001/session/<id>`. */\n sessionUrl: string;\n /** Called whenever the server sends a document update. */\n onDocument: (document: unknown) => void;\n /** Called when the server explicitly closes the session. */\n onSessionClosed: (reason: string) => void;\n /** Called when the EventSource reports a connection error. */\n onConnectionError: () => void;\n}\n\n/**\n * Normalize a user-provided MCP editor session URL.\n * @param sessionUrl - The URL entered by the user or returned by an MCP tool.\n * @returns The URL without trailing slash characters.\n */\nexport function NormalizeMcpEditorSessionUrl(sessionUrl: string): string {\n return sessionUrl.replace(/\\/+$/, \"\");\n}\n\n/**\n * Open an EventSource for server-to-editor MCP session updates.\n * @param options - Event stream options and callbacks.\n * @returns The opened EventSource. Call `CloseMcpEditorSessionEventSource` to disconnect it.\n */\nexport function OpenMcpEditorSessionEventSource(options: IMcpEditorSessionEventSourceOptions): EventSource {\n const normalizedUrl = NormalizeMcpEditorSessionUrl(options.sessionUrl);\n const eventSource = new EventSource(`${normalizedUrl}/events`);\n\n eventSource.onmessage = (event) => {\n try {\n options.onDocument(JSON.parse(event.data));\n } catch {\n // Ignore malformed document updates and keep the live session open.\n }\n };\n\n eventSource.addEventListener(\"session-closed\", (event) => {\n options.onSessionClosed(ReadSessionClosedReason(event));\n eventSource.close();\n });\n\n eventSource.onerror = () => {\n options.onConnectionError();\n eventSource.close();\n };\n\n return eventSource;\n}\n\n/**\n * Close an MCP editor session EventSource if one is active.\n * @param eventSource - EventSource to close.\n */\nexport function CloseMcpEditorSessionEventSource(eventSource: EventSource | null | undefined): void {\n eventSource?.close();\n}\n\n/**\n * Post a document JSON payload to an MCP editor session.\n * @param sessionUrl - Base session URL, such as `http://localhost:3001/session/<id>`.\n * @param document - Serialized JSON document to send to the MCP server.\n * @param legacyDocumentRoute - Optional compatibility route to try when `/document` is unavailable.\n * @returns The final fetch response from the standard or compatibility route.\n */\nexport async function PostMcpEditorSessionDocumentAsync(sessionUrl: string, document: string, legacyDocumentRoute?: string): Promise<Response> {\n const normalizedUrl = NormalizeMcpEditorSessionUrl(sessionUrl);\n const response = await PostDocumentToRouteAsync(normalizedUrl, DocumentRoute, document);\n if (response.ok || !legacyDocumentRoute || (response.status !== 404 && response.status !== 405)) {\n return response;\n }\n\n return await PostDocumentToRouteAsync(normalizedUrl, legacyDocumentRoute.replace(/^\\//, \"\"), document);\n}\n\nasync function PostDocumentToRouteAsync(sessionUrl: string, route: string, document: string): Promise<Response> {\n const headers = new Headers();\n headers.set(\"Content-Type\", \"application/json\");\n\n return await fetch(`${sessionUrl}/${route}`, {\n method: \"POST\",\n headers,\n body: document,\n });\n}\n\nfunction ReadSessionClosedReason(event: Event): string {\n try {\n const data = JSON.parse((event as MessageEvent).data);\n return typeof data.reason === \"string\" ? data.reason : \"Session closed by MCP server\";\n } catch {\n return \"Session closed by MCP server\";\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type TeachingMomentsContext = {
|
|
2
|
+
/**
|
|
3
|
+
* When true, all teaching moments are suppressed regardless of any caller-supplied
|
|
4
|
+
* `suppress` argument and regardless of whether the user has previously dismissed
|
|
5
|
+
* the teaching moment.
|
|
6
|
+
*/
|
|
7
|
+
disabled: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const TeachingMomentsContext: import("react").Context<TeachingMomentsContext>;
|
|
10
|
+
/**
|
|
11
|
+
* Returns the teaching moments context provided by the surrounding modular tool framework.
|
|
12
|
+
* @returns The current teaching moments context.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useTeachingMomentsContext(): TeachingMomentsContext;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
export const TeachingMomentsContext = createContext({ disabled: false });
|
|
3
|
+
/**
|
|
4
|
+
* Returns the teaching moments context provided by the surrounding modular tool framework.
|
|
5
|
+
* @returns The current teaching moments context.
|
|
6
|
+
*/
|
|
7
|
+
export function useTeachingMomentsContext() {
|
|
8
|
+
return useContext(TeachingMomentsContext);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=teachingMomentsContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teachingMomentsContext.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/contexts/teachingMomentsContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAWlD,MAAM,CAAC,MAAM,sBAAsB,GAAG,aAAa,CAAyB,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAEjG;;;GAGG;AACH,MAAM,UAAU,yBAAyB;IACrC,OAAO,UAAU,CAAC,sBAAsB,CAAC,CAAC;AAC9C,CAAC","sourcesContent":["import { createContext, useContext } from \"react\";\r\n\r\ntype TeachingMomentsContext = {\r\n /**\r\n * When true, all teaching moments are suppressed regardless of any caller-supplied\r\n * `suppress` argument and regardless of whether the user has previously dismissed\r\n * the teaching moment.\r\n */\r\n disabled: boolean;\r\n};\r\n\r\nexport const TeachingMomentsContext = createContext<TeachingMomentsContext>({ disabled: false });\r\n\r\n/**\r\n * Returns the teaching moments context provided by the surrounding modular tool framework.\r\n * @returns The current teaching moments context.\r\n */\r\nexport function useTeachingMomentsContext(): TeachingMomentsContext {\r\n return useContext(TeachingMomentsContext);\r\n}\r\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { useSetting } from "./settingsHooks.js";
|
|
3
|
+
import { useTeachingMomentsContext } from "../contexts/teachingMomentsContext.js";
|
|
3
4
|
import { AsyncLock } from "@babylonjs/core/Misc/asyncLock.js";
|
|
4
5
|
import { Deferred } from "@babylonjs/core/Misc/deferred.js";
|
|
5
6
|
const SequencerLock = new AsyncLock();
|
|
@@ -10,6 +11,8 @@ const SequencerLock = new AsyncLock();
|
|
|
10
11
|
*/
|
|
11
12
|
export function MakeTeachingMoment(name) {
|
|
12
13
|
return (suppress) => {
|
|
14
|
+
const { disabled } = useTeachingMomentsContext();
|
|
15
|
+
suppress = suppress || disabled;
|
|
13
16
|
const [hasDisplayed, setHasDisplayed, resetDisplayed] = useSetting({ key: `TeachingMoments/${name}`, defaultValue: false });
|
|
14
17
|
const [shouldDisplay, setShouldDisplay] = useState(false);
|
|
15
18
|
const deferredRef = useRef();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teachingMomentHooks.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/teachingMomentHooks.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"teachingMomentHooks.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/modularTool/hooks/teachingMomentHooks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Nullable } from \"core/index\";\r\n\r\nimport { type OnOpenChangeData, type PositioningImperativeRef } from \"@fluentui/react-components\";\r\n\r\nimport { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useSetting } from \"./settingsHooks\";\r\nimport { useTeachingMomentsContext } from \"../contexts/teachingMomentsContext\";\r\n\r\nimport { AsyncLock } from \"core/Misc/asyncLock\";\r\nimport { Deferred } from \"core/Misc/deferred\";\r\n\r\nconst SequencerLock = new AsyncLock();\r\n\r\n/**\r\n * Creates a hook for managing teaching moment state.\r\n * @param name The unique name of the teaching moment.\r\n * @returns A hook that returns the teaching moment state.\r\n */\r\nexport function MakeTeachingMoment(name: string) {\r\n return (suppress?: boolean) => {\r\n const { disabled } = useTeachingMomentsContext();\r\n suppress = suppress || disabled;\r\n const [hasDisplayed, setHasDisplayed, resetDisplayed] = useSetting({ key: `TeachingMoments/${name}`, defaultValue: false });\r\n const [shouldDisplay, setShouldDisplay] = useState(false);\r\n\r\n const deferredRef = useRef<Deferred<void>>();\r\n\r\n useEffect(() => {\r\n if (!hasDisplayed && !suppress && !deferredRef.current) {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n SequencerLock.lockAsync(async () => {\r\n deferredRef.current = new Deferred<void>();\r\n setShouldDisplay(true);\r\n // Just hold the lock until the hook cleanup, which is effectively component unmount (e.g. the teaching moment is dismissed).\r\n await deferredRef.current.promise;\r\n });\r\n }\r\n\r\n return () => {\r\n deferredRef.current?.resolve();\r\n };\r\n }, [hasDisplayed, suppress]);\r\n\r\n const onDismissed = useCallback(() => {\r\n setHasDisplayed(true);\r\n deferredRef.current?.resolve();\r\n deferredRef.current = undefined;\r\n setShouldDisplay(false);\r\n }, []);\r\n\r\n return {\r\n shouldDisplay,\r\n onDismissed,\r\n reset: resetDisplayed,\r\n } as const;\r\n };\r\n}\r\n\r\n/**\r\n * Creates a hook for managing teaching moment state for a dialog.\r\n * @param name The unique name of the teaching moment.\r\n * @returns A hook that returns the teaching moment state for a dialog.\r\n */\r\nexport function MakeDialogTeachingMoment(name: string) {\r\n const useTeachingMoment = MakeTeachingMoment(name);\r\n\r\n return (suppress?: boolean) => {\r\n const { shouldDisplay, onDismissed, reset } = useTeachingMoment(suppress);\r\n\r\n const onOpenChange = useCallback((e: unknown, data: OnOpenChangeData) => {\r\n if (!data.open) {\r\n onDismissed();\r\n }\r\n }, []);\r\n\r\n return {\r\n shouldDisplay,\r\n onOpenChange,\r\n reset,\r\n } as const;\r\n };\r\n}\r\n\r\n/**\r\n * Creates a hook for managing teaching moment state for a popover.\r\n * @param name The unique name of the teaching moment.\r\n * @returns A hook that returns the teaching moment state for a popover.\r\n */\r\nexport function MakePopoverTeachingMoment(name: string) {\r\n const useDialogTeachingMoment = MakeDialogTeachingMoment(name);\r\n\r\n return (suppress?: boolean) => {\r\n const [target, setTarget] = useState<Nullable<HTMLElement>>(null);\r\n const [positioningRef, setPositioningRef] = useState<Nullable<PositioningImperativeRef>>(null);\r\n\r\n const { shouldDisplay, onOpenChange, reset } = useDialogTeachingMoment(suppress || !target || !positioningRef);\r\n\r\n useEffect(() => {\r\n if (target && positioningRef) {\r\n positioningRef.setTarget(target);\r\n }\r\n }, [target, positioningRef]);\r\n\r\n return {\r\n shouldDisplay,\r\n positioningRef: setPositioningRef,\r\n targetRef: setTarget,\r\n onOpenChange,\r\n reset,\r\n } as const;\r\n };\r\n}\r\n"]}
|
|
@@ -32,6 +32,10 @@ export type ModularToolOptions = {
|
|
|
32
32
|
* will be resolved from this parent.
|
|
33
33
|
*/
|
|
34
34
|
parentContainer?: ServiceContainer;
|
|
35
|
+
/**
|
|
36
|
+
* When true, all teaching moments are disabled and will not be shown.
|
|
37
|
+
*/
|
|
38
|
+
disableTeachingMoments?: boolean;
|
|
35
39
|
} & ShellServiceOptions;
|
|
36
40
|
/**
|
|
37
41
|
* Creates a modular tool with a base set of common tool services, including the toolbar/side pane basic UI layout.
|
|
@@ -17,6 +17,7 @@ import { Theme } from "./components/theme.js";
|
|
|
17
17
|
import { DialogContext } from "./contexts/dialogContext.js";
|
|
18
18
|
import { ExtensionManagerContext } from "./contexts/extensionManagerContext.js";
|
|
19
19
|
import { SettingsStoreContext } from "./contexts/settingsContext.js";
|
|
20
|
+
import { TeachingMomentsContext } from "./contexts/teachingMomentsContext.js";
|
|
20
21
|
import { ReactContextServiceIdentity } from "./services/reactContextService.js";
|
|
21
22
|
import { ThemeSelectorServiceDefinition } from "./services/themeSelectorService.js";
|
|
22
23
|
const useStyles = makeStyles({
|
|
@@ -72,7 +73,8 @@ const ReactContextsWrapper = ({ contexts, children }) => {
|
|
|
72
73
|
* @returns A token that can be used to dispose of the tool.
|
|
73
74
|
*/
|
|
74
75
|
export function MakeModularTool(options) {
|
|
75
|
-
const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer } = options;
|
|
76
|
+
const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer, disableTeachingMoments = false } = options;
|
|
77
|
+
const teachingMomentsContextValue = { disabled: disableTeachingMoments };
|
|
76
78
|
// Create the settings store immediately as it will be exposed to services and through React context.
|
|
77
79
|
const settingsStore = new SettingsStore(namespace);
|
|
78
80
|
// If a theme mode is provided, just write the setting so it is the active theme.
|
|
@@ -83,7 +85,6 @@ export function MakeModularTool(options) {
|
|
|
83
85
|
const disposeDeferred = new Deferred();
|
|
84
86
|
const modularToolRootComponent = () => {
|
|
85
87
|
const classes = useStyles();
|
|
86
|
-
const [extensionManagerContext, setExtensionManagerContext] = useState();
|
|
87
88
|
const [requiredExtensions, setRequiredExtensions] = useState();
|
|
88
89
|
const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState();
|
|
89
90
|
const [extensionInstallError, setExtensionInstallError] = useState();
|
|
@@ -122,7 +123,13 @@ export function MakeModularTool(options) {
|
|
|
122
123
|
case "update":
|
|
123
124
|
return state.map((e) => (e.provider === action.provider ? { ...e, value: action.value } : e));
|
|
124
125
|
}
|
|
125
|
-
}, [
|
|
126
|
+
}, [
|
|
127
|
+
// Static contexts seeded into the wrapper so they don't add JSX nesting below.
|
|
128
|
+
// Negative orders keep them as the outermost providers (lowest order = outermost via reduceRight).
|
|
129
|
+
{ provider: TeachingMomentsContext.Provider, value: teachingMomentsContextValue, order: -2 },
|
|
130
|
+
{ provider: SettingsStoreContext.Provider, value: settingsStore, order: -1 },
|
|
131
|
+
{ provider: ExtensionManagerContext.Provider, value: undefined, order: 0 },
|
|
132
|
+
]);
|
|
126
133
|
// This is the main async initialization.
|
|
127
134
|
useEffect(() => {
|
|
128
135
|
const initializeExtensionManagerAsync = async () => {
|
|
@@ -139,14 +146,13 @@ export function MakeModularTool(options) {
|
|
|
139
146
|
produces: [ReactContextServiceIdentity],
|
|
140
147
|
factory: () => ({
|
|
141
148
|
addContext(provider, initialValue, options) {
|
|
142
|
-
|
|
143
|
-
updateContexts({ type: "add", entry: { provider: typedProvider, value: initialValue, order: options?.order ?? 0 } });
|
|
149
|
+
updateContexts({ type: "add", entry: { provider: provider, value: initialValue, order: options?.order ?? 0 } });
|
|
144
150
|
return {
|
|
145
151
|
updateValue: (newValue) => {
|
|
146
|
-
updateContexts({ type: "update", provider:
|
|
152
|
+
updateContexts({ type: "update", provider: provider, value: newValue });
|
|
147
153
|
},
|
|
148
154
|
dispose: () => {
|
|
149
|
-
updateContexts({ type: "remove", provider:
|
|
155
|
+
updateContexts({ type: "remove", provider: provider });
|
|
150
156
|
},
|
|
151
157
|
};
|
|
152
158
|
},
|
|
@@ -227,7 +233,7 @@ export function MakeModularTool(options) {
|
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
// Set the contexts.
|
|
230
|
-
|
|
236
|
+
updateContexts({ type: "update", provider: ExtensionManagerContext.Provider, value: { extensionManager } });
|
|
231
237
|
return () => {
|
|
232
238
|
extensionManager.dispose();
|
|
233
239
|
serviceContainer.dispose();
|
|
@@ -274,12 +280,12 @@ export function MakeModularTool(options) {
|
|
|
274
280
|
})();
|
|
275
281
|
// Show a spinner until a main view has been set.
|
|
276
282
|
if (!rootComponentService) {
|
|
277
|
-
return (_jsx(ReactContextsWrapper, { contexts: contexts, children: _jsx(
|
|
283
|
+
return (_jsx(ReactContextsWrapper, { contexts: contexts, children: _jsx(Theme, { className: classes.app, targetDocument: targetDocument, children: _jsx(Spinner, { className: classes.spinner }) }) }));
|
|
278
284
|
}
|
|
279
285
|
else {
|
|
280
286
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
281
287
|
const Content = rootComponentService.rootComponent;
|
|
282
|
-
return (_jsx(ReactContextsWrapper, { contexts: contexts, children: _jsx(
|
|
288
|
+
return (_jsx(ReactContextsWrapper, { contexts: contexts, children: _jsx(Theme, { className: classes.app, targetDocument: targetDocument, children: _jsxs(ToastProvider, { imperativeRef: setToastHandle, 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(Dialog, { open: !!currentDialog, modalType: "alert", children: _jsx(DialogSurface, { children: _jsxs(DialogBody, { children: [_jsx(DialogTitle, { children: _jsxs("div", { className: classes.dialogTitle, children: [currentDialogIcon, currentDialog?.title] }) }), currentDialog?.content && _jsx(DialogContent, { children: currentDialog.content }), _jsx(DialogActions, { children: _jsx(Button, { appearance: "primary", onClick: onDismissDialog, children: "OK" }) })] }) }) }), _jsx(Suspense, { fallback: _jsx(Spinner, { className: classes.spinner }), children: _jsx(DialogContext.Provider, { value: { showDialog }, children: _jsx(Content, {}) }) })] }) }) }));
|
|
283
289
|
}
|
|
284
290
|
};
|
|
285
291
|
// Derive the target document from the container element. When the container is in a popup
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modularTool.js","sourceRoot":"","sources":["../../../../dev/sharedUiComponents/src/modularTool/modularTool.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\r\n type ComponentType,\r\n type Context,\r\n type FunctionComponent,\r\n type PropsWithChildren,\r\n type ReactNode,\r\n createElement,\r\n Suspense,\r\n useCallback,\r\n useEffect,\r\n useReducer,\r\n useState,\r\n} from \"react\";\r\n\r\nimport { type IExtensionFeed } from \"./extensibility/extensionFeed\";\r\nimport { type IExtension, type InstallFailedInfo, ExtensionManager } from \"./extensibility/extensionManager\";\r\nimport { type WeaklyTypedServiceDefinition, ServiceContainer } from \"./modularity/serviceContainer\";\r\nimport { type ISettingsStore, SettingsStore, SettingsStoreIdentity } from \"./services/settingsStore\";\r\nimport { type IRootComponentService, type ShellServiceOptions, MakeShellServiceDefinition, RootComponentServiceIdentity } from \"./services/shellService\";\r\nimport { type ThemeMode, ThemeModeSettingDescriptor, ThemeServiceDefinition } from \"./services/themeService\";\r\n\r\nimport {\r\n Body1,\r\n Button,\r\n Dialog,\r\n DialogActions,\r\n DialogBody,\r\n DialogContent,\r\n DialogSurface,\r\n DialogTitle,\r\n List,\r\n ListItem,\r\n makeStyles,\r\n Spinner,\r\n tokens,\r\n} from \"@fluentui/react-components\";\r\nimport { CheckmarkCircleRegular, ErrorCircleRegular, InfoRegular, WarningRegular } from \"@fluentui/react-icons\";\r\nimport { createRoot } from \"react-dom/client\";\r\n\r\nimport { Deferred } from \"core/Misc/deferred\";\r\nimport { Logger } from \"core/Misc/logger\";\r\nimport { type ToastHandle, type ToastOptions, ToastProvider } from \"shared-ui-components/fluent/primitives/toast\";\r\nimport { type DialogOptions, type IDialogService, DialogServiceIdentity } from \"./services/dialogService\";\r\nimport { type IToastService, ToastServiceIdentity } from \"./services/toastService\";\r\nimport { Theme } from \"./components/theme\";\r\nimport { DialogContext } from \"./contexts/dialogContext\";\r\nimport { ExtensionManagerContext } from \"./contexts/extensionManagerContext\";\r\nimport { SettingsStoreContext } from \"./contexts/settingsContext\";\r\nimport { type IReactContextService, type ReactContextHandle, ReactContextServiceIdentity } from \"./services/reactContextService\";\r\nimport { ThemeSelectorServiceDefinition } from \"./services/themeSelectorService\";\r\n\r\nconst useStyles = makeStyles({\r\n app: {\r\n colorScheme: \"light dark\",\r\n flexGrow: 1,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n backgroundColor: tokens.colorTransparentBackground,\r\n },\r\n spinner: {\r\n flexGrow: 1,\r\n animationDuration: \"1s\",\r\n animationName: {\r\n from: { opacity: 0 },\r\n to: { opacity: 1 },\r\n },\r\n },\r\n extensionErrorTitleDiv: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n alignItems: \"center\",\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n extensionErrorIcon: {\r\n color: tokens.colorPaletteRedForeground1,\r\n },\r\n dialogTitle: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n alignItems: \"center\",\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n dialogIconSuccess: {\r\n color: tokens.colorStatusSuccessForeground1,\r\n },\r\n dialogIconError: {\r\n color: tokens.colorStatusDangerForeground1,\r\n },\r\n dialogIconWarning: {\r\n color: tokens.colorStatusWarningForeground1,\r\n },\r\n dialogIconInfo: {\r\n color: tokens.colorBrandForeground1,\r\n },\r\n});\r\n\r\ntype ReactContextEntry = {\r\n provider: Context<unknown>[\"Provider\"];\r\n value: unknown;\r\n order: number;\r\n};\r\n\r\ntype ReactContextAction =\r\n | { type: \"add\"; entry: ReactContextEntry }\r\n | { type: \"remove\"; provider: Context<unknown>[\"Provider\"] }\r\n | { type: \"update\"; provider: Context<unknown>[\"Provider\"]; value: unknown };\r\n\r\ntype DialogQueueAction = { type: \"enqueue\"; options: DialogOptions } | { type: \"dequeue\" };\r\n\r\nconst ReactContextsWrapper: FunctionComponent<PropsWithChildren<{ contexts: readonly Readonly<ReactContextEntry>[] }>> = ({ contexts, children }) => {\r\n return <>{contexts.reduceRight<ReactNode>((acc, entry) => createElement(entry.provider, { value: entry.value }, acc), children)}</>;\r\n};\r\n\r\nexport type ModularToolOptions = {\r\n /**\r\n * The namespace for the tool, used for scoping persisted settings and other storage.\r\n */\r\n namespace: string;\r\n\r\n /**\r\n * The container element where the tool will be rendered.\r\n */\r\n containerElement: HTMLElement;\r\n\r\n /**\r\n * The service definitions to be registered with the tool.\r\n */\r\n serviceDefinitions: readonly WeaklyTypedServiceDefinition[];\r\n\r\n /**\r\n * 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.\r\n */\r\n themeMode?: ThemeMode;\r\n\r\n /**\r\n * Whether to show the theme selector in the toolbar. Default is true.\r\n */\r\n showThemeSelector?: boolean;\r\n\r\n /**\r\n * The extension feeds that provide optional extensions the user can install.\r\n */\r\n extensionFeeds?: readonly IExtensionFeed[];\r\n\r\n /**\r\n * An optional parent ServiceContainer. Dependencies not found in the tool's own container\r\n * will be resolved from this parent.\r\n */\r\n parentContainer?: ServiceContainer;\r\n} & ShellServiceOptions;\r\n\r\n/**\r\n * Creates a modular tool with a base set of common tool services, including the toolbar/side pane basic UI layout.\r\n * @param options The options for the tool.\r\n * @returns A token that can be used to dispose of the tool.\r\n */\r\nexport function MakeModularTool(options: ModularToolOptions) {\r\n const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer } = options;\r\n\r\n // Create the settings store immediately as it will be exposed to services and through React context.\r\n const settingsStore = new SettingsStore(namespace);\r\n\r\n // If a theme mode is provided, just write the setting so it is the active theme.\r\n if (themeMode) {\r\n settingsStore.writeSetting(ThemeModeSettingDescriptor, themeMode);\r\n }\r\n\r\n // This deferred resolves once the React effect cleanup (which disposes the ServiceContainer) is complete.\r\n const disposeDeferred = new Deferred<void>();\r\n\r\n const modularToolRootComponent: FunctionComponent = () => {\r\n const classes = useStyles();\r\n const [extensionManagerContext, setExtensionManagerContext] = useState<ExtensionManagerContext>();\r\n const [requiredExtensions, setRequiredExtensions] = useState<string[]>();\r\n const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState<Deferred<boolean>>();\r\n const [extensionInstallError, setExtensionInstallError] = useState<InstallFailedInfo>();\r\n\r\n const [toastHandle, setToastHandle] = useState<ToastHandle | null>(null);\r\n const [toastQueue, setToastQueue] = useState<{ message: string; options?: ToastOptions }[]>([]);\r\n\r\n useEffect(() => {\r\n if (toastHandle && toastQueue.length > 0) {\r\n for (const { message, options } of toastQueue) {\r\n toastHandle.showToast(message, options);\r\n }\r\n setToastQueue([]);\r\n }\r\n }, [toastHandle, toastQueue]);\r\n\r\n // Queue of dialogs to display. We show one at a time (the one at the head of the queue).\r\n const [dialogQueue, dispatchDialogQueue] = useReducer((state: readonly DialogOptions[], action: DialogQueueAction): readonly DialogOptions[] => {\r\n switch (action.type) {\r\n case \"enqueue\":\r\n return [...state, action.options];\r\n case \"dequeue\":\r\n return state.slice(1);\r\n }\r\n }, []);\r\n\r\n const showDialog = useCallback((dialogOptions: DialogOptions) => {\r\n dispatchDialogQueue({ type: \"enqueue\", options: dialogOptions });\r\n }, []);\r\n\r\n const onDismissDialog = useCallback(() => {\r\n dispatchDialogQueue({ type: \"dequeue\" });\r\n }, []);\r\n\r\n const [rootComponentService, setRootComponentService] = useState<IRootComponentService>();\r\n\r\n const [contexts, updateContexts] = useReducer((state: ReactContextEntry[], action: ReactContextAction): ReactContextEntry[] => {\r\n switch (action.type) {\r\n case \"add\":\r\n return [...state, action.entry].sort((a, b) => a.order - b.order);\r\n case \"remove\":\r\n return state.filter((e) => e.provider !== action.provider);\r\n case \"update\":\r\n return state.map((e) => (e.provider === action.provider ? { ...e, value: action.value } : e));\r\n }\r\n }, []);\r\n\r\n // This is the main async initialization.\r\n useEffect(() => {\r\n const initializeExtensionManagerAsync = async () => {\r\n const serviceContainer = new ServiceContainer(\"ModularToolContainer\", parentContainer);\r\n\r\n // Expose the settings store as a service so other services can read/write settings.\r\n serviceContainer.addService<[ISettingsStore], []>({\r\n friendlyName: \"Settings Store\",\r\n produces: [SettingsStoreIdentity],\r\n factory: () => settingsStore,\r\n });\r\n\r\n // Expose the react context service so other services can add React context providers.\r\n serviceContainer.addService<[IReactContextService], []>({\r\n friendlyName: \"React Context Service\",\r\n produces: [ReactContextServiceIdentity],\r\n factory: (): IReactContextService => ({\r\n addContext<T>(provider: Context<T>[\"Provider\"], initialValue: T, options?: { order?: number }): ReactContextHandle<T> {\r\n const typedProvider = provider as Context<unknown>[\"Provider\"];\r\n updateContexts({ type: \"add\", entry: { provider: typedProvider, value: initialValue, order: options?.order ?? 0 } });\r\n return {\r\n updateValue: (newValue: T) => {\r\n updateContexts({ type: \"update\", provider: typedProvider, value: newValue });\r\n },\r\n dispose: () => {\r\n updateContexts({ type: \"remove\", provider: typedProvider });\r\n },\r\n };\r\n },\r\n }),\r\n });\r\n\r\n // Expose the toast service so non-React code (e.g. Observable callbacks) can show toasts.\r\n serviceContainer.addService<[IToastService], []>({\r\n friendlyName: \"Toast Service\",\r\n produces: [ToastServiceIdentity],\r\n factory: (): IToastService => ({\r\n showToast: (message, options) => {\r\n setToastQueue((prev) => [...prev, { message, options }]);\r\n },\r\n }),\r\n });\r\n\r\n // Expose the dialog service so non-React code (e.g. Observable callbacks) can show dialogs.\r\n serviceContainer.addService<[IDialogService], []>({\r\n friendlyName: \"Dialog Service\",\r\n produces: [DialogServiceIdentity],\r\n factory: (): IDialogService => ({ showDialog }),\r\n });\r\n\r\n // Register the shell service (top level toolbar/side pane UI layout).\r\n serviceContainer.addService(MakeShellServiceDefinition(options));\r\n\r\n // Register a service that simply consumes the services we need before first render.\r\n serviceContainer.addService<[], [IRootComponentService]>({\r\n friendlyName: \"Service Bootstrapper\",\r\n consumes: [RootComponentServiceIdentity],\r\n factory: (rootComponent) => {\r\n // Use function syntax for the state setter since the root component may be a function component.\r\n setRootComponentService(() => rootComponent);\r\n return {\r\n dispose: () => setRootComponentService(undefined),\r\n };\r\n },\r\n });\r\n\r\n // Register the theme service (exposes the current theme to other services).\r\n serviceContainer.addService(ThemeServiceDefinition);\r\n\r\n // Register the theme selector service (for selecting the theme) if theming is configured.\r\n if (showThemeSelector) {\r\n serviceContainer.addService(ThemeSelectorServiceDefinition);\r\n }\r\n\r\n // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.\r\n if (extensionFeeds.length > 0) {\r\n const { ExtensionListServiceDefinition } = await import(\"./services/extensionsListService\");\r\n serviceContainer.addService(ExtensionListServiceDefinition);\r\n }\r\n\r\n // Register all external services (that make up a unique tool).\r\n serviceContainer.addServices(...serviceDefinitions);\r\n\r\n // Create the extension manager, passing along the registry for runtime changes to the registered services.\r\n const extensionManager = await ExtensionManager.CreateAsync(namespace, serviceContainer, extensionFeeds, setExtensionInstallError);\r\n\r\n // Check query params for required extensions. This lets users share links with sets of extensions.\r\n const queryParams = new URLSearchParams(window.location.search);\r\n const requiredExtensions = queryParams.getAll(\"babylon.requiredExtension\");\r\n const uninstalledExtensions: IExtension[] = [];\r\n for (const requiredExtension of requiredExtensions) {\r\n // These could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.\r\n // eslint-disable-next-line no-await-in-loop\r\n const query = await extensionManager.queryExtensionsAsync(requiredExtension);\r\n // eslint-disable-next-line no-await-in-loop\r\n const extensions = await query.getExtensionsAsync(0, query.totalCount);\r\n for (const extension of extensions) {\r\n if (!extension.isInstalled) {\r\n uninstalledExtensions.push(extension);\r\n }\r\n }\r\n }\r\n\r\n // Check if any required extensions are uninstalled or disabled. If so, show a dialog to the user.\r\n if (uninstalledExtensions.length > 0) {\r\n setRequiredExtensions(uninstalledExtensions.map((extension) => extension.metadata.name));\r\n const deferred = new Deferred<boolean>();\r\n setRequiredExtensionsDeferred(deferred);\r\n if (await deferred.promise) {\r\n for (const extension of uninstalledExtensions) {\r\n // This could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.\r\n // eslint-disable-next-line no-await-in-loop\r\n await extension.installAsync();\r\n }\r\n }\r\n }\r\n\r\n // Set the contexts.\r\n setExtensionManagerContext({ extensionManager });\r\n\r\n return () => {\r\n extensionManager.dispose();\r\n serviceContainer.dispose();\r\n serviceContainer.dispose();\r\n };\r\n };\r\n\r\n const disposePromise = initializeExtensionManagerAsync();\r\n\r\n return () => {\r\n disposePromise\r\n // eslint-disable-next-line github/no-then\r\n .then((dispose) => dispose())\r\n // eslint-disable-next-line github/no-then\r\n .catch((error) => {\r\n Logger.Error(`Failed to dispose of the modular tool: ${error}`);\r\n })\r\n .finally(() => disposeDeferred.resolve());\r\n };\r\n }, []);\r\n\r\n const onAcceptRequiredExtensions = useCallback(() => {\r\n setRequiredExtensions(undefined);\r\n requiredExtensionsDeferred?.resolve(true);\r\n }, [setRequiredExtensions, requiredExtensionsDeferred]);\r\n\r\n const onRejectRequiredExtensions = useCallback(() => {\r\n setRequiredExtensions(undefined);\r\n requiredExtensionsDeferred?.resolve(false);\r\n }, [setRequiredExtensions, requiredExtensionsDeferred]);\r\n\r\n const onAcknowledgedExtensionInstallError = useCallback(() => {\r\n setExtensionInstallError(undefined);\r\n }, [setExtensionInstallError]);\r\n\r\n // The dialog at the head of the queue, if any, is the one currently being displayed.\r\n const currentDialog = dialogQueue[0];\r\n const currentDialogIcon = (() => {\r\n switch (currentDialog?.intent) {\r\n case \"success\":\r\n return <CheckmarkCircleRegular className={classes.dialogIconSuccess} />;\r\n case \"error\":\r\n return <ErrorCircleRegular className={classes.dialogIconError} />;\r\n case \"warning\":\r\n return <WarningRegular className={classes.dialogIconWarning} />;\r\n case \"info\":\r\n case undefined:\r\n return <InfoRegular className={classes.dialogIconInfo} />;\r\n }\r\n })();\r\n\r\n // Show a spinner until a main view has been set.\r\n if (!rootComponentService) {\r\n return (\r\n <ReactContextsWrapper contexts={contexts}>\r\n <SettingsStoreContext.Provider value={settingsStore}>\r\n <Theme className={classes.app} targetDocument={targetDocument}>\r\n <Spinner className={classes.spinner} />\r\n </Theme>\r\n </SettingsStoreContext.Provider>\r\n </ReactContextsWrapper>\r\n );\r\n } else {\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n const Content: ComponentType = rootComponentService.rootComponent;\r\n\r\n return (\r\n <ReactContextsWrapper contexts={contexts}>\r\n {/* Expose the settings store as a React context so that UI components can read/write\r\n settings without the ISettingsService needing to be explicitly passed around. */}\r\n <SettingsStoreContext.Provider value={settingsStore}>\r\n <ExtensionManagerContext.Provider value={extensionManagerContext}>\r\n <Theme className={classes.app} targetDocument={targetDocument}>\r\n <ToastProvider imperativeRef={setToastHandle}>\r\n <Dialog open={!!requiredExtensions} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>Required Extensions</DialogTitle>\r\n <DialogContent>\r\n Opening this URL requires the following extensions to be installed and enabled:\r\n <ul>\r\n {requiredExtensions?.map((name) => (\r\n <li key={name}>{name}</li>\r\n ))}\r\n </ul>\r\n </DialogContent>\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onAcceptRequiredExtensions}>\r\n Install & Enable\r\n </Button>\r\n <Button appearance=\"secondary\" onClick={onRejectRequiredExtensions}>\r\n No Thanks\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Dialog open={!!extensionInstallError} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>\r\n <div className={classes.extensionErrorTitleDiv}>\r\n Extension Install Error\r\n <ErrorCircleRegular className={classes.extensionErrorIcon} />\r\n </div>\r\n </DialogTitle>\r\n <DialogContent>\r\n <List>\r\n <ListItem>\r\n <Body1>{`Extension \"${extensionInstallError?.extension.name}\" failed to install and was removed.`}</Body1>\r\n </ListItem>\r\n <ListItem>\r\n <Body1>{`${extensionInstallError?.error}`}</Body1>\r\n </ListItem>\r\n </List>\r\n </DialogContent>\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onAcknowledgedExtensionInstallError}>\r\n Close\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Dialog open={!!currentDialog} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>\r\n <div className={classes.dialogTitle}>\r\n {currentDialogIcon}\r\n {currentDialog?.title}\r\n </div>\r\n </DialogTitle>\r\n {currentDialog?.content && <DialogContent>{currentDialog.content}</DialogContent>}\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onDismissDialog}>\r\n OK\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Suspense fallback={<Spinner className={classes.spinner} />}>\r\n <DialogContext.Provider value={{ showDialog }}>\r\n <Content />\r\n </DialogContext.Provider>\r\n </Suspense>\r\n </ToastProvider>\r\n </Theme>\r\n </ExtensionManagerContext.Provider>\r\n </SettingsStoreContext.Provider>\r\n </ReactContextsWrapper>\r\n );\r\n }\r\n };\r\n\r\n // Derive the target document from the container element. When the container is in a popup\r\n // window (or other document distinct from the main one), this is what makes Fluent inject\r\n // styles and render portals into the correct document.\r\n const containerOwnerDocument = containerElement.ownerDocument;\r\n const targetDocument = containerOwnerDocument && containerOwnerDocument !== document ? containerOwnerDocument : undefined;\r\n\r\n // Set the container element to be a flex container so that the tool can be displayed properly.\r\n const originalContainerElementDisplay = containerElement.style.display;\r\n containerElement.style.display = \"flex\";\r\n\r\n // Create and render the react root component.\r\n const reactRoot = createRoot(containerElement);\r\n reactRoot.render(createElement(modularToolRootComponent));\r\n\r\n let disposed = false;\r\n return {\r\n // eslint-disable-next-line @typescript-eslint/promise-function-async\r\n dispose: () => {\r\n // Unmount and restore the original container element display.\r\n if (!disposed) {\r\n disposed = true;\r\n reactRoot.unmount();\r\n containerElement.style.display = originalContainerElementDisplay;\r\n }\r\n // The promise resolves once the React effect cleanup\r\n // (which disposes the ServiceContainer) has completed.\r\n return disposeDeferred.promise;\r\n },\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"modularTool.js","sourceRoot":"","sources":["../../../../dev/sharedUiComponents/src/modularTool/modularTool.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\r\n type ComponentType,\r\n type Context,\r\n type FunctionComponent,\r\n type PropsWithChildren,\r\n type ReactNode,\r\n createElement,\r\n Suspense,\r\n useCallback,\r\n useEffect,\r\n useReducer,\r\n useState,\r\n} from \"react\";\r\n\r\nimport { type IExtensionFeed } from \"./extensibility/extensionFeed\";\r\nimport { type IExtension, type InstallFailedInfo, ExtensionManager } from \"./extensibility/extensionManager\";\r\nimport { type WeaklyTypedServiceDefinition, ServiceContainer } from \"./modularity/serviceContainer\";\r\nimport { type ISettingsStore, SettingsStore, SettingsStoreIdentity } from \"./services/settingsStore\";\r\nimport { type IRootComponentService, type ShellServiceOptions, MakeShellServiceDefinition, RootComponentServiceIdentity } from \"./services/shellService\";\r\nimport { type ThemeMode, ThemeModeSettingDescriptor, ThemeServiceDefinition } from \"./services/themeService\";\r\n\r\nimport {\r\n Body1,\r\n Button,\r\n Dialog,\r\n DialogActions,\r\n DialogBody,\r\n DialogContent,\r\n DialogSurface,\r\n DialogTitle,\r\n List,\r\n ListItem,\r\n makeStyles,\r\n Spinner,\r\n tokens,\r\n} from \"@fluentui/react-components\";\r\nimport { CheckmarkCircleRegular, ErrorCircleRegular, InfoRegular, WarningRegular } from \"@fluentui/react-icons\";\r\nimport { createRoot } from \"react-dom/client\";\r\n\r\nimport { Deferred } from \"core/Misc/deferred\";\r\nimport { Logger } from \"core/Misc/logger\";\r\nimport { type ToastHandle, type ToastOptions, ToastProvider } from \"shared-ui-components/fluent/primitives/toast\";\r\nimport { type DialogOptions, type IDialogService, DialogServiceIdentity } from \"./services/dialogService\";\r\nimport { type IToastService, ToastServiceIdentity } from \"./services/toastService\";\r\nimport { Theme } from \"./components/theme\";\r\nimport { DialogContext } from \"./contexts/dialogContext\";\r\nimport { ExtensionManagerContext } from \"./contexts/extensionManagerContext\";\r\nimport { SettingsStoreContext } from \"./contexts/settingsContext\";\r\nimport { TeachingMomentsContext } from \"./contexts/teachingMomentsContext\";\r\nimport { type IReactContextService, type ReactContextHandle, ReactContextServiceIdentity } from \"./services/reactContextService\";\r\nimport { ThemeSelectorServiceDefinition } from \"./services/themeSelectorService\";\r\n\r\nconst useStyles = makeStyles({\r\n app: {\r\n colorScheme: \"light dark\",\r\n flexGrow: 1,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n backgroundColor: tokens.colorTransparentBackground,\r\n },\r\n spinner: {\r\n flexGrow: 1,\r\n animationDuration: \"1s\",\r\n animationName: {\r\n from: { opacity: 0 },\r\n to: { opacity: 1 },\r\n },\r\n },\r\n extensionErrorTitleDiv: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n alignItems: \"center\",\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n extensionErrorIcon: {\r\n color: tokens.colorPaletteRedForeground1,\r\n },\r\n dialogTitle: {\r\n display: \"flex\",\r\n flexDirection: \"row\",\r\n alignItems: \"center\",\r\n gap: tokens.spacingHorizontalS,\r\n },\r\n dialogIconSuccess: {\r\n color: tokens.colorStatusSuccessForeground1,\r\n },\r\n dialogIconError: {\r\n color: tokens.colorStatusDangerForeground1,\r\n },\r\n dialogIconWarning: {\r\n color: tokens.colorStatusWarningForeground1,\r\n },\r\n dialogIconInfo: {\r\n color: tokens.colorBrandForeground1,\r\n },\r\n});\r\n\r\ntype ReactContextEntry = {\r\n provider: Context<any>[\"Provider\"];\r\n value: unknown;\r\n order: number;\r\n};\r\n\r\ntype ReactContextAction =\r\n | { type: \"add\"; entry: ReactContextEntry }\r\n | { type: \"remove\"; provider: Context<any>[\"Provider\"] }\r\n | { type: \"update\"; provider: Context<any>[\"Provider\"]; value: unknown };\r\n\r\ntype DialogQueueAction = { type: \"enqueue\"; options: DialogOptions } | { type: \"dequeue\" };\r\n\r\nconst ReactContextsWrapper: FunctionComponent<PropsWithChildren<{ contexts: readonly Readonly<ReactContextEntry>[] }>> = ({ contexts, children }) => {\r\n return <>{contexts.reduceRight<ReactNode>((acc, entry) => createElement(entry.provider, { value: entry.value }, acc), children)}</>;\r\n};\r\n\r\nexport type ModularToolOptions = {\r\n /**\r\n * The namespace for the tool, used for scoping persisted settings and other storage.\r\n */\r\n namespace: string;\r\n\r\n /**\r\n * The container element where the tool will be rendered.\r\n */\r\n containerElement: HTMLElement;\r\n\r\n /**\r\n * The service definitions to be registered with the tool.\r\n */\r\n serviceDefinitions: readonly WeaklyTypedServiceDefinition[];\r\n\r\n /**\r\n * 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.\r\n */\r\n themeMode?: ThemeMode;\r\n\r\n /**\r\n * Whether to show the theme selector in the toolbar. Default is true.\r\n */\r\n showThemeSelector?: boolean;\r\n\r\n /**\r\n * The extension feeds that provide optional extensions the user can install.\r\n */\r\n extensionFeeds?: readonly IExtensionFeed[];\r\n\r\n /**\r\n * An optional parent ServiceContainer. Dependencies not found in the tool's own container\r\n * will be resolved from this parent.\r\n */\r\n parentContainer?: ServiceContainer;\r\n\r\n /**\r\n * When true, all teaching moments are disabled and will not be shown.\r\n */\r\n disableTeachingMoments?: boolean;\r\n} & ShellServiceOptions;\r\n\r\n/**\r\n * Creates a modular tool with a base set of common tool services, including the toolbar/side pane basic UI layout.\r\n * @param options The options for the tool.\r\n * @returns A token that can be used to dispose of the tool.\r\n */\r\nexport function MakeModularTool(options: ModularToolOptions) {\r\n const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer, disableTeachingMoments = false } = options;\r\n\r\n const teachingMomentsContextValue = { disabled: disableTeachingMoments };\r\n\r\n // Create the settings store immediately as it will be exposed to services and through React context.\r\n const settingsStore = new SettingsStore(namespace);\r\n\r\n // If a theme mode is provided, just write the setting so it is the active theme.\r\n if (themeMode) {\r\n settingsStore.writeSetting(ThemeModeSettingDescriptor, themeMode);\r\n }\r\n\r\n // This deferred resolves once the React effect cleanup (which disposes the ServiceContainer) is complete.\r\n const disposeDeferred = new Deferred<void>();\r\n\r\n const modularToolRootComponent: FunctionComponent = () => {\r\n const classes = useStyles();\r\n const [requiredExtensions, setRequiredExtensions] = useState<string[]>();\r\n const [requiredExtensionsDeferred, setRequiredExtensionsDeferred] = useState<Deferred<boolean>>();\r\n const [extensionInstallError, setExtensionInstallError] = useState<InstallFailedInfo>();\r\n\r\n const [toastHandle, setToastHandle] = useState<ToastHandle | null>(null);\r\n const [toastQueue, setToastQueue] = useState<{ message: string; options?: ToastOptions }[]>([]);\r\n\r\n useEffect(() => {\r\n if (toastHandle && toastQueue.length > 0) {\r\n for (const { message, options } of toastQueue) {\r\n toastHandle.showToast(message, options);\r\n }\r\n setToastQueue([]);\r\n }\r\n }, [toastHandle, toastQueue]);\r\n\r\n // Queue of dialogs to display. We show one at a time (the one at the head of the queue).\r\n const [dialogQueue, dispatchDialogQueue] = useReducer((state: readonly DialogOptions[], action: DialogQueueAction): readonly DialogOptions[] => {\r\n switch (action.type) {\r\n case \"enqueue\":\r\n return [...state, action.options];\r\n case \"dequeue\":\r\n return state.slice(1);\r\n }\r\n }, []);\r\n\r\n const showDialog = useCallback((dialogOptions: DialogOptions) => {\r\n dispatchDialogQueue({ type: \"enqueue\", options: dialogOptions });\r\n }, []);\r\n\r\n const onDismissDialog = useCallback(() => {\r\n dispatchDialogQueue({ type: \"dequeue\" });\r\n }, []);\r\n\r\n const [rootComponentService, setRootComponentService] = useState<IRootComponentService>();\r\n\r\n const [contexts, updateContexts] = useReducer(\r\n (state: ReactContextEntry[], action: ReactContextAction): ReactContextEntry[] => {\r\n switch (action.type) {\r\n case \"add\":\r\n return [...state, action.entry].sort((a, b) => a.order - b.order);\r\n case \"remove\":\r\n return state.filter((e) => e.provider !== action.provider);\r\n case \"update\":\r\n return state.map((e) => (e.provider === action.provider ? { ...e, value: action.value } : e));\r\n }\r\n },\r\n [\r\n // Static contexts seeded into the wrapper so they don't add JSX nesting below.\r\n // Negative orders keep them as the outermost providers (lowest order = outermost via reduceRight).\r\n { provider: TeachingMomentsContext.Provider, value: teachingMomentsContextValue, order: -2 },\r\n { provider: SettingsStoreContext.Provider, value: settingsStore, order: -1 },\r\n { provider: ExtensionManagerContext.Provider, value: undefined, order: 0 },\r\n ]\r\n );\r\n\r\n // This is the main async initialization.\r\n useEffect(() => {\r\n const initializeExtensionManagerAsync = async () => {\r\n const serviceContainer = new ServiceContainer(\"ModularToolContainer\", parentContainer);\r\n\r\n // Expose the settings store as a service so other services can read/write settings.\r\n serviceContainer.addService<[ISettingsStore], []>({\r\n friendlyName: \"Settings Store\",\r\n produces: [SettingsStoreIdentity],\r\n factory: () => settingsStore,\r\n });\r\n\r\n // Expose the react context service so other services can add React context providers.\r\n serviceContainer.addService<[IReactContextService], []>({\r\n friendlyName: \"React Context Service\",\r\n produces: [ReactContextServiceIdentity],\r\n factory: (): IReactContextService => ({\r\n addContext<T>(provider: Context<T>[\"Provider\"], initialValue: T, options?: { order?: number }): ReactContextHandle<T> {\r\n updateContexts({ type: \"add\", entry: { provider: provider, value: initialValue, order: options?.order ?? 0 } });\r\n return {\r\n updateValue: (newValue: T) => {\r\n updateContexts({ type: \"update\", provider: provider, value: newValue });\r\n },\r\n dispose: () => {\r\n updateContexts({ type: \"remove\", provider: provider });\r\n },\r\n };\r\n },\r\n }),\r\n });\r\n\r\n // Expose the toast service so non-React code (e.g. Observable callbacks) can show toasts.\r\n serviceContainer.addService<[IToastService], []>({\r\n friendlyName: \"Toast Service\",\r\n produces: [ToastServiceIdentity],\r\n factory: (): IToastService => ({\r\n showToast: (message, options) => {\r\n setToastQueue((prev) => [...prev, { message, options }]);\r\n },\r\n }),\r\n });\r\n\r\n // Expose the dialog service so non-React code (e.g. Observable callbacks) can show dialogs.\r\n serviceContainer.addService<[IDialogService], []>({\r\n friendlyName: \"Dialog Service\",\r\n produces: [DialogServiceIdentity],\r\n factory: (): IDialogService => ({ showDialog }),\r\n });\r\n\r\n // Register the shell service (top level toolbar/side pane UI layout).\r\n serviceContainer.addService(MakeShellServiceDefinition(options));\r\n\r\n // Register a service that simply consumes the services we need before first render.\r\n serviceContainer.addService<[], [IRootComponentService]>({\r\n friendlyName: \"Service Bootstrapper\",\r\n consumes: [RootComponentServiceIdentity],\r\n factory: (rootComponent) => {\r\n // Use function syntax for the state setter since the root component may be a function component.\r\n setRootComponentService(() => rootComponent);\r\n return {\r\n dispose: () => setRootComponentService(undefined),\r\n };\r\n },\r\n });\r\n\r\n // Register the theme service (exposes the current theme to other services).\r\n serviceContainer.addService(ThemeServiceDefinition);\r\n\r\n // Register the theme selector service (for selecting the theme) if theming is configured.\r\n if (showThemeSelector) {\r\n serviceContainer.addService(ThemeSelectorServiceDefinition);\r\n }\r\n\r\n // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.\r\n if (extensionFeeds.length > 0) {\r\n const { ExtensionListServiceDefinition } = await import(\"./services/extensionsListService\");\r\n serviceContainer.addService(ExtensionListServiceDefinition);\r\n }\r\n\r\n // Register all external services (that make up a unique tool).\r\n serviceContainer.addServices(...serviceDefinitions);\r\n\r\n // Create the extension manager, passing along the registry for runtime changes to the registered services.\r\n const extensionManager = await ExtensionManager.CreateAsync(namespace, serviceContainer, extensionFeeds, setExtensionInstallError);\r\n\r\n // Check query params for required extensions. This lets users share links with sets of extensions.\r\n const queryParams = new URLSearchParams(window.location.search);\r\n const requiredExtensions = queryParams.getAll(\"babylon.requiredExtension\");\r\n const uninstalledExtensions: IExtension[] = [];\r\n for (const requiredExtension of requiredExtensions) {\r\n // These could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.\r\n // eslint-disable-next-line no-await-in-loop\r\n const query = await extensionManager.queryExtensionsAsync(requiredExtension);\r\n // eslint-disable-next-line no-await-in-loop\r\n const extensions = await query.getExtensionsAsync(0, query.totalCount);\r\n for (const extension of extensions) {\r\n if (!extension.isInstalled) {\r\n uninstalledExtensions.push(extension);\r\n }\r\n }\r\n }\r\n\r\n // Check if any required extensions are uninstalled or disabled. If so, show a dialog to the user.\r\n if (uninstalledExtensions.length > 0) {\r\n setRequiredExtensions(uninstalledExtensions.map((extension) => extension.metadata.name));\r\n const deferred = new Deferred<boolean>();\r\n setRequiredExtensionsDeferred(deferred);\r\n if (await deferred.promise) {\r\n for (const extension of uninstalledExtensions) {\r\n // This could possibly be parallelized to speed things up, but it's more complex so let's wait and see if we need it.\r\n // eslint-disable-next-line no-await-in-loop\r\n await extension.installAsync();\r\n }\r\n }\r\n }\r\n\r\n // Set the contexts.\r\n updateContexts({ type: \"update\", provider: ExtensionManagerContext.Provider, value: { extensionManager } });\r\n\r\n return () => {\r\n extensionManager.dispose();\r\n serviceContainer.dispose();\r\n serviceContainer.dispose();\r\n };\r\n };\r\n\r\n const disposePromise = initializeExtensionManagerAsync();\r\n\r\n return () => {\r\n disposePromise\r\n // eslint-disable-next-line github/no-then\r\n .then((dispose) => dispose())\r\n // eslint-disable-next-line github/no-then\r\n .catch((error) => {\r\n Logger.Error(`Failed to dispose of the modular tool: ${error}`);\r\n })\r\n .finally(() => disposeDeferred.resolve());\r\n };\r\n }, []);\r\n\r\n const onAcceptRequiredExtensions = useCallback(() => {\r\n setRequiredExtensions(undefined);\r\n requiredExtensionsDeferred?.resolve(true);\r\n }, [setRequiredExtensions, requiredExtensionsDeferred]);\r\n\r\n const onRejectRequiredExtensions = useCallback(() => {\r\n setRequiredExtensions(undefined);\r\n requiredExtensionsDeferred?.resolve(false);\r\n }, [setRequiredExtensions, requiredExtensionsDeferred]);\r\n\r\n const onAcknowledgedExtensionInstallError = useCallback(() => {\r\n setExtensionInstallError(undefined);\r\n }, [setExtensionInstallError]);\r\n\r\n // The dialog at the head of the queue, if any, is the one currently being displayed.\r\n const currentDialog = dialogQueue[0];\r\n const currentDialogIcon = (() => {\r\n switch (currentDialog?.intent) {\r\n case \"success\":\r\n return <CheckmarkCircleRegular className={classes.dialogIconSuccess} />;\r\n case \"error\":\r\n return <ErrorCircleRegular className={classes.dialogIconError} />;\r\n case \"warning\":\r\n return <WarningRegular className={classes.dialogIconWarning} />;\r\n case \"info\":\r\n case undefined:\r\n return <InfoRegular className={classes.dialogIconInfo} />;\r\n }\r\n })();\r\n\r\n // Show a spinner until a main view has been set.\r\n if (!rootComponentService) {\r\n return (\r\n <ReactContextsWrapper contexts={contexts}>\r\n <Theme className={classes.app} targetDocument={targetDocument}>\r\n <Spinner className={classes.spinner} />\r\n </Theme>\r\n </ReactContextsWrapper>\r\n );\r\n } else {\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n const Content: ComponentType = rootComponentService.rootComponent;\r\n\r\n return (\r\n <ReactContextsWrapper contexts={contexts}>\r\n <Theme className={classes.app} targetDocument={targetDocument}>\r\n <ToastProvider imperativeRef={setToastHandle}>\r\n <Dialog open={!!requiredExtensions} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>Required Extensions</DialogTitle>\r\n <DialogContent>\r\n Opening this URL requires the following extensions to be installed and enabled:\r\n <ul>\r\n {requiredExtensions?.map((name) => (\r\n <li key={name}>{name}</li>\r\n ))}\r\n </ul>\r\n </DialogContent>\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onAcceptRequiredExtensions}>\r\n Install & Enable\r\n </Button>\r\n <Button appearance=\"secondary\" onClick={onRejectRequiredExtensions}>\r\n No Thanks\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Dialog open={!!extensionInstallError} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>\r\n <div className={classes.extensionErrorTitleDiv}>\r\n Extension Install Error\r\n <ErrorCircleRegular className={classes.extensionErrorIcon} />\r\n </div>\r\n </DialogTitle>\r\n <DialogContent>\r\n <List>\r\n <ListItem>\r\n <Body1>{`Extension \"${extensionInstallError?.extension.name}\" failed to install and was removed.`}</Body1>\r\n </ListItem>\r\n <ListItem>\r\n <Body1>{`${extensionInstallError?.error}`}</Body1>\r\n </ListItem>\r\n </List>\r\n </DialogContent>\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onAcknowledgedExtensionInstallError}>\r\n Close\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Dialog open={!!currentDialog} modalType=\"alert\">\r\n <DialogSurface>\r\n <DialogBody>\r\n <DialogTitle>\r\n <div className={classes.dialogTitle}>\r\n {currentDialogIcon}\r\n {currentDialog?.title}\r\n </div>\r\n </DialogTitle>\r\n {currentDialog?.content && <DialogContent>{currentDialog.content}</DialogContent>}\r\n <DialogActions>\r\n <Button appearance=\"primary\" onClick={onDismissDialog}>\r\n OK\r\n </Button>\r\n </DialogActions>\r\n </DialogBody>\r\n </DialogSurface>\r\n </Dialog>\r\n <Suspense fallback={<Spinner className={classes.spinner} />}>\r\n <DialogContext.Provider value={{ showDialog }}>\r\n <Content />\r\n </DialogContext.Provider>\r\n </Suspense>\r\n </ToastProvider>\r\n </Theme>\r\n </ReactContextsWrapper>\r\n );\r\n }\r\n };\r\n\r\n // Derive the target document from the container element. When the container is in a popup\r\n // window (or other document distinct from the main one), this is what makes Fluent inject\r\n // styles and render portals into the correct document.\r\n const containerOwnerDocument = containerElement.ownerDocument;\r\n const targetDocument = containerOwnerDocument && containerOwnerDocument !== document ? containerOwnerDocument : undefined;\r\n\r\n // Set the container element to be a flex container so that the tool can be displayed properly.\r\n const originalContainerElementDisplay = containerElement.style.display;\r\n containerElement.style.display = \"flex\";\r\n\r\n // Create and render the react root component.\r\n const reactRoot = createRoot(containerElement);\r\n reactRoot.render(createElement(modularToolRootComponent));\r\n\r\n let disposed = false;\r\n return {\r\n // eslint-disable-next-line @typescript-eslint/promise-function-async\r\n dispose: () => {\r\n // Unmount and restore the original container element display.\r\n if (!disposed) {\r\n disposed = true;\r\n reactRoot.unmount();\r\n containerElement.style.display = originalContainerElementDisplay;\r\n }\r\n // The promise resolves once the React effect cleanup\r\n // (which disposes the ServiceContainer) has completed.\r\n return disposeDeferred.promise;\r\n },\r\n };\r\n}\r\n"]}
|