@noodleseed/assistant 1.8.0 → 1.10.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/README.md +83 -14
- package/dist/{appearance-DowzFJjh.d.cts → appearance-Cc-eqSbp.d.cts} +1 -1
- package/dist/{appearance-DowzFJjh.d.ts → appearance-Cc-eqSbp.d.ts} +1 -1
- package/dist/{chunk-JMBUJSFP.js → chunk-RSNMY27B.js} +96 -18
- package/dist/chunk-RSNMY27B.js.map +1 -0
- package/dist/{client-ERQCAH_0.d.ts → client-B803xZVn.d.ts} +2 -2
- package/dist/{client-BxoNZkWp.d.cts → client-CsmtpD1z.d.cts} +2 -2
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/index.cjs +89 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/react/client.d.cts +2 -2
- package/dist/react/client.d.ts +2 -2
- package/dist/react.cjs +151 -24
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +22 -4
- package/dist/react.d.ts +22 -4
- package/dist/react.js +65 -9
- package/dist/react.js.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-JMBUJSFP.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Customer-branded embedded assistant surfaces for Noodle Seed deployments.
|
|
4
4
|
|
|
5
|
-
The package exports the canonical `<noodle-assistant>` Web Component, a managed React wrapper
|
|
6
|
-
`@noodleseed/assistant/react`, a renderer-free React hook from
|
|
5
|
+
The package exports the canonical `<noodle-assistant>` Web Component, a managed React wrapper and secure
|
|
6
|
+
`NoodleAppView` MCP App renderer from `@noodleseed/assistant/react`, a renderer-free React hook from
|
|
7
7
|
`@noodleseed/assistant/react/client`, a DOM-free client from `@noodleseed/assistant/client`, and the
|
|
8
8
|
backend-only `createAssistantSession` helper from `@noodleseed/assistant/server`. Light, dark, and automatic
|
|
9
9
|
themes work without configuration; the component inherits the deployed MCP server's brand kit while slots,
|
|
@@ -247,6 +247,32 @@ interaction streams a continuation. `principalKey` is browser-local and never se
|
|
|
247
247
|
whenever the authenticated user or tenant changes so the hook aborts and clears the previous session and
|
|
248
248
|
transcript.
|
|
249
249
|
|
|
250
|
+
When the customer-owned transcript should render the linked MCP App itself, pass the typed `data-view`
|
|
251
|
+
payload and the same client to the supported host component:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { NoodleAppView } from "@noodleseed/assistant/react";
|
|
255
|
+
|
|
256
|
+
if (part.type === "data-view") {
|
|
257
|
+
return (
|
|
258
|
+
<NoodleAppView
|
|
259
|
+
key={`${part.data.id}:${part.data.resourceUri}`}
|
|
260
|
+
client={client}
|
|
261
|
+
view={part.data}
|
|
262
|
+
theme={resolvedTheme}
|
|
263
|
+
onError={(failure) => reportAssistantError(failure)}
|
|
264
|
+
/>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`NoodleAppView` owns the double iframe and AppBridge. Its lifecycle identity is the supplied client plus
|
|
270
|
+
`view.id` plus `view.resourceUri`: ordinary parent rerenders and fresh view/callback objects retain the
|
|
271
|
+
iframe, while a semantic view replacement or unmount requests standard App teardown and closes the bridge.
|
|
272
|
+
Do not also key an ancestor by the whole view object or a callback. Pass the embedding application's
|
|
273
|
+
resolved `"light"` or `"dark"` theme; later changes are published through MCP Apps host context without
|
|
274
|
+
replacing the iframe.
|
|
275
|
+
|
|
250
276
|
Outside React, subscribe to the DOM-free AI SDK `UIMessage` state without registering a custom element or
|
|
251
277
|
touching browser storage:
|
|
252
278
|
|
|
@@ -331,11 +357,13 @@ leaves the same input interaction pending so the renderer can submit a corrected
|
|
|
331
357
|
flow collects all elicited input before its first connector operation.
|
|
332
358
|
|
|
333
359
|
A completed widget-linked tool emits typed `view_available` data with its call/interaction id, tool,
|
|
334
|
-
`ui://` resource identity, optional title,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
360
|
+
`ui://` resource identity, optional title, bounded/redacted public result, and—on current services—the
|
|
361
|
+
self-contained App document. This is an availability signal, not proof of rendering. A customer-owned React
|
|
362
|
+
renderer either mounts the actual App with `NoodleAppView` or deliberately substitutes a component already
|
|
363
|
+
trusted by the application and selected by `resourceUri`/tool. The JSON `result` is data for a native
|
|
364
|
+
component; serializing it is not a rendering of the linked App. Never fetch the `ui://` URI, inject
|
|
365
|
+
`part.data.html`, or assign it to `srcdoc` yourself. The standard element also forwards the same detail as a
|
|
366
|
+
DOM event:
|
|
339
367
|
|
|
340
368
|
```ts
|
|
341
369
|
element.addEventListener("assistant-view-available", (event) => {
|
|
@@ -367,10 +395,19 @@ exact control over assistant-specific roles:
|
|
|
367
395
|
```tsx
|
|
368
396
|
<NoodleAssistant
|
|
369
397
|
sessionEndpoint="/api/assistant/session"
|
|
398
|
+
theme={resolvedTheme}
|
|
370
399
|
appearance={{
|
|
371
400
|
light: {
|
|
372
|
-
panel: {
|
|
373
|
-
|
|
401
|
+
panel: {
|
|
402
|
+
surface: "var(--app-surface)",
|
|
403
|
+
text: "var(--app-text)",
|
|
404
|
+
border: "var(--app-border)",
|
|
405
|
+
},
|
|
406
|
+
composer: {
|
|
407
|
+
surface: "var(--app-input)",
|
|
408
|
+
text: "var(--app-text)",
|
|
409
|
+
border: "var(--app-border)",
|
|
410
|
+
},
|
|
374
411
|
confirmation: { surface: "#F8FAFC", text: "#101828", border: "#CBD5E1" },
|
|
375
412
|
primaryButton: { surface: "#635BFF", text: "#FFFFFF" },
|
|
376
413
|
},
|
|
@@ -379,11 +416,38 @@ exact control over assistant-specific roles:
|
|
|
379
416
|
/>
|
|
380
417
|
```
|
|
381
418
|
|
|
382
|
-
The same object is available as `element.appearance`.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
`assistant-appearance-warning` event.
|
|
386
|
-
|
|
419
|
+
The same object is available as `element.appearance`. CSS custom properties inherit through the assistant
|
|
420
|
+
host into its shadow tree, so `var(--app-token)` references reuse the embedding application's existing
|
|
421
|
+
tokens without copying literal colors. Exact parseable literal colors are preserved and checked;
|
|
422
|
+
insufficient contrast emits the typed `assistant-appearance-warning` event. Contrast for unresolved CSS
|
|
423
|
+
references remains the host application's responsibility.
|
|
424
|
+
|
|
425
|
+
The complete typed appearance role map is:
|
|
426
|
+
|
|
427
|
+
| Appearance role | Public CSS custom properties |
|
|
428
|
+
| --- | --- |
|
|
429
|
+
| `canvas` | `--ns-assistant-canvas` |
|
|
430
|
+
| `text` | `--ns-assistant-text` |
|
|
431
|
+
| `mutedText` | `--ns-assistant-muted-text` |
|
|
432
|
+
| `link` | `--ns-assistant-link` |
|
|
433
|
+
| `focus` | `--ns-assistant-focus` |
|
|
434
|
+
| `success` | `--ns-assistant-success` |
|
|
435
|
+
| `warning` | `--ns-assistant-warning` |
|
|
436
|
+
| `danger` | `--ns-assistant-danger` |
|
|
437
|
+
| `panel` | `--ns-assistant-panel`, `--ns-assistant-panel-text`, `--ns-assistant-panel-border` |
|
|
438
|
+
| `header` | `--ns-assistant-header`, `--ns-assistant-header-text`, `--ns-assistant-header-border` |
|
|
439
|
+
| `assistantMessage` | `--ns-assistant-assistant-message`, `--ns-assistant-assistant-message-text`, `--ns-assistant-assistant-message-border` |
|
|
440
|
+
| `userMessage` | `--ns-assistant-user-message`, `--ns-assistant-user-message-text`, `--ns-assistant-user-message-border` |
|
|
441
|
+
| `composer` | `--ns-assistant-composer`, `--ns-assistant-composer-text`, `--ns-assistant-composer-border` |
|
|
442
|
+
| `suggestion` | `--ns-assistant-suggestion`, `--ns-assistant-suggestion-text`, `--ns-assistant-suggestion-border` |
|
|
443
|
+
| `confirmation` | `--ns-assistant-confirmation`, `--ns-assistant-confirmation-text`, `--ns-assistant-confirmation-border` |
|
|
444
|
+
| `primaryButton` | `--ns-assistant-primary-button`, `--ns-assistant-primary-button-text`, `--ns-assistant-primary-button-border` |
|
|
445
|
+
| `secondaryButton` | `--ns-assistant-secondary-button`, `--ns-assistant-secondary-button-text`, `--ns-assistant-secondary-button-border` |
|
|
446
|
+
| `launcher` | `--ns-assistant-launcher`, `--ns-assistant-launcher-text`, `--ns-assistant-launcher-border` |
|
|
447
|
+
| `code` | `--ns-assistant-code`, `--ns-assistant-code-text`, `--ns-assistant-code-border` |
|
|
448
|
+
| `app` | `--ns-assistant-app`, `--ns-assistant-app-text`, `--ns-assistant-app-border` |
|
|
449
|
+
|
|
450
|
+
Stable public CSS variables also remain available directly for stylesheet-based integration:
|
|
387
451
|
|
|
388
452
|
```css
|
|
389
453
|
noodle-assistant {
|
|
@@ -401,6 +465,11 @@ defaults. The public slots are `launcher-icon`,
|
|
|
401
465
|
embedding-page integration API, not a way to put HTML or callbacks in deployment configuration. Internal
|
|
402
466
|
shadow-DOM selectors and classes are not public API.
|
|
403
467
|
|
|
468
|
+
`theme="auto"` follows the browser's operating-system color preference. When the embedding application has
|
|
469
|
+
its own theme toggle, pass its resolved `"light"` or `"dark"` value to `NoodleAssistant` and every
|
|
470
|
+
`NoodleAppView`. Updates change the assistant in place and notify mounted MCP Apps through standard host
|
|
471
|
+
context.
|
|
472
|
+
|
|
404
473
|
Server branding controls customer name, themed logo/mark/avatar assets, semantic light/dark colors, density,
|
|
405
474
|
radius, typography, and automatic theme. `embeddedAssistant(...)` controls floating/inline/drawer layout,
|
|
406
475
|
position and dimensions, mobile and launcher/header/avatar/timestamp behavior, visible labels, suggested
|
|
@@ -3072,6 +3072,8 @@ function createAssistantAppHostContext(theme) {
|
|
|
3072
3072
|
};
|
|
3073
3073
|
}
|
|
3074
3074
|
var APP_RENDER_TIMEOUT_MS = 1e4;
|
|
3075
|
+
var APP_TEARDOWN_TIMEOUT_MS = 1e3;
|
|
3076
|
+
var mountedApps = /* @__PURE__ */ new WeakMap();
|
|
3075
3077
|
function resolveSandboxUrl(value) {
|
|
3076
3078
|
if (!value) return void 0;
|
|
3077
3079
|
try {
|
|
@@ -3100,7 +3102,12 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3100
3102
|
if (sandboxUrl) frame.src = sandboxUrl;
|
|
3101
3103
|
else frame.srcdoc = PROXY_DOCUMENT;
|
|
3102
3104
|
card.append(frame);
|
|
3105
|
+
let mountedBridge;
|
|
3106
|
+
let mountedTransport;
|
|
3107
|
+
let destroyed = false;
|
|
3103
3108
|
let initialized = false;
|
|
3109
|
+
let hostContext = createAssistantAppHostContext(actions.theme);
|
|
3110
|
+
let destroyPromise;
|
|
3104
3111
|
const renderTimer = setTimeout(() => {
|
|
3105
3112
|
if (initialized || !frame.isConnected) return;
|
|
3106
3113
|
const note = document.createElement("div");
|
|
@@ -3112,6 +3119,7 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3112
3119
|
frame.addEventListener(
|
|
3113
3120
|
"load",
|
|
3114
3121
|
() => {
|
|
3122
|
+
if (destroyed) return;
|
|
3115
3123
|
const target = frame.contentWindow;
|
|
3116
3124
|
if (!target) return;
|
|
3117
3125
|
const bridge = new DX(
|
|
@@ -3125,22 +3133,27 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3125
3133
|
message: { text: {} }
|
|
3126
3134
|
},
|
|
3127
3135
|
{
|
|
3128
|
-
hostContext
|
|
3136
|
+
hostContext
|
|
3129
3137
|
}
|
|
3130
3138
|
);
|
|
3139
|
+
mountedBridge = bridge;
|
|
3131
3140
|
bridge.onsandboxready = () => {
|
|
3141
|
+
if (destroyed) return;
|
|
3142
|
+
const current = options.getDetail?.() ?? detail;
|
|
3132
3143
|
void bridge.sendSandboxResourceReady({
|
|
3133
|
-
html:
|
|
3144
|
+
html: current.html,
|
|
3134
3145
|
sandbox: "allow-scripts allow-forms",
|
|
3135
|
-
...
|
|
3146
|
+
...current.resourceMeta ?? {}
|
|
3136
3147
|
});
|
|
3137
3148
|
};
|
|
3138
3149
|
bridge.onsizechange = ({ height }) => {
|
|
3150
|
+
if (destroyed) return;
|
|
3139
3151
|
if (typeof height === "number" && Number.isFinite(height)) {
|
|
3140
3152
|
frame.style.height = `${Math.min(1200, Math.max(120, Math.ceil(height)))}px`;
|
|
3141
3153
|
}
|
|
3142
3154
|
};
|
|
3143
3155
|
bridge.onmessage = async ({ content }) => {
|
|
3156
|
+
if (destroyed) return {};
|
|
3144
3157
|
const text2 = content.flatMap(
|
|
3145
3158
|
(part) => part.type === "text" && typeof part.text === "string" ? [part.text] : []
|
|
3146
3159
|
).join("\n").trim();
|
|
@@ -3148,13 +3161,16 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3148
3161
|
return {};
|
|
3149
3162
|
};
|
|
3150
3163
|
bridge.onupdatemodelcontext = async (update) => {
|
|
3164
|
+
if (destroyed) return {};
|
|
3151
3165
|
actions.updateModelContext(update);
|
|
3152
3166
|
return {};
|
|
3153
3167
|
};
|
|
3154
3168
|
bridge.onopenlink = async ({ url }) => {
|
|
3169
|
+
if (destroyed) return { isError: true };
|
|
3170
|
+
const current = options.getDetail?.() ?? detail;
|
|
3155
3171
|
const allowed = isAllowedAppLink(
|
|
3156
3172
|
url,
|
|
3157
|
-
|
|
3173
|
+
current.allowedOpenDomains ?? [],
|
|
3158
3174
|
globalThis.location?.href ?? "https://invalid.example"
|
|
3159
3175
|
);
|
|
3160
3176
|
if (!allowed) return { isError: true };
|
|
@@ -3163,30 +3179,83 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3163
3179
|
};
|
|
3164
3180
|
bridge.onrequestdisplaymode = async ({ mode }) => {
|
|
3165
3181
|
const accepted = mode === "fullscreen" ? "fullscreen" : "inline";
|
|
3182
|
+
if (destroyed) return { mode: accepted };
|
|
3166
3183
|
card.toggleAttribute("data-fullscreen", accepted === "fullscreen");
|
|
3167
3184
|
return { mode: accepted };
|
|
3168
3185
|
};
|
|
3169
|
-
bridge.oncalltool = async (params) =>
|
|
3170
|
-
|
|
3171
|
-
|
|
3186
|
+
bridge.oncalltool = async (params) => {
|
|
3187
|
+
if (destroyed) {
|
|
3188
|
+
return {
|
|
3189
|
+
content: [{ type: "text", text: "App view is closing." }],
|
|
3190
|
+
isError: true
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
return await actions.client.requestApp("tools/call", params);
|
|
3194
|
+
};
|
|
3195
|
+
bridge.onlistresources = async (params) => {
|
|
3196
|
+
if (destroyed) {
|
|
3197
|
+
return { resources: [] };
|
|
3198
|
+
}
|
|
3199
|
+
return await actions.client.requestApp("resources/list", params ?? {});
|
|
3200
|
+
};
|
|
3201
|
+
bridge.onreadresource = async (params) => {
|
|
3202
|
+
if (destroyed) {
|
|
3203
|
+
return { contents: [] };
|
|
3204
|
+
}
|
|
3205
|
+
return await actions.client.requestApp("resources/read", params);
|
|
3206
|
+
};
|
|
3172
3207
|
bridge.oninitialized = () => {
|
|
3208
|
+
if (destroyed) return;
|
|
3173
3209
|
initialized = true;
|
|
3174
3210
|
clearTimeout(renderTimer);
|
|
3175
|
-
|
|
3211
|
+
bridge.setHostContext(hostContext);
|
|
3212
|
+
const current = options.getDetail?.() ?? detail;
|
|
3213
|
+
void bridge.sendToolInput({ arguments: current.arguments ?? {} });
|
|
3176
3214
|
void bridge.sendToolResult({
|
|
3177
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
3178
|
-
...typeof
|
|
3215
|
+
content: [{ type: "text", text: JSON.stringify(current.result) }],
|
|
3216
|
+
...typeof current.result === "object" && current.result !== null && !Array.isArray(current.result) ? { structuredContent: current.result } : {}
|
|
3179
3217
|
});
|
|
3180
3218
|
};
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3219
|
+
const transport = new E(target, target);
|
|
3220
|
+
mountedTransport = transport;
|
|
3221
|
+
void bridge.connect(transport).catch(() => {
|
|
3222
|
+
void destroy();
|
|
3184
3223
|
});
|
|
3185
3224
|
},
|
|
3186
3225
|
{ once: true }
|
|
3187
3226
|
);
|
|
3227
|
+
const updateTheme = (theme) => {
|
|
3228
|
+
if (destroyed) return;
|
|
3229
|
+
hostContext = createAssistantAppHostContext(theme);
|
|
3230
|
+
if (initialized) mountedBridge?.setHostContext(hostContext);
|
|
3231
|
+
};
|
|
3232
|
+
const destroy = () => {
|
|
3233
|
+
if (destroyPromise) return destroyPromise;
|
|
3234
|
+
destroyed = true;
|
|
3235
|
+
clearTimeout(renderTimer);
|
|
3236
|
+
destroyPromise = (async () => {
|
|
3237
|
+
try {
|
|
3238
|
+
if (initialized) {
|
|
3239
|
+
await mountedBridge?.teardownResource({}, { timeout: APP_TEARDOWN_TIMEOUT_MS });
|
|
3240
|
+
}
|
|
3241
|
+
} catch {
|
|
3242
|
+
}
|
|
3243
|
+
await mountedTransport?.close().catch(() => {
|
|
3244
|
+
});
|
|
3245
|
+
card.remove();
|
|
3246
|
+
mountedApps.delete(card);
|
|
3247
|
+
})();
|
|
3248
|
+
return destroyPromise;
|
|
3249
|
+
};
|
|
3250
|
+
mountedApps.set(card, { destroy, updateTheme });
|
|
3188
3251
|
return card;
|
|
3189
3252
|
}
|
|
3253
|
+
function updateAssistantAppTheme(card, theme) {
|
|
3254
|
+
mountedApps.get(card)?.updateTheme(theme);
|
|
3255
|
+
}
|
|
3256
|
+
function unmountAssistantApp(card) {
|
|
3257
|
+
return mountedApps.get(card)?.destroy() ?? Promise.resolve();
|
|
3258
|
+
}
|
|
3190
3259
|
|
|
3191
3260
|
// src/appearance.ts
|
|
3192
3261
|
var common = {
|
|
@@ -7109,7 +7178,7 @@ function contrastWarnings(theme, mode) {
|
|
|
7109
7178
|
for (const [role, foreground, background] of pairs) {
|
|
7110
7179
|
if (!foreground || !background) continue;
|
|
7111
7180
|
const ratio = contrastRatio2(foreground, background);
|
|
7112
|
-
if (ratio >= 4.5) continue;
|
|
7181
|
+
if (ratio === void 0 || ratio >= 4.5) continue;
|
|
7113
7182
|
warnings.push({
|
|
7114
7183
|
code: "low_contrast",
|
|
7115
7184
|
theme: mode,
|
|
@@ -7123,13 +7192,16 @@ function contrastWarnings(theme, mode) {
|
|
|
7123
7192
|
return warnings;
|
|
7124
7193
|
}
|
|
7125
7194
|
function contrastRatio2(first, second) {
|
|
7126
|
-
const
|
|
7127
|
-
const
|
|
7195
|
+
const firstLuminance = luminance(first);
|
|
7196
|
+
const secondLuminance = luminance(second);
|
|
7197
|
+
if (firstLuminance === void 0 || secondLuminance === void 0) return void 0;
|
|
7198
|
+
const lighter = Math.max(firstLuminance, secondLuminance);
|
|
7199
|
+
const darker = Math.min(firstLuminance, secondLuminance);
|
|
7128
7200
|
return (lighter + 0.05) / (darker + 0.05);
|
|
7129
7201
|
}
|
|
7130
7202
|
function luminance(color) {
|
|
7131
7203
|
const match = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
|
|
7132
|
-
if (!match) return
|
|
7204
|
+
if (!match) return void 0;
|
|
7133
7205
|
const values = match.slice(1).map((value) => {
|
|
7134
7206
|
const channel = Number.parseInt(value, 16) / 255;
|
|
7135
7207
|
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
|
@@ -7962,6 +8034,9 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
7962
8034
|
})
|
|
7963
8035
|
);
|
|
7964
8036
|
}
|
|
8037
|
+
for (const app of this.shadowRoot?.querySelectorAll(".noodle-app-card") ?? []) {
|
|
8038
|
+
updateAssistantAppTheme(app, mode);
|
|
8039
|
+
}
|
|
7965
8040
|
}
|
|
7966
8041
|
#setSessionState(state) {
|
|
7967
8042
|
this.#sessionState = state;
|
|
@@ -8136,6 +8211,9 @@ function registerNoodleAssistant() {
|
|
|
8136
8211
|
}
|
|
8137
8212
|
|
|
8138
8213
|
export {
|
|
8214
|
+
mountAssistantApp,
|
|
8215
|
+
updateAssistantAppTheme,
|
|
8216
|
+
unmountAssistantApp,
|
|
8139
8217
|
ASSISTANT_TAG_NAME,
|
|
8140
8218
|
NoodleAssistantElement,
|
|
8141
8219
|
registerNoodleAssistant
|
|
@@ -8145,4 +8223,4 @@ export {
|
|
|
8145
8223
|
dompurify/dist/purify.es.mjs:
|
|
8146
8224
|
(*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE *)
|
|
8147
8225
|
*/
|
|
8148
|
-
//# sourceMappingURL=chunk-
|
|
8226
|
+
//# sourceMappingURL=chunk-RSNMY27B.js.map
|