radiant-docs 0.1.61 → 0.1.62
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/package-lock.json +10 -4
- package/template/package.json +11 -2
- package/template/scripts/generate-proxy-allowed-origins.mjs +14 -6
- package/template/scripts/publish-shiki-platform-assets.mjs +1151 -0
- package/template/src/components/Header.astro +6 -1
- package/template/src/components/NavigationTabList.astro +65 -0
- package/template/src/components/NavigationTabs.astro +109 -0
- package/template/src/components/OpenApiPage.astro +17 -1
- package/template/src/components/Sidebar.astro +2 -2
- package/template/src/components/SidebarDropdown.astro +105 -44
- package/template/src/components/SidebarMenu.astro +3 -0
- package/template/src/components/SidebarSegmented.astro +87 -52
- package/template/src/components/SidebarTabs.astro +86 -0
- package/template/src/components/chat/AssistantDocsWidget.tsx +127 -2
- package/template/src/components/chat/AssistantEmbedPanel.tsx +269 -283
- package/template/src/components/user/Accordion.astro +1 -1
- package/template/src/components/user/Callout.astro +2 -2
- package/template/src/components/user/CodeBlock.astro +58 -7
- package/template/src/components/user/CodeGroup.astro +52 -1
- package/template/src/components/user/Column.astro +1 -1
- package/template/src/components/user/Step.astro +1 -1
- package/template/src/components/user/Tabs.astro +1 -1
- package/template/src/generated/shiki-platform-assets.json +24 -0
- package/template/src/layouts/Layout.astro +111 -8
- package/template/src/lib/assistant-panel-config.ts +59 -0
- package/template/src/lib/assistant-shiki-client.ts +506 -0
- package/template/src/lib/mdx/remark-resolve-internal-links.ts +334 -17
- package/template/src/lib/routes.ts +66 -24
- package/template/src/styles/global.css +12 -0
|
@@ -1,34 +1,17 @@
|
|
|
1
1
|
import type { JSX } from "preact";
|
|
2
2
|
import { useEffect, useRef, useState } from "preact/hooks";
|
|
3
3
|
import { Icon } from "@iconify/react";
|
|
4
|
-
import Prism from "prismjs";
|
|
5
|
-
import "prismjs/components/prism-markup.js";
|
|
6
|
-
import "prismjs/components/prism-clike.js";
|
|
7
|
-
import "prismjs/components/prism-javascript.js";
|
|
8
|
-
import "prismjs/components/prism-typescript.js";
|
|
9
|
-
import "prismjs/components/prism-jsx.js";
|
|
10
|
-
import "prismjs/components/prism-tsx.js";
|
|
11
|
-
import "prismjs/components/prism-json.js";
|
|
12
|
-
import "prismjs/components/prism-markdown.js";
|
|
13
|
-
import "prismjs/components/prism-bash.js";
|
|
14
|
-
import "prismjs/components/prism-python.js";
|
|
15
|
-
import "prismjs/components/prism-yaml.js";
|
|
16
|
-
import "prismjs/components/prism-sql.js";
|
|
17
|
-
import "prismjs/components/prism-rust.js";
|
|
18
|
-
import "prismjs/components/prism-go.js";
|
|
19
|
-
import "prismjs/components/prism-java.js";
|
|
20
|
-
import "prismjs/components/prism-markup-templating.js";
|
|
21
|
-
import "prismjs/components/prism-php.js";
|
|
22
|
-
import "prismjs/components/prism-ruby.js";
|
|
23
|
-
import "prismjs/components/prism-css.js";
|
|
24
|
-
import "prismjs/components/prism-diff.js";
|
|
25
|
-
import "prism-themes/themes/prism-one-light.css";
|
|
26
4
|
import { type Plugin, unified } from "unified";
|
|
27
5
|
import remarkParse from "remark-parse";
|
|
28
6
|
import remarkGfm from "remark-gfm";
|
|
29
7
|
import remarkRehype from "remark-rehype";
|
|
30
8
|
import rehypeStringify from "rehype-stringify";
|
|
31
9
|
import { getDocsBasePath, withBasePath } from "../../lib/base-path";
|
|
10
|
+
import {
|
|
11
|
+
highlightAssistantCodeToHtml,
|
|
12
|
+
normalizeAssistantCodeLanguage,
|
|
13
|
+
type AssistantShikiRuntimeConfig,
|
|
14
|
+
} from "../../lib/assistant-shiki-client";
|
|
32
15
|
|
|
33
16
|
type AssistantLinkTarget = "current" | "blank";
|
|
34
17
|
export type AssistantPanelSize = "default" | "expanded";
|
|
@@ -59,10 +42,13 @@ type AssistantEmbedPanelProps = {
|
|
|
59
42
|
allowApiPathQueryOverride?: boolean;
|
|
60
43
|
openSignal?: number;
|
|
61
44
|
panelSize?: AssistantPanelSize;
|
|
45
|
+
mobileBreakpoint?: string;
|
|
46
|
+
panelFullscreenHeightBreakpoint?: string;
|
|
62
47
|
onRequestOpen?: () => void;
|
|
63
48
|
onRequestClose?: () => void;
|
|
64
49
|
onRequestPanelSizeToggle?: (size: AssistantPanelSize) => void;
|
|
65
50
|
onCurrentLinkNavigate?: (href: string, sourceElement?: Element) => void;
|
|
51
|
+
shiki?: AssistantShikiRuntimeConfig;
|
|
66
52
|
};
|
|
67
53
|
|
|
68
54
|
type AssistantColorByMode = {
|
|
@@ -120,41 +106,50 @@ const HANDOFF_ACK_TYPE = "assistant-handoff:ack";
|
|
|
120
106
|
const IN_FLIGHT_STALE_MS = 10000;
|
|
121
107
|
const IN_FLIGHT_HEARTBEAT_MS = 3000;
|
|
122
108
|
const MARKDOWN_HTML_CACHE_LIMIT = 300;
|
|
109
|
+
const DEFAULT_ASSISTANT_MOBILE_BREAKPOINT = "640px";
|
|
110
|
+
const DEFAULT_ASSISTANT_PANEL_FULLSCREEN_HEIGHT_BREAKPOINT = "560px";
|
|
123
111
|
const markdownHtmlCache = new Map<string, string>();
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
mdx: "markdown",
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const PRISM_LANGUAGE_LABEL: Record<string, string> = {
|
|
112
|
+
const markdownHtmlPromiseCache = new Map<string, Promise<string>>();
|
|
113
|
+
const ASSISTANT_LANGUAGE_LABEL: Record<string, string> = {
|
|
114
|
+
bash: "Bash",
|
|
115
|
+
css: "CSS",
|
|
116
|
+
diff: "Diff",
|
|
117
|
+
go: "Go",
|
|
118
|
+
html: "HTML",
|
|
119
|
+
java: "Java",
|
|
136
120
|
javascript: "JavaScript",
|
|
137
|
-
typescript: "TypeScript",
|
|
138
121
|
jsx: "JSX",
|
|
139
|
-
tsx: "TSX",
|
|
140
122
|
json: "JSON",
|
|
141
123
|
markdown: "Markdown",
|
|
142
|
-
|
|
143
|
-
|
|
124
|
+
mdx: "MDX",
|
|
125
|
+
php: "PHP",
|
|
126
|
+
plaintext: "Text",
|
|
144
127
|
python: "Python",
|
|
145
|
-
|
|
146
|
-
sql: "SQL",
|
|
128
|
+
ruby: "Ruby",
|
|
147
129
|
rust: "Rust",
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
mdx: "MDX",
|
|
130
|
+
shell: "Shell",
|
|
131
|
+
sql: "SQL",
|
|
132
|
+
tsx: "TSX",
|
|
133
|
+
typescript: "TypeScript",
|
|
134
|
+
yaml: "YAML",
|
|
154
135
|
};
|
|
155
136
|
|
|
156
137
|
const EXTERNAL_PROTOCOL_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
|
|
157
138
|
|
|
139
|
+
function buildMarkdownCacheKey(
|
|
140
|
+
normalizedMarkdown: string,
|
|
141
|
+
linkTarget: AssistantLinkTarget,
|
|
142
|
+
shiki: AssistantShikiRuntimeConfig | undefined,
|
|
143
|
+
): string {
|
|
144
|
+
return [
|
|
145
|
+
linkTarget,
|
|
146
|
+
shiki?.assetBaseUrl ?? "",
|
|
147
|
+
shiki?.syntaxThemes.light ?? "",
|
|
148
|
+
shiki?.syntaxThemes.dark ?? "",
|
|
149
|
+
normalizedMarkdown,
|
|
150
|
+
].join("\0");
|
|
151
|
+
}
|
|
152
|
+
|
|
158
153
|
function normalizeRelTokens(value: unknown): string[] {
|
|
159
154
|
if (Array.isArray(value)) {
|
|
160
155
|
return value
|
|
@@ -395,6 +390,13 @@ function normalizePanelSize(value: unknown): AssistantPanelSize {
|
|
|
395
390
|
return value === "expanded" ? "expanded" : "default";
|
|
396
391
|
}
|
|
397
392
|
|
|
393
|
+
function getPanelFullscreenMediaQuery(
|
|
394
|
+
mobileBreakpoint: string,
|
|
395
|
+
panelFullscreenHeightBreakpoint: string,
|
|
396
|
+
): string {
|
|
397
|
+
return `(max-width: ${mobileBreakpoint}), (max-height: ${panelFullscreenHeightBreakpoint})`;
|
|
398
|
+
}
|
|
399
|
+
|
|
398
400
|
function normalizePersistedPanelState(rawState: unknown): PersistedPanelState {
|
|
399
401
|
if (!rawState || typeof rawState !== "object") {
|
|
400
402
|
return createEmptyPersistedPanelState();
|
|
@@ -574,32 +576,20 @@ function escapeHtml(value: string): string {
|
|
|
574
576
|
.replaceAll("'", "'");
|
|
575
577
|
}
|
|
576
578
|
|
|
577
|
-
function
|
|
578
|
-
const normalized = rawLanguage.trim().toLowerCase();
|
|
579
|
-
return PRISM_LANGUAGE_ALIAS[normalized] ?? normalized;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function resolvePrismLanguageLabel(language: string): string {
|
|
579
|
+
function resolveAssistantLanguageLabel(language: string): string {
|
|
583
580
|
const normalized = language.trim().toLowerCase();
|
|
584
581
|
if (!normalized) {
|
|
585
582
|
return "";
|
|
586
583
|
}
|
|
587
584
|
|
|
588
585
|
return (
|
|
589
|
-
|
|
586
|
+
ASSISTANT_LANGUAGE_LABEL[normalized] ??
|
|
590
587
|
normalized
|
|
591
588
|
.replace(/[-_]+/g, " ")
|
|
592
589
|
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
593
590
|
);
|
|
594
591
|
}
|
|
595
592
|
|
|
596
|
-
function resolvePrismGrammar(language: string) {
|
|
597
|
-
return (
|
|
598
|
-
Prism.languages[language] ??
|
|
599
|
-
Prism.languages[PRISM_LANGUAGE_ALIAS[language] ?? ""]
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
593
|
function ensureCodeBlockCopyButton(preElement: HTMLPreElement): void {
|
|
604
594
|
const existingButton = Array.from(preElement.children).find(
|
|
605
595
|
(child) =>
|
|
@@ -697,12 +687,23 @@ function unwrapEscapedFenceCodeBlock(rawCode: string): {
|
|
|
697
687
|
}
|
|
698
688
|
|
|
699
689
|
return {
|
|
700
|
-
language:
|
|
690
|
+
language: normalizeAssistantCodeLanguage(languageToken),
|
|
701
691
|
code: lines.slice(1, -1).join("\n"),
|
|
702
692
|
};
|
|
703
693
|
}
|
|
704
694
|
|
|
705
|
-
function
|
|
695
|
+
function stripSerializedCodeBlockTrailingNewline(rawCode: string): string {
|
|
696
|
+
if (rawCode.endsWith("\n")) {
|
|
697
|
+
return rawCode.slice(0, -1);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return rawCode;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
async function highlightCodeBlocksInHtml(
|
|
704
|
+
html: string,
|
|
705
|
+
shiki: AssistantShikiRuntimeConfig | undefined,
|
|
706
|
+
): Promise<string> {
|
|
706
707
|
if (typeof document === "undefined" || !html.trim()) {
|
|
707
708
|
return html;
|
|
708
709
|
}
|
|
@@ -711,8 +712,8 @@ function highlightCodeBlocksInHtml(html: string): string {
|
|
|
711
712
|
const container = document.createElement("div");
|
|
712
713
|
container.innerHTML = html;
|
|
713
714
|
|
|
714
|
-
const codeNodes = container.querySelectorAll("pre > code");
|
|
715
|
-
|
|
715
|
+
const codeNodes = Array.from(container.querySelectorAll("pre > code"));
|
|
716
|
+
for (const codeNode of codeNodes) {
|
|
716
717
|
const getLanguageClassName = (element: Element | null) =>
|
|
717
718
|
Array.from(element?.classList ?? []).find(
|
|
718
719
|
(className) =>
|
|
@@ -741,13 +742,14 @@ function highlightCodeBlocksInHtml(html: string): string {
|
|
|
741
742
|
}
|
|
742
743
|
});
|
|
743
744
|
preParent.classList.add(`language-${language}`);
|
|
744
|
-
const languageLabel =
|
|
745
|
+
const languageLabel = resolveAssistantLanguageLabel(language);
|
|
745
746
|
if (languageLabel) {
|
|
746
747
|
preParent.setAttribute("data-language", languageLabel);
|
|
747
748
|
} else {
|
|
748
749
|
preParent.removeAttribute("data-language");
|
|
749
750
|
}
|
|
750
751
|
|
|
752
|
+
codeNode.setAttribute("data-rd-code-theme", "");
|
|
751
753
|
ensureCodeBlockCopyButton(preParent);
|
|
752
754
|
}
|
|
753
755
|
};
|
|
@@ -759,7 +761,9 @@ function highlightCodeBlocksInHtml(html: string): string {
|
|
|
759
761
|
preLanguageClassName?.replace(/^(language-|lang-)/, "") ??
|
|
760
762
|
"";
|
|
761
763
|
|
|
762
|
-
let rawCode =
|
|
764
|
+
let rawCode = stripSerializedCodeBlockTrailingNewline(
|
|
765
|
+
codeNode.textContent ?? "",
|
|
766
|
+
);
|
|
763
767
|
if (!rawLanguage) {
|
|
764
768
|
const unwrappedFence = unwrapEscapedFenceCodeBlock(rawCode);
|
|
765
769
|
if (unwrappedFence) {
|
|
@@ -769,28 +773,37 @@ function highlightCodeBlocksInHtml(html: string): string {
|
|
|
769
773
|
}
|
|
770
774
|
|
|
771
775
|
if (!rawLanguage) {
|
|
772
|
-
|
|
776
|
+
const preParent = codeNode.parentElement;
|
|
777
|
+
if (preParent instanceof HTMLPreElement) {
|
|
778
|
+
ensureCodeBlockCopyButton(preParent);
|
|
779
|
+
}
|
|
780
|
+
continue;
|
|
773
781
|
}
|
|
774
782
|
|
|
775
|
-
const resolvedLanguage =
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
783
|
+
const resolvedLanguage = normalizeAssistantCodeLanguage(rawLanguage);
|
|
784
|
+
setLanguageClass(resolvedLanguage);
|
|
785
|
+
|
|
786
|
+
if (!shiki) {
|
|
787
|
+
codeNode.textContent = rawCode;
|
|
788
|
+
continue;
|
|
781
789
|
}
|
|
782
790
|
|
|
783
|
-
setLanguageClass(resolvedLanguage);
|
|
784
791
|
try {
|
|
785
|
-
const highlighted =
|
|
786
|
-
|
|
792
|
+
const highlighted = await highlightAssistantCodeToHtml({
|
|
793
|
+
code: rawCode,
|
|
794
|
+
config: shiki,
|
|
795
|
+
language: resolvedLanguage,
|
|
796
|
+
});
|
|
797
|
+
setLanguageClass(highlighted.language);
|
|
798
|
+
codeNode.innerHTML = highlighted.html;
|
|
787
799
|
} catch (error) {
|
|
788
800
|
console.error("Assistant embed code highlighting failed", {
|
|
789
801
|
language: resolvedLanguage,
|
|
790
802
|
error,
|
|
791
803
|
});
|
|
804
|
+
codeNode.textContent = rawCode;
|
|
792
805
|
}
|
|
793
|
-
}
|
|
806
|
+
}
|
|
794
807
|
|
|
795
808
|
return container.innerHTML;
|
|
796
809
|
} catch (error) {
|
|
@@ -805,45 +818,139 @@ function highlightCodeBlocksInHtml(html: string): string {
|
|
|
805
818
|
function renderMarkdownToHtml(
|
|
806
819
|
markdown: string,
|
|
807
820
|
linkTarget: AssistantLinkTarget,
|
|
808
|
-
|
|
821
|
+
shiki: AssistantShikiRuntimeConfig | undefined,
|
|
822
|
+
): Promise<string> {
|
|
809
823
|
const normalizedMarkdown = markdown.replaceAll("\r\n", "\n");
|
|
810
|
-
const cacheKey =
|
|
824
|
+
const cacheKey = buildMarkdownCacheKey(normalizedMarkdown, linkTarget, shiki);
|
|
811
825
|
const cached = markdownHtmlCache.get(cacheKey);
|
|
812
826
|
if (cached !== undefined) {
|
|
813
|
-
return cached;
|
|
827
|
+
return Promise.resolve(cached);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const cachedPromise = markdownHtmlPromiseCache.get(cacheKey);
|
|
831
|
+
if (cachedPromise) {
|
|
832
|
+
return cachedPromise;
|
|
814
833
|
}
|
|
815
834
|
|
|
816
|
-
|
|
835
|
+
const renderPromise = (async () => {
|
|
836
|
+
let html = "";
|
|
817
837
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
838
|
+
try {
|
|
839
|
+
const processor = unified()
|
|
840
|
+
.use(remarkParse)
|
|
841
|
+
.use(remarkGfm)
|
|
842
|
+
.use(remarkRehype, { allowDangerousHtml: false })
|
|
843
|
+
.use(rehypeRebaseInternalLinks);
|
|
844
|
+
|
|
845
|
+
if (linkTarget === "blank") {
|
|
846
|
+
processor.use(rehypeOpenLinksInNewTab);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
html = String(
|
|
850
|
+
processor.use(rehypeStringify).processSync(normalizedMarkdown),
|
|
851
|
+
);
|
|
852
|
+
} catch {
|
|
853
|
+
html = escapeHtml(normalizedMarkdown).replaceAll("\n", "<br/>");
|
|
827
854
|
}
|
|
828
855
|
|
|
829
|
-
html =
|
|
830
|
-
|
|
831
|
-
);
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
856
|
+
html = await highlightCodeBlocksInHtml(html, shiki);
|
|
857
|
+
|
|
858
|
+
markdownHtmlCache.set(cacheKey, html);
|
|
859
|
+
if (markdownHtmlCache.size > MARKDOWN_HTML_CACHE_LIMIT) {
|
|
860
|
+
const oldestKey = markdownHtmlCache.keys().next().value;
|
|
861
|
+
if (oldestKey) {
|
|
862
|
+
markdownHtmlCache.delete(oldestKey);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
markdownHtmlPromiseCache.delete(cacheKey);
|
|
867
|
+
return html;
|
|
868
|
+
})();
|
|
869
|
+
|
|
870
|
+
markdownHtmlPromiseCache.set(cacheKey, renderPromise);
|
|
871
|
+
return renderPromise;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function getCachedMarkdownHtml(
|
|
875
|
+
markdown: string,
|
|
876
|
+
linkTarget: AssistantLinkTarget,
|
|
877
|
+
shiki: AssistantShikiRuntimeConfig | undefined,
|
|
878
|
+
): string | undefined {
|
|
879
|
+
return markdownHtmlCache.get(
|
|
880
|
+
buildMarkdownCacheKey(markdown.replaceAll("\r\n", "\n"), linkTarget, shiki),
|
|
881
|
+
);
|
|
882
|
+
}
|
|
835
883
|
|
|
836
|
-
|
|
884
|
+
function AssistantMarkdownBlock({
|
|
885
|
+
className,
|
|
886
|
+
linkTarget,
|
|
887
|
+
markdown,
|
|
888
|
+
onClick,
|
|
889
|
+
onRendered,
|
|
890
|
+
shiki,
|
|
891
|
+
}: {
|
|
892
|
+
className: string;
|
|
893
|
+
linkTarget: AssistantLinkTarget;
|
|
894
|
+
markdown: string;
|
|
895
|
+
onClick: (event: JSX.TargetedMouseEvent<HTMLDivElement>) => void;
|
|
896
|
+
onRendered?: () => void;
|
|
897
|
+
shiki?: AssistantShikiRuntimeConfig;
|
|
898
|
+
}) {
|
|
899
|
+
const onRenderedRef = useRef(onRendered);
|
|
900
|
+
const [html, setHtml] = useState(
|
|
901
|
+
() => getCachedMarkdownHtml(markdown, linkTarget, shiki) ?? "",
|
|
902
|
+
);
|
|
837
903
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
904
|
+
useEffect(() => {
|
|
905
|
+
onRenderedRef.current = onRendered;
|
|
906
|
+
}, [onRendered]);
|
|
907
|
+
|
|
908
|
+
useEffect(() => {
|
|
909
|
+
let isCancelled = false;
|
|
910
|
+
const cached = getCachedMarkdownHtml(markdown, linkTarget, shiki);
|
|
911
|
+
if (cached !== undefined) {
|
|
912
|
+
setHtml(cached);
|
|
913
|
+
onRenderedRef.current?.();
|
|
914
|
+
return () => {
|
|
915
|
+
isCancelled = true;
|
|
916
|
+
};
|
|
843
917
|
}
|
|
844
|
-
}
|
|
845
918
|
|
|
846
|
-
|
|
919
|
+
void renderMarkdownToHtml(markdown, linkTarget, shiki)
|
|
920
|
+
.then((nextHtml) => {
|
|
921
|
+
if (isCancelled) return;
|
|
922
|
+
setHtml((previousHtml) =>
|
|
923
|
+
previousHtml === nextHtml ? previousHtml : nextHtml,
|
|
924
|
+
);
|
|
925
|
+
onRenderedRef.current?.();
|
|
926
|
+
})
|
|
927
|
+
.catch((error) => {
|
|
928
|
+
if (isCancelled) return;
|
|
929
|
+
console.error("Assistant embed markdown rendering failed", error);
|
|
930
|
+
setHtml(escapeHtml(markdown).replaceAll("\n", "<br/>"));
|
|
931
|
+
onRenderedRef.current?.();
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
return () => {
|
|
935
|
+
isCancelled = true;
|
|
936
|
+
};
|
|
937
|
+
}, [
|
|
938
|
+
linkTarget,
|
|
939
|
+
markdown,
|
|
940
|
+
shiki?.assetBaseUrl,
|
|
941
|
+
shiki?.syntaxThemes.dark,
|
|
942
|
+
shiki?.syntaxThemes.light,
|
|
943
|
+
]);
|
|
944
|
+
|
|
945
|
+
return (
|
|
946
|
+
<div
|
|
947
|
+
className={className}
|
|
948
|
+
onClick={onClick}
|
|
949
|
+
dangerouslySetInnerHTML={{
|
|
950
|
+
__html: html,
|
|
951
|
+
}}
|
|
952
|
+
/>
|
|
953
|
+
);
|
|
847
954
|
}
|
|
848
955
|
|
|
849
956
|
function extractErrorMessage(rawBody: string): string {
|
|
@@ -944,11 +1051,18 @@ export default function AssistantEmbedPanel({
|
|
|
944
1051
|
allowApiPathQueryOverride = true,
|
|
945
1052
|
openSignal = 0,
|
|
946
1053
|
panelSize,
|
|
1054
|
+
mobileBreakpoint = DEFAULT_ASSISTANT_MOBILE_BREAKPOINT,
|
|
1055
|
+
panelFullscreenHeightBreakpoint = DEFAULT_ASSISTANT_PANEL_FULLSCREEN_HEIGHT_BREAKPOINT,
|
|
947
1056
|
onRequestOpen,
|
|
948
1057
|
onRequestClose,
|
|
949
1058
|
onRequestPanelSizeToggle,
|
|
950
1059
|
onCurrentLinkNavigate,
|
|
1060
|
+
shiki,
|
|
951
1061
|
}: AssistantEmbedPanelProps) {
|
|
1062
|
+
const panelFullscreenMediaQuery = getPanelFullscreenMediaQuery(
|
|
1063
|
+
mobileBreakpoint,
|
|
1064
|
+
panelFullscreenHeightBreakpoint,
|
|
1065
|
+
);
|
|
952
1066
|
const [initialPanelState] = useState<PersistedPanelState>(() =>
|
|
953
1067
|
canSendChatRequest
|
|
954
1068
|
? readPersistedPanelState()
|
|
@@ -969,7 +1083,17 @@ export default function AssistantEmbedPanel({
|
|
|
969
1083
|
const [localPanelSize, setLocalPanelSize] = useState<AssistantPanelSize>(
|
|
970
1084
|
panelSize ?? initialPanelState.panelSize,
|
|
971
1085
|
);
|
|
972
|
-
const [isShellFullscreen, setIsShellFullscreen] = useState(
|
|
1086
|
+
const [isShellFullscreen, setIsShellFullscreen] = useState(() => {
|
|
1087
|
+
if (
|
|
1088
|
+
panelSurface !== "inline" ||
|
|
1089
|
+
typeof window === "undefined" ||
|
|
1090
|
+
typeof window.matchMedia !== "function"
|
|
1091
|
+
) {
|
|
1092
|
+
return false;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
return window.matchMedia(panelFullscreenMediaQuery).matches;
|
|
1096
|
+
});
|
|
973
1097
|
const activeRequestAbortRef = useRef<AbortController | null>(null);
|
|
974
1098
|
const scrollViewportRef = useRef<HTMLDivElement | null>(null);
|
|
975
1099
|
const savedScrollTopRef = useRef(initialPanelState.scrollTop);
|
|
@@ -1541,6 +1665,27 @@ export default function AssistantEmbedPanel({
|
|
|
1541
1665
|
notifyPanelSizeChange(resolvedPanelSize);
|
|
1542
1666
|
}, [resolvedPanelSize]);
|
|
1543
1667
|
|
|
1668
|
+
useEffect(() => {
|
|
1669
|
+
if (
|
|
1670
|
+
panelSurface !== "inline" ||
|
|
1671
|
+
typeof window === "undefined" ||
|
|
1672
|
+
typeof window.matchMedia !== "function"
|
|
1673
|
+
) {
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
const mediaQuery = window.matchMedia(panelFullscreenMediaQuery);
|
|
1678
|
+
const updateShellLayout = () => {
|
|
1679
|
+
setIsShellFullscreen(mediaQuery.matches);
|
|
1680
|
+
};
|
|
1681
|
+
|
|
1682
|
+
updateShellLayout();
|
|
1683
|
+
mediaQuery.addEventListener("change", updateShellLayout);
|
|
1684
|
+
return () => {
|
|
1685
|
+
mediaQuery.removeEventListener("change", updateShellLayout);
|
|
1686
|
+
};
|
|
1687
|
+
}, [panelFullscreenMediaQuery, panelSurface]);
|
|
1688
|
+
|
|
1544
1689
|
useEffect(() => {
|
|
1545
1690
|
if (typeof window === "undefined") {
|
|
1546
1691
|
return;
|
|
@@ -1949,6 +2094,12 @@ export default function AssistantEmbedPanel({
|
|
|
1949
2094
|
});
|
|
1950
2095
|
};
|
|
1951
2096
|
|
|
2097
|
+
const handleMarkdownRendered = () => {
|
|
2098
|
+
if (isBusyRef.current) {
|
|
2099
|
+
queueThreadScrollToBottom(messagesRef.current);
|
|
2100
|
+
}
|
|
2101
|
+
};
|
|
2102
|
+
|
|
1952
2103
|
const panelClassName = [
|
|
1953
2104
|
"relative flex min-h-0 flex-col overflow-hidden text-neutral-900 shadow-2xl dark:text-neutral-50",
|
|
1954
2105
|
panelSurface === "inline"
|
|
@@ -2041,7 +2192,7 @@ export default function AssistantEmbedPanel({
|
|
|
2041
2192
|
{messages.length === 0 ? (
|
|
2042
2193
|
<div
|
|
2043
2194
|
key={emptyStateAnimationKey}
|
|
2044
|
-
className="flex min-h-full flex-col items-center justify-center
|
|
2195
|
+
className="flex min-h-full flex-col items-center justify-center py-8 text-center"
|
|
2045
2196
|
>
|
|
2046
2197
|
<AssistantPanelIcon
|
|
2047
2198
|
color={launcherIconColor}
|
|
@@ -2069,7 +2220,7 @@ export default function AssistantEmbedPanel({
|
|
|
2069
2220
|
<button
|
|
2070
2221
|
key={question}
|
|
2071
2222
|
type="button"
|
|
2072
|
-
className="assistant-empty-state-item w-fit rounded-full border-neutral-900/8 bg-white/90 px-3.5 py-2 text-
|
|
2223
|
+
className="assistant-empty-state-item w-fit rounded-full border-neutral-900/8 bg-white/90 px-3.5 py-2 text-center text-[13px] leading-5 text-neutral-700 shadow-[0px_1px_3px_0px_rgba(0,0,0,0.04),0px_0px_0px_1px_rgba(0,0,0,0.06)_inset,0px_-1px_0px_0px_rgba(0,0,0,0.06)_inset] transition hover:bg-white dark:bg-white/4 dark:text-neutral-200 dark:hover:bg-white/8 cursor-pointer dark:shadow-[0_1px_3px_0_rgba(0,0,0,0.04),inset_0_1px_0_0_rgba(255,255,255,0.04),inset_0_0_0_1px_rgba(0,0,0,0.06),inset_0_-1px_0_0_rgba(0,0,0,0.06),inset_0_0_0_1px_rgba(196,196,196,0.07)]"
|
|
2073
2224
|
style={
|
|
2074
2225
|
{
|
|
2075
2226
|
"--assistant-empty-state-delay": `${400 + index * 100}ms`,
|
|
@@ -2101,17 +2252,18 @@ export default function AssistantEmbedPanel({
|
|
|
2101
2252
|
key={message.id}
|
|
2102
2253
|
className={isUser ? "text-right" : "text-left"}
|
|
2103
2254
|
>
|
|
2104
|
-
<
|
|
2255
|
+
<AssistantMarkdownBlock
|
|
2105
2256
|
className={[
|
|
2106
2257
|
"ask-ai-markdown prose-rules text-[15px]! max-w-full min-w-0 wrap-break-word prose-code:text-neutral-700 dark:prose-code:text-neutral-200 prose-pre:shadow-xs prose-pre:text-neutral-700! dark:prose-pre:text-neutral-100! prose-pre:border prose-pre:border-neutral-200 dark:prose-pre:border-neutral-800 prose-pre:rounded-xl!",
|
|
2107
2258
|
isUser
|
|
2108
2259
|
? "inline-block ml-2 px-3 py-1.5 rounded-2xl rounded-br-sm bg-neutral-900/5 text-neutral-700/85 dark:bg-neutral-800 dark:text-neutral-100 *:text-left"
|
|
2109
2260
|
: "block w-full bg-transparent text-neutral-900 dark:text-neutral-100",
|
|
2110
2261
|
].join(" ")}
|
|
2262
|
+
linkTarget={linkTarget}
|
|
2263
|
+
markdown={message.content}
|
|
2111
2264
|
onClick={handleRenderedMarkdownClick}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
}}
|
|
2265
|
+
onRendered={handleMarkdownRendered}
|
|
2266
|
+
shiki={shiki}
|
|
2115
2267
|
/>
|
|
2116
2268
|
</div>
|
|
2117
2269
|
);
|
|
@@ -2152,7 +2304,10 @@ export default function AssistantEmbedPanel({
|
|
|
2152
2304
|
}}
|
|
2153
2305
|
onKeyDown={handleChatInputKeyDown}
|
|
2154
2306
|
placeholder="Ask a question..."
|
|
2155
|
-
className=
|
|
2307
|
+
className={[
|
|
2308
|
+
"assistant-embed-input my-auto min-w-0 flex-1 bg-transparent pl-4 py-2.5 text-neutral-900 placeholder:text-neutral-400 focus:outline-none dark:text-white dark:placeholder:text-neutral-400 leading-5 resize-none [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
2309
|
+
isShellFullscreen ? "text-base" : "text-sm",
|
|
2310
|
+
].join(" ")}
|
|
2156
2311
|
disabled={isBusy}
|
|
2157
2312
|
rows={1}
|
|
2158
2313
|
/>
|
|
@@ -2261,7 +2416,7 @@ export default function AssistantEmbedPanel({
|
|
|
2261
2416
|
max-width: 100%;
|
|
2262
2417
|
min-width: 0;
|
|
2263
2418
|
box-sizing: border-box;
|
|
2264
|
-
padding
|
|
2419
|
+
padding: 2.25rem 0 0 !important;
|
|
2265
2420
|
background: var(--color-neutral-50) !important;
|
|
2266
2421
|
overflow-x: hidden;
|
|
2267
2422
|
overflow-y: hidden;
|
|
@@ -2326,10 +2481,6 @@ export default function AssistantEmbedPanel({
|
|
|
2326
2481
|
justify-content: center;
|
|
2327
2482
|
width: 1.75rem;
|
|
2328
2483
|
height: 1.75rem;
|
|
2329
|
-
border: 1px solid
|
|
2330
|
-
color-mix(in oklab, var(--color-neutral-200) 80%, transparent);
|
|
2331
|
-
background: color-mix(in oklab, #fff 80%, transparent);
|
|
2332
|
-
backdrop-filter: blur(4px);
|
|
2333
2484
|
color: color-mix(
|
|
2334
2485
|
in oklab,
|
|
2335
2486
|
var(--color-neutral-500) 80%,
|
|
@@ -2344,22 +2495,14 @@ export default function AssistantEmbedPanel({
|
|
|
2344
2495
|
}
|
|
2345
2496
|
|
|
2346
2497
|
.dark .ask-ai-markdown .ask-ai-copy-code-button {
|
|
2347
|
-
border-color: color-mix(
|
|
2348
|
-
in oklab,
|
|
2349
|
-
var(--color-neutral-700) 50%,
|
|
2350
|
-
transparent
|
|
2351
|
-
);
|
|
2352
|
-
background: var(--rd-code-surface);
|
|
2353
2498
|
color: var(--color-neutral-400);
|
|
2354
2499
|
}
|
|
2355
2500
|
|
|
2356
2501
|
.ask-ai-markdown .ask-ai-copy-code-button:hover {
|
|
2357
|
-
background: var(--color-neutral-50);
|
|
2358
2502
|
color: var(--color-neutral-600);
|
|
2359
2503
|
}
|
|
2360
2504
|
|
|
2361
2505
|
.dark .ask-ai-markdown .ask-ai-copy-code-button:hover {
|
|
2362
|
-
background: var(--color-neutral-800);
|
|
2363
2506
|
color: var(--color-neutral-200);
|
|
2364
2507
|
}
|
|
2365
2508
|
|
|
@@ -2437,6 +2580,7 @@ export default function AssistantEmbedPanel({
|
|
|
2437
2580
|
display: block;
|
|
2438
2581
|
width: 100%;
|
|
2439
2582
|
min-width: 100%;
|
|
2583
|
+
padding-block: 0.625rem;
|
|
2440
2584
|
background: transparent !important;
|
|
2441
2585
|
white-space: inherit;
|
|
2442
2586
|
word-break: normal;
|
|
@@ -2482,164 +2626,6 @@ export default function AssistantEmbedPanel({
|
|
|
2482
2626
|
background: var(--color-neutral-800);
|
|
2483
2627
|
}
|
|
2484
2628
|
|
|
2485
|
-
.dark .ask-ai-markdown .token.comment,
|
|
2486
|
-
.dark .ask-ai-markdown .token.prolog,
|
|
2487
|
-
.dark .ask-ai-markdown .token.doctype,
|
|
2488
|
-
.dark .ask-ai-markdown .token.cdata {
|
|
2489
|
-
color: #7f848e;
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
.dark .ask-ai-markdown .token.punctuation {
|
|
2493
|
-
color: #abb2bf;
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
.dark .ask-ai-markdown .token.property,
|
|
2497
|
-
.dark .ask-ai-markdown .token.tag,
|
|
2498
|
-
.dark .ask-ai-markdown .token.boolean,
|
|
2499
|
-
.dark .ask-ai-markdown .token.number,
|
|
2500
|
-
.dark .ask-ai-markdown .token.constant,
|
|
2501
|
-
.dark .ask-ai-markdown .token.symbol,
|
|
2502
|
-
.dark .ask-ai-markdown .token.deleted {
|
|
2503
|
-
color: #d19a66;
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
.dark .ask-ai-markdown .token.selector,
|
|
2507
|
-
.dark .ask-ai-markdown .token.attr-name,
|
|
2508
|
-
.dark .ask-ai-markdown .token.string,
|
|
2509
|
-
.dark .ask-ai-markdown .token.char,
|
|
2510
|
-
.dark .ask-ai-markdown .token.builtin,
|
|
2511
|
-
.dark .ask-ai-markdown .token.inserted {
|
|
2512
|
-
color: #98c379;
|
|
2513
|
-
}
|
|
2514
|
-
|
|
2515
|
-
.dark .ask-ai-markdown .token.operator,
|
|
2516
|
-
.dark .ask-ai-markdown .token.entity,
|
|
2517
|
-
.dark .ask-ai-markdown .token.url,
|
|
2518
|
-
.dark .ask-ai-markdown .language-css .token.string,
|
|
2519
|
-
.dark .ask-ai-markdown .style .token.string {
|
|
2520
|
-
color: #56b6c2;
|
|
2521
|
-
}
|
|
2522
|
-
|
|
2523
|
-
.dark .ask-ai-markdown .token.atrule,
|
|
2524
|
-
.dark .ask-ai-markdown .token.attr-value,
|
|
2525
|
-
.dark .ask-ai-markdown .token.keyword {
|
|
2526
|
-
color: #c678dd;
|
|
2527
|
-
}
|
|
2528
|
-
|
|
2529
|
-
.dark .ask-ai-markdown .token.function,
|
|
2530
|
-
.dark .ask-ai-markdown .token.class-name {
|
|
2531
|
-
color: #e5c07b;
|
|
2532
|
-
}
|
|
2533
|
-
|
|
2534
|
-
.dark .ask-ai-markdown .token.regex,
|
|
2535
|
-
.dark .ask-ai-markdown .token.important,
|
|
2536
|
-
.dark .ask-ai-markdown .token.variable {
|
|
2537
|
-
color: #e06c75;
|
|
2538
|
-
}
|
|
2539
|
-
|
|
2540
|
-
.dark .ask-ai-markdown .token.attr-value > .token.punctuation.attr-equals,
|
|
2541
|
-
.dark
|
|
2542
|
-
.ask-ai-markdown
|
|
2543
|
-
.token.special-attr
|
|
2544
|
-
> .token.attr-value
|
|
2545
|
-
> .token.value.css {
|
|
2546
|
-
color: #abb2bf;
|
|
2547
|
-
}
|
|
2548
|
-
|
|
2549
|
-
.dark .ask-ai-markdown .language-css .token.selector {
|
|
2550
|
-
color: #d19a66;
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
.dark .ask-ai-markdown .language-css .token.property {
|
|
2554
|
-
color: #abb2bf;
|
|
2555
|
-
}
|
|
2556
|
-
|
|
2557
|
-
.dark .ask-ai-markdown .language-css .token.function,
|
|
2558
|
-
.dark .ask-ai-markdown .language-css .token.url > .token.function {
|
|
2559
|
-
color: #56b6c2;
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
.dark .ask-ai-markdown .language-css .token.url > .token.string.url {
|
|
2563
|
-
color: #98c379;
|
|
2564
|
-
}
|
|
2565
|
-
|
|
2566
|
-
.dark .ask-ai-markdown .language-css .token.important,
|
|
2567
|
-
.dark .ask-ai-markdown .language-css .token.atrule .token.rule {
|
|
2568
|
-
color: #c678dd;
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
.dark .ask-ai-markdown .language-javascript .token.operator {
|
|
2572
|
-
color: #c678dd;
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
|
-
.dark
|
|
2576
|
-
.ask-ai-markdown
|
|
2577
|
-
.language-javascript
|
|
2578
|
-
.token.template-string
|
|
2579
|
-
> .token.interpolation
|
|
2580
|
-
> .token.interpolation-punctuation.punctuation {
|
|
2581
|
-
color: #e06c75;
|
|
2582
|
-
}
|
|
2583
|
-
|
|
2584
|
-
.dark .ask-ai-markdown .language-json .token.operator {
|
|
2585
|
-
color: #abb2bf;
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
|
-
.dark .ask-ai-markdown .language-json .token.null.keyword {
|
|
2589
|
-
color: #d19a66;
|
|
2590
|
-
}
|
|
2591
|
-
|
|
2592
|
-
.dark .ask-ai-markdown .language-markdown .token.url,
|
|
2593
|
-
.dark .ask-ai-markdown .language-markdown .token.url > .token.operator,
|
|
2594
|
-
.dark
|
|
2595
|
-
.ask-ai-markdown
|
|
2596
|
-
.language-markdown
|
|
2597
|
-
.token.url-reference.url
|
|
2598
|
-
> .token.string {
|
|
2599
|
-
color: #abb2bf;
|
|
2600
|
-
}
|
|
2601
|
-
|
|
2602
|
-
.dark .ask-ai-markdown .language-markdown .token.url > .token.content {
|
|
2603
|
-
color: #56b6c2;
|
|
2604
|
-
}
|
|
2605
|
-
|
|
2606
|
-
.dark .ask-ai-markdown .language-markdown .token.url > .token.url,
|
|
2607
|
-
.dark .ask-ai-markdown .language-markdown .token.url-reference.url {
|
|
2608
|
-
color: #98c379;
|
|
2609
|
-
}
|
|
2610
|
-
|
|
2611
|
-
.dark .ask-ai-markdown .language-markdown .token.blockquote.punctuation,
|
|
2612
|
-
.dark .ask-ai-markdown .language-markdown .token.hr.punctuation {
|
|
2613
|
-
color: #7f848e;
|
|
2614
|
-
}
|
|
2615
|
-
|
|
2616
|
-
.dark .ask-ai-markdown .language-markdown .token.code-snippet {
|
|
2617
|
-
color: #98c379;
|
|
2618
|
-
}
|
|
2619
|
-
|
|
2620
|
-
.dark .ask-ai-markdown .language-markdown .token.bold .token.content {
|
|
2621
|
-
color: #e5c07b;
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
.dark .ask-ai-markdown .language-markdown .token.italic .token.content {
|
|
2625
|
-
color: #c678dd;
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
.dark .ask-ai-markdown .language-markdown .token.strike .token.content,
|
|
2629
|
-
.dark
|
|
2630
|
-
.ask-ai-markdown
|
|
2631
|
-
.language-markdown
|
|
2632
|
-
.token.strike
|
|
2633
|
-
.token.punctuation,
|
|
2634
|
-
.dark .ask-ai-markdown .language-markdown .token.list.punctuation,
|
|
2635
|
-
.dark
|
|
2636
|
-
.ask-ai-markdown
|
|
2637
|
-
.language-markdown
|
|
2638
|
-
.token.title.important
|
|
2639
|
-
> .token.punctuation {
|
|
2640
|
-
color: #e06c75;
|
|
2641
|
-
}
|
|
2642
|
-
|
|
2643
2629
|
.ask-ai-markdown pre > code::-webkit-scrollbar {
|
|
2644
2630
|
width: 0;
|
|
2645
2631
|
height: 0;
|