@dust-tt/sparkle 0.2.637 → 0.2.638-rc-1

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 (65) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/esm/components/LoadingBlock.js +1 -1
  3. package/dist/esm/components/LoadingBlock.js.map +1 -1
  4. package/dist/esm/components/markdown/Markdown.d.ts +14 -0
  5. package/dist/esm/components/markdown/Markdown.d.ts.map +1 -1
  6. package/dist/esm/components/markdown/Markdown.js +18 -7
  7. package/dist/esm/components/markdown/Markdown.js.map +1 -1
  8. package/dist/esm/components/streaming/BlockStreamer.d.ts +8 -0
  9. package/dist/esm/components/streaming/BlockStreamer.d.ts.map +1 -0
  10. package/dist/esm/components/streaming/BlockStreamer.js +40 -0
  11. package/dist/esm/components/streaming/BlockStreamer.js.map +1 -0
  12. package/dist/esm/components/streaming/StreamingListItem.d.ts +14 -0
  13. package/dist/esm/components/streaming/StreamingListItem.d.ts.map +1 -0
  14. package/dist/esm/components/streaming/StreamingListItem.js +93 -0
  15. package/dist/esm/components/streaming/StreamingListItem.js.map +1 -0
  16. package/dist/esm/components/streaming/StreamingMarkdown.d.ts +5 -0
  17. package/dist/esm/components/streaming/StreamingMarkdown.d.ts.map +1 -0
  18. package/dist/esm/components/streaming/StreamingMarkdown.js +80 -0
  19. package/dist/esm/components/streaming/StreamingMarkdown.js.map +1 -0
  20. package/dist/esm/components/streaming/StreamingParagraph.d.ts +14 -0
  21. package/dist/esm/components/streaming/StreamingParagraph.d.ts.map +1 -0
  22. package/dist/esm/components/streaming/StreamingParagraph.js +41 -0
  23. package/dist/esm/components/streaming/StreamingParagraph.js.map +1 -0
  24. package/dist/esm/components/streaming/index.d.ts +3 -0
  25. package/dist/esm/components/streaming/index.d.ts.map +1 -0
  26. package/dist/esm/components/streaming/index.js +2 -0
  27. package/dist/esm/components/streaming/index.js.map +1 -0
  28. package/dist/esm/components/streaming/markdownComponents.d.ts +11 -0
  29. package/dist/esm/components/streaming/markdownComponents.d.ts.map +1 -0
  30. package/dist/esm/components/streaming/markdownComponents.js +85 -0
  31. package/dist/esm/components/streaming/markdownComponents.js.map +1 -0
  32. package/dist/esm/components/streaming/types.d.ts +70 -0
  33. package/dist/esm/components/streaming/types.d.ts.map +1 -0
  34. package/dist/esm/components/streaming/types.js +10 -0
  35. package/dist/esm/components/streaming/types.js.map +1 -0
  36. package/dist/esm/components/streaming/utils.d.ts +18 -0
  37. package/dist/esm/components/streaming/utils.d.ts.map +1 -0
  38. package/dist/esm/components/streaming/utils.js +97 -0
  39. package/dist/esm/components/streaming/utils.js.map +1 -0
  40. package/dist/esm/index.d.ts +1 -0
  41. package/dist/esm/index.d.ts.map +1 -1
  42. package/dist/esm/index.js +1 -0
  43. package/dist/esm/index.js.map +1 -1
  44. package/dist/esm/stories/StreamingMarkdown.stories.d.ts +12 -0
  45. package/dist/esm/stories/StreamingMarkdown.stories.d.ts.map +1 -0
  46. package/dist/esm/stories/StreamingMarkdown.stories.js +620 -0
  47. package/dist/esm/stories/StreamingMarkdown.stories.js.map +1 -0
  48. package/dist/esm/styles/global.css +43 -0
  49. package/dist/esm/styles/tailwind.css +43 -0
  50. package/dist/sparkle.css +56 -0
  51. package/package.json +2 -1
  52. package/src/components/LoadingBlock.tsx +1 -1
  53. package/src/components/markdown/Markdown.tsx +35 -12
  54. package/src/components/streaming/BlockStreamer.tsx +61 -0
  55. package/src/components/streaming/StreamingListItem.tsx +176 -0
  56. package/src/components/streaming/StreamingMarkdown.tsx +126 -0
  57. package/src/components/streaming/StreamingParagraph.tsx +104 -0
  58. package/src/components/streaming/index.ts +2 -0
  59. package/src/components/streaming/markdownComponents.tsx +270 -0
  60. package/src/components/streaming/types.ts +72 -0
  61. package/src/components/streaming/utils.ts +126 -0
  62. package/src/index.ts +1 -0
  63. package/src/stories/StreamingMarkdown.stories.tsx +1454 -0
  64. package/src/styles/global.css +43 -0
  65. package/src/styles/tailwind.css +43 -0
