doccupine 0.0.63 → 0.0.64
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 +2 -0
- 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 +251 -158
- package/dist/templates/components/DocsSideBar.d.ts +1 -1
- package/dist/templates/components/DocsSideBar.js +34 -11
- package/dist/templates/components/SideBar.d.ts +1 -1
- package/dist/templates/components/SideBar.js +12 -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 +15 -60
- 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/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 +37 -11
- 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/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 +4 -4
- 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,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
styledAnchor,
|
|
21
22
|
styledTable,
|
|
22
23
|
stylesLists,
|
|
24
|
+
StyledSmallButton,
|
|
23
25
|
} from "@/components/layout/SharedStyled";
|
|
24
26
|
|
|
25
27
|
const mdxComponents = getMDXComponents({});
|
|
@@ -49,6 +51,7 @@ const StyledChat = styled.div<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
49
51
|
transform: translateX(0);
|
|
50
52
|
background: \${({ theme }) => theme.colors.light};
|
|
51
53
|
-webkit-overflow-scrolling: touch;
|
|
54
|
+
opacity: 1;
|
|
52
55
|
|
|
53
56
|
&::-webkit-scrollbar {
|
|
54
57
|
display: none;
|
|
@@ -58,10 +61,11 @@ const StyledChat = styled.div<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
58
61
|
!$isVisible &&
|
|
59
62
|
css\`
|
|
60
63
|
transform: translateX(100%);
|
|
64
|
+
opacity: 0;
|
|
61
65
|
\`}
|
|
62
66
|
|
|
63
67
|
\${mq("lg")} {
|
|
64
|
-
width:
|
|
68
|
+
width: ${CHAT_WIDTH}px;
|
|
65
69
|
border-left: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
66
70
|
}
|
|
67
71
|
\`;
|
|
@@ -352,15 +356,17 @@ const StyledChatForm = styled.form<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
352
356
|
border-top: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
353
357
|
transition: all 0.3s ease;
|
|
354
358
|
transform: translateX(100%);
|
|
359
|
+
opacity: 0;
|
|
355
360
|
|
|
356
361
|
\${mq("lg")} {
|
|
357
|
-
width:
|
|
362
|
+
width: ${CHAT_WIDTH}px;
|
|
358
363
|
border-left: solid 1px \${({ theme }) => theme.colors.grayLight};
|
|
359
364
|
}
|
|
360
365
|
|
|
361
366
|
\${({ $isVisible }) =>
|
|
362
367
|
$isVisible &&
|
|
363
368
|
css\`
|
|
369
|
+
opacity: 1;
|
|
364
370
|
transform: translateX(0);
|
|
365
371
|
\`}
|
|
366
372
|
|
|
@@ -369,53 +375,94 @@ const StyledChatForm = styled.form<{ theme: Theme; $isVisible: boolean }>\`
|
|
|
369
375
|
}
|
|
370
376
|
\`;
|
|
371
377
|
|
|
372
|
-
const
|
|
378
|
+
const StyledGlowSmallButton = styled(StyledSmallButton)<{
|
|
373
379
|
theme: Theme;
|
|
374
|
-
$
|
|
380
|
+
$hasContent: boolean;
|
|
375
381
|
}>\`
|
|
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;
|
|
382
|
+
@property --gradient-angle {
|
|
383
|
+
syntax: "<angle>";
|
|
384
|
+
initial-value: 0deg;
|
|
385
|
+
inherits: false;
|
|
391
386
|
}
|
|
392
387
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
388
|
+
position: relative;
|
|
389
|
+
isolation: isolate;
|
|
390
|
+
margin-right: 0;
|
|
391
|
+
background: \${({ theme }) => theme.colors.light};
|
|
392
|
+
padding: 0;
|
|
397
393
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
394
|
+
&::before {
|
|
395
|
+
content: "";
|
|
396
|
+
inset: -2px;
|
|
397
|
+
border-radius: 8px;
|
|
398
|
+
background: conic-gradient(
|
|
399
|
+
from var(--gradient-angle),
|
|
400
|
+
#cc5555,
|
|
401
|
+
#d9a745,
|
|
402
|
+
#3ab0cc,
|
|
403
|
+
#cc7fc2,
|
|
404
|
+
#4380cc,
|
|
405
|
+
#4c1fa3,
|
|
406
|
+
#cc5555
|
|
407
|
+
);
|
|
408
|
+
opacity: 0;
|
|
409
|
+
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
410
|
+
animation: \${rotateGradient} 3s linear infinite;
|
|
411
|
+
z-index: -1;
|
|
412
|
+
position: absolute;
|
|
413
|
+
top: -2px;
|
|
414
|
+
left: -2px;
|
|
415
|
+
width: calc(100% + 4px);
|
|
416
|
+
height: calc(100% + 4px);
|
|
417
|
+
}
|
|
403
418
|
|
|
404
|
-
|
|
405
|
-
|
|
419
|
+
&::after {
|
|
420
|
+
content: "";
|
|
421
|
+
position: absolute;
|
|
422
|
+
inset: -8px;
|
|
423
|
+
border-radius: 14px;
|
|
424
|
+
background: conic-gradient(
|
|
425
|
+
from var(--gradient-angle),
|
|
426
|
+
\${rgba("#ff6b6b", 0.4)},
|
|
427
|
+
\${rgba("#feca57", 0.4)},
|
|
428
|
+
\${rgba("#48dbfb", 0.4)},
|
|
429
|
+
\${rgba("#ff9ff3", 0.4)},
|
|
430
|
+
\${rgba("#54a0ff", 0.4)},
|
|
431
|
+
\${rgba("#5f27cd", 0.4)},
|
|
432
|
+
\${rgba("#ff6b6b", 0.4)}
|
|
433
|
+
);
|
|
434
|
+
opacity: 0;
|
|
435
|
+
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
436
|
+
animation:
|
|
437
|
+
\${rotateGradient} 3s linear infinite,
|
|
438
|
+
\${pulseGlow} 2s ease-in-out infinite;
|
|
439
|
+
z-index: -2;
|
|
440
|
+
pointer-events: none;
|
|
406
441
|
}
|
|
407
|
-
\`;
|
|
408
442
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
justify-content: center;
|
|
414
|
-
align-items: center;
|
|
443
|
+
&:hover::before,
|
|
444
|
+
&:hover::after {
|
|
445
|
+
opacity: 1;
|
|
446
|
+
}
|
|
415
447
|
|
|
416
|
-
|
|
417
|
-
|
|
448
|
+
& span {
|
|
449
|
+
padding: 6px 8px;
|
|
450
|
+
display: flex;
|
|
451
|
+
background: \${({ theme }) => theme.colors.light};
|
|
452
|
+
border-radius: \${({ theme }) => theme.spacing.radius.xs};
|
|
453
|
+
gap: 6px;
|
|
418
454
|
}
|
|
455
|
+
|
|
456
|
+
\${({ $hasContent }) =>
|
|
457
|
+
$hasContent &&
|
|
458
|
+
css\`
|
|
459
|
+
&::before {
|
|
460
|
+
opacity: 1;
|
|
461
|
+
}
|
|
462
|
+
&::after {
|
|
463
|
+
opacity: 1;
|
|
464
|
+
}
|
|
465
|
+
\`}
|
|
419
466
|
\`;
|
|
420
467
|
|
|
421
468
|
const StyledError = styled.div<{ theme: Theme }>\`
|
|
@@ -636,6 +683,7 @@ interface RainbowInputProps {
|
|
|
636
683
|
placeholder?: string;
|
|
637
684
|
autoComplete?: string;
|
|
638
685
|
"aria-label"?: string;
|
|
686
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
639
687
|
}
|
|
640
688
|
|
|
641
689
|
function RainbowInput({
|
|
@@ -645,6 +693,7 @@ function RainbowInput({
|
|
|
645
693
|
placeholder,
|
|
646
694
|
autoComplete,
|
|
647
695
|
"aria-label": ariaLabel,
|
|
696
|
+
inputRef,
|
|
648
697
|
}: RainbowInputProps) {
|
|
649
698
|
const [isFocused, setIsFocused] = useState(false);
|
|
650
699
|
const [isHovered, setIsHovered] = useState(false);
|
|
@@ -675,6 +724,7 @@ function RainbowInput({
|
|
|
675
724
|
))}
|
|
676
725
|
</StyledSparkleContainer>
|
|
677
726
|
<StyledRainbowInput
|
|
727
|
+
ref={inputRef}
|
|
678
728
|
id={id}
|
|
679
729
|
value={value}
|
|
680
730
|
onChange={onChange}
|
|
@@ -688,14 +738,51 @@ function RainbowInput({
|
|
|
688
738
|
);
|
|
689
739
|
}
|
|
690
740
|
|
|
741
|
+
function ChatButtonCTA() {
|
|
742
|
+
const { setIsOpen, isOpen, answer, setAnswer, chatInputRef } =
|
|
743
|
+
useContext(ChatContext);
|
|
744
|
+
|
|
745
|
+
return (
|
|
746
|
+
<StyledGlowSmallButton
|
|
747
|
+
onClick={() => {
|
|
748
|
+
const next = !isOpen;
|
|
749
|
+
setIsOpen(next);
|
|
750
|
+
if (next) {
|
|
751
|
+
if (answer.length === 0) {
|
|
752
|
+
setAnswer([
|
|
753
|
+
{ text: "Hey there, how can I assist you?", answer: true },
|
|
754
|
+
]);
|
|
755
|
+
}
|
|
756
|
+
setTimeout(() => {
|
|
757
|
+
chatInputRef.current?.focus();
|
|
758
|
+
}, 350);
|
|
759
|
+
}
|
|
760
|
+
}}
|
|
761
|
+
aria-label="Ask AI Assistant"
|
|
762
|
+
$hasContent={isOpen}
|
|
763
|
+
type="button"
|
|
764
|
+
>
|
|
765
|
+
<span>
|
|
766
|
+
<Sparkles size={16} />
|
|
767
|
+
Ask AI
|
|
768
|
+
</span>
|
|
769
|
+
</StyledGlowSmallButton>
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
|
|
691
773
|
function Chat() {
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
774
|
+
const {
|
|
775
|
+
isOpen,
|
|
776
|
+
question,
|
|
777
|
+
setQuestion,
|
|
778
|
+
loading,
|
|
779
|
+
error,
|
|
780
|
+
answer,
|
|
781
|
+
ask,
|
|
782
|
+
clearChat,
|
|
783
|
+
chatInputRef,
|
|
784
|
+
} = useContext(ChatContext);
|
|
696
785
|
const endRef = useRef<HTMLDivElement | null>(null);
|
|
697
|
-
const abortRef = useRef<AbortController | null>(null);
|
|
698
|
-
const { isOpen, setIsOpen } = useContext(ChatContext);
|
|
699
786
|
|
|
700
787
|
useEffect(() => {
|
|
701
788
|
endRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
@@ -703,12 +790,111 @@ function Chat() {
|
|
|
703
790
|
|
|
704
791
|
useEffect(() => {
|
|
705
792
|
if (answer?.length > 0) {
|
|
706
|
-
|
|
707
|
-
"chat-bottom-input",
|
|
708
|
-
) as HTMLInputElement | null;
|
|
709
|
-
el?.focus();
|
|
793
|
+
chatInputRef.current?.focus();
|
|
710
794
|
}
|
|
711
|
-
}, [answer]);
|
|
795
|
+
}, [answer, chatInputRef]);
|
|
796
|
+
|
|
797
|
+
return (
|
|
798
|
+
<>
|
|
799
|
+
<StyledChat $isVisible={isOpen}>
|
|
800
|
+
<StyledChatTitle>
|
|
801
|
+
<StyledChatTitleIconWrapper>
|
|
802
|
+
<Sparkles />
|
|
803
|
+
<h3>AI Assistant</h3>
|
|
804
|
+
</StyledChatTitleIconWrapper>
|
|
805
|
+
<StyledChatCloseButton onClick={clearChat} aria-label="Close chat">
|
|
806
|
+
<X />
|
|
807
|
+
</StyledChatCloseButton>
|
|
808
|
+
</StyledChatTitle>
|
|
809
|
+
{answer &&
|
|
810
|
+
answer.map((a, i) => (
|
|
811
|
+
<StyledAnswer key={i} $isAnswer={a.answer ?? false}>
|
|
812
|
+
{a.answer && a.mdx ? (
|
|
813
|
+
<MDXRemote {...a.mdx} components={mdxComponents} />
|
|
814
|
+
) : (
|
|
815
|
+
a.text
|
|
816
|
+
)}
|
|
817
|
+
</StyledAnswer>
|
|
818
|
+
))}
|
|
819
|
+
{loading && (
|
|
820
|
+
<StyledLoading>
|
|
821
|
+
Answering<span>.</span>
|
|
822
|
+
<span>.</span>
|
|
823
|
+
<span>.</span>
|
|
824
|
+
</StyledLoading>
|
|
825
|
+
)}
|
|
826
|
+
{error && (
|
|
827
|
+
<StyledError>
|
|
828
|
+
<strong>Error:</strong> {error}
|
|
829
|
+
</StyledError>
|
|
830
|
+
)}
|
|
831
|
+
<div ref={endRef} />
|
|
832
|
+
</StyledChat>
|
|
833
|
+
|
|
834
|
+
<StyledChatForm onSubmit={ask} $isVisible={isOpen}>
|
|
835
|
+
<RainbowInput
|
|
836
|
+
id="chat-bottom-input"
|
|
837
|
+
inputRef={chatInputRef}
|
|
838
|
+
value={question}
|
|
839
|
+
onChange={(e) => setQuestion(e.target.value)}
|
|
840
|
+
placeholder="Ask AI Assistant..."
|
|
841
|
+
autoComplete="off"
|
|
842
|
+
aria-label="Ask a follow-up question"
|
|
843
|
+
/>
|
|
844
|
+
<StyledRainbowButton
|
|
845
|
+
type="submit"
|
|
846
|
+
disabled={loading || question.trim() === ""}
|
|
847
|
+
$hasContent={question.trim().length > 0}
|
|
848
|
+
aria-label={loading ? "Loading response" : "Submit question"}
|
|
849
|
+
>
|
|
850
|
+
{loading ? <LoaderPinwheel className="loading" /> : <ArrowUp />}
|
|
851
|
+
</StyledRainbowButton>
|
|
852
|
+
</StyledChatForm>
|
|
853
|
+
</>
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const ChatContext = createContext<{
|
|
858
|
+
isOpen: boolean;
|
|
859
|
+
setIsOpen: (isOpen: boolean) => void;
|
|
860
|
+
isChatActive: boolean;
|
|
861
|
+
question: string;
|
|
862
|
+
setQuestion: (q: string) => void;
|
|
863
|
+
loading: boolean;
|
|
864
|
+
error: string | null;
|
|
865
|
+
answer: Answer[];
|
|
866
|
+
setAnswer: (answers: Answer[]) => void;
|
|
867
|
+
ask: (e: React.FormEvent) => void;
|
|
868
|
+
clearChat: () => void;
|
|
869
|
+
chatInputRef: React.RefObject<HTMLInputElement | null>;
|
|
870
|
+
}>({
|
|
871
|
+
isOpen: false,
|
|
872
|
+
setIsOpen: () => {},
|
|
873
|
+
isChatActive: false,
|
|
874
|
+
question: "",
|
|
875
|
+
setQuestion: () => {},
|
|
876
|
+
loading: false,
|
|
877
|
+
error: null,
|
|
878
|
+
answer: [],
|
|
879
|
+
setAnswer: () => {},
|
|
880
|
+
ask: () => {},
|
|
881
|
+
clearChat: () => {},
|
|
882
|
+
chatInputRef: { current: null },
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
interface ChatContextProviderProps {
|
|
886
|
+
children: React.ReactNode;
|
|
887
|
+
isChatActive: boolean;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const ChtProvider = ({ children, isChatActive }: ChatContextProviderProps) => {
|
|
891
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
892
|
+
const [question, setQuestion] = useState("");
|
|
893
|
+
const [loading, setLoading] = useState(false);
|
|
894
|
+
const [error, setError] = useState<string | null>(null);
|
|
895
|
+
const [answer, setAnswer] = useState<Answer[]>([]);
|
|
896
|
+
const abortRef = useRef<AbortController | null>(null);
|
|
897
|
+
const chatInputRef = useRef<HTMLInputElement | null>(null);
|
|
712
898
|
|
|
713
899
|
async function ask(e: React.FormEvent) {
|
|
714
900
|
e.preventDefault();
|
|
@@ -730,7 +916,6 @@ function Chat() {
|
|
|
730
916
|
abortRef.current = controller;
|
|
731
917
|
|
|
732
918
|
try {
|
|
733
|
-
// Build conversation history from previous Q&A pairs
|
|
734
919
|
const history = answer
|
|
735
920
|
.filter((a) => a.text.trim() !== "")
|
|
736
921
|
.map((a) => ({
|
|
@@ -757,7 +942,6 @@ function Chat() {
|
|
|
757
942
|
throw new Error("Failed to get response reader");
|
|
758
943
|
}
|
|
759
944
|
|
|
760
|
-
// Add a placeholder for the streaming answer
|
|
761
945
|
const streamingAnswerIndex = mergedQuestions.length;
|
|
762
946
|
setAnswer([...mergedQuestions, { text: "", answer: true }]);
|
|
763
947
|
|
|
@@ -768,7 +952,6 @@ function Chat() {
|
|
|
768
952
|
|
|
769
953
|
buffer += decoder.decode(value, { stream: true });
|
|
770
954
|
const parts = buffer.split("\\n");
|
|
771
|
-
// Keep the last (potentially incomplete) part in the buffer
|
|
772
955
|
buffer = parts.pop() ?? "";
|
|
773
956
|
|
|
774
957
|
for (const line of parts) {
|
|
@@ -792,7 +975,6 @@ function Chat() {
|
|
|
792
975
|
throw new Error(data.data);
|
|
793
976
|
} else if (data.type === "done") {
|
|
794
977
|
const streamedContent = contentParts.join("");
|
|
795
|
-
// Finalize with MDX serialization
|
|
796
978
|
let mdxSource: MDXRemoteSerializeResult | null = null;
|
|
797
979
|
try {
|
|
798
980
|
mdxSource = await serialize(streamedContent, {
|
|
@@ -838,109 +1020,11 @@ function Chat() {
|
|
|
838
1020
|
}
|
|
839
1021
|
}
|
|
840
1022
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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
|
-
}
|
|
941
|
-
|
|
942
|
-
const ChtProvider = ({ children, isChatActive }: ChatContextProviderProps) => {
|
|
943
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
1023
|
+
function clearChat() {
|
|
1024
|
+
abortRef.current?.abort();
|
|
1025
|
+
setAnswer([]);
|
|
1026
|
+
setIsOpen(false);
|
|
1027
|
+
}
|
|
944
1028
|
|
|
945
1029
|
return (
|
|
946
1030
|
<ChatContext.Provider
|
|
@@ -948,6 +1032,15 @@ const ChtProvider = ({ children, isChatActive }: ChatContextProviderProps) => {
|
|
|
948
1032
|
isOpen,
|
|
949
1033
|
setIsOpen,
|
|
950
1034
|
isChatActive,
|
|
1035
|
+
question,
|
|
1036
|
+
setQuestion,
|
|
1037
|
+
loading,
|
|
1038
|
+
error,
|
|
1039
|
+
answer,
|
|
1040
|
+
setAnswer,
|
|
1041
|
+
ask,
|
|
1042
|
+
clearChat,
|
|
1043
|
+
chatInputRef,
|
|
951
1044
|
}}
|
|
952
1045
|
>
|
|
953
1046
|
{children}
|
|
@@ -955,5 +1048,5 @@ const ChtProvider = ({ children, isChatActive }: ChatContextProviderProps) => {
|
|
|
955
1048
|
);
|
|
956
1049
|
};
|
|
957
1050
|
|
|
958
|
-
export { Chat, ChtProvider, ChatContext };
|
|
1051
|
+
export { Chat, ChtProvider, ChatContext, ChatButtonCTA };
|
|
959
1052
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const docsSideBarTemplate = "\"use client\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { Space } from \"cherry-styled-components\";\nimport {\n StyledIndexSidebar,\n StyledIndexSidebarLink,\n StyledIndexSidebarLabel,\n} from \"@/components/layout/DocsComponents\";\n\
|
|
1
|
+
export declare const docsSideBarTemplate = "\"use client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Space } from \"cherry-styled-components\";\nimport {\n StyledIndexSidebar,\n StyledIndexSidebarLink,\n StyledIndexSidebarLabel,\n StyledIndexSidebarLi,\n} from \"@/components/layout/DocsComponents\";\n\ninterface Heading {\n id: string;\n text: string;\n level: number;\n}\n\nconst FALLBACK_OFFSET = 60;\n\nfunction getOffset() {\n const header = document.getElementById(\"header\");\n return (header ? header.offsetHeight : FALLBACK_OFFSET) + 20;\n}\n\nexport function DocsSideBar({ headings }: { headings: Heading[] }) {\n const [activeId, setActiveId] = useState<string>(\"\");\n const activeRef = useRef<HTMLLIElement>(null);\n\n const handleScroll = useCallback(() => {\n if (headings.length === 0) return;\n\n const headingElements = headings\n .map((heading) => document.getElementById(heading.id))\n .filter((el): el is HTMLElement => el !== null);\n\n if (headingElements.length === 0) return;\n\n const windowHeight = window.innerHeight;\n\n const visibleHeadings = headingElements.filter((element) => {\n const rect = element.getBoundingClientRect();\n const elementTop = rect.top;\n const elementBottom = rect.bottom;\n return elementTop < windowHeight && elementBottom > -50;\n });\n\n if (visibleHeadings.length > 0) {\n let closestHeading = visibleHeadings[0];\n let closestDistance = Math.abs(\n closestHeading.getBoundingClientRect().top - getOffset(),\n );\n for (const heading of visibleHeadings) {\n const distance = Math.abs(heading.getBoundingClientRect().top - getOffset());\n if (\n distance < closestDistance &&\n heading.getBoundingClientRect().top <= windowHeight * 0.3\n ) {\n closestDistance = distance;\n closestHeading = heading;\n }\n }\n setActiveId(closestHeading.id);\n return;\n }\n\n let currentActiveId = headings[0].id;\n for (const element of headingElements) {\n const rect = element.getBoundingClientRect();\n if (rect.top <= getOffset()) {\n currentActiveId = element.id;\n } else {\n break;\n }\n }\n setActiveId(currentActiveId);\n }, [headings]);\n\n useEffect(() => {\n if (headings.length === 0) return;\n // Set active heading from URL hash immediately on mount\n if (window.location.hash) {\n setActiveId(window.location.hash.slice(1));\n }\n // Run initial scroll check on next frame to avoid synchronous setState in effect\n const rafId = requestAnimationFrame(handleScroll);\n // Re-check after browser finishes scrolling to hash target on new tab/page load\n const delayedId = setTimeout(handleScroll, 300);\n let timeoutId: NodeJS.Timeout;\n const throttledHandleScroll = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(handleScroll, 50);\n };\n window.addEventListener(\"scroll\", throttledHandleScroll);\n window.addEventListener(\"resize\", handleScroll);\n return () => {\n window.removeEventListener(\"scroll\", throttledHandleScroll);\n window.removeEventListener(\"resize\", handleScroll);\n cancelAnimationFrame(rafId);\n clearTimeout(delayedId);\n clearTimeout(timeoutId);\n };\n }, [handleScroll, headings]);\n\n useEffect(() => {\n const el = activeRef.current;\n const container = el?.closest(\"[data-sidebar]\") as HTMLElement | null;\n if (!el || !container) return;\n const elRect = el.getBoundingClientRect();\n const cRect = container.getBoundingClientRect();\n const pad = 140;\n if (elRect.bottom + pad > cRect.bottom) {\n container.scrollBy({ top: elRect.bottom - cRect.bottom + pad, behavior: \"smooth\" });\n } else if (elRect.top - pad < cRect.top) {\n container.scrollBy({ top: elRect.top - cRect.top - pad, behavior: \"smooth\" });\n }\n }, [activeId]);\n\n const handleHeadingClick = (headingId: string) => {\n const element = document.getElementById(headingId);\n if (element) {\n const elementPosition =\n element.getBoundingClientRect().top + window.scrollY;\n window.scrollTo({ top: elementPosition - getOffset(), behavior: \"smooth\" });\n }\n };\n\n return (\n <StyledIndexSidebar data-sidebar>\n {headings?.length > 0 && (\n <>\n <StyledIndexSidebarLabel>On this page</StyledIndexSidebarLabel>\n <Space $size={15} />\n </>\n )}\n {headings.map((heading, index) => (\n <StyledIndexSidebarLi\n key={index}\n ref={activeId === heading.id ? activeRef : null}\n $isActive={activeId === heading.id}\n style={{ paddingLeft: `${(heading.level - 1) * 16}px` }}\n >\n <StyledIndexSidebarLink\n href={`#${heading.id}`}\n onClick={(e) => {\n e.preventDefault();\n handleHeadingClick(heading.id);\n }}\n $isActive={activeId === heading.id}\n >\n {heading.text}\n </StyledIndexSidebarLink>\n </StyledIndexSidebarLi>\n ))}\n </StyledIndexSidebar>\n );\n}\n";
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
export const docsSideBarTemplate = `"use client";
|
|
2
|
-
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Space } from "cherry-styled-components";
|
|
4
4
|
import {
|
|
5
5
|
StyledIndexSidebar,
|
|
6
6
|
StyledIndexSidebarLink,
|
|
7
7
|
StyledIndexSidebarLabel,
|
|
8
|
+
StyledIndexSidebarLi,
|
|
8
9
|
} from "@/components/layout/DocsComponents";
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
interface Heading {
|
|
11
12
|
id: string;
|
|
12
13
|
text: string;
|
|
13
14
|
level: number;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
+
const FALLBACK_OFFSET = 60;
|
|
18
|
+
|
|
19
|
+
function getOffset() {
|
|
20
|
+
const header = document.getElementById("header");
|
|
21
|
+
return (header ? header.offsetHeight : FALLBACK_OFFSET) + 20;
|
|
22
|
+
}
|
|
17
23
|
|
|
18
24
|
export function DocsSideBar({ headings }: { headings: Heading[] }) {
|
|
19
25
|
const [activeId, setActiveId] = useState<string>("");
|
|
26
|
+
const activeRef = useRef<HTMLLIElement>(null);
|
|
20
27
|
|
|
21
28
|
const handleScroll = useCallback(() => {
|
|
22
29
|
if (headings.length === 0) return;
|
|
@@ -39,10 +46,10 @@ export function DocsSideBar({ headings }: { headings: Heading[] }) {
|
|
|
39
46
|
if (visibleHeadings.length > 0) {
|
|
40
47
|
let closestHeading = visibleHeadings[0];
|
|
41
48
|
let closestDistance = Math.abs(
|
|
42
|
-
closestHeading.getBoundingClientRect().top -
|
|
49
|
+
closestHeading.getBoundingClientRect().top - getOffset(),
|
|
43
50
|
);
|
|
44
51
|
for (const heading of visibleHeadings) {
|
|
45
|
-
const distance = Math.abs(heading.getBoundingClientRect().top -
|
|
52
|
+
const distance = Math.abs(heading.getBoundingClientRect().top - getOffset());
|
|
46
53
|
if (
|
|
47
54
|
distance < closestDistance &&
|
|
48
55
|
heading.getBoundingClientRect().top <= windowHeight * 0.3
|
|
@@ -58,7 +65,7 @@ export function DocsSideBar({ headings }: { headings: Heading[] }) {
|
|
|
58
65
|
let currentActiveId = headings[0].id;
|
|
59
66
|
for (const element of headingElements) {
|
|
60
67
|
const rect = element.getBoundingClientRect();
|
|
61
|
-
if (rect.top <=
|
|
68
|
+
if (rect.top <= getOffset()) {
|
|
62
69
|
currentActiveId = element.id;
|
|
63
70
|
} else {
|
|
64
71
|
break;
|
|
@@ -93,26 +100,42 @@ export function DocsSideBar({ headings }: { headings: Heading[] }) {
|
|
|
93
100
|
};
|
|
94
101
|
}, [handleScroll, headings]);
|
|
95
102
|
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
const el = activeRef.current;
|
|
105
|
+
const container = el?.closest("[data-sidebar]") as HTMLElement | null;
|
|
106
|
+
if (!el || !container) return;
|
|
107
|
+
const elRect = el.getBoundingClientRect();
|
|
108
|
+
const cRect = container.getBoundingClientRect();
|
|
109
|
+
const pad = 140;
|
|
110
|
+
if (elRect.bottom + pad > cRect.bottom) {
|
|
111
|
+
container.scrollBy({ top: elRect.bottom - cRect.bottom + pad, behavior: "smooth" });
|
|
112
|
+
} else if (elRect.top - pad < cRect.top) {
|
|
113
|
+
container.scrollBy({ top: elRect.top - cRect.top - pad, behavior: "smooth" });
|
|
114
|
+
}
|
|
115
|
+
}, [activeId]);
|
|
116
|
+
|
|
96
117
|
const handleHeadingClick = (headingId: string) => {
|
|
97
118
|
const element = document.getElementById(headingId);
|
|
98
119
|
if (element) {
|
|
99
120
|
const elementPosition =
|
|
100
121
|
element.getBoundingClientRect().top + window.scrollY;
|
|
101
|
-
window.scrollTo({ top: elementPosition -
|
|
122
|
+
window.scrollTo({ top: elementPosition - getOffset(), behavior: "smooth" });
|
|
102
123
|
}
|
|
103
124
|
};
|
|
104
125
|
|
|
105
126
|
return (
|
|
106
|
-
<StyledIndexSidebar>
|
|
127
|
+
<StyledIndexSidebar data-sidebar>
|
|
107
128
|
{headings?.length > 0 && (
|
|
108
129
|
<>
|
|
109
130
|
<StyledIndexSidebarLabel>On this page</StyledIndexSidebarLabel>
|
|
110
|
-
<Space $size={
|
|
131
|
+
<Space $size={15} />
|
|
111
132
|
</>
|
|
112
133
|
)}
|
|
113
134
|
{headings.map((heading, index) => (
|
|
114
|
-
<
|
|
135
|
+
<StyledIndexSidebarLi
|
|
115
136
|
key={index}
|
|
137
|
+
ref={activeId === heading.id ? activeRef : null}
|
|
138
|
+
$isActive={activeId === heading.id}
|
|
116
139
|
style={{ paddingLeft: \`\${(heading.level - 1) * 16}px\` }}
|
|
117
140
|
>
|
|
118
141
|
<StyledIndexSidebarLink
|
|
@@ -125,7 +148,7 @@ export function DocsSideBar({ headings }: { headings: Heading[] }) {
|
|
|
125
148
|
>
|
|
126
149
|
{heading.text}
|
|
127
150
|
</StyledIndexSidebarLink>
|
|
128
|
-
</
|
|
151
|
+
</StyledIndexSidebarLi>
|
|
129
152
|
))}
|
|
130
153
|
</StyledIndexSidebar>
|
|
131
154
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const sideBarTemplate = "\"use client\";\nimport { useContext, useState } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Space } from \"cherry-styled-components\";\nimport {\n DocsSidebar,\n SectionBarContext,\n StyledSidebar,\n StyledSidebarList,\n StyledSidebarListItem,\n StyledStrong,\n StyledSidebarListItemLink,\n StyleMobileBar,\n StyledMobileBurger,\n} from \"@/components/layout/DocsComponents\";\n\ntype NavItem = {\n label: string;\n links: NavItemLink[];\n};\n\ntype NavItemLink = {\n slug: string;\n title: string;\n};\n\ninterface SideBarProps {\n result: NavItem[];\n}\n\nfunction SideBar({ result }: SideBarProps) {\n const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);\n const hasSectionBar = useContext(SectionBarContext);\n const pathname = usePathname();\n\n return (\n <DocsSidebar>\n <StyleMobileBar\n onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}\n $isActive={isMobileMenuOpen}\n >\n <StyledMobileBurger $isActive={isMobileMenuOpen} />\n </StyleMobileBar>\n\n <StyledSidebar\n $isActive={isMobileMenuOpen}\n $hasSectionBar={hasSectionBar}\n >\n {result &&\n result.map((item: NavItem, index: number) => {\n return (\n <StyledSidebarList key={index}>\n <StyledSidebarListItem>\n <StyledStrong>{item.label}</StyledStrong>{\" \"}\n </StyledSidebarListItem>\n <li>\n <Space $size={20} />\n </li>\n {item.links &&\n item.links.map((link: NavItemLink, indexChild: number) => {\n return (\n <StyledSidebarListItem key={indexChild}>\n <StyledSidebarListItemLink\n href={`/${link.slug}`}\n $isActive={pathname === `/${link.slug}`}\n onClick={() => setIsMobileMenuOpen(false)}\n >\n {link.title}\n </StyledSidebarListItemLink>\n </StyledSidebarListItem>\n );\n })}\n <Space $size={20} />\n </StyledSidebarList>\n );\n })}\n </StyledSidebar>\n </DocsSidebar>\n );\n}\n\nexport { SideBar };\n";
|
|
1
|
+
export declare const sideBarTemplate = "\"use client\";\nimport { useContext, useState, Suspense } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Flex, Space } from \"cherry-styled-components\";\nimport {\n DocsSidebar,\n SectionBarContext,\n StyledSidebar,\n StyledSidebarList,\n StyledSidebarListItem,\n StyledStrong,\n StyledSidebarListItemLink,\n StyleMobileBar,\n StyledMobileBurger,\n} from \"@/components/layout/DocsComponents\";\nimport {\n ToggleTheme,\n ToggleThemeLoading,\n} from \"@/components/layout/ThemeToggle\";\n\ntype NavItem = {\n label: string;\n links: NavItemLink[];\n};\n\ntype NavItemLink = {\n slug: string;\n title: string;\n};\n\ninterface SideBarProps {\n result: NavItem[];\n}\n\nfunction SideBar({ result }: SideBarProps) {\n const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);\n const hasSectionBar = useContext(SectionBarContext);\n const pathname = usePathname();\n\n return (\n <DocsSidebar>\n <StyleMobileBar\n onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}\n $isActive={isMobileMenuOpen}\n >\n <StyledMobileBurger $isActive={isMobileMenuOpen} />\n </StyleMobileBar>\n\n <StyledSidebar\n $isActive={isMobileMenuOpen}\n $hasSectionBar={hasSectionBar}\n >\n {result &&\n result.map((item: NavItem, index: number) => {\n return (\n <StyledSidebarList key={index}>\n <StyledSidebarListItem>\n <StyledStrong>{item.label}</StyledStrong>{\" \"}\n </StyledSidebarListItem>\n <li>\n <Space $size={20} />\n </li>\n {item.links &&\n item.links.map((link: NavItemLink, indexChild: number) => {\n return (\n <StyledSidebarListItem key={indexChild}>\n <StyledSidebarListItemLink\n href={`/${link.slug}`}\n $isActive={pathname === `/${link.slug}`}\n onClick={() => setIsMobileMenuOpen(false)}\n >\n {link.title}\n </StyledSidebarListItemLink>\n </StyledSidebarListItem>\n );\n })}\n <Space $size={20} />\n </StyledSidebarList>\n );\n })}\n <Space $xs={40} $lg={20} />\n <Flex $xsJustifyContent=\"flex-start\" $lgJustifyContent=\"flex-end\">\n <Suspense fallback={<ToggleThemeLoading />}>\n <ToggleTheme />\n </Suspense>\n </Flex>\n </StyledSidebar>\n </DocsSidebar>\n );\n}\n\nexport { SideBar };\n";
|