@codrstudio/openclaude-chat 0.1.0 → 0.1.9

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.
Files changed (62) hide show
  1. package/dist/components/StreamingIndicator.js +5 -5
  2. package/dist/display/DisplayReactRenderer.js +12 -12
  3. package/dist/display/react-sandbox/bootstrap.js +150 -150
  4. package/dist/styles.css +1 -2
  5. package/package.json +64 -61
  6. package/src/components/Chat.tsx +107 -107
  7. package/src/components/ErrorNote.tsx +35 -35
  8. package/src/components/LazyRender.tsx +42 -42
  9. package/src/components/Markdown.tsx +114 -114
  10. package/src/components/MessageBubble.tsx +107 -107
  11. package/src/components/MessageInput.tsx +421 -421
  12. package/src/components/MessageList.tsx +153 -153
  13. package/src/components/StreamingIndicator.tsx +19 -19
  14. package/src/display/AlertRenderer.tsx +23 -23
  15. package/src/display/CarouselRenderer.tsx +141 -141
  16. package/src/display/ChartRenderer.tsx +195 -195
  17. package/src/display/ChoiceButtonsRenderer.tsx +114 -114
  18. package/src/display/CodeBlockRenderer.tsx +49 -49
  19. package/src/display/ComparisonTableRenderer.tsx +132 -132
  20. package/src/display/DataTableRenderer.tsx +144 -144
  21. package/src/display/DisplayReactRenderer.tsx +269 -269
  22. package/src/display/FileCardRenderer.tsx +55 -55
  23. package/src/display/GalleryRenderer.tsx +65 -65
  24. package/src/display/ImageViewerRenderer.tsx +114 -114
  25. package/src/display/LinkPreviewRenderer.tsx +74 -74
  26. package/src/display/MapViewRenderer.tsx +75 -75
  27. package/src/display/MetricCardRenderer.tsx +29 -29
  28. package/src/display/PriceHighlightRenderer.tsx +62 -62
  29. package/src/display/ProductCardRenderer.tsx +112 -112
  30. package/src/display/ProgressStepsRenderer.tsx +59 -59
  31. package/src/display/SourcesListRenderer.tsx +47 -47
  32. package/src/display/SpreadsheetRenderer.tsx +86 -86
  33. package/src/display/StepTimelineRenderer.tsx +75 -75
  34. package/src/display/index.ts +21 -21
  35. package/src/display/react-sandbox/bootstrap.ts +155 -155
  36. package/src/display/registry.ts +84 -84
  37. package/src/display/sdk-types.ts +217 -217
  38. package/src/hooks/ChatProvider.tsx +21 -21
  39. package/src/hooks/useIsMobile.ts +15 -15
  40. package/src/hooks/useOpenClaudeChat.ts +476 -476
  41. package/src/index.ts +76 -76
  42. package/src/lib/utils.ts +6 -6
  43. package/src/parts/PartErrorBoundary.tsx +51 -51
  44. package/src/parts/PartRenderer.tsx +145 -145
  45. package/src/parts/ReasoningBlock.tsx +41 -41
  46. package/src/parts/ToolActivity.tsx +78 -78
  47. package/src/parts/ToolResult.tsx +79 -79
  48. package/src/styles.css +2 -2
  49. package/src/types.ts +41 -41
  50. package/src/ui/alert.tsx +77 -77
  51. package/src/ui/badge.tsx +36 -36
  52. package/src/ui/button.tsx +54 -54
  53. package/src/ui/card.tsx +68 -68
  54. package/src/ui/collapsible.tsx +7 -7
  55. package/src/ui/dialog.tsx +122 -122
  56. package/src/ui/dropdown-menu.tsx +76 -76
  57. package/src/ui/input.tsx +24 -24
  58. package/src/ui/progress.tsx +36 -36
  59. package/src/ui/scroll-area.tsx +48 -48
  60. package/src/ui/separator.tsx +31 -31
  61. package/src/ui/skeleton.tsx +9 -9
  62. package/src/ui/table.tsx +114 -114
