radiant-docs 0.1.39 → 0.1.41

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 (49) hide show
  1. package/package.json +1 -1
  2. package/template/astro.config.mjs +38 -7
  3. package/template/package-lock.json +19 -7
  4. package/template/package.json +3 -3
  5. package/template/public/favicon.svg +16 -8
  6. package/template/scripts/generate-robots-txt.mjs +29 -1
  7. package/template/scripts/remove-assistant-for-non-pro.mjs +28 -0
  8. package/template/scripts/stamp-image-versions.mjs +59 -33
  9. package/template/src/components/Footer.astro +2 -1
  10. package/template/src/components/Header.astro +10 -8
  11. package/template/src/components/LogoLink.astro +2 -1
  12. package/template/src/components/MdxPage.astro +15 -4
  13. package/template/src/components/PagePagination.astro +61 -0
  14. package/template/src/components/SidebarDropdown.astro +12 -8
  15. package/template/src/components/SidebarGroup.astro +1 -1
  16. package/template/src/components/SidebarMenu.astro +1 -1
  17. package/template/src/components/SidebarSegmented.astro +6 -5
  18. package/template/src/components/TableOfContents.astro +4 -13
  19. package/template/src/components/chat/AskAiWidget.tsx +274 -39
  20. package/template/src/components/chat/AssistantDocsWidget.astro +16 -0
  21. package/template/src/components/chat/AssistantDocsWidget.tsx +402 -0
  22. package/template/src/components/chat/AssistantEmbedPanel.tsx +1693 -0
  23. package/template/src/components/chat/AssistantEmbedPanelPage.astro +95 -0
  24. package/template/src/components/endpoint/PlaygroundForm.astro +2 -1
  25. package/template/src/components/user/Callout.astro +10 -4
  26. package/template/src/components/user/CodeBlock.astro +1 -1
  27. package/template/src/components/user/CodeGroup.astro +16 -1
  28. package/template/src/components/user/ComponentPreviewBlock.astro +1 -0
  29. package/template/src/components/user/Image.astro +43 -53
  30. package/template/src/layouts/Layout.astro +104 -35
  31. package/template/src/lib/assistant-chrome-defaults.ts +74 -0
  32. package/template/src/lib/assistant-chrome.ts +39 -0
  33. package/template/src/lib/assistant-embed-script.ts +897 -0
  34. package/template/src/lib/assistant-panel-config.ts +80 -0
  35. package/template/src/lib/base-path.ts +98 -0
  36. package/template/src/lib/component-error.ts +49 -10
  37. package/template/src/lib/favicon.ts +31 -0
  38. package/template/src/lib/mdx/remark-resolve-internal-links.ts +128 -18
  39. package/template/src/lib/pagefind.ts +62 -14
  40. package/template/src/lib/routes.ts +49 -1
  41. package/template/src/lib/static-asset-url.ts +3 -1
  42. package/template/src/lib/theme-css.ts +176 -0
  43. package/template/src/lib/utils.ts +12 -4
  44. package/template/src/lib/validation.ts +754 -37
  45. package/template/src/pages/-/assistant/embed.js.ts +15 -0
  46. package/template/src/pages/-/assistant/panel.astro +5 -0
  47. package/template/src/pages/404.astro +6 -5
  48. package/template/src/pages/[...slug].astro +68 -6
  49. package/template/src/styles/global.css +62 -1