@@ -0,0 +1,104 @@
1
+ import React from "react";
2
+
3
+ import { ParagraphBlock } from "@sparkle/components/markdown/ParagraphBlock";
4
+ import { BlockStreamer } from "@sparkle/components/streaming/BlockStreamer";
5
+ import type { ComponentProps } from "@sparkle/components/streaming/types";
6
+ import {
7
+ getElementProps,
8
+ isReactElementWithProps,
9
+ keyOf,
10
+ } from "@sparkle/components/streaming/utils";
11
+
12
+ interface StreamingParagraphProps extends ComponentProps {
13
+ isStreaming: boolean;
14
+ animationName: string;
15
+ animationDuration: string;
16
+ animationTimingFunction: string;
17
+ textColor?: string;
18
+ forcedTextSize?: string;
19
+ sizes: Record<string, string>;
20
+ }
21
+
22
+ export function StreamingParagraph({
23
+ node,
24
+ isStreaming,
25
+ animationName,
26
+ animationDuration,
27
+ animationTimingFunction,
28
+ textColor,
29
+ forcedTextSize,
30
+ sizes,
31
+ ...props
32
+ }: StreamingParagraphProps) {
33
+ const bk = keyOf(node, "p");
34
+
35
+ // Process children to handle mixed content (text, bold, italic, etc.).
36
+ const processChildren = (children: React.ReactNode): React.ReactNode => {
37
+ if (!children) {return null;}
38
+
39
+ // If it's just a string, stream it.
40
+ if (typeof children === "string") {
41
+ // return createBlockStreamer(children);
42
+ return (
43
+ <BlockStreamer
44
+ text={children}
45
+ animate={isStreaming}
46
+ animationName={animationName}
47
+ animationDuration={animationDuration}
48
+ animationTimingFunction={animationTimingFunction}
49
+ />
50
+ );
51
+ }
52
+
53
+ // If it's an array, process each child.
54
+ if (Array.isArray(children)) {
55
+ return children.map((child, idx) => {
56
+ if (typeof child === "string") {
57
+ return (
58
+ <BlockStreamer
59
+ key={idx}
60
+ text={child}
61
+ animate={isStreaming}
62
+ animationName={animationName}
63
+ animationDuration={animationDuration}
64
+ animationTimingFunction={animationTimingFunction}
65
+ />
66
+ );
67
+ }
68
+
69
+ // React elements (strong, em, code, etc.) - recursively process their children.
70
+ if (isReactElementWithProps(child)) {
71
+ const childProps = getElementProps(child);
72
+ return React.cloneElement(
73
+ child,
74
+ { ...childProps, key: idx },
75
+ processChildren(childProps.children)
76
+ );
77
+ }
78
+
79
+ return child;
80
+ });
81
+ }
82
+
83
+ if (isReactElementWithProps(children)) {
84
+ const childProps = getElementProps(children);
85
+ return React.cloneElement(
86
+ children,
87
+ childProps,
88
+ processChildren(childProps.children)
89
+ );
90
+ }
91
+
92
+ return children;
93
+ };
94
+
95
+ return (
96
+ <ParagraphBlock
97
+ key={bk}
98
+ textColor={textColor || ""}
99
+ textSize={forcedTextSize || sizes.p}
100
+ >
101
+ {processChildren(props.children)}
102
+ </ParagraphBlock>
103
+ );
104
+ }
@@ -0,0 +1,2 @@
1
+ export { StreamingMarkdown } from "./StreamingMarkdown";
2
+ export type { StreamingMarkdownProps } from "./types";
@@ -0,0 +1,270 @@
1
+ import React from "react";
2
+ import type { Components } from "react-markdown";
3
+
4
+ import { BlockquoteBlock } from "@sparkle/components/markdown/BlockquoteBlock";
5
+ import { CodeBlockWithExtendedSupport } from "@sparkle/components/markdown/CodeBlockWithExtendedSupport";
6
+ import { OlBlock, UlBlock } from "@sparkle/components/markdown/List";
7
+ import {
8
+ HrBlock,
9
+ ImgBlock,
10
+ Input,
11
+ LinkBlock,
12
+ StrongBlock,
13
+ } from "@sparkle/components/markdown/Markdown";
14
+ import { PreBlock } from "@sparkle/components/markdown/PreBlock";
15
+ import {
16
+ TableBlock,
17
+ TableBodyBlock,
18
+ TableDataBlock,
19
+ TableHeadBlock,
20
+ TableHeaderBlock,
21
+ } from "@sparkle/components/markdown/TableBlock";
22
+ import { StreamingParagraph } from "@sparkle/components/streaming/StreamingParagraph";
23
+ import { cn } from "@sparkle/lib/utils";
24
+
25
+ import { BlockStreamer } from "./BlockStreamer";
26
+ import { StreamingListItem } from "./StreamingListItem";
27
+ import type { ComponentProps, ProcessChildrenContext } from "./types";
28
+ import { MARKDOWN_TEXT_SIZES } from "./types";
29
+ import { flatten, keyOf } from "./utils";
30
+
31
+ interface CreateMarkdownComponentsParams {
32
+ textColor: string;
33
+ forcedTextSize?: string;
34
+ additionalMarkdownComponents?: Components;
35
+ processContext: ProcessChildrenContext;
36
+ }
37
+
38
+ export function createMarkdownComponents({
39
+ textColor,
40
+ forcedTextSize,
41
+ additionalMarkdownComponents,
42
+ processContext,
43
+ }: CreateMarkdownComponentsParams): Components {
44
+ const {
45
+ isStreaming,
46
+ animationName,
47
+ animationDuration,
48
+ animationTimingFunction,
49
+ } = processContext;
50
+ const sizes = MARKDOWN_TEXT_SIZES;
51
+
52
+ return {
53
+ // Let parent blocks handle animation to keep identity stable
54
+ text: ({ ...props }: ComponentProps) => props.children,
55
+
56
+ h1: ({ node, ...props }: ComponentProps) => {
57
+ const text = flatten(props.children);
58
+ return (
59
+ <h1
60
+ key={keyOf(node, "h1")}
61
+ className={cn("s-pb-2 s-pt-4", forcedTextSize || sizes.h1, textColor)}
62
+ {...props}
63
+ >
64
+ <BlockStreamer
65
+ text={text}
66
+ animate={isStreaming}
67
+ animationName={animationName}
68
+ animationDuration={animationDuration}
69
+ animationTimingFunction={animationTimingFunction}
70
+ />
71
+ </h1>
72
+ );
73
+ },
74
+
75
+ h2: ({ node, ...props }: ComponentProps) => {
76
+ const text = flatten(props.children);
77
+ return (
78
+ <h2
79
+ key={keyOf(node, "h2")}
80
+ className={cn("s-pb-2 s-pt-4", forcedTextSize || sizes.h2, textColor)}
81
+ {...props}
82
+ >
83
+ <BlockStreamer
84
+ text={text}
85
+ animate={isStreaming}
86
+ animationName={animationName}
87
+ animationDuration={animationDuration}
88
+ animationTimingFunction={animationTimingFunction}
89
+ />
90
+ </h2>
91
+ );
92
+ },
93
+
94
+ h3: ({ node, ...props }: ComponentProps) => {
95
+ const text = flatten(props.children);
96
+ return (
97
+ <h3
98
+ key={keyOf(node, "h3")}
99
+ className={cn("s-pb-2 s-pt-4", forcedTextSize || sizes.h3, textColor)}
100
+ {...props}
101
+ >
102
+ <BlockStreamer
103
+ text={text}
104
+ animate={isStreaming}
105
+ animationName={animationName}
106
+ animationDuration={animationDuration}
107
+ animationTimingFunction={animationTimingFunction}
108
+ />
109
+ </h3>
110
+ );
111
+ },
112
+
113
+ h4: ({ node, ...props }: ComponentProps) => {
114
+ const text = flatten(props.children);
115
+ return (
116
+ <h4
117
+ key={keyOf(node, "h4")}
118
+ className={cn("s-pb-2 s-pt-3", forcedTextSize || sizes.h4, textColor)}
119
+ {...props}
120
+ >
121
+ <BlockStreamer
122
+ text={text}
123
+ animate={isStreaming}
124
+ animationName={animationName}
125
+ animationDuration={animationDuration}
126
+ animationTimingFunction={animationTimingFunction}
127
+ />
128
+ </h4>
129
+ );
130
+ },
131
+
132
+ h5: ({ node, ...props }: ComponentProps) => {
133
+ const text = flatten(props.children);
134
+ return (
135
+ <h5
136
+ key={keyOf(node, "h5")}
137
+ className={cn(
138
+ "s-pb-1.5 s-pt-2.5",
139
+ forcedTextSize || sizes.h5,
140
+ textColor
141
+ )}
142
+ {...props}
143
+ >
144
+ <BlockStreamer
145
+ text={text}
146
+ animate={isStreaming}
147
+ animationName={animationName}
148
+ animationDuration={animationDuration}
149
+ animationTimingFunction={animationTimingFunction}
150
+ />
151
+ </h5>
152
+ );
153
+ },
154
+
155
+ h6: ({ node, ...props }: ComponentProps) => {
156
+ const text = flatten(props.children);
157
+ return (
158
+ <h6
159
+ key={keyOf(node, "h6")}
160
+ className={cn(
161
+ "s-pb-1.5 s-pt-2.5",
162
+ forcedTextSize || sizes.h6,
163
+ textColor
164
+ )}
165
+ {...props}
166
+ >
167
+ <BlockStreamer
168
+ text={text}
169
+ animate={isStreaming}
170
+ animationName={animationName}
171
+ animationDuration={animationDuration}
172
+ animationTimingFunction={animationTimingFunction}
173
+ />
174
+ </h6>
175
+ );
176
+ },
177
+
178
+ p: ({ ...props }: ComponentProps) => (
179
+ <StreamingParagraph
180
+ {...props}
181
+ isStreaming={isStreaming}
182
+ animationName={animationName}
183
+ animationDuration={animationDuration}
184
+ animationTimingFunction={animationTimingFunction}
185
+ textColor={textColor}
186
+ forcedTextSize={forcedTextSize}
187
+ sizes={sizes}
188
+ />
189
+ ),
190
+ blockquote: ({ ...props }: ComponentProps) => (
191
+ <BlockquoteBlock>{props.children}</BlockquoteBlock>
192
+ ),
193
+
194
+ ul: ({ ...props }: ComponentProps) => (
195
+ <UlBlock textColor={textColor} textSize={forcedTextSize || sizes.p}>
196
+ {props.children}
197
+ </UlBlock>
198
+ ),
199
+
200
+ ol: ({ start, ...props }: ComponentProps & { start?: number }) => (
201
+ <OlBlock
202
+ start={start}
203
+ textColor={textColor}
204
+ textSize={forcedTextSize || sizes.p}
205
+ >
206
+ {props.children}
207
+ </OlBlock>
208
+ ),
209
+
210
+ li: (
211
+ props: ComponentProps & {
212
+ ordered?: boolean;
213
+ index?: number;
214
+ checked?: boolean | null;
215
+ }
216
+ ) => (
217
+ <StreamingListItem
218
+ {...props}
219
+ isStreaming={isStreaming}
220
+ animationName={animationName}
221
+ animationDuration={animationDuration}
222
+ animationTimingFunction={animationTimingFunction}
223
+ textColor={textColor}
224
+ forcedTextSize={forcedTextSize}
225
+ sizes={sizes}
226
+ />
227
+ ),
228
+
229
+ a: ({ ...props }: ComponentProps & { href?: string }) => (
230
+ <LinkBlock href={props.href}>{props.children}</LinkBlock>
231
+ ),
232
+
233
+ strong: ({ ...props }: ComponentProps) => (
234
+ <StrongBlock>{props.children}</StrongBlock>
235
+ ),
236
+
237
+ em: ({ ...props }: ComponentProps) => <em {...props}>{props.children}</em>,
238
+
239
+ pre: ({ children }: { children?: React.ReactNode }) => (
240
+ <PreBlock>{children}</PreBlock>
241
+ ),
242
+
243
+ code: CodeBlockWithExtendedSupport,
244
+
245
+ hr: HrBlock,
246
+
247
+ img: ImgBlock,
248
+
249
+ table: TableBlock,
250
+ thead: TableHeadBlock,
251
+ tbody: TableBodyBlock,
252
+ th: TableHeaderBlock,
253
+ td: ({ ...props }: ComponentProps) => {
254
+ const text = flatten(props.children);
255
+ return (
256
+ <TableDataBlock>
257
+ <BlockStreamer
258
+ text={text}
259
+ animate={isStreaming}
260
+ animationName={animationName}
261
+ animationDuration={animationDuration}
262
+ animationTimingFunction={animationTimingFunction}
263
+ />
264
+ </TableDataBlock>
265
+ );
266
+ },
267
+ input: Input,
268
+ ...additionalMarkdownComponents,
269
+ };
270
+ }
@@ -0,0 +1,72 @@
1
+ import type React from "react";
2
+ import type { Components } from "react-markdown";
3
+ import type { PluggableList } from "react-markdown/lib/react-markdown";
4
+
5
+ export interface StreamingMarkdownProps {
6
+ content: string;
7
+ animationName?: string;
8
+ animationDuration?: string;
9
+ animationTimingFunction?: string;
10
+ animationCurve?: "linear" | "accelerate" | "accelerate-fast" | "custom";
11
+ animationSteps?: Array<{ percent: number; opacity: number }>;
12
+ codeStyle?: React.CSSProperties;
13
+ isStreaming?: boolean;
14
+ isLastMessage?: boolean;
15
+ textColor?: string;
16
+ forcedTextSize?: string;
17
+ additionalMarkdownComponents?: Components;
18
+ additionalMarkdownPlugins?: PluggableList;
19
+ }
20
+
21
+ export interface DirectiveNode {
22
+ type: string;
23
+ name?: string;
24
+ value?: string;
25
+ children?: Array<{ value?: string }>;
26
+ }
27
+
28
+ export interface MarkdownNode {
29
+ position?: {
30
+ start?: {
31
+ line: number;
32
+ column: number;
33
+ };
34
+ };
35
+ }
36
+
37
+ export interface MdastNode {
38
+ type: string;
39
+ value?: string;
40
+ children?: MdastNode[];
41
+ }
42
+
43
+ export type ComponentProps = {
44
+ node?: MarkdownNode;
45
+ children?: React.ReactNode;
46
+ [key: string]: unknown;
47
+ };
48
+
49
+ export interface BlockStreamerProps {
50
+ text: string;
51
+ animate?: boolean;
52
+ animationName: string;
53
+ animationDuration: string;
54
+ animationTimingFunction: string;
55
+ }
56
+
57
+ export interface ProcessChildrenContext {
58
+ isStreaming: boolean;
59
+ animationName: string;
60
+ animationDuration: string;
61
+ animationTimingFunction: string;
62
+ }
63
+
64
+ export const MARKDOWN_TEXT_SIZES = {
65
+ p: "s-copy-sm @sm:s-text-base @sm:s-leading-7",
66
+ h1: "s-heading-2xl",
67
+ h2: "s-heading-xl",
68
+ h3: "s-heading-lg",
69
+ h4: "s-text-base s-font-semibold",
70
+ h5: "s-text-sm s-font-semibold",
71
+ h6: "s-text-sm s-font-regular s-italic",
72
+ } as const;
@@ -0,0 +1,126 @@
1
+ import type { Root } from "hast";
2
+ import React from "react";
3
+ import { visit } from "unist-util-visit";
4
+
5
+ import { CodeBlockWithExtendedSupport } from "@sparkle/components/markdown/CodeBlockWithExtendedSupport";
6
+ import { OlBlock, UlBlock } from "@sparkle/components/markdown/List";
7
+ import { PreBlock } from "@sparkle/components/markdown/PreBlock";
8
+ import {
9
+ TableBlock,
10
+ TableBodyBlock,
11
+ TableDataBlock,
12
+ TableHeadBlock,
13
+ TableHeaderBlock,
14
+ } from "@sparkle/components/markdown/TableBlock";
15
+
16
+ import type { DirectiveNode, MarkdownNode, MdastNode } from "./types";
17
+
18
+ export function isDirectiveNode(node: unknown): node is DirectiveNode {
19
+ return (
20
+ typeof node === "object" &&
21
+ node !== null &&
22
+ "type" in node &&
23
+ typeof (node as Record<string, unknown>).type === "string"
24
+ );
25
+ }
26
+
27
+ export function isReactElementWithProps(
28
+ el: unknown
29
+ ): el is React.ReactElement<{ children?: React.ReactNode }> {
30
+ return React.isValidElement(el);
31
+ }
32
+
33
+ export function getElementProps(
34
+ el: React.ReactElement
35
+ ): { children?: React.ReactNode } & Record<string, unknown> {
36
+ return el.props as { children?: React.ReactNode } & Record<string, unknown>;
37
+ }
38
+
39
+ export function isListElement(el: unknown): boolean {
40
+ return (
41
+ React.isValidElement(el) &&
42
+ (el.type === "ul" ||
43
+ el.type === "ol" ||
44
+ el.type === UlBlock ||
45
+ el.type === OlBlock)
46
+ );
47
+ }
48
+
49
+ export function isTableElement(el: unknown): boolean {
50
+ return (
51
+ React.isValidElement(el) &&
52
+ (el.type === "table" ||
53
+ el.type === TableBlock ||
54
+ el.type === "thead" ||
55
+ el.type === TableHeadBlock ||
56
+ el.type === "tbody" ||
57
+ el.type === TableBodyBlock ||
58
+ el.type === "th" ||
59
+ el.type === TableHeaderBlock ||
60
+ el.type === "td" ||
61
+ el.type === TableDataBlock ||
62
+ el.type === "tr")
63
+ );
64
+ }
65
+
66
+ export function isCodeElement(el: unknown): boolean {
67
+ return (
68
+ React.isValidElement(el) &&
69
+ (el.type === "code" ||
70
+ el.type === CodeBlockWithExtendedSupport ||
71
+ el.type === "pre" ||
72
+ el.type === PreBlock)
73
+ );
74
+ }
75
+
76
+ export function showUnsupportedDirective() {
77
+ return (tree: Root) => {
78
+ visit(tree, ["textDirective"], (node: unknown) => {
79
+ if (isDirectiveNode(node) && node.type === "textDirective") {
80
+ node.type = "text";
81
+ node.value = `:${node.name}${
82
+ node.children ? node.children.map((c) => c.value).join("") : ""
83
+ }`;
84
+ }
85
+ });
86
+ };
87
+ }
88
+
89
+ export function keyOf(
90
+ node: MarkdownNode | undefined,
91
+ fallback: string
92
+ ): string {
93
+ return node?.position?.start
94
+ ? `${node.position.start.line}:${node.position.start.column}`
95
+ : fallback;
96
+ }
97
+
98
+ export function flatten(child: React.ReactNode): string {
99
+ if (typeof child === "string") {return child;}
100
+ if (Array.isArray(child)) {return child.map((c) => flatten(c)).join("");}
101
+ if (isReactElementWithProps(child)) {
102
+ const props = getElementProps(child);
103
+ return flatten(props.children);
104
+ }
105
+ return "";
106
+ }
107
+
108
+ export function mdToText(n: MdastNode | undefined): string {
109
+ if (!n) {return "";}
110
+ switch (n.type) {
111
+ case "text":
112
+ return n.value || "";
113
+ case "paragraph":
114
+ case "emphasis":
115
+ case "strong":
116
+ case "delete":
117
+ case "link":
118
+ return (n.children || []).map(mdToText).join("");
119
+ case "inlineCode":
120
+ return n.value || "";
121
+ case "break":
122
+ return "\n";
123
+ default:
124
+ return "";
125
+ }
126
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./components";
2
+ export * from "./components/streaming";
2
3
  export * from "./components/WindowUtility";
3
4
  export { SparkleContext } from "./context";
4
5
  export * from "./hooks";