doccupine 0.0.63 → 0.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/lib/structures.js +4 -0
- package/dist/templates/app/api/rag/route.d.ts +1 -1
- package/dist/templates/app/api/rag/route.js +11 -4
- package/dist/templates/app/theme.d.ts +3 -1
- package/dist/templates/app/theme.js +17 -15
- package/dist/templates/components/Chat.d.ts +1 -1
- package/dist/templates/components/Chat.js +358 -161
- package/dist/templates/components/DocsSideBar.d.ts +1 -1
- package/dist/templates/components/DocsSideBar.js +45 -11
- package/dist/templates/components/LockBodyScroll.d.ts +1 -0
- package/dist/templates/components/LockBodyScroll.js +17 -0
- package/dist/templates/components/SideBar.d.ts +1 -1
- package/dist/templates/components/SideBar.js +15 -2
- package/dist/templates/components/layout/Accordion.d.ts +1 -1
- package/dist/templates/components/layout/Accordion.js +1 -1
- package/dist/templates/components/layout/ActionBar.d.ts +1 -1
- package/dist/templates/components/layout/ActionBar.js +17 -87
- package/dist/templates/components/layout/Callout.d.ts +1 -1
- package/dist/templates/components/layout/Callout.js +1 -1
- package/dist/templates/components/layout/Card.d.ts +1 -1
- package/dist/templates/components/layout/Card.js +26 -7
- package/dist/templates/components/layout/Code.d.ts +1 -1
- package/dist/templates/components/layout/Code.js +1 -1
- package/dist/templates/components/layout/Columns.d.ts +1 -1
- package/dist/templates/components/layout/Columns.js +1 -1
- package/dist/templates/components/layout/DocsComponents.d.ts +1 -1
- package/dist/templates/components/layout/DocsComponents.js +40 -14
- package/dist/templates/components/layout/DocsNavigation.d.ts +1 -1
- package/dist/templates/components/layout/DocsNavigation.js +3 -2
- package/dist/templates/components/layout/Field.d.ts +1 -1
- package/dist/templates/components/layout/Field.js +1 -1
- package/dist/templates/components/layout/Footer.d.ts +1 -1
- package/dist/templates/components/layout/Footer.js +28 -6
- package/dist/templates/components/layout/Header.d.ts +1 -1
- package/dist/templates/components/layout/Header.js +10 -12
- package/dist/templates/components/layout/SharedStyles.d.ts +1 -1
- package/dist/templates/components/layout/SharedStyles.js +26 -2
- package/dist/templates/components/layout/StaticLinks.d.ts +1 -1
- package/dist/templates/components/layout/StaticLinks.js +7 -3
- package/dist/templates/components/layout/Steps.d.ts +1 -1
- package/dist/templates/components/layout/Steps.js +7 -2
- package/dist/templates/components/layout/Tabs.d.ts +1 -1
- package/dist/templates/components/layout/Tabs.js +2 -2
- package/dist/templates/components/layout/Update.d.ts +1 -1
- package/dist/templates/components/layout/Update.js +1 -1
- package/dist/templates/mdx/ai-assistant.mdx.d.ts +1 -1
- package/dist/templates/mdx/ai-assistant.mdx.js +8 -0
- package/dist/templates/mdx/callouts.mdx.d.ts +1 -1
- package/dist/templates/mdx/callouts.mdx.js +6 -2
- package/dist/templates/mdx/cards.mdx.d.ts +1 -1
- package/dist/templates/mdx/cards.mdx.js +19 -3
- package/dist/templates/mdx/columns.mdx.d.ts +1 -1
- package/dist/templates/mdx/columns.mdx.js +2 -2
- package/dist/templates/mdx/commands.mdx.d.ts +1 -1
- package/dist/templates/mdx/commands.mdx.js +10 -2
- package/dist/templates/mdx/components.mdx.d.ts +1 -0
- package/dist/templates/mdx/components.mdx.js +56 -0
- package/dist/templates/mdx/deployment.mdx.d.ts +1 -1
- package/dist/templates/mdx/deployment.mdx.js +1 -1
- package/dist/templates/mdx/globals.mdx.d.ts +1 -1
- package/dist/templates/mdx/globals.mdx.js +5 -0
- package/dist/templates/mdx/index.mdx.d.ts +1 -1
- package/dist/templates/mdx/index.mdx.js +5 -5
- package/dist/templates/mdx/model-context-protocol.mdx.d.ts +1 -1
- package/dist/templates/mdx/model-context-protocol.mdx.js +2 -2
- package/dist/templates/mdx/navigation.mdx.d.ts +1 -1
- package/dist/templates/mdx/navigation.mdx.js +1 -1
- package/dist/templates/mdx/platform/ai-assistant.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/ai-assistant.mdx.js +20 -0
- package/dist/templates/mdx/platform/external-links.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/external-links.mdx.js +2 -0
- package/dist/templates/mdx/platform/fonts-settings.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/fonts-settings.mdx.js +8 -5
- package/dist/templates/mdx/platform/index.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/index.mdx.js +10 -1
- package/dist/templates/mdx/platform/site-settings.mdx.d.ts +1 -1
- package/dist/templates/mdx/platform/site-settings.mdx.js +4 -4
- package/dist/templates/mdx/sections.mdx.d.ts +1 -1
- package/dist/templates/mdx/sections.mdx.js +2 -2
- package/dist/templates/mdx/steps.mdx.d.ts +1 -1
- package/dist/templates/mdx/steps.mdx.js +4 -0
- package/dist/templates/mdx/tabs.mdx.d.ts +1 -1
- package/dist/templates/mdx/tabs.mdx.js +1 -1
- package/dist/templates/package.js +5 -5
- package/dist/templates/services/llm/types.d.ts +1 -1
- package/dist/templates/services/llm/types.js +1 -1
- package/dist/templates/services/mcp/server.d.ts +1 -1
- package/dist/templates/services/mcp/server.js +1 -1
- package/dist/templates/services/mcp/tools.d.ts +1 -1
- package/dist/templates/services/mcp/tools.js +1 -5
- package/dist/templates/utils/config.d.ts +1 -1
- package/dist/templates/utils/config.js +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CHAT_WIDTH } from "../app/theme.js";
|
|
1
2
|
export const chatTemplate = `"use client";
|
|
2
3
|
import React, {
|
|
3
4
|
createContext,
|
|
@@ -9,17 +10,21 @@ import React, {
|
|
|
9
10
|
import styled, { css, keyframes } from "styled-components";
|
|
10
11
|
import { rgba } from "polished";
|
|
11
12
|
import { Button } from "cherry-styled-components";
|
|
12
|
-
import { ArrowUp, LoaderPinwheel, Sparkles, X } from "lucide-react";
|
|
13
|
+
import { ArrowUp, LoaderPinwheel, RotateCcw, Sparkles, X } from "lucide-react";
|
|
13
14
|
import remarkGfm from "remark-gfm";
|
|
14
15
|
import rehypeHighlight from "rehype-highlight";
|
|
15
16
|
import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote";
|
|
16
17
|
import { serialize } from "next-mdx-remote/serialize";
|
|
18
|
+
import Link from "next/link";
|
|
17
19
|
import { mq, Theme } from "@/app/theme";
|
|
20
|
+
import { useLockBodyScroll } from "@/components/LockBodyScroll";
|
|
18
21
|
import { useMDXComponents as getMDXComponents } from "@/components/MDXComponents";
|
|
19
22
|
import {
|
|
20
23
|
styledAnchor,
|
|
21
24
|
styledTable,
|
|
22
25
|
stylesLists,
|
|
26
|
+
StyledSmallButton,
|
|
27
|
+
interactiveStyles,
|
|
23
28
|
} from "@/components/layout/SharedStyled";
|
|
24
29
|
|
|
25
30
|
const mdxComponents = getMDXComponents({});
|
|
@@ -40,7 +45,7 @@ const StyledChat = styled.div<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
40
45
|
top: 0;
|
|
41
46
|
right: 0;
|
|
42
47
|
width: 100%;
|
|
43
|
-
height: calc(
|
|
48
|
+
height: calc(100dvh - 90px);
|
|
44
49
|
overflow-y: scroll;
|
|
45
50
|
overflow-x: hidden;
|
|
46
51
|
z-index: 1000;
|
|
@@ -49,6 +54,7 @@ const StyledChat = styled.div<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
49
54
|
transform: translateX(0);
|
|
50
55
|
background: \${({ theme }) => theme.colors.light};
|
|
51
56
|
-webkit-overflow-scrolling: touch;
|
|
57
|
+
opacity: 1;
|
|
52
58
|
|
|
53
59
|
&::-webkit-scrollbar {
|
|
54
60
|
display: none;
|
|
@@ -58,10 +64,11 @@ const StyledChat = styled.div<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
58
64
|
!$isVisible &&
|
|
59
65
|
css\`
|
|
60
66
|
transform: translateX(100%);
|
|
67
|
+
opacity: 0;
|
|
61
68
|
\`}
|
|
62
69
|
|
|
63
70
|
\${mq("lg")} {
|
|
64
|
-
width:
|
|
71
|
+
width: ${CHAT_WIDTH}px;
|
|
65
72
|
border-left: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
66
73
|
}
|
|
67
74
|
\`;
|
|
@@ -352,15 +359,17 @@ const StyledChatForm = styled.form<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
352
359
|
border-top: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
353
360
|
transition: all 0.3s ease;
|
|
354
361
|
transform: translateX(100%);
|
|
362
|
+
opacity: 0;
|
|
355
363
|
|
|
356
364
|
\${mq("lg")} {
|
|
357
|
-
width:
|
|
365
|
+
width: ${CHAT_WIDTH}px;
|
|
358
366
|
border-left: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
359
367
|
}
|
|
360
368
|
|
|
361
369
|
\${({ $isVisible }) =>
|
|
362
370
|
$isVisible &&
|
|
363
371
|
css\`
|
|
372
|
+
opacity: 1;
|
|
364
373
|
transform: translateX(0);
|
|
365
374
|
\`}
|
|
366
375
|
|
|
@@ -369,53 +378,94 @@ const StyledChatForm = styled.form<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
369
378
|
}
|
|
370
379
|
\`;
|
|
371
380
|
|
|
372
|
-
const
|
|
381
|
+
const StyledGlowSmallButton = styled(StyledSmallButton)<{
|
|
373
382
|
theme: Theme;
|
|
374
|
-
$
|
|
383
|
+
$hasContent: boolean;
|
|
375
384
|
}>\`
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
width: calc(100% - 115px);
|
|
381
|
-
z-index: 998;
|
|
382
|
-
|
|
383
|
-
\${mq("lg")} {
|
|
384
|
-
left: 50%;
|
|
385
|
-
transform: translateX(-50%) translateY(0);
|
|
386
|
-
bottom: initial;
|
|
387
|
-
position: absolute;
|
|
388
|
-
top: 153px;
|
|
389
|
-
width: calc(100% - 320px * 2 - 40px);
|
|
390
|
-
opacity: 1;
|
|
385
|
+
@property --gradient-angle {
|
|
386
|
+
syntax: "<angle>";
|
|
387
|
+
initial-value: 0deg;
|
|
388
|
+
inherits: false;
|
|
391
389
|
}
|
|
392
390
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
391
|
+
position: relative;
|
|
392
|
+
isolation: isolate;
|
|
393
|
+
margin-right: 0;
|
|
394
|
+
background: \${({ theme }) => theme.colors.light};
|
|
395
|
+
padding: 0;
|
|
397
396
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
397
|
+
&::before {
|
|
398
|
+
content: "";
|
|
399
|
+
inset: -2px;
|
|
400
|
+
border-radius: 8px;
|
|
401
|
+
background: conic-gradient(
|
|
402
|
+
from var(--gradient-angle),
|
|
403
|
+
#cc5555,
|
|
404
|
+
#d9a745,
|
|
405
|
+
#3ab0cc,
|
|
406
|
+
#cc7fc2,
|
|
407
|
+
#4380cc,
|
|
408
|
+
#4c1fa3,
|
|
409
|
+
#cc5555
|
|
410
|
+
);
|
|
411
|
+
opacity: 0;
|
|
412
|
+
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
413
|
+
animation: \${rotateGradient} 3s linear infinite;
|
|
414
|
+
z-index: -1;
|
|
415
|
+
position: absolute;
|
|
416
|
+
top: -2px;
|
|
417
|
+
left: -2px;
|
|
418
|
+
width: calc(100% + 4px);
|
|
419
|
+
height: calc(100% + 4px);
|
|
420
|
+
}
|
|
403
421
|
|
|
404
|
-
|
|
405
|
-
|
|
422
|
+
&::after {
|
|
423
|
+
content: "";
|
|
424
|
+
position: absolute;
|
|
425
|
+
inset: -8px;
|
|
426
|
+
border-radius: 14px;
|
|
427
|
+
background: conic-gradient(
|
|
428
|
+
from var(--gradient-angle),
|
|
429
|
+
\${rgba("#ff6b6b", 0.4)},
|
|
430
|
+
\${rgba("#feca57", 0.4)},
|
|
431
|
+
\${rgba("#48dbfb", 0.4)},
|
|
432
|
+
\${rgba("#ff9ff3", 0.4)},
|
|
433
|
+
\${rgba("#54a0ff", 0.4)},
|
|
434
|
+
\${rgba("#5f27cd", 0.4)},
|
|
435
|
+
\${rgba("#ff6b6b", 0.4)}
|
|
436
|
+
);
|
|
437
|
+
opacity: 0;
|
|
438
|
+
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
439
|
+
animation:
|
|
440
|
+
\${rotateGradient} 3s linear infinite,
|
|
441
|
+
\${pulseGlow} 2s ease-in-out infinite;
|
|
442
|
+
z-index: -2;
|
|
443
|
+
pointer-events: none;
|
|
406
444
|
}
|
|
407
|
-
\`;
|
|
408
445
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
justify-content: center;
|
|
414
|
-
align-items: center;
|
|
446
|
+
&:hover::before,
|
|
447
|
+
&:hover::after {
|
|
448
|
+
opacity: 1;
|
|
449
|
+
}
|
|
415
450
|
|
|
416
|
-
|
|
417
|
-
|
|
451
|
+
& span {
|
|
452
|
+
padding: 6px 8px;
|
|
453
|
+
display: flex;
|
|
454
|
+
background: \${({ theme }) => theme.colors.light};
|
|
455
|
+
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
456
|
+
gap: 6px;
|
|
418
457
|
}
|
|
458
|
+
|
|
459
|
+
\${({ $hasContent }) =>
|
|
460
|
+
$hasContent &&
|
|
461
|
+
css\`
|
|
462
|
+
&::before {
|
|
463
|
+
opacity: 1;
|
|
464
|
+
}
|
|
465
|
+
&::after {
|
|
466
|
+
opacity: 1;
|
|
467
|
+
}
|
|
468
|
+
\`}
|
|
419
469
|
\`;
|
|
420
470
|
|
|
421
471
|
const StyledError = styled.div<{ theme: Theme }>\`
|
|
@@ -561,6 +611,40 @@ const StyledAnswer = styled.div<{ theme: Theme; $isAnswer: boolean }>\`
|
|
|
561
611
|
}
|
|
562
612
|
\`;
|
|
563
613
|
|
|
614
|
+
const StyledSources = styled.div\`
|
|
615
|
+
display: flex;
|
|
616
|
+
gap: 16px;
|
|
617
|
+
flex-wrap: wrap;
|
|
618
|
+
margin: -5px 0 20px;
|
|
619
|
+
\`;
|
|
620
|
+
|
|
621
|
+
const StyledSourceLink = styled(Link)<{ theme: Theme }>\`
|
|
622
|
+
position: relative;
|
|
623
|
+
text-decoration: none;
|
|
624
|
+
font-size: \${({ theme }) => theme.fontSizes.small.lg};
|
|
625
|
+
line-height: 1;
|
|
626
|
+
color: \${({ theme }) => theme.colors.primary};
|
|
627
|
+
display: flex;
|
|
628
|
+
gap: 6px;
|
|
629
|
+
transition: all 0.3s ease;
|
|
630
|
+
font-weight: 600;
|
|
631
|
+
white-space: nowrap;
|
|
632
|
+
min-width: fit-content;
|
|
633
|
+
background: \${({ theme }) => rgba(theme.colors.primaryLight, 0.1)};
|
|
634
|
+
padding: 6px 8px;
|
|
635
|
+
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
636
|
+
\${interactiveStyles};
|
|
637
|
+
|
|
638
|
+
& * {
|
|
639
|
+
margin: auto 0;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
&:hover {
|
|
643
|
+
color: \${({ theme }) =>
|
|
644
|
+
theme.isDark ? theme.colors.primaryLight : theme.colors.primaryDark};
|
|
645
|
+
}
|
|
646
|
+
\`;
|
|
647
|
+
|
|
564
648
|
const StyledChatTitle = styled.div<{ theme: Theme }>\`
|
|
565
649
|
display: flex;
|
|
566
650
|
flex-wrap: nowrap;
|
|
@@ -578,7 +662,7 @@ const StyledChatTitle = styled.div<{ theme: Theme }>\`
|
|
|
578
662
|
const StyledChatTitleIconWrapper = styled.span<{ theme: Theme }>\`
|
|
579
663
|
display: flex;
|
|
580
664
|
align-items: center;
|
|
581
|
-
gap:
|
|
665
|
+
gap: 12px;
|
|
582
666
|
color: \${({ theme }) => theme.colors.dark};
|
|
583
667
|
\`;
|
|
584
668
|
|
|
@@ -600,10 +684,18 @@ const StyledChatCloseButton = styled.button<{ theme: Theme }>\`
|
|
|
600
684
|
}
|
|
601
685
|
\`;
|
|
602
686
|
|
|
687
|
+
type Source = {
|
|
688
|
+
id: string;
|
|
689
|
+
path: string;
|
|
690
|
+
uri: string;
|
|
691
|
+
score: number;
|
|
692
|
+
};
|
|
693
|
+
|
|
603
694
|
type Answer = {
|
|
604
695
|
text: string;
|
|
605
696
|
answer?: boolean;
|
|
606
697
|
mdx?: MDXRemoteSerializeResult;
|
|
698
|
+
sources?: Source[];
|
|
607
699
|
};
|
|
608
700
|
|
|
609
701
|
const SPARKLE_COLORS = [
|
|
@@ -636,6 +728,7 @@ interface RainbowInputProps {
|
|
|
636
728
|
placeholder?: string;
|
|
637
729
|
autoComplete?: string;
|
|
638
730
|
"aria-label"?: string;
|
|
731
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
639
732
|
}
|
|
640
733
|
|
|
641
734
|
function RainbowInput({
|
|
@@ -645,6 +738,7 @@ function RainbowInput({
|
|
|
645
738
|
placeholder,
|
|
646
739
|
autoComplete,
|
|
647
740
|
"aria-label": ariaLabel,
|
|
741
|
+
inputRef,
|
|
648
742
|
}: RainbowInputProps) {
|
|
649
743
|
const [isFocused, setIsFocused] = useState(false);
|
|
650
744
|
const [isHovered, setIsHovered] = useState(false);
|
|
@@ -675,6 +769,7 @@ function RainbowInput({
|
|
|
675
769
|
))}
|
|
676
770
|
</StyledSparkleContainer>
|
|
677
771
|
<StyledRainbowInput
|
|
772
|
+
ref={inputRef}
|
|
678
773
|
id={id}
|
|
679
774
|
value={value}
|
|
680
775
|
onChange={onChange}
|
|
@@ -688,14 +783,54 @@ function RainbowInput({
|
|
|
688
783
|
);
|
|
689
784
|
}
|
|
690
785
|
|
|
786
|
+
function ChatButtonCTA() {
|
|
787
|
+
const { setIsOpen, isOpen, answer, setAnswer, chatInputRef } =
|
|
788
|
+
useContext(ChatContext);
|
|
789
|
+
|
|
790
|
+
return (
|
|
791
|
+
<StyledGlowSmallButton
|
|
792
|
+
onClick={() => {
|
|
793
|
+
const next = !isOpen;
|
|
794
|
+
setIsOpen(next);
|
|
795
|
+
if (next) {
|
|
796
|
+
if (answer.length === 0) {
|
|
797
|
+
setAnswer([
|
|
798
|
+
{ text: "Hey there, how can I assist you?", answer: true },
|
|
799
|
+
]);
|
|
800
|
+
}
|
|
801
|
+
setTimeout(() => {
|
|
802
|
+
chatInputRef.current?.focus();
|
|
803
|
+
}, 350);
|
|
804
|
+
}
|
|
805
|
+
}}
|
|
806
|
+
aria-label="Ask AI Assistant"
|
|
807
|
+
$hasContent={isOpen}
|
|
808
|
+
type="button"
|
|
809
|
+
>
|
|
810
|
+
<span>
|
|
811
|
+
<Sparkles size={16} />
|
|
812
|
+
Ask AI
|
|
813
|
+
</span>
|
|
814
|
+
</StyledGlowSmallButton>
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
|
|
691
818
|
function Chat() {
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
819
|
+
const {
|
|
820
|
+
isOpen,
|
|
821
|
+
question,
|
|
822
|
+
setQuestion,
|
|
823
|
+
loading,
|
|
824
|
+
error,
|
|
825
|
+
answer,
|
|
826
|
+
ask,
|
|
827
|
+
closeChat,
|
|
828
|
+
resetChat,
|
|
829
|
+
chatInputRef,
|
|
830
|
+
} = useContext(ChatContext);
|
|
696
831
|
const endRef = useRef<HTMLDivElement | null>(null);
|
|
697
|
-
|
|
698
|
-
|
|
832
|
+
|
|
833
|
+
useLockBodyScroll(isOpen);
|
|
699
834
|
|
|
700
835
|
useEffect(() => {
|
|
701
836
|
endRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
@@ -703,12 +838,150 @@ function Chat() {
|
|
|
703
838
|
|
|
704
839
|
useEffect(() => {
|
|
705
840
|
if (answer?.length > 0) {
|
|
706
|
-
|
|
707
|
-
"chat-bottom-input",
|
|
708
|
-
) as HTMLInputElement | null;
|
|
709
|
-
el?.focus();
|
|
841
|
+
chatInputRef.current?.focus();
|
|
710
842
|
}
|
|
711
|
-
}, [answer]);
|
|
843
|
+
}, [answer, chatInputRef]);
|
|
844
|
+
|
|
845
|
+
return (
|
|
846
|
+
<>
|
|
847
|
+
<StyledChat $isVisible={isOpen}>
|
|
848
|
+
<StyledChatTitle>
|
|
849
|
+
<StyledChatTitleIconWrapper>
|
|
850
|
+
<Sparkles />
|
|
851
|
+
<h3>AI Assistant</h3>
|
|
852
|
+
</StyledChatTitleIconWrapper>
|
|
853
|
+
<StyledChatTitleIconWrapper>
|
|
854
|
+
<StyledChatCloseButton
|
|
855
|
+
onClick={resetChat}
|
|
856
|
+
aria-label="Reset chat history"
|
|
857
|
+
title="Reset chat history"
|
|
858
|
+
>
|
|
859
|
+
<RotateCcw size={18} />
|
|
860
|
+
</StyledChatCloseButton>
|
|
861
|
+
<StyledChatCloseButton
|
|
862
|
+
onClick={closeChat}
|
|
863
|
+
aria-label="Close chat"
|
|
864
|
+
title="Close chat"
|
|
865
|
+
>
|
|
866
|
+
<X />
|
|
867
|
+
</StyledChatCloseButton>
|
|
868
|
+
</StyledChatTitleIconWrapper>
|
|
869
|
+
</StyledChatTitle>
|
|
870
|
+
{answer &&
|
|
871
|
+
answer.map((a, i) => (
|
|
872
|
+
<React.Fragment key={i}>
|
|
873
|
+
<StyledAnswer $isAnswer={a.answer ?? false}>
|
|
874
|
+
{a.answer && a.mdx ? (
|
|
875
|
+
<MDXRemote {...a.mdx} components={mdxComponents} />
|
|
876
|
+
) : (
|
|
877
|
+
a.text
|
|
878
|
+
)}
|
|
879
|
+
</StyledAnswer>
|
|
880
|
+
{a.answer && a.sources && a.sources.length > 0 && (
|
|
881
|
+
<StyledSources>
|
|
882
|
+
{a.sources.map((src) => {
|
|
883
|
+
const slug = src.uri
|
|
884
|
+
.replace("docs://", "")
|
|
885
|
+
.replace(/^\\/+/, "");
|
|
886
|
+
const href = slug ? \`/\${slug}/\` : "/";
|
|
887
|
+
const label = slug
|
|
888
|
+
? slug
|
|
889
|
+
.split("/")
|
|
890
|
+
.pop()!
|
|
891
|
+
.replace(/-/g, " ")
|
|
892
|
+
.replace(/\\b\\w/g, (c: string) => c.toUpperCase())
|
|
893
|
+
: "Home";
|
|
894
|
+
return (
|
|
895
|
+
<StyledSourceLink key={src.id} href={href}>
|
|
896
|
+
{label}
|
|
897
|
+
</StyledSourceLink>
|
|
898
|
+
);
|
|
899
|
+
})}
|
|
900
|
+
</StyledSources>
|
|
901
|
+
)}
|
|
902
|
+
</React.Fragment>
|
|
903
|
+
))}
|
|
904
|
+
{loading && (
|
|
905
|
+
<StyledLoading>
|
|
906
|
+
Answering<span>.</span>
|
|
907
|
+
<span>.</span>
|
|
908
|
+
<span>.</span>
|
|
909
|
+
</StyledLoading>
|
|
910
|
+
)}
|
|
911
|
+
{error && (
|
|
912
|
+
<StyledError>
|
|
913
|
+
<strong>Error:</strong> {error}
|
|
914
|
+
</StyledError>
|
|
915
|
+
)}
|
|
916
|
+
<div ref={endRef} />
|
|
917
|
+
</StyledChat>
|
|
918
|
+
|
|
919
|
+
<StyledChatForm onSubmit={ask} $isVisible={isOpen}>
|
|
920
|
+
<RainbowInput
|
|
921
|
+
id="chat-bottom-input"
|
|
922
|
+
inputRef={chatInputRef}
|
|
923
|
+
value={question}
|
|
924
|
+
onChange={(e) => setQuestion(e.target.value)}
|
|
925
|
+
placeholder="Ask AI Assistant..."
|
|
926
|
+
autoComplete="off"
|
|
927
|
+
aria-label="Ask a follow-up question"
|
|
928
|
+
/>
|
|
929
|
+
<StyledRainbowButton
|
|
930
|
+
type="submit"
|
|
931
|
+
disabled={loading || question.trim() === ""}
|
|
932
|
+
$hasContent={question.trim().length > 0}
|
|
933
|
+
aria-label={loading ? "Loading response" : "Submit question"}
|
|
934
|
+
>
|
|
935
|
+
{loading ? <LoaderPinwheel className="loading" /> : <ArrowUp />}
|
|
936
|
+
</StyledRainbowButton>
|
|
937
|
+
</StyledChatForm>
|
|
938
|
+
</>
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
const ChatContext = createContext<{
|
|
943
|
+
isOpen: boolean;
|
|
944
|
+
setIsOpen: (isOpen: boolean) => void;
|
|
945
|
+
isChatActive: boolean;
|
|
946
|
+
question: string;
|
|
947
|
+
setQuestion: (q: string) => void;
|
|
948
|
+
loading: boolean;
|
|
949
|
+
error: string | null;
|
|
950
|
+
answer: Answer[];
|
|
951
|
+
setAnswer: (answers: Answer[]) => void;
|
|
952
|
+
ask: (e: React.FormEvent) => void;
|
|
953
|
+
closeChat: () => void;
|
|
954
|
+
resetChat: () => void;
|
|
955
|
+
chatInputRef: React.RefObject<HTMLInputElement | null>;
|
|
956
|
+
}>({
|
|
957
|
+
isOpen: false,
|
|
958
|
+
setIsOpen: () => {},
|
|
959
|
+
isChatActive: false,
|
|
960
|
+
question: "",
|
|
961
|
+
setQuestion: () => {},
|
|
962
|
+
loading: false,
|
|
963
|
+
error: null,
|
|
964
|
+
answer: [],
|
|
965
|
+
setAnswer: () => {},
|
|
966
|
+
ask: () => {},
|
|
967
|
+
closeChat: () => {},
|
|
968
|
+
resetChat: () => {},
|
|
969
|
+
chatInputRef: { current: null },
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
interface ChatContextProviderProps {
|
|
973
|
+
children: React.ReactNode;
|
|
974
|
+
isChatActive: boolean;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
const ChtProvider = ({ children, isChatActive }: ChatContextProviderProps) => {
|
|
978
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
979
|
+
const [question, setQuestion] = useState("");
|
|
980
|
+
const [loading, setLoading] = useState(false);
|
|
981
|
+
const [error, setError] = useState<string | null>(null);
|
|
982
|
+
const [answer, setAnswer] = useState<Answer[]>([]);
|
|
983
|
+
const abortRef = useRef<AbortController | null>(null);
|
|
984
|
+
const chatInputRef = useRef<HTMLInputElement | null>(null);
|
|
712
985
|
|
|
713
986
|
async function ask(e: React.FormEvent) {
|
|
714
987
|
e.preventDefault();
|
|
@@ -730,7 +1003,6 @@ function Chat() {
|
|
|
730
1003
|
abortRef.current = controller;
|
|
731
1004
|
|
|
732
1005
|
try {
|
|
733
|
-
// Build conversation history from previous Q&A pairs
|
|
734
1006
|
const history = answer
|
|
735
1007
|
.filter((a) => a.text.trim() !== "")
|
|
736
1008
|
.map((a) => ({
|
|
@@ -753,11 +1025,11 @@ function Chat() {
|
|
|
753
1025
|
const reader = res.body?.getReader();
|
|
754
1026
|
const decoder = new TextDecoder();
|
|
755
1027
|
const contentParts: string[] = [];
|
|
1028
|
+
let sources: Source[] = [];
|
|
756
1029
|
if (!reader) {
|
|
757
1030
|
throw new Error("Failed to get response reader");
|
|
758
1031
|
}
|
|
759
1032
|
|
|
760
|
-
// Add a placeholder for the streaming answer
|
|
761
1033
|
const streamingAnswerIndex = mergedQuestions.length;
|
|
762
1034
|
setAnswer([...mergedQuestions, { text: "", answer: true }]);
|
|
763
1035
|
|
|
@@ -768,7 +1040,6 @@ function Chat() {
|
|
|
768
1040
|
|
|
769
1041
|
buffer += decoder.decode(value, { stream: true });
|
|
770
1042
|
const parts = buffer.split("\\n");
|
|
771
|
-
// Keep the last (potentially incomplete) part in the buffer
|
|
772
1043
|
buffer = parts.pop() ?? "";
|
|
773
1044
|
|
|
774
1045
|
for (const line of parts) {
|
|
@@ -776,7 +1047,15 @@ function Chat() {
|
|
|
776
1047
|
try {
|
|
777
1048
|
const data = JSON.parse(line.slice(6));
|
|
778
1049
|
|
|
779
|
-
if (data.type === "
|
|
1050
|
+
if (data.type === "metadata") {
|
|
1051
|
+
const allSources: Source[] = data.data?.sources ?? [];
|
|
1052
|
+
const seen = new Set<string>();
|
|
1053
|
+
sources = allSources.filter((s: Source) => {
|
|
1054
|
+
if (s.score < 0.4 || seen.has(s.uri)) return false;
|
|
1055
|
+
seen.add(s.uri);
|
|
1056
|
+
return true;
|
|
1057
|
+
});
|
|
1058
|
+
} else if (data.type === "content") {
|
|
780
1059
|
contentParts.push(data.data);
|
|
781
1060
|
const streamedContent = contentParts.join("");
|
|
782
1061
|
|
|
@@ -785,6 +1064,7 @@ function Chat() {
|
|
|
785
1064
|
newAnswers[streamingAnswerIndex] = {
|
|
786
1065
|
text: streamedContent,
|
|
787
1066
|
answer: true,
|
|
1067
|
+
sources,
|
|
788
1068
|
};
|
|
789
1069
|
return newAnswers;
|
|
790
1070
|
});
|
|
@@ -792,7 +1072,6 @@ function Chat() {
|
|
|
792
1072
|
throw new Error(data.data);
|
|
793
1073
|
} else if (data.type === "done") {
|
|
794
1074
|
const streamedContent = contentParts.join("");
|
|
795
|
-
// Finalize with MDX serialization
|
|
796
1075
|
let mdxSource: MDXRemoteSerializeResult | null = null;
|
|
797
1076
|
try {
|
|
798
1077
|
mdxSource = await serialize(streamedContent, {
|
|
@@ -814,6 +1093,7 @@ function Chat() {
|
|
|
814
1093
|
text: streamedContent,
|
|
815
1094
|
answer: true,
|
|
816
1095
|
mdx: mdxSource || undefined,
|
|
1096
|
+
sources,
|
|
817
1097
|
};
|
|
818
1098
|
return newAnswers;
|
|
819
1099
|
});
|
|
@@ -838,109 +1118,16 @@ function Chat() {
|
|
|
838
1118
|
}
|
|
839
1119
|
}
|
|
840
1120
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
<StyledChatFixedInner>
|
|
845
|
-
<RainbowInput
|
|
846
|
-
value={question}
|
|
847
|
-
onChange={(e) => setQuestion(e.target.value)}
|
|
848
|
-
placeholder="Ask AI Assistant..."
|
|
849
|
-
autoComplete="off"
|
|
850
|
-
aria-label="Ask a question about the documentation"
|
|
851
|
-
/>
|
|
852
|
-
<StyledRainbowButton
|
|
853
|
-
type="submit"
|
|
854
|
-
disabled={loading}
|
|
855
|
-
$hasContent={question.trim().length > 0}
|
|
856
|
-
aria-label={loading ? "Loading response" : "Submit question"}
|
|
857
|
-
>
|
|
858
|
-
{loading ? <LoaderPinwheel className="loading" /> : <ArrowUp />}
|
|
859
|
-
</StyledRainbowButton>
|
|
860
|
-
</StyledChatFixedInner>
|
|
861
|
-
</StyledChatFixedForm>
|
|
862
|
-
|
|
863
|
-
<StyledChat $isVisible={isOpen}>
|
|
864
|
-
<StyledChatTitle>
|
|
865
|
-
<StyledChatTitleIconWrapper>
|
|
866
|
-
<Sparkles />
|
|
867
|
-
<h3>AI Assistant</h3>
|
|
868
|
-
</StyledChatTitleIconWrapper>
|
|
869
|
-
<StyledChatCloseButton
|
|
870
|
-
onClick={() => {
|
|
871
|
-
abortRef.current?.abort();
|
|
872
|
-
setAnswer([]);
|
|
873
|
-
setIsOpen(false);
|
|
874
|
-
}}
|
|
875
|
-
aria-label="Close chat"
|
|
876
|
-
>
|
|
877
|
-
<X />
|
|
878
|
-
</StyledChatCloseButton>
|
|
879
|
-
</StyledChatTitle>
|
|
880
|
-
{answer &&
|
|
881
|
-
answer.map((a, i) => (
|
|
882
|
-
<StyledAnswer key={i} $isAnswer={a.answer ?? false}>
|
|
883
|
-
{a.answer && a.mdx ? (
|
|
884
|
-
<MDXRemote {...a.mdx} components={mdxComponents} />
|
|
885
|
-
) : (
|
|
886
|
-
a.text
|
|
887
|
-
)}
|
|
888
|
-
</StyledAnswer>
|
|
889
|
-
))}
|
|
890
|
-
{loading && (
|
|
891
|
-
<StyledLoading>
|
|
892
|
-
Answering<span>.</span>
|
|
893
|
-
<span>.</span>
|
|
894
|
-
<span>.</span>
|
|
895
|
-
</StyledLoading>
|
|
896
|
-
)}
|
|
897
|
-
{error && (
|
|
898
|
-
<StyledError>
|
|
899
|
-
<strong>Error:</strong> {error}
|
|
900
|
-
</StyledError>
|
|
901
|
-
)}
|
|
902
|
-
<div ref={endRef} />
|
|
903
|
-
</StyledChat>
|
|
904
|
-
|
|
905
|
-
<StyledChatForm onSubmit={ask} $isVisible={isOpen}>
|
|
906
|
-
<RainbowInput
|
|
907
|
-
id="chat-bottom-input"
|
|
908
|
-
value={question}
|
|
909
|
-
onChange={(e) => setQuestion(e.target.value)}
|
|
910
|
-
placeholder="Ask AI Assistant..."
|
|
911
|
-
autoComplete="off"
|
|
912
|
-
aria-label="Ask a follow-up question"
|
|
913
|
-
/>
|
|
914
|
-
<StyledRainbowButton
|
|
915
|
-
type="submit"
|
|
916
|
-
disabled={loading || question.trim() === ""}
|
|
917
|
-
$hasContent={question.trim().length > 0}
|
|
918
|
-
aria-label={loading ? "Loading response" : "Submit question"}
|
|
919
|
-
>
|
|
920
|
-
{loading ? <LoaderPinwheel className="loading" /> : <ArrowUp />}
|
|
921
|
-
</StyledRainbowButton>
|
|
922
|
-
</StyledChatForm>
|
|
923
|
-
</>
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
const ChatContext = createContext<{
|
|
928
|
-
isOpen: boolean;
|
|
929
|
-
setIsOpen: (isOpen: boolean) => void;
|
|
930
|
-
isChatActive: boolean;
|
|
931
|
-
}>({
|
|
932
|
-
isOpen: false,
|
|
933
|
-
setIsOpen: () => {},
|
|
934
|
-
isChatActive: false,
|
|
935
|
-
});
|
|
936
|
-
|
|
937
|
-
interface ChatContextProviderProps {
|
|
938
|
-
children: React.ReactNode;
|
|
939
|
-
isChatActive: boolean;
|
|
940
|
-
}
|
|
1121
|
+
function closeChat() {
|
|
1122
|
+
setIsOpen(false);
|
|
1123
|
+
}
|
|
941
1124
|
|
|
942
|
-
|
|
943
|
-
|
|
1125
|
+
function resetChat() {
|
|
1126
|
+
abortRef.current?.abort();
|
|
1127
|
+
setLoading(false);
|
|
1128
|
+
setError(null);
|
|
1129
|
+
setAnswer([{ text: "Hey there, how can I assist you?", answer: true }]);
|
|
1130
|
+
}
|
|
944
1131
|
|
|
945
1132
|
return (
|
|
946
1133
|
<ChatContext.Provider
|
|
@@ -948,6 +1135,16 @@ const ChtProvider = ({ children, isChatActive }: ChatContextProviderProps) => {
|
|
|
948
1135
|
isOpen,
|
|
949
1136
|
setIsOpen,
|
|
950
1137
|
isChatActive,
|
|
1138
|
+
question,
|
|
1139
|
+
setQuestion,
|
|
1140
|
+
loading,
|
|
1141
|
+
error,
|
|
1142
|
+
answer,
|
|
1143
|
+
setAnswer,
|
|
1144
|
+
ask,
|
|
1145
|
+
closeChat,
|
|
1146
|
+
resetChat,
|
|
1147
|
+
chatInputRef,
|
|
951
1148
|
}}
|
|
952
1149
|
>
|
|
953
1150
|
{children}
|
|
@@ -955,5 +1152,5 @@ const ChtProvider = ({ children, isChatActive }: ChatContextProviderProps) => {
|
|
|
955
1152
|
);
|
|
956
1153
|
};
|
|
957
1154
|
|
|
958
|
-
export { Chat, ChtProvider, ChatContext };
|
|
1155
|
+
export { Chat, ChtProvider, ChatContext, ChatButtonCTA };
|
|
959
1156
|
`;
|