@@ -0,0 +1,897 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { DEFAULT_ASSISTANT_CHROME_CONFIG } from "./assistant-chrome-defaults";
4
+ import {
5
+ type AssistantChromeConfig,
6
+ getAssistantChromeConfig,
7
+ } from "./assistant-chrome";
8
+ import { getDocsBasePath, withBasePath } from "./base-path";
9
+ import { getFaviconConfig } from "./favicon";
10
+ import {
11
+ getDocsBaseColorShade,
12
+ getThemeForegroundColor,
13
+ } from "./theme-css";
14
+ import { type DocsConfig } from "./validation";
15
+
16
+ type AssistantColorByMode = {
17
+ light: string;
18
+ dark: string;
19
+ };
20
+
21
+ type GeneratedEmbedConfig = {
22
+ docsOrigin: string;
23
+ docsBasePath: string;
24
+ panelSrc: string;
25
+ apiUrl: string;
26
+ themeColor: string;
27
+ themeColors: AssistantColorByMode;
28
+ iconColor: string;
29
+ iconColors: AssistantColorByMode;
30
+ iconImageSrc: string;
31
+ chrome: AssistantChromeConfig;
32
+ };
33
+
34
+ export type ResolvedAssistantLauncherIcon = {
35
+ themeColor: string;
36
+ themeColors: AssistantColorByMode;
37
+ color: string;
38
+ colors: AssistantColorByMode;
39
+ imageSrc: string;
40
+ };
41
+
42
+ const DOCS_DIR = path.join(process.cwd(), "src/content/docs");
43
+ const IMAGE_MIME_TYPES: Record<string, string> = {
44
+ ".avif": "image/avif",
45
+ ".gif": "image/gif",
46
+ ".ico": "image/x-icon",
47
+ ".jpg": "image/jpeg",
48
+ ".jpeg": "image/jpeg",
49
+ ".png": "image/png",
50
+ ".svg": "image/svg+xml",
51
+ ".webp": "image/webp",
52
+ };
53
+
54
+ function getDocsSiteUrl(): URL | null {
55
+ const rawSiteUrl = (import.meta.env.DOCS_SITE_URL ?? "").toString().trim();
56
+ if (!rawSiteUrl) return null;
57
+
58
+ try {
59
+ return new URL(rawSiteUrl);
60
+ } catch {
61
+ return null;
62
+ }
63
+ }
64
+
65
+ function getPanelSrc(docsSiteUrl: URL | null): string {
66
+ if (import.meta.env.DEV) {
67
+ return withBasePath("/-/assistant/panel");
68
+ }
69
+
70
+ if (!docsSiteUrl) {
71
+ return withBasePath("/-/assistant/panel");
72
+ }
73
+
74
+ const panelUrl = new URL(withBasePath("/-/assistant/panel"), docsSiteUrl);
75
+ const buildVersion = (import.meta.env.DOCS_BUILD_VERSION ?? "")
76
+ .toString()
77
+ .trim();
78
+
79
+ if (buildVersion) {
80
+ panelUrl.searchParams.set("v", buildVersion);
81
+ }
82
+
83
+ return panelUrl.toString();
84
+ }
85
+
86
+ function getApiUrl(docsSiteUrl: URL | null): string {
87
+ if (!docsSiteUrl) return "";
88
+
89
+ return new URL(withBasePath("/_platform/assistant"), docsSiteUrl).toString();
90
+ }
91
+
92
+ function getLocalImageDataUrl(iconSource: string): string {
93
+ const parsed = new URL(iconSource, "https://docs.invalid/");
94
+ const normalizedPath = parsed.pathname.replace(/^\/+/, "");
95
+ const fullPath = path.join(DOCS_DIR, normalizedPath);
96
+ const extension = path.extname(parsed.pathname).toLowerCase();
97
+ const mimeType = IMAGE_MIME_TYPES[extension];
98
+
99
+ if (!mimeType || !fs.existsSync(fullPath)) {
100
+ return "";
101
+ }
102
+
103
+ const data = fs.readFileSync(fullPath);
104
+ return `data:${mimeType};base64,${data.toString("base64")}`;
105
+ }
106
+
107
+ function resolveLauncherIcon(
108
+ iconSource: string | undefined,
109
+ ): Pick<ResolvedAssistantLauncherIcon, "imageSrc"> {
110
+ const resolvedIconSource = iconSource ?? getFaviconConfig().href;
111
+
112
+ return {
113
+ imageSrc: getLocalImageDataUrl(resolvedIconSource),
114
+ };
115
+ }
116
+
117
+ function getConfiguredColorForMode(
118
+ value: DocsConfig["theme"]["themeColor"] | undefined,
119
+ mode: "light" | "dark",
120
+ ): string | undefined {
121
+ if (typeof value === "string") return value;
122
+ return value?.[mode];
123
+ }
124
+
125
+ function getDefaultAssistantThemeColor(
126
+ config: DocsConfig,
127
+ mode: "light" | "dark",
128
+ ): string {
129
+ const configuredThemeColor = getConfiguredColorForMode(
130
+ config.theme?.themeColor,
131
+ mode,
132
+ );
133
+ if (configuredThemeColor) return configuredThemeColor;
134
+
135
+ return getDocsBaseColorShade(config.theme, mode, mode === "light" ? "900" : "100");
136
+ }
137
+
138
+ function getAssistantThemeColor(
139
+ config: DocsConfig,
140
+ mode: "light" | "dark",
141
+ ): string {
142
+ const configuredAssistantColor = getConfiguredColorForMode(
143
+ config.assistant?.themeColor,
144
+ mode,
145
+ );
146
+ return configuredAssistantColor ?? getDefaultAssistantThemeColor(config, mode);
147
+ }
148
+
149
+ export function getAssistantLauncherIconConfig(
150
+ config: DocsConfig,
151
+ ): ResolvedAssistantLauncherIcon {
152
+ const assistantConfig = config.assistant;
153
+ const themeColors = {
154
+ light: getAssistantThemeColor(config, "light"),
155
+ dark: getAssistantThemeColor(config, "dark"),
156
+ };
157
+ const iconColors = {
158
+ light:
159
+ assistantConfig?.icon?.color ??
160
+ getThemeForegroundColor(
161
+ themeColors.light,
162
+ getDocsBaseColorShade(config.theme, "light", "900"),
163
+ ),
164
+ dark:
165
+ assistantConfig?.icon?.color ??
166
+ getThemeForegroundColor(
167
+ themeColors.dark,
168
+ getDocsBaseColorShade(config.theme, "dark", "900"),
169
+ ),
170
+ };
171
+ const icon = resolveLauncherIcon(assistantConfig?.icon?.src);
172
+
173
+ return {
174
+ themeColor: themeColors.light,
175
+ themeColors,
176
+ color: iconColors.light,
177
+ colors: iconColors,
178
+ imageSrc: icon.imageSrc,
179
+ };
180
+ }
181
+
182
+ function getGeneratedConfig(config: DocsConfig): GeneratedEmbedConfig {
183
+ const docsSiteUrl = getDocsSiteUrl();
184
+ const icon = getAssistantLauncherIconConfig(config);
185
+
186
+ return {
187
+ docsOrigin: docsSiteUrl?.origin ?? "",
188
+ docsBasePath: getDocsBasePath(),
189
+ panelSrc: getPanelSrc(docsSiteUrl),
190
+ apiUrl: getApiUrl(docsSiteUrl),
191
+ themeColor: icon.themeColor,
192
+ themeColors: icon.themeColors,
193
+ iconColor: icon.color,
194
+ iconColors: icon.colors,
195
+ iconImageSrc: icon.imageSrc,
196
+ chrome: getAssistantChromeConfig(config),
197
+ };
198
+ }
199
+
200
+ export function renderAssistantEmbedScript(config: DocsConfig): string {
201
+ const generatedConfig = JSON.stringify(getGeneratedConfig(config));
202
+ const fallbackPanelDarkBackground = JSON.stringify(
203
+ DEFAULT_ASSISTANT_CHROME_CONFIG.panelDarkBackground,
204
+ );
205
+
206
+ return `(function () {
207
+ var generatedConfig = ${generatedConfig};
208
+ var existing = window.AssistantEmbed;
209
+ if (existing && existing.installed) {
210
+ return;
211
+ }
212
+
213
+ var script = document.currentScript;
214
+ var scriptDataset = script ? script.dataset : {};
215
+ var globalConfig = window.AssistantEmbedConfig || {};
216
+ var configuredDocsOrigin =
217
+ scriptDataset.docsOrigin ||
218
+ globalConfig.docsOrigin ||
219
+ generatedConfig.docsOrigin ||
220
+ "";
221
+ var configuredDocsBasePath =
222
+ scriptDataset.docsBasePath ||
223
+ globalConfig.docsBasePath ||
224
+ generatedConfig.docsBasePath ||
225
+ "";
226
+ var configuredPanelSrc =
227
+ scriptDataset.panelSrc || globalConfig.panelSrc || generatedConfig.panelSrc || "";
228
+ var configuredApiUrl =
229
+ scriptDataset.apiUrl || globalConfig.apiUrl || generatedConfig.apiUrl || "";
230
+ var configuredIconImageSrc = generatedConfig.iconImageSrc || "";
231
+
232
+ function resolveDocsOrigin() {
233
+ if (configuredDocsOrigin) {
234
+ try {
235
+ return new URL(configuredDocsOrigin).origin;
236
+ } catch (error) {
237
+ console.warn("[Assistant embed] Invalid data-docs-origin", error);
238
+ }
239
+ }
240
+
241
+ if (script && script.src) {
242
+ try {
243
+ return new URL(script.src).origin;
244
+ } catch {
245
+ return "";
246
+ }
247
+ }
248
+
249
+ return "";
250
+ }
251
+
252
+ function normalizeBasePath(value) {
253
+ var trimmed = String(value || "").trim();
254
+ if (!trimmed || trimmed === "/") return "";
255
+ var pathname = trimmed.charAt(0) === "/" ? trimmed : "/" + trimmed;
256
+ pathname = pathname.replace(/\\/{2,}/g, "/").replace(/\\/+$/, "");
257
+ return pathname === "/" ? "" : pathname;
258
+ }
259
+
260
+ function resolveDocsBasePath() {
261
+ var explicitBasePath = normalizeBasePath(configuredDocsBasePath);
262
+ if (explicitBasePath) return explicitBasePath;
263
+
264
+ if (script && script.src) {
265
+ try {
266
+ var scriptPathname = new URL(script.src).pathname;
267
+ var suffixes = ["/-/assistant/embed.js", "/_platform/assistant/embed.js"];
268
+ for (var index = 0; index < suffixes.length; index += 1) {
269
+ var suffix = suffixes[index];
270
+ if (scriptPathname.endsWith(suffix)) {
271
+ return normalizeBasePath(scriptPathname.slice(0, -suffix.length));
272
+ }
273
+ }
274
+ } catch {
275
+ return "";
276
+ }
277
+ }
278
+
279
+ return "";
280
+ }
281
+
282
+ function withBasePath(pathname) {
283
+ var basePath = docsBasePath;
284
+ if (!basePath) return pathname;
285
+ if (pathname === basePath || pathname.startsWith(basePath + "/")) {
286
+ return pathname;
287
+ }
288
+ if (pathname === "/") return basePath;
289
+ return basePath + pathname;
290
+ }
291
+
292
+ function escapeHtmlAttribute(value) {
293
+ return String(value).replace(/[&<>"']/g, function (character) {
294
+ if (character === "&") return "&amp;";
295
+ if (character === "<") return "&lt;";
296
+ if (character === ">") return "&gt;";
297
+ if (character === '"') return "&quot;";
298
+ return "&#39;";
299
+ });
300
+ }
301
+
302
+ var docsOrigin = resolveDocsOrigin();
303
+ var docsBasePath = resolveDocsBasePath();
304
+ var chrome = generatedConfig.chrome || {};
305
+ var zIndex = scriptDataset.zIndex || chrome.zIndex || "2147483000";
306
+ var configuredMode =
307
+ scriptDataset.mode ||
308
+ (typeof globalConfig.mode === "string" ? globalConfig.mode : "") ||
309
+ "system";
310
+ var defaultSide = chrome.side === "left" ? "left" : "right";
311
+ var side =
312
+ scriptDataset.side === "left" || scriptDataset.side === "right"
313
+ ? scriptDataset.side
314
+ : defaultSide;
315
+ var offsetX = scriptDataset.offsetX || chrome.offsetX || "20px";
316
+ var offsetY = scriptDataset.offsetY || chrome.offsetY || "20px";
317
+ var mobileOffsetX = chrome.mobileOffsetX || "16px";
318
+ var mobileOffsetY = chrome.mobileOffsetY || "16px";
319
+ var panelWidth = scriptDataset.panelWidth || chrome.panelWidth || "420px";
320
+ var panelHeight = scriptDataset.panelHeight || chrome.panelHeight || "640px";
321
+ var panelGap = chrome.panelGap || "12px";
322
+ var panelRadius = chrome.panelRadius || "16px";
323
+ var panelBorder = chrome.panelBorder || "1px solid rgb(9 14 21 / 8%)";
324
+ var panelBackground = chrome.panelBackground || "rgb(255 255 255 / 90%)";
325
+ var panelDarkBorder = chrome.panelDarkBorder || "1px solid rgb(255 255 255 / 10%)";
326
+ var panelDarkBackground =
327
+ chrome.panelDarkBackground ||
328
+ ${fallbackPanelDarkBackground};
329
+ var panelBackdropBlur = chrome.panelBackdropBlur || "48px";
330
+ var panelShadow =
331
+ chrome.panelShadow || "rgba(9, 14, 21, 0.16) 0px 5px 40px 0px";
332
+ var panelDarkShadow =
333
+ chrome.panelDarkShadow || "rgba(0, 0, 0, 0.36) 0px 8px 44px 0px";
334
+ var panelOpenScale = chrome.panelOpenScale || ".9";
335
+ var panelOpenDuration = chrome.panelOpenDuration || ".5s";
336
+ var panelOpenTiming = chrome.panelOpenTiming || "cubic-bezier(.34,1.56,.64,1)";
337
+ var panelCloseDuration = chrome.panelCloseDuration || ".2s";
338
+ var panelCloseTiming = chrome.panelCloseTiming || "cubic-bezier(.25,1,.5,1)";
339
+ var mobileBreakpoint = chrome.mobileBreakpoint || "640px";
340
+ var launcherSize = scriptDataset.launcherSize || chrome.launcherSize || "48px";
341
+ var launcherIconSize = chrome.launcherIconSize || "20px";
342
+ var launcherShadow =
343
+ chrome.launcherShadow ||
344
+ "0 18px 40px rgb(0 0 0 / 18%),0 3px 10px rgb(0 0 0 / 12%)";
345
+ var numericZIndex = Number.parseInt(zIndex, 10) || 2147483000;
346
+ var generatedThemeColors = generatedConfig.themeColors || {
347
+ light: generatedConfig.themeColor || "#171717",
348
+ dark: generatedConfig.themeColor || "#171717",
349
+ };
350
+ var generatedIconColors = generatedConfig.iconColors || {
351
+ light: generatedConfig.iconColor || "#ffffff",
352
+ dark: generatedConfig.iconColor || "#ffffff",
353
+ };
354
+ var themeColor = "";
355
+ var iconColor = "";
356
+ var styleId = "assistant-embed-style";
357
+ var launcherButton;
358
+ var panelShell;
359
+ var panelFrame;
360
+ var isOpen = false;
361
+ var systemThemeMediaQuery =
362
+ typeof window.matchMedia === "function"
363
+ ? window.matchMedia("(prefers-color-scheme: dark)")
364
+ : null;
365
+ var isListeningForSystemTheme = false;
366
+
367
+ function normalizeThemeMode(value) {
368
+ var normalized = String(value || "").trim().toLowerCase();
369
+ if (
370
+ normalized === "light" ||
371
+ normalized === "dark" ||
372
+ normalized === "system"
373
+ ) {
374
+ return normalized;
375
+ }
376
+ return "";
377
+ }
378
+
379
+ function getSystemTheme() {
380
+ if (systemThemeMediaQuery && systemThemeMediaQuery.matches) {
381
+ return "dark";
382
+ }
383
+ return "light";
384
+ }
385
+
386
+ function resolveThemeMode(nextMode) {
387
+ if (nextMode === "light" || nextMode === "dark") {
388
+ return nextMode;
389
+ }
390
+ return getSystemTheme();
391
+ }
392
+
393
+ var themeMode = normalizeThemeMode(configuredMode) || "system";
394
+ var resolvedTheme = resolveThemeMode(themeMode);
395
+
396
+ function getColorForMode(value, mode) {
397
+ if (typeof value === "string" && value.trim().length > 0) {
398
+ return value;
399
+ }
400
+
401
+ if (value && typeof value === "object" && typeof value[mode] === "string") {
402
+ return value[mode];
403
+ }
404
+
405
+ return "";
406
+ }
407
+
408
+ function resolveLauncherThemeColor() {
409
+ return (
410
+ (resolvedTheme === "dark"
411
+ ? scriptDataset.themeColorDark
412
+ : scriptDataset.themeColorLight) ||
413
+ getColorForMode(scriptDataset.themeColor, resolvedTheme) ||
414
+ getColorForMode(globalConfig.themeColor, resolvedTheme) ||
415
+ getColorForMode(generatedThemeColors, resolvedTheme) ||
416
+ "#171717"
417
+ );
418
+ }
419
+
420
+ function resolveLauncherIconColor() {
421
+ return (
422
+ getColorForMode(generatedIconColors, resolvedTheme) ||
423
+ generatedConfig.iconColor ||
424
+ "#ffffff"
425
+ );
426
+ }
427
+
428
+ function applyLauncherColors() {
429
+ themeColor = resolveLauncherThemeColor();
430
+ iconColor = resolveLauncherIconColor();
431
+
432
+ if (!launcherButton) {
433
+ return;
434
+ }
435
+
436
+ launcherButton.style.setProperty("--assistant-embed-theme", themeColor);
437
+ launcherButton.style.setProperty("--assistant-embed-icon-color", iconColor);
438
+ }
439
+
440
+ applyLauncherColors();
441
+
442
+ function resolveUrl(value, fallbackPathname) {
443
+ var rawValue = value || withBasePath(fallbackPathname);
444
+ try {
445
+ return new URL(rawValue, docsOrigin || window.location.origin);
446
+ } catch {
447
+ return new URL(withBasePath(fallbackPathname), window.location.origin);
448
+ }
449
+ }
450
+
451
+ function buildPanelUrl() {
452
+ var url = resolveUrl(configuredPanelSrc, "/-/assistant/panel");
453
+ url.searchParams.set("mode", resolvedTheme);
454
+ if (configuredApiUrl) {
455
+ url.searchParams.set("apiUrl", configuredApiUrl);
456
+ }
457
+ return url.toString();
458
+ }
459
+
460
+ function buildIconUrl() {
461
+ if (!configuredIconImageSrc) {
462
+ return "";
463
+ }
464
+
465
+ try {
466
+ return new URL(configuredIconImageSrc, docsOrigin || window.location.origin).toString();
467
+ } catch {
468
+ return configuredIconImageSrc;
469
+ }
470
+ }
471
+
472
+ function ensureStyle() {
473
+ if (document.getElementById(styleId)) {
474
+ return;
475
+ }
476
+
477
+ var style = document.createElement("style");
478
+ style.id = styleId;
479
+ style.textContent = [
480
+ ".assistant-embed-frame{display:block;width:100%;height:100%;border:0;background:transparent;color-scheme:normal;}",
481
+ "@keyframes assistant-embed-launcher-enter{0%{opacity:0;transform:translateY(3px) scale(.96);}46%{opacity:1;transform:translateY(0) scale(1.012);}72%{opacity:1;transform:translateY(0) scale(.998);}100%{opacity:1;transform:translateY(0) scale(1);}}",
482
+ ".assistant-embed-launcher{position:fixed;display:inline-flex;align-items:center;justify-content:center;border:0;padding:0;cursor:pointer;color:var(--assistant-embed-icon-color);background:linear-gradient(to bottom,color-mix(in oklab,var(--assistant-embed-theme) 88%,white),color-mix(in oklab,var(--assistant-embed-theme) 90%,black));animation:assistant-embed-launcher-enter 460ms linear backwards;transition:transform 180ms ease,opacity 160ms ease;transform-origin:center center;}",
483
+ ".assistant-embed-launcher:hover{opacity:.96;transform:translateY(-1px) scale(1.03);}",
484
+ ".assistant-embed-launcher:active{transform:translateY(0) scale(.96);}",
485
+ ".assistant-embed-launcher{width:" +
486
+ launcherSize +
487
+ ";height:" +
488
+ launcherSize +
489
+ ";bottom:" +
490
+ offsetY +
491
+ ";" +
492
+ side +
493
+ ":" +
494
+ offsetX +
495
+ ";z-index:" +
496
+ (numericZIndex + 1) +
497
+ ";border-radius:999px;box-shadow:" +
498
+ launcherShadow +
499
+ ";}",
500
+ ".assistant-embed-launcher-icon{position:absolute;width:" +
501
+ launcherIconSize +
502
+ ";height:" +
503
+ launcherIconSize +
504
+ ";pointer-events:none;transform-origin:center;transition:opacity 150ms ease,transform 180ms cubic-bezier(.2,.8,.2,1);}",
505
+ ".assistant-embed-launcher-custom-icon{object-fit:contain;}",
506
+ ".assistant-embed-launcher-mark{opacity:1;transform:scale(1);}",
507
+ ".assistant-embed-launcher-chevron{opacity:0;transform:scale(.55);}",
508
+ ".assistant-embed-launcher[data-open='true'] .assistant-embed-launcher-mark{opacity:0;transform:scale(.55);}",
509
+ ".assistant-embed-launcher[data-open='true'] .assistant-embed-launcher-chevron{opacity:1;transform:scale(1);}",
510
+ ".assistant-embed-panel-shell{position:fixed;width:min(" +
511
+ panelWidth +
512
+ ",calc(100vw - 32px));height:min(" +
513
+ panelHeight +
514
+ ",calc(100dvh - 32px));bottom:calc(" +
515
+ offsetY +
516
+ " + " +
517
+ launcherSize +
518
+ " + " +
519
+ panelGap +
520
+ ");" +
521
+ side +
522
+ ":" +
523
+ offsetX +
524
+ ";z-index:" +
525
+ zIndex +
526
+ ";box-sizing:border-box;border:" +
527
+ panelBorder +
528
+ ";border-radius:" +
529
+ panelRadius +
530
+ ";box-shadow:" +
531
+ panelShadow +
532
+ ";background:" +
533
+ panelBackground +
534
+ ";-webkit-backdrop-filter:blur(" +
535
+ panelBackdropBlur +
536
+ ");backdrop-filter:blur(" +
537
+ panelBackdropBlur +
538
+ ");overflow:hidden;opacity:1;pointer-events:auto;transform:scale(1);transform-origin:bottom " +
539
+ side +
540
+ ";transition-property:transform,opacity,display;transition-duration:" +
541
+ panelOpenDuration +
542
+ ";transition-timing-function:" +
543
+ panelOpenTiming +
544
+ ";transition-behavior:allow-discrete;}",
545
+ ".assistant-embed-panel-shell[data-theme='dark']{border:" +
546
+ panelDarkBorder +
547
+ ";background:" +
548
+ panelDarkBackground +
549
+ ";box-shadow:" +
550
+ panelDarkShadow +
551
+ ";}",
552
+ "@starting-style{.assistant-embed-panel-shell[data-state='open']{opacity:0;transform:scale(" +
553
+ panelOpenScale +
554
+ ");}}",
555
+ ".assistant-embed-panel-shell[data-state='closed']{display:none;opacity:0;pointer-events:none;transform:scale(" +
556
+ panelOpenScale +
557
+ ");transition-property:transform,opacity,display;transition-duration:" +
558
+ panelCloseDuration +
559
+ ";transition-timing-function:" +
560
+ panelCloseTiming +
561
+ ";transition-behavior:allow-discrete;}",
562
+ "@media (max-width:" +
563
+ mobileBreakpoint +
564
+ "){.assistant-embed-panel-shell{inset:0!important;width:100vw!important;height:100dvh!important;border:0!important;border-radius:0!important;z-index:" +
565
+ (numericZIndex + 2) +
566
+ "!important;}}",
567
+ "@media (max-width:" +
568
+ mobileBreakpoint +
569
+ "){.assistant-embed-launcher{bottom:" +
570
+ mobileOffsetY +
571
+ "!important;" +
572
+ side +
573
+ ":" +
574
+ mobileOffsetX +
575
+ "!important;}}",
576
+ "@media (prefers-reduced-motion:reduce){.assistant-embed-launcher{animation:none!important;}.assistant-embed-launcher,.assistant-embed-launcher-icon,.assistant-embed-panel-shell{transition:none!important;}}",
577
+ ].join("");
578
+ document.head.appendChild(style);
579
+ }
580
+
581
+ function setPanelOpen(nextOpen) {
582
+ if (!panelShell) {
583
+ return;
584
+ }
585
+
586
+ if (nextOpen) {
587
+ if (panelShell.dataset.state === "open") {
588
+ return;
589
+ }
590
+ panelShell.dataset.state = "open";
591
+ return;
592
+ }
593
+
594
+ if (panelShell.dataset.state === "closed") {
595
+ return;
596
+ }
597
+
598
+ panelShell.dataset.state = "closed";
599
+ }
600
+
601
+ function postPanelOpenedMessage() {
602
+ if (!panelFrame || !panelFrame.contentWindow) {
603
+ return;
604
+ }
605
+
606
+ try {
607
+ panelFrame.contentWindow.postMessage(
608
+ { type: "assistant-embed:panel-opened" },
609
+ getPanelTargetOrigin(),
610
+ );
611
+ } catch {
612
+ // Ignore cross-window messaging failures.
613
+ }
614
+ }
615
+
616
+ function getPanelTargetOrigin() {
617
+ if (!panelFrame || !panelFrame.src) {
618
+ return "*";
619
+ }
620
+
621
+ try {
622
+ return new URL(panelFrame.src).origin;
623
+ } catch {
624
+ return "*";
625
+ }
626
+ }
627
+
628
+ function postPanelThemeMessage() {
629
+ if (!panelFrame || !panelFrame.contentWindow) {
630
+ return;
631
+ }
632
+
633
+ try {
634
+ panelFrame.contentWindow.postMessage(
635
+ {
636
+ type: "assistant-embed:set-theme",
637
+ theme: resolvedTheme,
638
+ },
639
+ getPanelTargetOrigin(),
640
+ );
641
+ } catch {
642
+ // Ignore cross-window messaging failures.
643
+ }
644
+ }
645
+
646
+ function applyResolvedTheme(shouldNotifyPanel) {
647
+ if (panelShell) {
648
+ panelShell.dataset.theme = resolvedTheme;
649
+ }
650
+
651
+ applyLauncherColors();
652
+
653
+ if (shouldNotifyPanel) {
654
+ postPanelThemeMessage();
655
+ }
656
+ }
657
+
658
+ function handleSystemThemeChange() {
659
+ if (themeMode !== "system") {
660
+ return;
661
+ }
662
+
663
+ var nextResolvedTheme = resolveThemeMode(themeMode);
664
+ if (nextResolvedTheme === resolvedTheme) {
665
+ return;
666
+ }
667
+
668
+ resolvedTheme = nextResolvedTheme;
669
+ applyResolvedTheme(true);
670
+ }
671
+
672
+ function setSystemThemeListener(shouldListen) {
673
+ if (!systemThemeMediaQuery) {
674
+ return;
675
+ }
676
+
677
+ if (shouldListen && !isListeningForSystemTheme) {
678
+ if (typeof systemThemeMediaQuery.addEventListener === "function") {
679
+ systemThemeMediaQuery.addEventListener("change", handleSystemThemeChange);
680
+ } else if (typeof systemThemeMediaQuery.addListener === "function") {
681
+ systemThemeMediaQuery.addListener(handleSystemThemeChange);
682
+ }
683
+ isListeningForSystemTheme = true;
684
+ return;
685
+ }
686
+
687
+ if (!shouldListen && isListeningForSystemTheme) {
688
+ if (typeof systemThemeMediaQuery.removeEventListener === "function") {
689
+ systemThemeMediaQuery.removeEventListener(
690
+ "change",
691
+ handleSystemThemeChange,
692
+ );
693
+ } else if (typeof systemThemeMediaQuery.removeListener === "function") {
694
+ systemThemeMediaQuery.removeListener(handleSystemThemeChange);
695
+ }
696
+ isListeningForSystemTheme = false;
697
+ }
698
+ }
699
+
700
+ function setTheme(nextMode) {
701
+ var normalizedMode = normalizeThemeMode(nextMode);
702
+ if (!normalizedMode) {
703
+ console.warn(
704
+ "[Assistant embed] Theme must be 'light', 'dark', or 'system'.",
705
+ );
706
+ return;
707
+ }
708
+
709
+ themeMode = normalizedMode;
710
+ setSystemThemeListener(themeMode === "system");
711
+ resolvedTheme = resolveThemeMode(themeMode);
712
+ applyResolvedTheme(true);
713
+ }
714
+
715
+ function notifyPanelOpened() {
716
+ if (typeof window.requestAnimationFrame === "function") {
717
+ window.requestAnimationFrame(function () {
718
+ window.requestAnimationFrame(postPanelOpenedMessage);
719
+ });
720
+ return;
721
+ }
722
+
723
+ window.setTimeout(postPanelOpenedMessage, 32);
724
+ }
725
+
726
+ function setOpen(nextOpen) {
727
+ var wasOpen = isOpen;
728
+ isOpen = nextOpen === true;
729
+ setPanelOpen(isOpen);
730
+ if (isOpen && !wasOpen) {
731
+ notifyPanelOpened();
732
+ }
733
+ if (launcherButton) {
734
+ launcherButton.dataset.open = String(isOpen);
735
+ launcherButton.setAttribute(
736
+ "aria-label",
737
+ isOpen ? "Close chat" : "Open chat",
738
+ );
739
+ launcherButton.setAttribute("title", isOpen ? "Close chat" : "Open chat");
740
+ }
741
+ }
742
+
743
+ function createFrame(className, title, src) {
744
+ var frame = document.createElement("iframe");
745
+ frame.className = "assistant-embed-frame " + className;
746
+ frame.title = title;
747
+ frame.src = src;
748
+ frame.allow = "clipboard-write";
749
+ frame.setAttribute("data-assistant-embed-frame", "true");
750
+ frame.addEventListener("load", function () {
751
+ postPanelThemeMessage();
752
+ if (isOpen) {
753
+ notifyPanelOpened();
754
+ }
755
+ });
756
+ return frame;
757
+ }
758
+
759
+ function createPanelShell(frame) {
760
+ var shell = document.createElement("div");
761
+ shell.className = "assistant-embed-panel-shell";
762
+ shell.dataset.state = "closed";
763
+ shell.dataset.theme = resolvedTheme;
764
+ shell.setAttribute("data-assistant-embed-panel", "true");
765
+ shell.appendChild(frame);
766
+ return shell;
767
+ }
768
+
769
+ function getLauncherMarkHtml() {
770
+ var iconUrl = buildIconUrl();
771
+ if (iconUrl) {
772
+ return (
773
+ '<img class="assistant-embed-launcher-icon assistant-embed-launcher-mark assistant-embed-launcher-custom-icon" src="' +
774
+ escapeHtmlAttribute(iconUrl) +
775
+ '" alt="" aria-hidden="true" />'
776
+ );
777
+ }
778
+
779
+ return "";
780
+ }
781
+
782
+ function createLauncherButton() {
783
+ var button = document.createElement("button");
784
+ button.type = "button";
785
+ button.className = "assistant-embed-launcher";
786
+ button.dataset.open = "false";
787
+ button.setAttribute("aria-label", "Open chat");
788
+ button.setAttribute("title", "Open chat");
789
+ button.setAttribute("data-assistant-embed-launcher", "true");
790
+ button.style.setProperty("--assistant-embed-theme", themeColor);
791
+ button.style.setProperty(
792
+ "--assistant-embed-icon-color",
793
+ iconColor,
794
+ );
795
+
796
+ button.innerHTML =
797
+ getLauncherMarkHtml() +
798
+ '<svg class="assistant-embed-launcher-icon assistant-embed-launcher-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"></path></svg>';
799
+
800
+ button.addEventListener("click", function () {
801
+ setOpen(!isOpen);
802
+ });
803
+
804
+ return button;
805
+ }
806
+
807
+ function mount() {
808
+ if (!document.body) {
809
+ document.addEventListener("DOMContentLoaded", mount, { once: true });
810
+ return;
811
+ }
812
+
813
+ ensureStyle();
814
+
815
+ panelFrame = createFrame(
816
+ "assistant-embed-panel-frame",
817
+ "Chat",
818
+ buildPanelUrl(),
819
+ );
820
+ panelShell = createPanelShell(panelFrame);
821
+ launcherButton = createLauncherButton();
822
+
823
+ document.body.appendChild(panelShell);
824
+ document.body.appendChild(launcherButton);
825
+ }
826
+
827
+ window.addEventListener("message", function (event) {
828
+ if (
829
+ !event.data ||
830
+ typeof event.data.type !== "string" ||
831
+ !event.data.type.startsWith("assistant-embed:")
832
+ ) {
833
+ return;
834
+ }
835
+
836
+ if (event.source !== (panelFrame && panelFrame.contentWindow)) {
837
+ return;
838
+ }
839
+
840
+ if (event.data.type === "assistant-embed:open") {
841
+ setOpen(true);
842
+ return;
843
+ }
844
+
845
+ if (event.data.type === "assistant-embed:close") {
846
+ setOpen(false);
847
+ return;
848
+ }
849
+
850
+ if (event.data.type === "assistant-embed:toggle") {
851
+ setOpen(!isOpen);
852
+ }
853
+ });
854
+
855
+ window.addEventListener("ask-ai:open", function () {
856
+ setOpen(true);
857
+ });
858
+
859
+ window.AssistantEmbed = {
860
+ installed: true,
861
+ open: function () {
862
+ setOpen(true);
863
+ },
864
+ close: function () {
865
+ setOpen(false);
866
+ },
867
+ toggle: function () {
868
+ setOpen(!isOpen);
869
+ },
870
+ setTheme: setTheme,
871
+ getTheme: function () {
872
+ return {
873
+ mode: themeMode,
874
+ resolvedTheme: resolvedTheme,
875
+ };
876
+ },
877
+ destroy: function () {
878
+ setSystemThemeListener(false);
879
+ if (launcherButton) {
880
+ launcherButton.remove();
881
+ }
882
+ if (panelShell) {
883
+ panelShell.remove();
884
+ }
885
+ var style = document.getElementById(styleId);
886
+ if (style) {
887
+ style.remove();
888
+ }
889
+ window.AssistantEmbed = undefined;
890
+ },
891
+ };
892
+
893
+ setSystemThemeListener(themeMode === "system");
894
+ mount();
895
+ })();
896
+ `;
897
+ }