radiant-docs 0.1.68 → 0.1.70
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 +5 -0
- package/template/package.json +1 -0
- package/template/src/components/Header.astro +346 -71
- package/template/src/components/MdxPage.astro +59 -6
- package/template/src/components/MobileNavbarActions.astro +410 -0
- package/template/src/components/OpenApiPage.astro +1 -1
- package/template/src/components/PageAiActions.astro +49 -43
- package/template/src/components/PagePagination.astro +1 -1
- package/template/src/components/Search.astro +24 -3
- package/template/src/components/Sidebar.astro +36 -1
- package/template/src/components/SidebarTabs.astro +3 -1
- package/template/src/components/TableOfContents.astro +14 -1
- package/template/src/components/chat/AssistantDocsWidget.tsx +1 -1
- package/template/src/components/chat/AssistantEmbedPanel.tsx +160 -50
- package/template/src/components/endpoint/PlaygroundForm.astro +1 -1
- package/template/src/components/user/CodeBlock.astro +102 -72
- package/template/src/components/user/ComponentPreviewBlock.astro +73 -32
- package/template/src/layouts/Layout.astro +45 -3
- package/template/src/lib/assistant-embed-script.ts +33 -5
- package/template/src/lib/assistant-panel-config.ts +1 -1
- package/template/src/lib/client-shiki-config.ts +7 -12
- package/template/src/generated/shiki-platform-assets.json +0 -24
- package/template/src/lib/assistant-shiki-client.ts +0 -522
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { MarkdownHeading } from "astro";
|
|
3
3
|
import { Icon } from "astro-icon/components";
|
|
4
|
+
import { getConfig } from "../lib/validation";
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
headings: MarkdownHeading[];
|
|
@@ -11,6 +12,18 @@ interface GroupedHeading extends MarkdownHeading {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
const { headings } = Astro.props;
|
|
15
|
+
type TabsPresentation = "topbar" | "sidebar";
|
|
16
|
+
const config = await getConfig();
|
|
17
|
+
const navigationTabs = config.navigation.tabs as
|
|
18
|
+
| (typeof config.navigation.tabs & { presentation?: TabsPresentation })
|
|
19
|
+
| undefined;
|
|
20
|
+
const hasTopbarNavigationTabs =
|
|
21
|
+
Array.isArray(navigationTabs?.items) &&
|
|
22
|
+
navigationTabs.items.length > 0 &&
|
|
23
|
+
(navigationTabs.presentation ?? "topbar") === "topbar";
|
|
24
|
+
const stickyTopClass = hasTopbarNavigationTabs
|
|
25
|
+
? "top-[calc(68px+44px+64px)]"
|
|
26
|
+
: "top-[calc(68px+64px)]";
|
|
14
27
|
|
|
15
28
|
// Group depth 3 headings under their preceding depth 2 heading
|
|
16
29
|
const groupedHeadings: GroupedHeading[] = [];
|
|
@@ -30,7 +43,7 @@ for (const heading of headings) {
|
|
|
30
43
|
---
|
|
31
44
|
|
|
32
45
|
<nav
|
|
33
|
-
class="sticky
|
|
46
|
+
class:list={["sticky text-[13px]/5", stickyTopClass]}
|
|
34
47
|
aria-label="Table of Contents"
|
|
35
48
|
data-slugs={JSON.stringify(headings.map((h) => h.slug))}
|
|
36
49
|
>
|
|
@@ -6,7 +6,7 @@ import AssistantEmbedPanel, {
|
|
|
6
6
|
AssistantPanelIcon,
|
|
7
7
|
type AssistantPanelSize,
|
|
8
8
|
} from "./AssistantEmbedPanel";
|
|
9
|
-
import type { AssistantShikiRuntimeConfig } from "
|
|
9
|
+
import type { AssistantShikiRuntimeConfig } from "@radiant-docs/assistant-shiki";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_ASSISTANT_CHROME_CONFIG,
|
|
12
12
|
type AssistantChromeConfig,
|
|
@@ -6,13 +6,14 @@ import remarkParse from "remark-parse";
|
|
|
6
6
|
import remarkGfm from "remark-gfm";
|
|
7
7
|
import remarkRehype from "remark-rehype";
|
|
8
8
|
import rehypeStringify from "rehype-stringify";
|
|
9
|
-
import { getDocsBasePath, withBasePath } from "../../lib/base-path";
|
|
10
9
|
import {
|
|
11
10
|
highlightAssistantCodeToHtml,
|
|
12
11
|
normalizeAssistantCodeLanguage,
|
|
12
|
+
resolveAssistantLanguageLabel,
|
|
13
13
|
warmAssistantShikiRuntime,
|
|
14
14
|
type AssistantShikiRuntimeConfig,
|
|
15
|
-
} from "
|
|
15
|
+
} from "@radiant-docs/assistant-shiki";
|
|
16
|
+
import { getDocsBasePath, withBasePath } from "../../lib/base-path";
|
|
16
17
|
|
|
17
18
|
type AssistantLinkTarget = "current" | "blank";
|
|
18
19
|
export type AssistantPanelSize = "default" | "expanded";
|
|
@@ -80,6 +81,7 @@ type ChatViewportWheelEvent = JSX.TargetedWheelEvent<HTMLDivElement>;
|
|
|
80
81
|
type ChatViewportScrollEvent = JSX.TargetedEvent<HTMLDivElement, Event>;
|
|
81
82
|
|
|
82
83
|
type PersistedPanelState = {
|
|
84
|
+
conversationId: string;
|
|
83
85
|
messages: ChatMessage[];
|
|
84
86
|
scrollTop: number;
|
|
85
87
|
inFlight: boolean;
|
|
@@ -104,6 +106,16 @@ type PendingAssistantHandoff = {
|
|
|
104
106
|
timeoutId: number;
|
|
105
107
|
};
|
|
106
108
|
|
|
109
|
+
type AssistantPageContext = {
|
|
110
|
+
hostname: string | null;
|
|
111
|
+
pagePath: string | null;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
type PendingPageContextRequest = {
|
|
115
|
+
resolve: (context: AssistantPageContext) => void;
|
|
116
|
+
timeoutId: number;
|
|
117
|
+
};
|
|
118
|
+
|
|
107
119
|
export const ASSISTANT_PANEL_STORAGE_KEY = "docs:assistant-embed-panel:v1";
|
|
108
120
|
const HANDOFF_QUERY_PARAM = "assistantHandoff";
|
|
109
121
|
const OPEN_QUERY_PARAM = "assistant";
|
|
@@ -118,30 +130,6 @@ const DEFAULT_ASSISTANT_MOBILE_BREAKPOINT = "640px";
|
|
|
118
130
|
const DEFAULT_ASSISTANT_PANEL_FULLSCREEN_HEIGHT_BREAKPOINT = "560px";
|
|
119
131
|
const markdownHtmlCache = new Map<string, string>();
|
|
120
132
|
const markdownHtmlPromiseCache = new Map<string, Promise<string>>();
|
|
121
|
-
const ASSISTANT_LANGUAGE_LABEL: Record<string, string> = {
|
|
122
|
-
bash: "Bash",
|
|
123
|
-
css: "CSS",
|
|
124
|
-
diff: "Diff",
|
|
125
|
-
go: "Go",
|
|
126
|
-
html: "HTML",
|
|
127
|
-
java: "Java",
|
|
128
|
-
javascript: "JavaScript",
|
|
129
|
-
jsx: "JSX",
|
|
130
|
-
json: "JSON",
|
|
131
|
-
markdown: "Markdown",
|
|
132
|
-
mdx: "MDX",
|
|
133
|
-
php: "PHP",
|
|
134
|
-
plaintext: "Text",
|
|
135
|
-
python: "Python",
|
|
136
|
-
ruby: "Ruby",
|
|
137
|
-
rust: "Rust",
|
|
138
|
-
shell: "Shell",
|
|
139
|
-
sql: "SQL",
|
|
140
|
-
tsx: "TSX",
|
|
141
|
-
typescript: "TypeScript",
|
|
142
|
-
yaml: "YAML",
|
|
143
|
-
};
|
|
144
|
-
|
|
145
133
|
const EXTERNAL_PROTOCOL_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
|
|
146
134
|
|
|
147
135
|
function buildMarkdownCacheKey(
|
|
@@ -413,6 +401,7 @@ function normalizePersistedMessages(rawMessages: unknown): ChatMessage[] {
|
|
|
413
401
|
|
|
414
402
|
function createEmptyPersistedPanelState(): PersistedPanelState {
|
|
415
403
|
return {
|
|
404
|
+
conversationId: createMessageId(),
|
|
416
405
|
messages: [],
|
|
417
406
|
scrollTop: 0,
|
|
418
407
|
inFlight: false,
|
|
@@ -426,6 +415,12 @@ function normalizePanelSize(value: unknown): AssistantPanelSize {
|
|
|
426
415
|
return value === "expanded" ? "expanded" : "default";
|
|
427
416
|
}
|
|
428
417
|
|
|
418
|
+
function normalizeConversationId(value: unknown): string {
|
|
419
|
+
return typeof value === "string" && value.trim()
|
|
420
|
+
? value.trim().slice(0, 128)
|
|
421
|
+
: createMessageId();
|
|
422
|
+
}
|
|
423
|
+
|
|
429
424
|
function getPanelFullscreenMediaQuery(
|
|
430
425
|
mobileBreakpoint: string,
|
|
431
426
|
panelFullscreenHeightBreakpoint: string,
|
|
@@ -439,6 +434,7 @@ function normalizePersistedPanelState(rawState: unknown): PersistedPanelState {
|
|
|
439
434
|
}
|
|
440
435
|
|
|
441
436
|
const state = rawState as {
|
|
437
|
+
conversationId?: unknown;
|
|
442
438
|
messages?: unknown;
|
|
443
439
|
scrollTop?: unknown;
|
|
444
440
|
inFlight?: unknown;
|
|
@@ -461,6 +457,7 @@ function normalizePersistedPanelState(rawState: unknown): PersistedPanelState {
|
|
|
461
457
|
: 0;
|
|
462
458
|
|
|
463
459
|
return {
|
|
460
|
+
conversationId: normalizeConversationId(state.conversationId),
|
|
464
461
|
messages: normalizePersistedMessages(state.messages),
|
|
465
462
|
scrollTop,
|
|
466
463
|
inFlight: isInFlightFresh,
|
|
@@ -515,6 +512,7 @@ function writePersistedPanelState(state: PersistedPanelState) {
|
|
|
515
512
|
ASSISTANT_PANEL_STORAGE_KEY,
|
|
516
513
|
JSON.stringify({
|
|
517
514
|
messages: state.messages,
|
|
515
|
+
conversationId: state.conversationId,
|
|
518
516
|
scrollTop:
|
|
519
517
|
Number.isFinite(state.scrollTop) && state.scrollTop > 0
|
|
520
518
|
? state.scrollTop
|
|
@@ -612,20 +610,6 @@ function escapeHtml(value: string): string {
|
|
|
612
610
|
.replaceAll("'", "'");
|
|
613
611
|
}
|
|
614
612
|
|
|
615
|
-
function resolveAssistantLanguageLabel(language: string): string {
|
|
616
|
-
const normalized = language.trim().toLowerCase();
|
|
617
|
-
if (!normalized) {
|
|
618
|
-
return "";
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
return (
|
|
622
|
-
ASSISTANT_LANGUAGE_LABEL[normalized] ??
|
|
623
|
-
normalized
|
|
624
|
-
.replace(/[-_]+/g, " ")
|
|
625
|
-
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
613
|
function ensureCodeBlockCopyButton(preElement: HTMLPreElement): void {
|
|
630
614
|
const existingButton = Array.from(preElement.children).find(
|
|
631
615
|
(child) =>
|
|
@@ -1004,20 +988,33 @@ function AssistantSourcesBlock({
|
|
|
1004
988
|
}
|
|
1005
989
|
|
|
1006
990
|
return (
|
|
1007
|
-
<div
|
|
1008
|
-
className="
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
<
|
|
1012
|
-
<ul>
|
|
991
|
+
<div className="ask-ai-sources mt-4 max-w-full min-w-0" onClick={onClick}>
|
|
992
|
+
<div className="text-xs font-semibold text-neutral-700 dark:text-neutral-200">
|
|
993
|
+
Sources
|
|
994
|
+
</div>
|
|
995
|
+
<ul className="mt-2 flex flex-wrap gap-2">
|
|
1013
996
|
{visibleSources.map((source) => (
|
|
1014
|
-
<li
|
|
997
|
+
<li
|
|
998
|
+
key={`${source.href}\0${source.label}`}
|
|
999
|
+
className="min-w-0 max-w-full"
|
|
1000
|
+
>
|
|
1015
1001
|
<a
|
|
1016
|
-
href={source.href}
|
|
1002
|
+
href={withBasePath(source.href)}
|
|
1017
1003
|
target={linkTarget === "blank" ? "_blank" : undefined}
|
|
1018
1004
|
rel={linkTarget === "blank" ? "noopener noreferrer" : undefined}
|
|
1005
|
+
className="inline-flex max-w-full items-center gap-1.5 rounded-md border border-neutral-200 bg-white px-2 py-1 text-xs font-medium text-neutral-700 transition-colors hover:bg-neutral-50 hover:text-neutral-950 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-200 dark:hover:bg-neutral-800 dark:hover:text-white"
|
|
1019
1006
|
>
|
|
1020
|
-
|
|
1007
|
+
<Icon
|
|
1008
|
+
icon="lucide:file-text"
|
|
1009
|
+
className="size-3.5 shrink-0"
|
|
1010
|
+
aria-hidden="true"
|
|
1011
|
+
/>
|
|
1012
|
+
<span className="truncate">{source.label}</span>
|
|
1013
|
+
<Icon
|
|
1014
|
+
icon="lucide:external-link"
|
|
1015
|
+
className="size-3 shrink-0 text-neutral-500 dark:text-neutral-400"
|
|
1016
|
+
aria-hidden="true"
|
|
1017
|
+
/>
|
|
1021
1018
|
</a>
|
|
1022
1019
|
</li>
|
|
1023
1020
|
))}
|
|
@@ -1057,6 +1054,36 @@ function postParentMessage(
|
|
|
1057
1054
|
window.parent.postMessage({ type, ...payload }, "*");
|
|
1058
1055
|
}
|
|
1059
1056
|
|
|
1057
|
+
function getCurrentPageContext(): AssistantPageContext {
|
|
1058
|
+
if (typeof window === "undefined") {
|
|
1059
|
+
return { hostname: null, pagePath: null };
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
return {
|
|
1063
|
+
hostname: window.location.hostname || null,
|
|
1064
|
+
pagePath: window.location.pathname || "/",
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
function normalizeParentPageContext(
|
|
1069
|
+
hostname: unknown,
|
|
1070
|
+
pagePath: unknown,
|
|
1071
|
+
): AssistantPageContext {
|
|
1072
|
+
const normalizedHostname =
|
|
1073
|
+
typeof hostname === "string" && hostname.trim().length > 0
|
|
1074
|
+
? hostname.trim().slice(0, 512)
|
|
1075
|
+
: null;
|
|
1076
|
+
const normalizedPagePath =
|
|
1077
|
+
typeof pagePath === "string" && pagePath.trim().startsWith("/")
|
|
1078
|
+
? pagePath.trim().slice(0, 2048)
|
|
1079
|
+
: null;
|
|
1080
|
+
|
|
1081
|
+
return {
|
|
1082
|
+
hostname: normalizedHostname,
|
|
1083
|
+
pagePath: normalizedPagePath,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1060
1087
|
function getApiPath(
|
|
1061
1088
|
apiPath: string,
|
|
1062
1089
|
allowApiPathQueryOverride: boolean,
|
|
@@ -1174,6 +1201,11 @@ export default function AssistantEmbedPanel({
|
|
|
1174
1201
|
const scrollPersistenceUnlockTimeoutRef = useRef<number | null>(null);
|
|
1175
1202
|
const isScrollPersistenceLockedRef = useRef(false);
|
|
1176
1203
|
const pendingHandoffsRef = useRef(new Map<string, PendingAssistantHandoff>());
|
|
1204
|
+
const pendingPageContextRequestsRef = useRef(
|
|
1205
|
+
new Map<string, PendingPageContextRequest>(),
|
|
1206
|
+
);
|
|
1207
|
+
const pageContextRef = useRef<AssistantPageContext>(getCurrentPageContext());
|
|
1208
|
+
const conversationIdRef = useRef(initialPanelState.conversationId);
|
|
1177
1209
|
const messagesRef = useRef(initialPanelState.messages);
|
|
1178
1210
|
const isBusyRef = useRef(initialPanelState.inFlight);
|
|
1179
1211
|
const isAwaitingFirstTokenRef = useRef(
|
|
@@ -1203,6 +1235,32 @@ export default function AssistantEmbedPanel({
|
|
|
1203
1235
|
launcherIconColors?.dark ?? launcherIconColor ?? "#ffffff";
|
|
1204
1236
|
const resolvedPanelSize = panelSize ?? localPanelSize;
|
|
1205
1237
|
|
|
1238
|
+
const requestPageContext = (): Promise<AssistantPageContext> => {
|
|
1239
|
+
const currentContext = getCurrentPageContext();
|
|
1240
|
+
if (
|
|
1241
|
+
panelSurface !== "iframe" ||
|
|
1242
|
+
typeof window === "undefined" ||
|
|
1243
|
+
window.parent === window
|
|
1244
|
+
) {
|
|
1245
|
+
pageContextRef.current = currentContext;
|
|
1246
|
+
return Promise.resolve(currentContext);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
return new Promise((resolve) => {
|
|
1250
|
+
const requestId = createMessageId();
|
|
1251
|
+
const timeoutId = window.setTimeout(() => {
|
|
1252
|
+
pendingPageContextRequestsRef.current.delete(requestId);
|
|
1253
|
+
resolve(pageContextRef.current);
|
|
1254
|
+
}, 250);
|
|
1255
|
+
|
|
1256
|
+
pendingPageContextRequestsRef.current.set(requestId, {
|
|
1257
|
+
resolve,
|
|
1258
|
+
timeoutId,
|
|
1259
|
+
});
|
|
1260
|
+
postParentMessage("assistant-embed:get-page-context", { requestId });
|
|
1261
|
+
});
|
|
1262
|
+
};
|
|
1263
|
+
|
|
1206
1264
|
const persistPanelState = (
|
|
1207
1265
|
nextMessages = messagesRef.current,
|
|
1208
1266
|
scrollTop = savedScrollTopRef.current,
|
|
@@ -1227,6 +1285,7 @@ export default function AssistantEmbedPanel({
|
|
|
1227
1285
|
}
|
|
1228
1286
|
|
|
1229
1287
|
writePersistedPanelState({
|
|
1288
|
+
conversationId: conversationIdRef.current,
|
|
1230
1289
|
messages: nextMessages,
|
|
1231
1290
|
scrollTop,
|
|
1232
1291
|
inFlight: isBusyRef.current,
|
|
@@ -1388,6 +1447,7 @@ export default function AssistantEmbedPanel({
|
|
|
1388
1447
|
};
|
|
1389
1448
|
|
|
1390
1449
|
const applyPersistedPanelState = (nextState: PersistedPanelState) => {
|
|
1450
|
+
conversationIdRef.current = nextState.conversationId;
|
|
1391
1451
|
messagesRef.current = nextState.messages;
|
|
1392
1452
|
savedScrollTopRef.current = nextState.scrollTop;
|
|
1393
1453
|
isBusyRef.current = nextState.inFlight;
|
|
@@ -1445,6 +1505,7 @@ export default function AssistantEmbedPanel({
|
|
|
1445
1505
|
|
|
1446
1506
|
pendingHandoffsRef.current.set(handoffId, {
|
|
1447
1507
|
state: {
|
|
1508
|
+
conversationId: conversationIdRef.current,
|
|
1448
1509
|
messages: messagesRef.current,
|
|
1449
1510
|
scrollTop: savedScrollTopRef.current,
|
|
1450
1511
|
inFlight: isBusyRef.current,
|
|
@@ -1774,8 +1835,35 @@ export default function AssistantEmbedPanel({
|
|
|
1774
1835
|
type?: unknown;
|
|
1775
1836
|
size?: unknown;
|
|
1776
1837
|
isFullscreen?: unknown;
|
|
1838
|
+
hostname?: unknown;
|
|
1839
|
+
pagePath?: unknown;
|
|
1840
|
+
requestId?: unknown;
|
|
1777
1841
|
};
|
|
1778
1842
|
|
|
1843
|
+
if (data.type === "assistant-embed:set-page-context") {
|
|
1844
|
+
if (event.source !== window.parent) {
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
const pageContext = normalizeParentPageContext(
|
|
1849
|
+
data.hostname,
|
|
1850
|
+
data.pagePath,
|
|
1851
|
+
);
|
|
1852
|
+
pageContextRef.current = pageContext;
|
|
1853
|
+
|
|
1854
|
+
if (typeof data.requestId === "string") {
|
|
1855
|
+
const pendingRequest = pendingPageContextRequestsRef.current.get(
|
|
1856
|
+
data.requestId,
|
|
1857
|
+
);
|
|
1858
|
+
if (pendingRequest) {
|
|
1859
|
+
window.clearTimeout(pendingRequest.timeoutId);
|
|
1860
|
+
pendingPageContextRequestsRef.current.delete(data.requestId);
|
|
1861
|
+
pendingRequest.resolve(pageContext);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1779
1867
|
if (data.type === "assistant-embed:panel-opened") {
|
|
1780
1868
|
setEmptyStateAnimationKey((previous) => previous + 1);
|
|
1781
1869
|
queueSavedScrollRestore();
|
|
@@ -1799,6 +1887,10 @@ export default function AssistantEmbedPanel({
|
|
|
1799
1887
|
window.addEventListener("message", handlePanelMessage);
|
|
1800
1888
|
return () => {
|
|
1801
1889
|
window.removeEventListener("message", handlePanelMessage);
|
|
1890
|
+
for (const pendingRequest of pendingPageContextRequestsRef.current.values()) {
|
|
1891
|
+
window.clearTimeout(pendingRequest.timeoutId);
|
|
1892
|
+
}
|
|
1893
|
+
pendingPageContextRequestsRef.current.clear();
|
|
1802
1894
|
};
|
|
1803
1895
|
}, []);
|
|
1804
1896
|
|
|
@@ -1810,6 +1902,7 @@ export default function AssistantEmbedPanel({
|
|
|
1810
1902
|
const handleStartNewChat = () => {
|
|
1811
1903
|
activeRequestAbortRef.current?.abort();
|
|
1812
1904
|
activeRequestAbortRef.current = null;
|
|
1905
|
+
conversationIdRef.current = createMessageId();
|
|
1813
1906
|
setSharedBusy(false);
|
|
1814
1907
|
setSharedAwaitingFirstToken(false);
|
|
1815
1908
|
resetThreadScrollPosition([]);
|
|
@@ -1837,6 +1930,7 @@ export default function AssistantEmbedPanel({
|
|
|
1837
1930
|
const handleUnavailableBack = () => {
|
|
1838
1931
|
activeRequestAbortRef.current?.abort();
|
|
1839
1932
|
activeRequestAbortRef.current = null;
|
|
1933
|
+
conversationIdRef.current = createMessageId();
|
|
1840
1934
|
setSharedBusy(false);
|
|
1841
1935
|
setSharedAwaitingFirstToken(false);
|
|
1842
1936
|
resetThreadScrollPosition([]);
|
|
@@ -1893,10 +1987,14 @@ export default function AssistantEmbedPanel({
|
|
|
1893
1987
|
requestHeaders["x-ask-ai-dev-token"] = devProxyToken;
|
|
1894
1988
|
}
|
|
1895
1989
|
|
|
1990
|
+
const pageContext = await requestPageContext();
|
|
1896
1991
|
const response = await fetch(resolvedApiPathRef.current, {
|
|
1897
1992
|
method: "POST",
|
|
1898
1993
|
headers: requestHeaders,
|
|
1899
1994
|
body: JSON.stringify({
|
|
1995
|
+
conversationId: conversationIdRef.current,
|
|
1996
|
+
pagePath: pageContext.pagePath,
|
|
1997
|
+
hostname: pageContext.hostname,
|
|
1900
1998
|
messages: nextConversation.map((message) => ({
|
|
1901
1999
|
role: message.role,
|
|
1902
2000
|
content: message.content,
|
|
@@ -2141,7 +2239,7 @@ export default function AssistantEmbedPanel({
|
|
|
2141
2239
|
linkTarget === "current" && onCurrentLinkNavigate
|
|
2142
2240
|
? getSameOriginNavigationRequest(event)
|
|
2143
2241
|
: null;
|
|
2144
|
-
if (!navigationRequest) {
|
|
2242
|
+
if (!navigationRequest || !onCurrentLinkNavigate) {
|
|
2145
2243
|
return;
|
|
2146
2244
|
}
|
|
2147
2245
|
|
|
@@ -2724,6 +2822,18 @@ export default function AssistantEmbedPanel({
|
|
|
2724
2822
|
-ms-overflow-style: none;
|
|
2725
2823
|
}
|
|
2726
2824
|
|
|
2825
|
+
.ask-ai-markdown [data-rd-code-line] {
|
|
2826
|
+
display: flex;
|
|
2827
|
+
width: max-content;
|
|
2828
|
+
min-width: 100%;
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
.ask-ai-markdown [data-rd-code-line-content] {
|
|
2832
|
+
flex: 1;
|
|
2833
|
+
padding-inline: 0.75rem;
|
|
2834
|
+
white-space: pre;
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2727
2837
|
.dark .ask-ai-markdown pre > code {
|
|
2728
2838
|
scrollbar-width: none;
|
|
2729
2839
|
}
|
|
@@ -1074,7 +1074,7 @@ const headerValuePrefixes = Object.fromEntries(
|
|
|
1074
1074
|
import {
|
|
1075
1075
|
highlightAssistantCodeToHtml,
|
|
1076
1076
|
type AssistantShikiRuntimeConfig,
|
|
1077
|
-
} from "
|
|
1077
|
+
} from "@radiant-docs/assistant-shiki";
|
|
1078
1078
|
|
|
1079
1079
|
type PlaygroundShikiConfig = AssistantShikiRuntimeConfig | null | undefined;
|
|
1080
1080
|
|
|
@@ -397,90 +397,101 @@ const renderedCodeLinesHtml = normalizedTokenLines
|
|
|
397
397
|
</div>
|
|
398
398
|
</div>
|
|
399
399
|
</div>
|
|
400
|
-
<script
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
400
|
+
<script>
|
|
401
|
+
const codeBlockRootSelector = "[data-rd-code-block-root='true']";
|
|
402
|
+
const codeBlockControlsKey = "__rdCodeBlockControls";
|
|
403
|
+
|
|
404
|
+
const getCodeBlockControls = () => {
|
|
405
|
+
window[codeBlockControlsKey] = window[codeBlockControlsKey] || {
|
|
406
|
+
copiedStateTimers: new WeakMap(),
|
|
407
|
+
installed: false,
|
|
408
|
+
};
|
|
404
409
|
|
|
405
|
-
|
|
406
|
-
|
|
410
|
+
return window[codeBlockControlsKey];
|
|
411
|
+
};
|
|
407
412
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
);
|
|
411
|
-
if (copyButtons.length === 0) return;
|
|
413
|
+
const setCopiedState = (button, copied) => {
|
|
414
|
+
const copyIcon = button.querySelector("[data-rd-copy-icon]");
|
|
415
|
+
const checkIcon = button.querySelector("[data-rd-copy-check]");
|
|
412
416
|
|
|
413
|
-
|
|
414
|
-
const copyIcon = button.querySelector("[data-rd-copy-icon]");
|
|
415
|
-
const checkIcon = button.querySelector("[data-rd-copy-check]");
|
|
417
|
+
if (!copyIcon || !checkIcon) return;
|
|
416
418
|
|
|
417
|
-
|
|
419
|
+
if (copied) {
|
|
420
|
+
copyIcon.classList.add("scale-50", "opacity-0", "-rotate-6");
|
|
421
|
+
copyIcon.classList.remove("scale-100", "opacity-100", "rotate-0");
|
|
418
422
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
checkIcon.classList.remove("scale-50", "opacity-0", "rotate-6");
|
|
424
|
-
checkIcon.classList.add("scale-110", "opacity-100", "rotate-0");
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
copyIcon.classList.remove("scale-50", "opacity-0", "-rotate-6");
|
|
429
|
-
copyIcon.classList.add("scale-100", "opacity-100", "rotate-0");
|
|
423
|
+
checkIcon.classList.remove("scale-50", "opacity-0", "rotate-6");
|
|
424
|
+
checkIcon.classList.add("scale-110", "opacity-100", "rotate-0");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
430
427
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
428
|
+
copyIcon.classList.remove("scale-50", "opacity-0", "-rotate-6");
|
|
429
|
+
copyIcon.classList.add("scale-100", "opacity-100", "rotate-0");
|
|
430
|
+
|
|
431
|
+
checkIcon.classList.remove("scale-110", "opacity-100", "rotate-0");
|
|
432
|
+
checkIcon.classList.add("scale-50", "opacity-0", "rotate-6");
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const fallbackCopy = (text) => {
|
|
436
|
+
try {
|
|
437
|
+
const textarea = document.createElement("textarea");
|
|
438
|
+
textarea.value = text;
|
|
439
|
+
textarea.setAttribute("readonly", "");
|
|
440
|
+
textarea.style.position = "fixed";
|
|
441
|
+
textarea.style.top = "0";
|
|
442
|
+
textarea.style.left = "0";
|
|
443
|
+
textarea.style.width = "1px";
|
|
444
|
+
textarea.style.height = "1px";
|
|
445
|
+
textarea.style.padding = "0";
|
|
446
|
+
textarea.style.border = "0";
|
|
447
|
+
textarea.style.opacity = "0";
|
|
448
|
+
textarea.style.fontSize = "16px";
|
|
449
|
+
textarea.style.pointerEvents = "none";
|
|
450
|
+
document.body.appendChild(textarea);
|
|
434
451
|
|
|
435
|
-
const fallbackCopy = (text) => {
|
|
436
452
|
try {
|
|
437
|
-
|
|
438
|
-
textarea.value = text;
|
|
439
|
-
textarea.setAttribute("readonly", "");
|
|
440
|
-
textarea.style.position = "fixed";
|
|
441
|
-
textarea.style.top = "0";
|
|
442
|
-
textarea.style.left = "0";
|
|
443
|
-
textarea.style.width = "1px";
|
|
444
|
-
textarea.style.height = "1px";
|
|
445
|
-
textarea.style.padding = "0";
|
|
446
|
-
textarea.style.border = "0";
|
|
447
|
-
textarea.style.opacity = "0";
|
|
448
|
-
textarea.style.fontSize = "16px";
|
|
449
|
-
textarea.style.pointerEvents = "none";
|
|
450
|
-
document.body.appendChild(textarea);
|
|
451
|
-
|
|
452
|
-
try {
|
|
453
|
-
textarea.focus({ preventScroll: true });
|
|
454
|
-
} catch {
|
|
455
|
-
textarea.focus();
|
|
456
|
-
}
|
|
457
|
-
textarea.select();
|
|
458
|
-
textarea.setSelectionRange(0, textarea.value.length);
|
|
459
|
-
|
|
460
|
-
const copied = document.execCommand("copy");
|
|
461
|
-
document.body.removeChild(textarea);
|
|
462
|
-
return copied;
|
|
453
|
+
textarea.focus({ preventScroll: true });
|
|
463
454
|
} catch {
|
|
464
|
-
|
|
455
|
+
textarea.focus();
|
|
465
456
|
}
|
|
466
|
-
|
|
457
|
+
textarea.select();
|
|
458
|
+
textarea.setSelectionRange(0, textarea.value.length);
|
|
459
|
+
|
|
460
|
+
const copied = document.execCommand("copy");
|
|
461
|
+
document.body.removeChild(textarea);
|
|
462
|
+
return copied;
|
|
463
|
+
} catch {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
467
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
} catch {
|
|
475
|
-
// Fallback below when the async Clipboard API is unavailable or blocked.
|
|
468
|
+
const copyToClipboard = async (text) => {
|
|
469
|
+
try {
|
|
470
|
+
if (navigator.clipboard?.writeText) {
|
|
471
|
+
await navigator.clipboard.writeText(text);
|
|
472
|
+
return true;
|
|
476
473
|
}
|
|
474
|
+
} catch {
|
|
475
|
+
// Fallback below when the async Clipboard API is unavailable or blocked.
|
|
476
|
+
}
|
|
477
477
|
|
|
478
|
-
|
|
479
|
-
|
|
478
|
+
return fallbackCopy(text);
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const initCodeBlock = (root) => {
|
|
482
|
+
if (!(root instanceof HTMLElement)) return;
|
|
483
|
+
if (root.dataset.rdCodeBlockInitialized === "true") return;
|
|
484
|
+
|
|
485
|
+
const copyButtons = root.querySelectorAll("[data-rd-copy-trigger='true']");
|
|
486
|
+
if (copyButtons.length === 0) return;
|
|
487
|
+
|
|
488
|
+
root.dataset.rdCodeBlockInitialized = "true";
|
|
480
489
|
|
|
481
490
|
copyButtons.forEach((button) => {
|
|
482
|
-
|
|
491
|
+
if (!(button instanceof HTMLElement)) return;
|
|
492
|
+
|
|
483
493
|
button.addEventListener("click", async () => {
|
|
494
|
+
const controls = getCodeBlockControls();
|
|
484
495
|
const encodedCopyValue =
|
|
485
496
|
button.getAttribute("data-rd-copy-content") ?? "";
|
|
486
497
|
const copyValue = decodeURIComponent(encodedCopyValue);
|
|
@@ -494,16 +505,35 @@ const renderedCodeLinesHtml = normalizedTokenLines
|
|
|
494
505
|
|
|
495
506
|
setCopiedState(button, true);
|
|
496
507
|
|
|
497
|
-
|
|
498
|
-
|
|
508
|
+
const existingTimeoutId = controls.copiedStateTimers.get(button);
|
|
509
|
+
if (existingTimeoutId) window.clearTimeout(existingTimeoutId);
|
|
510
|
+
|
|
511
|
+
const timeoutId = window.setTimeout(() => {
|
|
499
512
|
setCopiedState(button, false);
|
|
500
|
-
|
|
513
|
+
controls.copiedStateTimers.delete(button);
|
|
501
514
|
}, 1200);
|
|
515
|
+
controls.copiedStateTimers.set(button, timeoutId);
|
|
502
516
|
} catch {
|
|
503
517
|
setCopiedState(button, false);
|
|
504
518
|
}
|
|
505
519
|
});
|
|
506
520
|
});
|
|
507
|
-
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const initCodeBlocks = () => {
|
|
524
|
+
document.querySelectorAll(codeBlockRootSelector).forEach((root) => {
|
|
525
|
+
initCodeBlock(root);
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const controls = getCodeBlockControls();
|
|
530
|
+
controls.init = initCodeBlocks;
|
|
531
|
+
|
|
532
|
+
if (!controls.installed) {
|
|
533
|
+
controls.installed = true;
|
|
534
|
+
document.addEventListener("astro:page-load", initCodeBlocks);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
initCodeBlocks();
|
|
508
538
|
</script>
|
|
509
539
|
</div>
|