@@ -1,107 +1,107 @@
1
- import React from "react";
2
- import { cn } from "../lib/utils.js";
3
- import { ChatProvider, useChatContext } from "../hooks/ChatProvider.js";
4
- import { MessageList } from "./MessageList.js";
5
- import { MessageInput } from "./MessageInput.js";
6
- import type { DisplayRendererMap } from "../display/registry.js";
7
- import type { Message } from "../types.js";
8
-
9
- export interface ChatProps {
10
- /** Base URL do servico openclaude (ex: http://localhost:9500/api/v1/ai). */
11
- endpoint: string;
12
- token?: string;
13
- /** Sessao live existente. Se omitida, o hook cria uma via POST /sessions. */
14
- sessionId?: string;
15
- initialMessages?: Message[];
16
- sessionOptions?: Record<string, unknown>;
17
- turnOptions?: Record<string, unknown>;
18
- displayRenderers?: DisplayRendererMap;
19
- placeholder?: string;
20
- header?: React.ReactNode;
21
- footer?: React.ReactNode;
22
- className?: string;
23
- enableAttachments?: boolean;
24
- enableVoice?: boolean;
25
- fetcher?: typeof fetch;
26
- emptyState?: React.ReactNode;
27
- }
28
-
29
- interface ChatContentProps {
30
- displayRenderers?: DisplayRendererMap;
31
- placeholder?: string;
32
- enableAttachments?: boolean;
33
- enableVoice?: boolean;
34
- emptyState?: React.ReactNode;
35
- }
36
-
37
- function ChatContent({ displayRenderers, placeholder, enableAttachments = true, enableVoice = true, emptyState }: ChatContentProps) {
38
- const { messages, input, setInput, handleSubmit, isLoading, stop, error, reload } = useChatContext();
39
-
40
- return (
41
- <>
42
- <MessageList
43
- messages={messages}
44
- isLoading={isLoading}
45
- displayRenderers={displayRenderers}
46
- error={error ?? undefined}
47
- onRetry={reload}
48
- emptyState={emptyState}
49
- />
50
- <div className="px-4 pb-4">
51
- <MessageInput
52
- input={input}
53
- setInput={setInput}
54
- handleSubmit={handleSubmit}
55
- isLoading={isLoading}
56
- stop={stop}
57
- placeholder={placeholder}
58
- enableAttachments={enableAttachments}
59
- enableVoice={enableVoice}
60
- />
61
- </div>
62
- </>
63
- );
64
- }
65
-
66
- export function Chat({
67
- endpoint,
68
- token,
69
- sessionId,
70
- initialMessages,
71
- sessionOptions,
72
- turnOptions,
73
- displayRenderers,
74
- placeholder,
75
- header,
76
- footer,
77
- className,
78
- enableAttachments,
79
- enableVoice,
80
- fetcher,
81
- emptyState,
82
- }: ChatProps) {
83
- return (
84
- <ChatProvider
85
- key={sessionId}
86
- endpoint={endpoint}
87
- token={token}
88
- sessionId={sessionId}
89
- initialMessages={initialMessages}
90
- sessionOptions={sessionOptions}
91
- turnOptions={turnOptions}
92
- fetcher={fetcher}
93
- >
94
- <div className={cn("flex flex-col h-full bg-background text-foreground", className)}>
95
- {header}
96
- <ChatContent
97
- displayRenderers={displayRenderers}
98
- placeholder={placeholder}
99
- enableAttachments={enableAttachments}
100
- enableVoice={enableVoice}
101
- emptyState={emptyState}
102
- />
103
- {footer}
104
- </div>
105
- </ChatProvider>
106
- );
107
- }
1
+ import React from "react";
2
+ import { cn } from "../lib/utils.js";
3
+ import { ChatProvider, useChatContext } from "../hooks/ChatProvider.js";
4
+ import { MessageList } from "./MessageList.js";
5
+ import { MessageInput } from "./MessageInput.js";
6
+ import type { DisplayRendererMap } from "../display/registry.js";
7
+ import type { Message } from "../types.js";
8
+
9
+ export interface ChatProps {
10
+ /** Base URL do servico openclaude (ex: http://localhost:9500/api/v1/ai). */
11
+ endpoint: string;
12
+ token?: string;
13
+ /** Sessao live existente. Se omitida, o hook cria uma via POST /sessions. */
14
+ sessionId?: string;
15
+ initialMessages?: Message[];
16
+ sessionOptions?: Record<string, unknown>;
17
+ turnOptions?: Record<string, unknown>;
18
+ displayRenderers?: DisplayRendererMap;
19
+ placeholder?: string;
20
+ header?: React.ReactNode;
21
+ footer?: React.ReactNode;
22
+ className?: string;
23
+ enableAttachments?: boolean;
24
+ enableVoice?: boolean;
25
+ fetcher?: typeof fetch;
26
+ emptyState?: React.ReactNode;
27
+ }
28
+
29
+ interface ChatContentProps {
30
+ displayRenderers?: DisplayRendererMap;
31
+ placeholder?: string;
32
+ enableAttachments?: boolean;
33
+ enableVoice?: boolean;
34
+ emptyState?: React.ReactNode;
35
+ }
36
+
37
+ function ChatContent({ displayRenderers, placeholder, enableAttachments = true, enableVoice = true, emptyState }: ChatContentProps) {
38
+ const { messages, input, setInput, handleSubmit, isLoading, stop, error, reload } = useChatContext();
39
+
40
+ return (
41
+ <>
42
+ <MessageList
43
+ messages={messages}
44
+ isLoading={isLoading}
45
+ displayRenderers={displayRenderers}
46
+ error={error ?? undefined}
47
+ onRetry={reload}
48
+ emptyState={emptyState}
49
+ />
50
+ <div className="px-4 pb-4">
51
+ <MessageInput
52
+ input={input}
53
+ setInput={setInput}
54
+ handleSubmit={handleSubmit}
55
+ isLoading={isLoading}
56
+ stop={stop}
57
+ placeholder={placeholder}
58
+ enableAttachments={enableAttachments}
59
+ enableVoice={enableVoice}
60
+ />
61
+ </div>
62
+ </>
63
+ );
64
+ }
65
+
66
+ export function Chat({
67
+ endpoint,
68
+ token,
69
+ sessionId,
70
+ initialMessages,
71
+ sessionOptions,
72
+ turnOptions,
73
+ displayRenderers,
74
+ placeholder,
75
+ header,
76
+ footer,
77
+ className,
78
+ enableAttachments,
79
+ enableVoice,
80
+ fetcher,
81
+ emptyState,
82
+ }: ChatProps) {
83
+ return (
84
+ <ChatProvider
85
+ key={sessionId}
86
+ endpoint={endpoint}
87
+ token={token}
88
+ sessionId={sessionId}
89
+ initialMessages={initialMessages}
90
+ sessionOptions={sessionOptions}
91
+ turnOptions={turnOptions}
92
+ fetcher={fetcher}
93
+ >
94
+ <div className={cn("flex flex-col h-full bg-background text-foreground", className)}>
95
+ {header}
96
+ <ChatContent
97
+ displayRenderers={displayRenderers}
98
+ placeholder={placeholder}
99
+ enableAttachments={enableAttachments}
100
+ enableVoice={enableVoice}
101
+ emptyState={emptyState}
102
+ />
103
+ {footer}
104
+ </div>
105
+ </ChatProvider>
106
+ );
107
+ }
@@ -1,35 +1,35 @@
1
- import { AlertTriangle, RotateCcw } from "lucide-react";
2
- import { cn } from "../lib/utils.js";
3
-
4
- export interface ErrorNoteProps {
5
- message?: string;
6
- onRetry?: () => void;
7
- className?: string;
8
- }
9
-
10
- export function ErrorNote({ message, onRetry, className }: ErrorNoteProps) {
11
- return (
12
- <div
13
- role="alert"
14
- className={cn(
15
- "flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive",
16
- className
17
- )}
18
- >
19
- <AlertTriangle className="size-4 shrink-0 mt-0.5" />
20
- <span className="flex-1 min-w-0 break-words">
21
- {message ?? "Falha ao processar mensagem"}
22
- </span>
23
- {onRetry && (
24
- <button
25
- type="button"
26
- onClick={onRetry}
27
- className="shrink-0 inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-destructive hover:bg-destructive/10 transition-colors"
28
- >
29
- <RotateCcw className="size-3.5" />
30
- Tentar novamente
31
- </button>
32
- )}
33
- </div>
34
- );
35
- }
1
+ import { AlertTriangle, RotateCcw } from "lucide-react";
2
+ import { cn } from "../lib/utils.js";
3
+
4
+ export interface ErrorNoteProps {
5
+ message?: string;
6
+ onRetry?: () => void;
7
+ className?: string;
8
+ }
9
+
10
+ export function ErrorNote({ message, onRetry, className }: ErrorNoteProps) {
11
+ return (
12
+ <div
13
+ role="alert"
14
+ className={cn(
15
+ "flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive",
16
+ className
17
+ )}
18
+ >
19
+ <AlertTriangle className="size-4 shrink-0 mt-0.5" />
20
+ <span className="flex-1 min-w-0 break-words">
21
+ {message ?? "Falha ao processar mensagem"}
22
+ </span>
23
+ {onRetry && (
24
+ <button
25
+ type="button"
26
+ onClick={onRetry}
27
+ className="shrink-0 inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-destructive hover:bg-destructive/10 transition-colors"
28
+ >
29
+ <RotateCcw className="size-3.5" />
30
+ Tentar novamente
31
+ </button>
32
+ )}
33
+ </div>
34
+ );
35
+ }
@@ -1,42 +1,42 @@
1
- import { useRef, useState, useEffect, type ReactNode } from "react";
2
-
3
- interface LazyRenderProps {
4
- children: ReactNode;
5
- minHeight?: number;
6
- rootMargin?: string;
7
- }
8
-
9
- export function LazyRender({ children, minHeight = 120, rootMargin = "200px" }: LazyRenderProps) {
10
- const ref = useRef<HTMLDivElement>(null);
11
- const [visible, setVisible] = useState(false);
12
-
13
- useEffect(() => {
14
- const el = ref.current;
15
- if (!el) return;
16
-
17
- const observer = new IntersectionObserver(
18
- ([entry]) => {
19
- if (entry.isIntersecting) {
20
- setVisible(true);
21
- observer.disconnect();
22
- }
23
- },
24
- { rootMargin }
25
- );
26
-
27
- observer.observe(el);
28
- return () => observer.disconnect();
29
- }, [rootMargin]);
30
-
31
- if (visible) return <>{children}</>;
32
-
33
- return (
34
- <div
35
- ref={ref}
36
- className="flex items-center justify-center text-muted-foreground text-xs rounded-md bg-muted/20 animate-pulse"
37
- style={{ minHeight }}
38
- >
39
- Carregando...
40
- </div>
41
- );
42
- }
1
+ import { useRef, useState, useEffect, type ReactNode } from "react";
2
+
3
+ interface LazyRenderProps {
4
+ children: ReactNode;
5
+ minHeight?: number;
6
+ rootMargin?: string;
7
+ }
8
+
9
+ export function LazyRender({ children, minHeight = 120, rootMargin = "200px" }: LazyRenderProps) {
10
+ const ref = useRef<HTMLDivElement>(null);
11
+ const [visible, setVisible] = useState(false);
12
+
13
+ useEffect(() => {
14
+ const el = ref.current;
15
+ if (!el) return;
16
+
17
+ const observer = new IntersectionObserver(
18
+ ([entry]) => {
19
+ if (entry.isIntersecting) {
20
+ setVisible(true);
21
+ observer.disconnect();
22
+ }
23
+ },
24
+ { rootMargin }
25
+ );
26
+
27
+ observer.observe(el);
28
+ return () => observer.disconnect();
29
+ }, [rootMargin]);
30
+
31
+ if (visible) return <>{children}</>;
32
+
33
+ return (
34
+ <div
35
+ ref={ref}
36
+ className="flex items-center justify-center text-muted-foreground text-xs rounded-md bg-muted/20 animate-pulse"
37
+ style={{ minHeight }}
38
+ >
39
+ Carregando...
40
+ </div>
41
+ );
42
+ }
@@ -1,114 +1,114 @@
1
- import { memo } from "react";
2
- import ReactMarkdown from "react-markdown";
3
- import remarkGfm from "remark-gfm";
4
- import rehypeHighlight from "rehype-highlight";
5
- import type { Components } from "react-markdown";
6
- import { cn } from "../lib/utils.js";
7
-
8
- const REMARK_PLUGINS = [remarkGfm];
9
- const REHYPE_PLUGINS = [rehypeHighlight];
10
-
11
- const components: Components = {
12
- h1({ children }) {
13
- return <h1 className="text-xl font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h1>;
14
- },
15
- h2({ children }) {
16
- return <h2 className="text-lg font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h2>;
17
- },
18
- h3({ children }) {
19
- return <h3 className="text-base font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h3>;
20
- },
21
- h4({ children }) {
22
- return <h4 className="font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h4>;
23
- },
24
- p({ children }) {
25
- return <p className="mb-4 last:mb-0">{children}</p>;
26
- },
27
- ul({ children }) {
28
- return <ul style={{ paddingLeft: "24px", marginTop: "8px", marginBottom: "8px", listStyleType: "disc" }}>{children}</ul>;
29
- },
30
- ol({ children }) {
31
- return <ol style={{ paddingLeft: "24px", marginTop: "8px", marginBottom: "8px", listStyleType: "decimal" }}>{children}</ol>;
32
- },
33
- li({ children }) {
34
- return <li style={{ marginTop: "4px", marginBottom: "4px" }}>{children}</li>;
35
- },
36
- hr() {
37
- return <hr className="border-border" style={{ marginTop: "16px", marginBottom: "16px" }} />;
38
- },
39
- pre({ children }) {
40
- return (
41
- <pre className="bg-muted border border-border rounded-md overflow-hidden" style={{ marginTop: "12px", marginBottom: "12px" }}>
42
- {children}
43
- </pre>
44
- );
45
- },
46
- code({ className, children }) {
47
- const isBlock = className?.startsWith("language-");
48
- if (isBlock) {
49
- return (
50
- <code className={cn("block p-4 overflow-x-auto font-mono text-sm", className)}>
51
- {children}
52
- </code>
53
- );
54
- }
55
- return (
56
- <code className="bg-muted border border-border rounded-sm px-1.5 py-0.5 font-mono text-sm">
57
- {children}
58
- </code>
59
- );
60
- },
61
- a({ href, children }) {
62
- return (
63
- <a href={href} target="_blank" rel="noopener noreferrer" className="text-primary underline underline-offset-2 hover:opacity-80">
64
- {children}
65
- </a>
66
- );
67
- },
68
- blockquote({ children }) {
69
- return (
70
- <blockquote className="border-l-[3px] border-border py-1 px-3 text-muted-foreground" style={{ marginTop: "12px", marginBottom: "12px" }}>
71
- {children}
72
- </blockquote>
73
- );
74
- },
75
- table({ children }) {
76
- return (
77
- <div className="overflow-x-auto">
78
- <table className="w-full border-collapse text-sm">{children}</table>
79
- </div>
80
- );
81
- },
82
- th({ children }) {
83
- return (
84
- <th className="border border-border px-3 py-1.5 text-left font-semibold bg-muted">
85
- {children}
86
- </th>
87
- );
88
- },
89
- td({ children }) {
90
- return (
91
- <td className="border border-border px-3 py-1.5 text-left">
92
- {children}
93
- </td>
94
- );
95
- },
96
- };
97
-
98
- interface MarkdownProps {
99
- children: string;
100
- }
101
-
102
- export const Markdown = memo(function Markdown({ children }: MarkdownProps) {
103
- return (
104
- <div className="text-foreground text-sm" style={{ lineHeight: "1.625em" }}>
105
- <ReactMarkdown
106
- remarkPlugins={REMARK_PLUGINS}
107
- rehypePlugins={REHYPE_PLUGINS}
108
- components={components}
109
- >
110
- {children}
111
- </ReactMarkdown>
112
- </div>
113
- );
114
- });
1
+ import { memo } from "react";
2
+ import ReactMarkdown from "react-markdown";
3
+ import remarkGfm from "remark-gfm";
4
+ import rehypeHighlight from "rehype-highlight";
5
+ import type { Components } from "react-markdown";
6
+ import { cn } from "../lib/utils.js";
7
+
8
+ const REMARK_PLUGINS = [remarkGfm];
9
+ const REHYPE_PLUGINS = [rehypeHighlight];
10
+
11
+ const components: Components = {
12
+ h1({ children }) {
13
+ return <h1 className="text-xl font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h1>;
14
+ },
15
+ h2({ children }) {
16
+ return <h2 className="text-lg font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h2>;
17
+ },
18
+ h3({ children }) {
19
+ return <h3 className="text-base font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h3>;
20
+ },
21
+ h4({ children }) {
22
+ return <h4 className="font-semibold" style={{ marginTop: "20px", marginBottom: "8px" }}>{children}</h4>;
23
+ },
24
+ p({ children }) {
25
+ return <p className="mb-4 last:mb-0">{children}</p>;
26
+ },
27
+ ul({ children }) {
28
+ return <ul style={{ paddingLeft: "24px", marginTop: "8px", marginBottom: "8px", listStyleType: "disc" }}>{children}</ul>;
29
+ },
30
+ ol({ children }) {
31
+ return <ol style={{ paddingLeft: "24px", marginTop: "8px", marginBottom: "8px", listStyleType: "decimal" }}>{children}</ol>;
32
+ },
33
+ li({ children }) {
34
+ return <li style={{ marginTop: "4px", marginBottom: "4px" }}>{children}</li>;
35
+ },
36
+ hr() {
37
+ return <hr className="border-border" style={{ marginTop: "16px", marginBottom: "16px" }} />;
38
+ },
39
+ pre({ children }) {
40
+ return (
41
+ <pre className="bg-muted border border-border rounded-md overflow-hidden" style={{ marginTop: "12px", marginBottom: "12px" }}>
42
+ {children}
43
+ </pre>
44
+ );
45
+ },
46
+ code({ className, children }) {
47
+ const isBlock = className?.startsWith("language-");
48
+ if (isBlock) {
49
+ return (
50
+ <code className={cn("block p-4 overflow-x-auto font-mono text-sm", className)}>
51
+ {children}
52
+ </code>
53
+ );
54
+ }
55
+ return (
56
+ <code className="bg-muted border border-border rounded-sm px-1.5 py-0.5 font-mono text-sm">
57
+ {children}
58
+ </code>
59
+ );
60
+ },
61
+ a({ href, children }) {
62
+ return (
63
+ <a href={href} target="_blank" rel="noopener noreferrer" className="text-primary underline underline-offset-2 hover:opacity-80">
64
+ {children}
65
+ </a>
66
+ );
67
+ },
68
+ blockquote({ children }) {
69
+ return (
70
+ <blockquote className="border-l-[3px] border-border py-1 px-3 text-muted-foreground" style={{ marginTop: "12px", marginBottom: "12px" }}>
71
+ {children}
72
+ </blockquote>
73
+ );
74
+ },
75
+ table({ children }) {
76
+ return (
77
+ <div className="overflow-x-auto">
78
+ <table className="w-full border-collapse text-sm">{children}</table>
79
+ </div>
80
+ );
81
+ },
82
+ th({ children }) {
83
+ return (
84
+ <th className="border border-border px-3 py-1.5 text-left font-semibold bg-muted">
85
+ {children}
86
+ </th>
87
+ );
88
+ },
89
+ td({ children }) {
90
+ return (
91
+ <td className="border border-border px-3 py-1.5 text-left">
92
+ {children}
93
+ </td>
94
+ );
95
+ },
96
+ };
97
+
98
+ interface MarkdownProps {
99
+ children: string;
100
+ }
101
+
102
+ export const Markdown = memo(function Markdown({ children }: MarkdownProps) {
103
+ return (
104
+ <div className="text-foreground text-sm" style={{ lineHeight: "1.625em" }}>
105
+ <ReactMarkdown
106
+ remarkPlugins={REMARK_PLUGINS}
107
+ rehypePlugins={REHYPE_PLUGINS}
108
+ components={components}
109
+ >
110
+ {children}
111
+ </ReactMarkdown>
112
+ </div>
113
+ );
114
+ });