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.
- package/package.json +1 -1
- package/template/astro.config.mjs +38 -7
- package/template/package-lock.json +19 -7
- package/template/package.json +3 -3
- package/template/public/favicon.svg +16 -8
- package/template/scripts/generate-robots-txt.mjs +29 -1
- package/template/scripts/remove-assistant-for-non-pro.mjs +28 -0
- package/template/scripts/stamp-image-versions.mjs +59 -33
- package/template/src/components/Footer.astro +2 -1
- package/template/src/components/Header.astro +10 -8
- package/template/src/components/LogoLink.astro +2 -1
- package/template/src/components/MdxPage.astro +15 -4
- package/template/src/components/PagePagination.astro +61 -0
- package/template/src/components/SidebarDropdown.astro +12 -8
- package/template/src/components/SidebarGroup.astro +1 -1
- package/template/src/components/SidebarMenu.astro +1 -1
- package/template/src/components/SidebarSegmented.astro +6 -5
- package/template/src/components/TableOfContents.astro +4 -13
- package/template/src/components/chat/AskAiWidget.tsx +274 -39
- package/template/src/components/chat/AssistantDocsWidget.astro +16 -0
- package/template/src/components/chat/AssistantDocsWidget.tsx +402 -0
- package/template/src/components/chat/AssistantEmbedPanel.tsx +1693 -0
- package/template/src/components/chat/AssistantEmbedPanelPage.astro +95 -0
- package/template/src/components/endpoint/PlaygroundForm.astro +2 -1
- package/template/src/components/user/Callout.astro +10 -4
- package/template/src/components/user/CodeBlock.astro +1 -1
- package/template/src/components/user/CodeGroup.astro +16 -1
- package/template/src/components/user/ComponentPreviewBlock.astro +1 -0
- package/template/src/components/user/Image.astro +43 -53
- package/template/src/layouts/Layout.astro +104 -35
- package/template/src/lib/assistant-chrome-defaults.ts +74 -0
- package/template/src/lib/assistant-chrome.ts +39 -0
- package/template/src/lib/assistant-embed-script.ts +897 -0
- package/template/src/lib/assistant-panel-config.ts +80 -0
- package/template/src/lib/base-path.ts +98 -0
- package/template/src/lib/component-error.ts +49 -10
- package/template/src/lib/favicon.ts +31 -0
- package/template/src/lib/mdx/remark-resolve-internal-links.ts +128 -18
- package/template/src/lib/pagefind.ts +62 -14
- package/template/src/lib/routes.ts +49 -1
- package/template/src/lib/static-asset-url.ts +3 -1
- package/template/src/lib/theme-css.ts +176 -0
- package/template/src/lib/utils.ts +12 -4
- package/template/src/lib/validation.ts +754 -37
- package/template/src/pages/-/assistant/embed.js.ts +15 -0
- package/template/src/pages/-/assistant/panel.astro +5 -0
- package/template/src/pages/404.astro +6 -5
- package/template/src/pages/[...slug].astro +68 -6
- package/template/src/styles/global.css +62 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
import "../../styles/global.css";
|
|
3
|
+
import "../../styles/google-sans-flex.css";
|
|
4
|
+
import "../../styles/geist-mono.css";
|
|
5
|
+
import AssistantEmbedPanel from "./AssistantEmbedPanel";
|
|
6
|
+
import { getAssistantPanelRuntimeConfig } from "../../lib/assistant-panel-config";
|
|
7
|
+
import { getDocsThemeCss } from "../../lib/theme-css";
|
|
8
|
+
import { getConfig } from "../../lib/validation";
|
|
9
|
+
|
|
10
|
+
const config = await getConfig();
|
|
11
|
+
const themeCss = getDocsThemeCss(config.theme);
|
|
12
|
+
const assistantConfig = getAssistantPanelRuntimeConfig(config);
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<!doctype html>
|
|
16
|
+
<html lang="en">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="utf-8" />
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
20
|
+
<meta name="robots" content="noindex" />
|
|
21
|
+
<style is:inline is:global set:html={themeCss}></style>
|
|
22
|
+
<script is:inline>
|
|
23
|
+
(() => {
|
|
24
|
+
const applyTheme = (theme) => {
|
|
25
|
+
if (theme !== "light" && theme !== "dark") {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
document.documentElement.dataset.theme = theme;
|
|
30
|
+
document.documentElement.classList.toggle("dark", theme === "dark");
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const params = new URLSearchParams(window.location.search);
|
|
34
|
+
const forcedTheme = params.get("mode");
|
|
35
|
+
const localStorageTheme = localStorage.getItem("theme");
|
|
36
|
+
const prefersDark = window.matchMedia(
|
|
37
|
+
"(prefers-color-scheme: dark)",
|
|
38
|
+
).matches;
|
|
39
|
+
const theme =
|
|
40
|
+
forcedTheme === "light" || forcedTheme === "dark"
|
|
41
|
+
? forcedTheme
|
|
42
|
+
: localStorageTheme === "light" || localStorageTheme === "dark"
|
|
43
|
+
? localStorageTheme
|
|
44
|
+
: prefersDark
|
|
45
|
+
? "dark"
|
|
46
|
+
: "light";
|
|
47
|
+
|
|
48
|
+
applyTheme(theme);
|
|
49
|
+
|
|
50
|
+
window.addEventListener("message", (event) => {
|
|
51
|
+
if (
|
|
52
|
+
!event.data ||
|
|
53
|
+
typeof event.data !== "object" ||
|
|
54
|
+
event.data.type !== "assistant-embed:set-theme"
|
|
55
|
+
) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
applyTheme(event.data.theme);
|
|
60
|
+
});
|
|
61
|
+
})();
|
|
62
|
+
</script>
|
|
63
|
+
<style is:inline>
|
|
64
|
+
html,
|
|
65
|
+
body {
|
|
66
|
+
width: 100%;
|
|
67
|
+
height: 100%;
|
|
68
|
+
margin: 0;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
background: transparent;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
body {
|
|
74
|
+
color-scheme: inherit;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (max-width: 640px) {
|
|
78
|
+
.assistant-panel-shell > div {
|
|
79
|
+
border-radius: 0;
|
|
80
|
+
border-left: 0;
|
|
81
|
+
border-right: 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
85
|
+
</head>
|
|
86
|
+
<body data-pagefind-ignore>
|
|
87
|
+
<div class="assistant-panel-shell h-full">
|
|
88
|
+
<AssistantEmbedPanel
|
|
89
|
+
client:only="preact"
|
|
90
|
+
{...assistantConfig}
|
|
91
|
+
linkTarget="blank"
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
</body>
|
|
95
|
+
</html>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { Icon } from "astro-icon/components";
|
|
3
|
+
import { withBasePath } from "../../lib/base-path";
|
|
3
4
|
import type { OpenApiRoute } from "../../lib/routes";
|
|
4
5
|
import { getConfig } from "../../lib/validation";
|
|
5
6
|
import { renderMarkdown } from "../../lib/utils";
|
|
@@ -37,7 +38,7 @@ const configuredProxyUrl =
|
|
|
37
38
|
typeof import.meta.env.PUBLIC_PROXY_URL === "string"
|
|
38
39
|
? import.meta.env.PUBLIC_PROXY_URL.trim()
|
|
39
40
|
: "";
|
|
40
|
-
const proxyUrl = configuredProxyUrl || "/_platform/proxy";
|
|
41
|
+
const proxyUrl = configuredProxyUrl || withBasePath("/_platform/proxy");
|
|
41
42
|
const proxyEnabled = config.playground?.proxy !== false && proxyUrl.length > 0;
|
|
42
43
|
const formattedBodyDescription = bodyDescription
|
|
43
44
|
? await renderMarkdown(bodyDescription)
|
|
@@ -72,13 +72,13 @@ const resolvedTitle = title ?? defaults.title;
|
|
|
72
72
|
|
|
73
73
|
<aside
|
|
74
74
|
class:list={[
|
|
75
|
-
"my-5 space-y-1 rounded-
|
|
75
|
+
"relative my-5 space-y-1 rounded-xl border-[0.5px]. border-neutral-900/8 bg-white px-4 py-3.5 pl-6 dark:border-neutral-800 dark:bg-(--rd-code-surface) shadow-[0px_1px_3px_0px_rgba(0,0,0,0.04),0px_0px_0px_0.8px_rgba(0,0,0,0.06)_inset,0px_-1px_0px_0px_rgba(0,0,0,0.06)_inset]",
|
|
76
76
|
]}
|
|
77
77
|
role="note"
|
|
78
78
|
>
|
|
79
79
|
<div
|
|
80
80
|
class:list={[
|
|
81
|
-
"absolute left-2 inset-y-2 w-[
|
|
81
|
+
"absolute left-2.5 inset-y-2.5 w-[2px] rounded-full mb-0",
|
|
82
82
|
defaults.color,
|
|
83
83
|
]}
|
|
84
84
|
style={color && { backgroundColor: color }}
|
|
@@ -96,11 +96,17 @@ const resolvedTitle = title ?? defaults.title;
|
|
|
96
96
|
<img src={defaults.icon} alt="" width="16" height="16" class="" />
|
|
97
97
|
)
|
|
98
98
|
}
|
|
99
|
-
<h6
|
|
99
|
+
<h6
|
|
100
|
+
class:list={[
|
|
101
|
+
"font-semibold text-sm text-neutral-900 dark:text-neutral-100",
|
|
102
|
+
]}
|
|
103
|
+
>
|
|
100
104
|
{resolvedTitle}
|
|
101
105
|
</h6>
|
|
102
106
|
</div>
|
|
103
|
-
<div
|
|
107
|
+
<div
|
|
108
|
+
class="**:first:mt-0! **:last:mb-0! text-sm text-neutral-700 dark:text-neutral-300"
|
|
109
|
+
>
|
|
104
110
|
<slot />
|
|
105
111
|
</div>
|
|
106
112
|
</aside>
|
|
@@ -376,7 +376,7 @@ const renderedCodeLinesHtml = normalizedTokenLines
|
|
|
376
376
|
</div>
|
|
377
377
|
</div>
|
|
378
378
|
</div>
|
|
379
|
-
<script is:inline>
|
|
379
|
+
<script is:inline data-astro-rerun>
|
|
380
380
|
(() => {
|
|
381
381
|
const script = document.currentScript;
|
|
382
382
|
if (!(script instanceof HTMLScriptElement)) return;
|
|
@@ -64,7 +64,7 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
64
64
|
>
|
|
65
65
|
<slot />
|
|
66
66
|
</div>
|
|
67
|
-
<script is:inline>
|
|
67
|
+
<script is:inline data-astro-rerun>
|
|
68
68
|
(() => {
|
|
69
69
|
const script = document.currentScript;
|
|
70
70
|
if (!(script instanceof HTMLScriptElement)) return;
|
|
@@ -421,6 +421,21 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
|
|
|
421
421
|
</div>
|
|
422
422
|
|
|
423
423
|
<style>
|
|
424
|
+
[data-rd-code-group-content] > [data-rd-code-group-item="true"] {
|
|
425
|
+
position: relative;
|
|
426
|
+
z-index: 1;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
[data-rd-code-group-content]
|
|
430
|
+
> [data-rd-code-group-item="true"]:not(:first-child) {
|
|
431
|
+
position: absolute;
|
|
432
|
+
inset: 0;
|
|
433
|
+
opacity: 0;
|
|
434
|
+
visibility: hidden;
|
|
435
|
+
pointer-events: none;
|
|
436
|
+
z-index: 0;
|
|
437
|
+
}
|
|
438
|
+
|
|
424
439
|
@keyframes rd-code-group-slide-in-from-right {
|
|
425
440
|
from {
|
|
426
441
|
transform: translateX(100%);
|
|
@@ -1,28 +1,54 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
3
|
-
import { validateProps } from "../../lib/component-error";
|
|
2
|
+
import { validateNoUnknownProps, validateProps } from "../../lib/component-error";
|
|
4
3
|
import { renderMarkdown } from "../../lib/utils";
|
|
5
4
|
import { resolveStaticAssetUrl } from "../../lib/static-asset-url";
|
|
6
5
|
|
|
7
|
-
interface Props
|
|
6
|
+
interface Props {
|
|
8
7
|
src: string;
|
|
8
|
+
alt?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
width?: number | string;
|
|
9
11
|
zoom?: boolean;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
const
|
|
14
|
+
const imageProps = Astro.props as Record<string, unknown>;
|
|
15
|
+
|
|
16
|
+
validateNoUnknownProps(
|
|
17
|
+
"Image",
|
|
18
|
+
imageProps,
|
|
19
|
+
["src", "alt", "title", "width", "zoom"],
|
|
20
|
+
Astro.url.pathname,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
validateProps(
|
|
24
|
+
"Image",
|
|
25
|
+
imageProps,
|
|
26
|
+
{
|
|
27
|
+
src: { required: true, type: "string" },
|
|
28
|
+
alt: { type: "string" },
|
|
29
|
+
title: { type: "string" },
|
|
30
|
+
width: { type: ["number", "string"] },
|
|
31
|
+
zoom: { type: "boolean" },
|
|
32
|
+
},
|
|
33
|
+
Astro.url.pathname,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const { src, alt, title, width, zoom = true } = imageProps as Props;
|
|
13
37
|
const zoomEnabled = zoom !== false;
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
38
|
+
const resolvedSrc = resolveStaticAssetUrl(src);
|
|
39
|
+
const rawWidth = width;
|
|
40
|
+
const imageAttrs: Record<string, unknown> = { src: resolvedSrc };
|
|
41
|
+
if (typeof alt === "string") {
|
|
42
|
+
imageAttrs.alt = alt;
|
|
43
|
+
}
|
|
44
|
+
if (width !== undefined) {
|
|
45
|
+
imageAttrs.width = width;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const zoomAttrs: Record<string, unknown> = { src: resolvedSrc };
|
|
49
|
+
if (typeof alt === "string") {
|
|
50
|
+
zoomAttrs.alt = alt;
|
|
17
51
|
}
|
|
18
|
-
const rawStyle = attrsRecord.style;
|
|
19
|
-
const rawWidth = attrsRecord.width;
|
|
20
|
-
const zoomAttrs: Record<string, unknown> = { ...attrsRecord };
|
|
21
|
-
delete zoomAttrs.style;
|
|
22
|
-
delete zoomAttrs.width;
|
|
23
|
-
delete zoomAttrs.height;
|
|
24
|
-
delete zoomAttrs.class;
|
|
25
|
-
delete zoomAttrs.className;
|
|
26
52
|
|
|
27
53
|
function isConstrainedWidthValue(value: unknown): boolean {
|
|
28
54
|
if (typeof value === "number") {
|
|
@@ -55,48 +81,12 @@ function isConstrainedWidthValue(value: unknown): boolean {
|
|
|
55
81
|
return true;
|
|
56
82
|
}
|
|
57
83
|
|
|
58
|
-
|
|
59
|
-
if (typeof styleValue === "string") {
|
|
60
|
-
const widthMatch = styleValue.match(/(?:^|;)\s*width\s*:\s*([^;]+)/i);
|
|
61
|
-
const maxWidthMatch = styleValue.match(
|
|
62
|
-
/(?:^|;)\s*max-width\s*:\s*([^;]+)/i,
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
isConstrainedWidthValue(widthMatch?.[1]) ||
|
|
67
|
-
isConstrainedWidthValue(maxWidthMatch?.[1])
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (!styleValue || typeof styleValue !== "object") return false;
|
|
72
|
-
const styleObject = styleValue as Record<string, unknown>;
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
isConstrainedWidthValue(styleObject.width) ||
|
|
76
|
-
isConstrainedWidthValue(styleObject.maxWidth) ||
|
|
77
|
-
isConstrainedWidthValue(styleObject["max-width"])
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const hasCustomImageWidth =
|
|
82
|
-
isConstrainedWidthValue(rawWidth) || hasStyleWidthConstraint(rawStyle);
|
|
84
|
+
const hasCustomImageWidth = isConstrainedWidthValue(rawWidth);
|
|
83
85
|
|
|
84
86
|
const captionHtml =
|
|
85
87
|
typeof title === "string" && title.trim().length > 0
|
|
86
88
|
? (await renderMarkdown(title)).replace(/^<p>([\s\S]*)<\/p>\n?$/, "$1")
|
|
87
89
|
: "";
|
|
88
|
-
|
|
89
|
-
validateProps(
|
|
90
|
-
"Image",
|
|
91
|
-
Astro.props as Record<string, any>,
|
|
92
|
-
{
|
|
93
|
-
src: { required: true, type: "string" },
|
|
94
|
-
alt: { type: "string" },
|
|
95
|
-
title: { type: "string" },
|
|
96
|
-
zoom: { type: "boolean" },
|
|
97
|
-
},
|
|
98
|
-
Astro.url.pathname,
|
|
99
|
-
);
|
|
100
90
|
---
|
|
101
91
|
|
|
102
92
|
<figure
|
|
@@ -207,7 +197,7 @@ validateProps(
|
|
|
207
197
|
class="overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-(--rd-code-surface)"
|
|
208
198
|
>
|
|
209
199
|
<img
|
|
210
|
-
{...
|
|
200
|
+
{...imageAttrs}
|
|
211
201
|
x-ref="img"
|
|
212
202
|
title={title}
|
|
213
203
|
class:list={[
|
|
@@ -10,9 +10,12 @@ import Sidebar from "../components/Sidebar.astro";
|
|
|
10
10
|
import { getConfig } from "../lib/validation";
|
|
11
11
|
import Header from "../components/Header.astro";
|
|
12
12
|
import Footer from "../components/Footer.astro";
|
|
13
|
-
import
|
|
13
|
+
import AssistantDocsWidget from "../components/chat/AssistantDocsWidget.astro";
|
|
14
14
|
import { ClientRouter } from "astro:transitions";
|
|
15
|
+
import { prependBasePath, stripBasePath } from "../lib/base-path";
|
|
16
|
+
import { getFaviconConfig } from "../lib/favicon";
|
|
15
17
|
import { resolveStaticAssetUrl } from "../lib/static-asset-url";
|
|
18
|
+
import { getDocsThemeCss } from "../lib/theme-css";
|
|
16
19
|
|
|
17
20
|
interface Props {
|
|
18
21
|
pageTitle?: string;
|
|
@@ -36,6 +39,8 @@ function routePathToOgImagePath(routePath: string): string {
|
|
|
36
39
|
|
|
37
40
|
const { pageTitle, pageDescription } = Astro.props as Props;
|
|
38
41
|
const config = await getConfig();
|
|
42
|
+
const favicon = getFaviconConfig();
|
|
43
|
+
const neutralPaletteCss = getDocsThemeCss(config.theme);
|
|
39
44
|
const resolvedPageTitle = pageTitle?.trim();
|
|
40
45
|
const resolvedPageDescription =
|
|
41
46
|
typeof pageDescription === "string" && pageDescription.trim().length > 0
|
|
@@ -48,12 +53,13 @@ const resolvedMetaDescription = resolvedPageDescription ?? fallbackDescription;
|
|
|
48
53
|
const documentTitle = resolvedPageTitle
|
|
49
54
|
? `${resolvedPageTitle} | ${config.title}`
|
|
50
55
|
: `${config.title} Docs`;
|
|
56
|
+
const routePathname = stripBasePath(Astro.url.pathname);
|
|
51
57
|
const canonicalUrl = new URL(
|
|
52
|
-
|
|
58
|
+
prependBasePath(routePathname),
|
|
53
59
|
Astro.site ?? Astro.url,
|
|
54
60
|
).toString();
|
|
55
61
|
const ogImageUrl = new URL(
|
|
56
|
-
resolveStaticAssetUrl(routePathToOgImagePath(
|
|
62
|
+
resolveStaticAssetUrl(routePathToOgImagePath(routePathname)),
|
|
57
63
|
Astro.site ?? Astro.url,
|
|
58
64
|
);
|
|
59
65
|
const ogImageHref = ogImageUrl.toString();
|
|
@@ -64,28 +70,13 @@ const parsedOrgTier = Number.parseInt(
|
|
|
64
70
|
const orgTier =
|
|
65
71
|
Number.isFinite(parsedOrgTier) && parsedOrgTier > 0 ? parsedOrgTier : 1;
|
|
66
72
|
const isDev = import.meta.env.DEV;
|
|
67
|
-
const askAiDevHost = (import.meta.env.ASK_AI_DEV_HOST ?? "").toString().trim();
|
|
68
|
-
const askAiDevProxySecret = (import.meta.env.ASK_AI_DEV_PROXY_SECRET ?? "")
|
|
69
|
-
.toString()
|
|
70
|
-
.trim();
|
|
71
|
-
const hasAskAiDevConfig =
|
|
72
|
-
askAiDevHost.length > 0 && askAiDevProxySecret.length > 0;
|
|
73
73
|
const askAiEnabled = isDev || orgTier >= 3;
|
|
74
|
-
const askAiChatAvailable = isDev ? hasAskAiDevConfig : orgTier >= 3;
|
|
75
|
-
let askAiApiPath = "/_platform/ask-ai";
|
|
76
|
-
|
|
77
|
-
if (isDev && hasAskAiDevConfig) {
|
|
78
|
-
try {
|
|
79
|
-
askAiApiPath = new URL("/_platform/ask-ai", askAiDevHost).toString();
|
|
80
|
-
} catch {
|
|
81
|
-
askAiApiPath = "/_platform/ask-ai";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
74
|
---
|
|
85
75
|
|
|
86
76
|
<!doctype html>
|
|
87
77
|
<html lang="en">
|
|
88
78
|
<head>
|
|
79
|
+
<style is:inline is:global set:html={neutralPaletteCss}></style>
|
|
89
80
|
<ClientRouter />
|
|
90
81
|
<script is:inline>
|
|
91
82
|
const applyTheme = () => {
|
|
@@ -115,6 +106,96 @@ if (isDev && hasAskAiDevConfig) {
|
|
|
115
106
|
// Run on initial load
|
|
116
107
|
applyTheme();
|
|
117
108
|
</script>
|
|
109
|
+
<script is:inline>
|
|
110
|
+
(() => {
|
|
111
|
+
const isEmbedded = window.self !== window.top;
|
|
112
|
+
const isEmbedMode =
|
|
113
|
+
new URLSearchParams(window.location.search).get("embed") === "1";
|
|
114
|
+
|
|
115
|
+
if (!isEmbedMode || !isEmbedded) {
|
|
116
|
+
if (isEmbedMode && !isEmbedded) {
|
|
117
|
+
console.info("[iframe-ready] skipping: not embedded iframe");
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.info("[iframe-ready] init", {
|
|
123
|
+
href: window.location.href,
|
|
124
|
+
embedded: isEmbedded,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const readySentSessionKey = "radiant_docs_iframe_ready_sent";
|
|
128
|
+
const hasSentReady = () => {
|
|
129
|
+
try {
|
|
130
|
+
const sent = sessionStorage.getItem(readySentSessionKey) === "1";
|
|
131
|
+
console.info("[iframe-ready] session flag read", {
|
|
132
|
+
key: readySentSessionKey,
|
|
133
|
+
sent,
|
|
134
|
+
});
|
|
135
|
+
return sent;
|
|
136
|
+
} catch {
|
|
137
|
+
console.warn(
|
|
138
|
+
"[iframe-ready] failed to read sessionStorage flag",
|
|
139
|
+
readySentSessionKey,
|
|
140
|
+
);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const markReadySent = () => {
|
|
145
|
+
try {
|
|
146
|
+
sessionStorage.setItem(readySentSessionKey, "1");
|
|
147
|
+
console.info("[iframe-ready] session flag set", {
|
|
148
|
+
key: readySentSessionKey,
|
|
149
|
+
value: "1",
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
// Ignore storage failures (privacy mode, disabled storage, etc.)
|
|
153
|
+
console.warn(
|
|
154
|
+
"[iframe-ready] failed to set sessionStorage flag",
|
|
155
|
+
readySentSessionKey,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const notifyParentReady = () => {
|
|
161
|
+
const readyUrl = `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
162
|
+
console.info("[iframe-ready] posting IFRAME_READY", { readyUrl });
|
|
163
|
+
window.parent.postMessage(
|
|
164
|
+
{
|
|
165
|
+
type: "IFRAME_READY",
|
|
166
|
+
url: readyUrl,
|
|
167
|
+
},
|
|
168
|
+
"*",
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const handleReadyRequest = (event) => {
|
|
173
|
+
const payload = event.data;
|
|
174
|
+
if (!payload || typeof payload !== "object") return;
|
|
175
|
+
|
|
176
|
+
if (payload.type === "IFRAME_READY_REQUEST") {
|
|
177
|
+
console.info("[iframe-ready] received IFRAME_READY_REQUEST");
|
|
178
|
+
notifyParentReady();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
window.addEventListener("message", handleReadyRequest);
|
|
183
|
+
|
|
184
|
+
if (!hasSentReady()) {
|
|
185
|
+
const notifyParentReadyOnce = () => {
|
|
186
|
+
if (hasSentReady()) return;
|
|
187
|
+
markReadySent();
|
|
188
|
+
notifyParentReady();
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
if (document.readyState === "complete") notifyParentReadyOnce();
|
|
192
|
+
else
|
|
193
|
+
window.addEventListener("load", notifyParentReadyOnce, {
|
|
194
|
+
once: true,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
})();
|
|
198
|
+
</script>
|
|
118
199
|
<link
|
|
119
200
|
rel="preload"
|
|
120
201
|
href={googleSansLatinWoff2}
|
|
@@ -147,8 +228,8 @@ if (isDev && hasAskAiDevConfig) {
|
|
|
147
228
|
<meta name="viewport" content="width=device-width" />
|
|
148
229
|
<link
|
|
149
230
|
rel="icon"
|
|
150
|
-
type=
|
|
151
|
-
href={resolveStaticAssetUrl(
|
|
231
|
+
type={favicon.type}
|
|
232
|
+
href={resolveStaticAssetUrl(favicon.href)}
|
|
152
233
|
/>
|
|
153
234
|
<meta name="generator" content={Astro.generator} />
|
|
154
235
|
<link rel="canonical" href={canonicalUrl} />
|
|
@@ -224,20 +305,8 @@ if (isDev && hasAskAiDevConfig) {
|
|
|
224
305
|
>
|
|
225
306
|
<slot />
|
|
226
307
|
</main>
|
|
227
|
-
<Footer askAiEnabled />
|
|
308
|
+
<Footer askAiEnabled={askAiEnabled} />
|
|
228
309
|
</div>
|
|
229
|
-
{
|
|
230
|
-
askAiEnabled ? (
|
|
231
|
-
<div transition:persist>
|
|
232
|
-
<AskAiWidget
|
|
233
|
-
client:only="preact"
|
|
234
|
-
apiPath={askAiApiPath}
|
|
235
|
-
isChatAvailable={askAiChatAvailable}
|
|
236
|
-
devProxyToken={isDev ? askAiDevProxySecret : undefined}
|
|
237
|
-
unavailableMessage="This feature is available on production sites for Pro tier organizations."
|
|
238
|
-
/>
|
|
239
|
-
</div>
|
|
240
|
-
) : null
|
|
241
|
-
}
|
|
310
|
+
{askAiEnabled ? <AssistantDocsWidget /> : null}
|
|
242
311
|
</body>
|
|
243
312
|
</html>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type AssistantChromeConfig = {
|
|
2
|
+
zIndex: string;
|
|
3
|
+
side: "left" | "right";
|
|
4
|
+
offsetX: string;
|
|
5
|
+
offsetY: string;
|
|
6
|
+
mobileOffsetX: string;
|
|
7
|
+
mobileOffsetY: string;
|
|
8
|
+
launcherSize: string;
|
|
9
|
+
launcherIconSize: string;
|
|
10
|
+
launcherShadow: string;
|
|
11
|
+
panelWidth: string;
|
|
12
|
+
panelHeight: string;
|
|
13
|
+
panelGap: string;
|
|
14
|
+
panelRadius: string;
|
|
15
|
+
panelBorder: string;
|
|
16
|
+
panelBackground: string;
|
|
17
|
+
panelDarkBorder: string;
|
|
18
|
+
panelDarkBackground: string;
|
|
19
|
+
panelBackdropBlur: string;
|
|
20
|
+
panelShadow: string;
|
|
21
|
+
panelDarkShadow: string;
|
|
22
|
+
panelOpenScale: string;
|
|
23
|
+
panelOpenDuration: string;
|
|
24
|
+
panelOpenTiming: string;
|
|
25
|
+
panelCloseDuration: string;
|
|
26
|
+
panelCloseTiming: string;
|
|
27
|
+
mobileBreakpoint: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const ASSISTANT_PANEL_DARK_NEUTRAL_800_MIX = "20%";
|
|
31
|
+
const ASSISTANT_PANEL_DARK_NEUTRAL_900_MIX = "80%";
|
|
32
|
+
const ASSISTANT_PANEL_DARK_SURFACE_OPACITY = "92%";
|
|
33
|
+
|
|
34
|
+
export function getAssistantPanelDarkBackgroundCss({
|
|
35
|
+
neutral800,
|
|
36
|
+
neutral900,
|
|
37
|
+
}: {
|
|
38
|
+
neutral800: string;
|
|
39
|
+
neutral900: string;
|
|
40
|
+
}): string {
|
|
41
|
+
return `color-mix(in srgb, color-mix(in srgb, ${neutral800} ${ASSISTANT_PANEL_DARK_NEUTRAL_800_MIX}, ${neutral900} ${ASSISTANT_PANEL_DARK_NEUTRAL_900_MIX}) ${ASSISTANT_PANEL_DARK_SURFACE_OPACITY}, transparent)`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const DEFAULT_ASSISTANT_CHROME_CONFIG: AssistantChromeConfig = {
|
|
45
|
+
zIndex: "2147483000",
|
|
46
|
+
side: "right",
|
|
47
|
+
offsetX: "20px",
|
|
48
|
+
offsetY: "20px",
|
|
49
|
+
mobileOffsetX: "16px",
|
|
50
|
+
mobileOffsetY: "16px",
|
|
51
|
+
launcherSize: "48px",
|
|
52
|
+
launcherIconSize: "20px",
|
|
53
|
+
launcherShadow: "0 16px 32px -8px rgb(0 0 0 / 24%)",
|
|
54
|
+
panelWidth: "420px",
|
|
55
|
+
panelHeight: "640px",
|
|
56
|
+
panelGap: "12px",
|
|
57
|
+
panelRadius: "16px",
|
|
58
|
+
panelBorder: "1px solid rgb(9 14 21 / 8%)",
|
|
59
|
+
panelBackground: "rgb(255 255 255 / 90%)",
|
|
60
|
+
panelDarkBorder: "1px solid rgb(255 255 255 / 8%)",
|
|
61
|
+
panelDarkBackground: getAssistantPanelDarkBackgroundCss({
|
|
62
|
+
neutral800: "var(--color-neutral-800, oklch(26.9% 0 0))",
|
|
63
|
+
neutral900: "var(--color-neutral-900, oklch(20.5% 0 0))",
|
|
64
|
+
}),
|
|
65
|
+
panelBackdropBlur: "48px",
|
|
66
|
+
panelShadow: "rgba(9, 14, 21, 0.16) 0px 5px 40px 0px",
|
|
67
|
+
panelDarkShadow: "rgba(0, 0, 0, 0.36) 0px 8px 44px 0px",
|
|
68
|
+
panelOpenScale: ".9",
|
|
69
|
+
panelOpenDuration: ".5s",
|
|
70
|
+
panelOpenTiming: "cubic-bezier(.34, 1.56, .64, 1)",
|
|
71
|
+
panelCloseDuration: ".2s",
|
|
72
|
+
panelCloseTiming: "cubic-bezier(.25, 1, .5, 1)",
|
|
73
|
+
mobileBreakpoint: "640px",
|
|
74
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DocsConfig } from "./validation";
|
|
2
|
+
import { getDocsDarkBaseColorShade } from "./theme-css";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_ASSISTANT_CHROME_CONFIG,
|
|
5
|
+
getAssistantPanelDarkBackgroundCss,
|
|
6
|
+
type AssistantChromeConfig,
|
|
7
|
+
} from "./assistant-chrome-defaults";
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
DEFAULT_ASSISTANT_CHROME_CONFIG,
|
|
11
|
+
getAssistantPanelDarkBackgroundCss,
|
|
12
|
+
type AssistantChromeConfig,
|
|
13
|
+
} from "./assistant-chrome-defaults";
|
|
14
|
+
|
|
15
|
+
function getAssistantPanelDarkBackground(theme: DocsConfig["theme"]): string {
|
|
16
|
+
const neutral800 = getDocsDarkBaseColorShade(theme, "800");
|
|
17
|
+
const neutral900 = getDocsDarkBaseColorShade(theme, "900");
|
|
18
|
+
|
|
19
|
+
return getAssistantPanelDarkBackgroundCss({ neutral800, neutral900 });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getAssistantChromeConfig(
|
|
23
|
+
config: DocsConfig,
|
|
24
|
+
): AssistantChromeConfig {
|
|
25
|
+
const chromeConfig = {
|
|
26
|
+
...DEFAULT_ASSISTANT_CHROME_CONFIG,
|
|
27
|
+
panelDarkBackground: getAssistantPanelDarkBackground(config.theme),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (config.assistant?.button?.size === "small") {
|
|
31
|
+
return {
|
|
32
|
+
...chromeConfig,
|
|
33
|
+
launcherSize: "36px",
|
|
34
|
+
launcherIconSize: "16px",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return chromeConfig;
|
|
39
|
+
}
|