@dust-tt/sparkle 0.2.645 → 0.2.646-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 (82) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/esm/components/DataTable.d.ts +3 -13
  3. package/dist/esm/components/DataTable.d.ts.map +1 -1
  4. package/dist/esm/components/DataTable.js +8 -9
  5. package/dist/esm/components/DataTable.js.map +1 -1
  6. package/dist/esm/components/markdown/BlockquoteBlock.d.ts +3 -1
  7. package/dist/esm/components/markdown/BlockquoteBlock.d.ts.map +1 -1
  8. package/dist/esm/components/markdown/BlockquoteBlock.js +7 -3
  9. package/dist/esm/components/markdown/BlockquoteBlock.js.map +1 -1
  10. package/dist/esm/components/markdown/CodeBlockWithExtendedSupport.d.ts +4 -2
  11. package/dist/esm/components/markdown/CodeBlockWithExtendedSupport.d.ts.map +1 -1
  12. package/dist/esm/components/markdown/CodeBlockWithExtendedSupport.js +11 -6
  13. package/dist/esm/components/markdown/CodeBlockWithExtendedSupport.js.map +1 -1
  14. package/dist/esm/components/markdown/HeaderBlocks.d.ts +24 -0
  15. package/dist/esm/components/markdown/HeaderBlocks.d.ts.map +1 -0
  16. package/dist/esm/components/markdown/HeaderBlocks.js +54 -0
  17. package/dist/esm/components/markdown/HeaderBlocks.js.map +1 -0
  18. package/dist/esm/components/markdown/InputBlock.d.ts +10 -0
  19. package/dist/esm/components/markdown/InputBlock.d.ts.map +1 -0
  20. package/dist/esm/components/markdown/InputBlock.js +26 -0
  21. package/dist/esm/components/markdown/InputBlock.js.map +1 -0
  22. package/dist/esm/components/markdown/LinkBlock.d.ts +10 -0
  23. package/dist/esm/components/markdown/LinkBlock.d.ts.map +1 -0
  24. package/dist/esm/components/markdown/LinkBlock.js +11 -0
  25. package/dist/esm/components/markdown/LinkBlock.js.map +1 -0
  26. package/dist/esm/components/markdown/List.d.ts +17 -3
  27. package/dist/esm/components/markdown/List.d.ts.map +1 -1
  28. package/dist/esm/components/markdown/List.js +21 -7
  29. package/dist/esm/components/markdown/List.js.map +1 -1
  30. package/dist/esm/components/markdown/Markdown.d.ts +0 -8
  31. package/dist/esm/components/markdown/Markdown.d.ts.map +1 -1
  32. package/dist/esm/components/markdown/Markdown.js +39 -62
  33. package/dist/esm/components/markdown/Markdown.js.map +1 -1
  34. package/dist/esm/components/markdown/ParagraphBlock.d.ts +3 -1
  35. package/dist/esm/components/markdown/ParagraphBlock.d.ts.map +1 -1
  36. package/dist/esm/components/markdown/ParagraphBlock.js +7 -3
  37. package/dist/esm/components/markdown/ParagraphBlock.js.map +1 -1
  38. package/dist/esm/components/markdown/PreBlock.d.ts +3 -1
  39. package/dist/esm/components/markdown/PreBlock.d.ts.map +1 -1
  40. package/dist/esm/components/markdown/PreBlock.js +7 -3
  41. package/dist/esm/components/markdown/PreBlock.js.map +1 -1
  42. package/dist/esm/components/markdown/TableBlock.d.ts +17 -11
  43. package/dist/esm/components/markdown/TableBlock.d.ts.map +1 -1
  44. package/dist/esm/components/markdown/TableBlock.js +18 -14
  45. package/dist/esm/components/markdown/TableBlock.js.map +1 -1
  46. package/dist/esm/components/markdown/TextFormattingBlocks.d.ts +10 -0
  47. package/dist/esm/components/markdown/TextFormattingBlocks.d.ts.map +1 -0
  48. package/dist/esm/components/markdown/TextFormattingBlocks.js +12 -0
  49. package/dist/esm/components/markdown/TextFormattingBlocks.js.map +1 -0
  50. package/dist/esm/components/markdown/index.d.ts +4 -0
  51. package/dist/esm/components/markdown/index.d.ts.map +1 -1
  52. package/dist/esm/components/markdown/index.js +4 -0
  53. package/dist/esm/components/markdown/index.js.map +1 -1
  54. package/dist/esm/components/markdown/types.d.ts +13 -0
  55. package/dist/esm/components/markdown/types.d.ts.map +1 -0
  56. package/dist/esm/components/markdown/types.js +2 -0
  57. package/dist/esm/components/markdown/types.js.map +1 -0
  58. package/dist/esm/components/markdown/utils.d.ts +11 -0
  59. package/dist/esm/components/markdown/utils.d.ts.map +1 -1
  60. package/dist/esm/components/markdown/utils.js +21 -0
  61. package/dist/esm/components/markdown/utils.js.map +1 -1
  62. package/dist/esm/stories/DataTable.stories.d.ts +0 -1
  63. package/dist/esm/stories/DataTable.stories.d.ts.map +1 -1
  64. package/dist/esm/stories/DataTable.stories.js +0 -188
  65. package/dist/esm/stories/DataTable.stories.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/components/DataTable.tsx +2 -31
  68. package/src/components/markdown/BlockquoteBlock.tsx +36 -29
  69. package/src/components/markdown/CodeBlockWithExtendedSupport.tsx +142 -131
  70. package/src/components/markdown/HeaderBlocks.tsx +160 -0
  71. package/src/components/markdown/InputBlock.tsx +60 -0
  72. package/src/components/markdown/LinkBlock.tsx +36 -0
  73. package/src/components/markdown/List.tsx +53 -30
  74. package/src/components/markdown/Markdown.tsx +97 -169
  75. package/src/components/markdown/ParagraphBlock.tsx +18 -12
  76. package/src/components/markdown/PreBlock.tsx +28 -18
  77. package/src/components/markdown/TableBlock.tsx +125 -95
  78. package/src/components/markdown/TextFormattingBlocks.tsx +31 -0
  79. package/src/components/markdown/index.ts +4 -0
  80. package/src/components/markdown/types.ts +5 -0
  81. package/src/components/markdown/utils.ts +37 -0
  82. package/src/stories/DataTable.stories.tsx +0 -235
@@ -1,8 +1,11 @@
1
1
  import { cva } from "class-variance-authority";
2
- import React from "react";
2
+ import React, { memo } from "react";
3
3
 
4
4
  import { ContentBlockWrapper } from "@sparkle/components";
5
5
 
6
+ import { MarkdownNode } from "./types";
7
+ import { sameNodePosition } from "./utils";
8
+
6
9
  export const blockquoteVariants = cva(
7
10
  ["s-w-full s-text-base s-italic s-rounded-2xl s-py-3 s-pl-5 s-pr-12"],
8
11
  {
@@ -20,33 +23,37 @@ export const blockquoteVariants = cva(
20
23
  interface BlockquoteBlockProps {
21
24
  children: React.ReactNode;
22
25
  variant?: "surface";
26
+ node?: MarkdownNode;
23
27
  }
24
28
 
25
- export function BlockquoteBlock({
26
- children,
27
- variant = "surface",
28
- }: BlockquoteBlockProps) {
29
- const elementAt1 = React.Children.toArray(children)[1];
30
- const childrenContent =
31
- elementAt1 && React.isValidElement(elementAt1)
32
- ? elementAt1.props.children
33
- : null;
34
-
35
- // Convert array content to string if necessary
36
- const contentAsString = Array.isArray(childrenContent)
37
- ? childrenContent.join("")
38
- : childrenContent;
39
-
40
- // Only pass content if it exists
41
- const clipboardContent = contentAsString
42
- ? { "text/plain": contentAsString }
43
- : undefined;
44
-
45
- return (
46
- <ContentBlockWrapper content={clipboardContent} className="s-my-2">
47
- <blockquote className={blockquoteVariants({ variant })}>
48
- {children}
49
- </blockquote>
50
- </ContentBlockWrapper>
51
- );
52
- }
29
+ export const MemoBlockquoteBlock = memo(
30
+ ({ children, variant = "surface" }: BlockquoteBlockProps) => {
31
+ const elementAt1 = React.Children.toArray(children)[1];
32
+ const childrenContent =
33
+ elementAt1 && React.isValidElement(elementAt1)
34
+ ? elementAt1.props.children
35
+ : null;
36
+
37
+ // Convert array content to string if necessary
38
+ const contentAsString = Array.isArray(childrenContent)
39
+ ? childrenContent.join("")
40
+ : childrenContent;
41
+
42
+ // Only pass content if it exists
43
+ const clipboardContent = contentAsString
44
+ ? { "text/plain": contentAsString }
45
+ : undefined;
46
+
47
+ return (
48
+ <ContentBlockWrapper content={clipboardContent} className="s-my-2">
49
+ <blockquote className={blockquoteVariants({ variant })}>
50
+ {children}
51
+ </blockquote>
52
+ </ContentBlockWrapper>
53
+ );
54
+ },
55
+ (prev, next) =>
56
+ sameNodePosition(prev.node, next.node) && prev.variant === next.variant
57
+ );
58
+
59
+ MemoBlockquoteBlock.displayName = "BlockquoteBlock";
@@ -1,5 +1,5 @@
1
1
  import mermaid from "mermaid";
2
- import React, { useContext, useEffect, useRef, useState } from "react";
2
+ import React, { memo, useContext, useEffect, useRef, useState } from "react";
3
3
  import {
4
4
  amber,
5
5
  blue,
@@ -25,6 +25,8 @@ import {
25
25
  JsonValueType,
26
26
  PrettyJsonViewer,
27
27
  } from "@sparkle/components/markdown/PrettyJsonViewer";
28
+ import { MarkdownNode } from "@sparkle/components/markdown/types";
29
+ import { sameNodePosition } from "@sparkle/components/markdown/utils";
28
30
  import { CommandLineIcon, SparklesIcon } from "@sparkle/icons/app";
29
31
  import { cn } from "@sparkle/lib/utils";
30
32
 
@@ -313,150 +315,159 @@ export function StyledMermaidGraph({
313
315
  );
314
316
  }
315
317
 
316
- export function CodeBlockWithExtendedSupport({
317
- children,
318
- className,
319
- inline,
320
- }: {
321
- children?: React.ReactNode;
322
- className?: string;
323
- inline?: boolean;
324
- }) {
325
- const validChildrenContent = String(children).trim();
326
- const [showMermaid, setShowMermaid] = useState<boolean>(false);
327
- const [isValidMermaid, setIsValidMermaid] = useState<boolean>(false);
328
- const [showPrettyJson, setShowPrettyJson] = useState<boolean>(false);
329
- const [parsedJson, setParsedJson] = useState<JsonValueType | null>(null);
330
- const { isStreaming } = useContext(MarkdownContentContext);
331
-
332
- // Detect language from className
333
- const language = className?.split("-")[1];
334
-
335
- // Only create getContentToDownload when we actually want to enable downloads
336
- const getContentToDownload: GetContentToDownloadFunction | undefined =
337
- !inline &&
338
- validChildrenContent &&
339
- (language === "csv" || language === "json")
340
- ? async () => ({
341
- content: validChildrenContent,
342
- filename: `dust_output_${Date.now()}`,
343
- type: language === "csv" ? "text/csv" : "application/json",
344
- })
345
- : undefined;
346
-
347
- // Check for valid Mermaid and JSON.
348
- useEffect(() => {
349
- if (isStreaming || !validChildrenContent) {
350
- return;
351
- }
352
- if (language === "mermaid") {
353
- const checkValidMermaid = async () => {
318
+ export const MemoCodeBlockWithExtendedSupport = memo(
319
+ ({
320
+ children,
321
+ className,
322
+ inline,
323
+ }: {
324
+ children?: React.ReactNode;
325
+ className?: string;
326
+ inline?: boolean;
327
+ node?: MarkdownNode;
328
+ }) => {
329
+ const validChildrenContent = String(children).trim();
330
+ const [showMermaid, setShowMermaid] = useState<boolean>(false);
331
+ const [isValidMermaid, setIsValidMermaid] = useState<boolean>(false);
332
+ const [showPrettyJson, setShowPrettyJson] = useState<boolean>(false);
333
+ const [parsedJson, setParsedJson] = useState<JsonValueType | null>(null);
334
+ const { isStreaming } = useContext(MarkdownContentContext);
335
+
336
+ // Detect language from className
337
+ const language = className?.split("-")[1];
338
+
339
+ // Only create getContentToDownload when we actually want to enable downloads
340
+ const getContentToDownload: GetContentToDownloadFunction | undefined =
341
+ !inline &&
342
+ validChildrenContent &&
343
+ (language === "csv" || language === "json")
344
+ ? async () => ({
345
+ content: validChildrenContent,
346
+ filename: `dust_output_${Date.now()}`,
347
+ type: language === "csv" ? "text/csv" : "application/json",
348
+ })
349
+ : undefined;
350
+
351
+ // Check for valid Mermaid and JSON.
352
+ useEffect(() => {
353
+ if (isStreaming || !validChildrenContent) {
354
+ return;
355
+ }
356
+ if (language === "mermaid") {
357
+ const checkValidMermaid = async () => {
358
+ try {
359
+ await mermaid.parse(validChildrenContent);
360
+ setIsValidMermaid(true);
361
+ setShowMermaid(true);
362
+ } catch (e) {
363
+ setIsValidMermaid(false);
364
+ setShowMermaid(false);
365
+ }
366
+ };
367
+ void checkValidMermaid();
368
+ }
369
+ if (language === "json") {
354
370
  try {
355
- await mermaid.parse(validChildrenContent);
356
- setIsValidMermaid(true);
357
- setShowMermaid(true);
371
+ const parsed = JSON.parse(validChildrenContent);
372
+ setParsedJson(parsed);
373
+ const prettyJsonPreference = getPrettyJsonPreference();
374
+ setShowPrettyJson(prettyJsonPreference);
358
375
  } catch (e) {
359
- setIsValidMermaid(false);
360
- setShowMermaid(false);
376
+ setParsedJson(null);
377
+ setShowPrettyJson(false);
361
378
  }
362
- };
363
- void checkValidMermaid();
364
- }
365
- if (language === "json") {
366
- try {
367
- const parsed = JSON.parse(validChildrenContent);
368
- setParsedJson(parsed);
369
- const prettyJsonPreference = getPrettyJsonPreference();
370
- setShowPrettyJson(prettyJsonPreference);
371
- } catch (e) {
372
- setParsedJson(null);
373
- setShowPrettyJson(false);
374
379
  }
380
+ }, [isStreaming, language, validChildrenContent]);
381
+
382
+ if (inline) {
383
+ return (
384
+ <CodeBlock className={className} inline={inline}>
385
+ {children}
386
+ </CodeBlock>
387
+ );
375
388
  }
376
- }, [isStreaming, language, validChildrenContent]);
377
389
 
378
- if (inline) {
379
- return (
380
- <CodeBlock className={className} inline={inline}>
381
- {children}
382
- </CodeBlock>
383
- );
384
- }
390
+ if (isValidMermaid) {
391
+ return (
392
+ <ContentBlockWrapper
393
+ content={validChildrenContent}
394
+ getContentToDownload={getContentToDownload}
395
+ actions={
396
+ <Button
397
+ className="s-font-sans"
398
+ size="xs"
399
+ variant={"outline"}
400
+ label={showMermaid ? "Markdown" : "Mermaid"}
401
+ icon={showMermaid ? CommandLineIcon : SparklesIcon}
402
+ onClick={() => setShowMermaid(!showMermaid)}
403
+ tooltip={showMermaid ? "Switch to Markdown" : "Switch to Mermaid"}
404
+ />
405
+ }
406
+ >
407
+ {showMermaid ? (
408
+ <MermaidGraph chart={validChildrenContent} />
409
+ ) : (
410
+ <CodeBlock className={className} inline={inline}>
411
+ {children}
412
+ </CodeBlock>
413
+ )}
414
+ </ContentBlockWrapper>
415
+ );
416
+ }
385
417
 
386
- if (isValidMermaid) {
387
- return (
388
- <ContentBlockWrapper
389
- content={validChildrenContent}
390
- getContentToDownload={getContentToDownload}
391
- actions={
392
- <Button
393
- className="s-font-sans"
394
- size="xs"
395
- variant={"outline"}
396
- label={showMermaid ? "Markdown" : "Mermaid"}
397
- icon={showMermaid ? CommandLineIcon : SparklesIcon}
398
- onClick={() => setShowMermaid(!showMermaid)}
399
- tooltip={showMermaid ? "Switch to Markdown" : "Switch to Mermaid"}
400
- />
401
- }
402
- >
403
- {showMermaid ? (
404
- <MermaidGraph chart={validChildrenContent} />
405
- ) : (
406
- <CodeBlock className={className} inline={inline}>
407
- {children}
408
- </CodeBlock>
409
- )}
410
- </ContentBlockWrapper>
411
- );
412
- }
418
+ if (parsedJson !== null) {
419
+ return (
420
+ <ContentBlockWrapper
421
+ content={validChildrenContent}
422
+ getContentToDownload={getContentToDownload}
423
+ actions={
424
+ <Button
425
+ className="s-font-sans"
426
+ size="xs"
427
+ variant={"outline"}
428
+ label={showPrettyJson ? "Raw JSON" : "Pretty JSON"}
429
+ icon={showPrettyJson ? CommandLineIcon : SparklesIcon}
430
+ onClick={() => {
431
+ const newValue = !showPrettyJson;
432
+ setShowPrettyJson(newValue);
433
+ setPrettyJsonPreference(newValue);
434
+ }}
435
+ tooltip={
436
+ showPrettyJson ? "Switch to Raw JSON" : "Switch to Pretty View"
437
+ }
438
+ />
439
+ }
440
+ displayActions="hover"
441
+ buttonDisplay="inside"
442
+ >
443
+ {showPrettyJson ? (
444
+ <PrettyJsonViewer data={parsedJson} />
445
+ ) : (
446
+ <CodeBlock className={className} inline={inline}>
447
+ {children}
448
+ </CodeBlock>
449
+ )}
450
+ </ContentBlockWrapper>
451
+ );
452
+ }
413
453
 
414
- if (parsedJson !== null) {
415
454
  return (
416
455
  <ContentBlockWrapper
417
456
  content={validChildrenContent}
418
457
  getContentToDownload={getContentToDownload}
419
- actions={
420
- <Button
421
- className="s-font-sans"
422
- size="xs"
423
- variant={"outline"}
424
- label={showPrettyJson ? "Raw JSON" : "Pretty JSON"}
425
- icon={showPrettyJson ? CommandLineIcon : SparklesIcon}
426
- onClick={() => {
427
- const newValue = !showPrettyJson;
428
- setShowPrettyJson(newValue);
429
- setPrettyJsonPreference(newValue);
430
- }}
431
- tooltip={
432
- showPrettyJson ? "Switch to Raw JSON" : "Switch to Pretty View"
433
- }
434
- />
435
- }
436
- displayActions="hover"
437
458
  buttonDisplay="inside"
459
+ displayActions="hover"
438
460
  >
439
- {showPrettyJson ? (
440
- <PrettyJsonViewer data={parsedJson} />
441
- ) : (
442
- <CodeBlock className={className} inline={inline}>
443
- {children}
444
- </CodeBlock>
445
- )}
461
+ <CodeBlock className={className} inline={inline}>
462
+ {children}
463
+ </CodeBlock>
446
464
  </ContentBlockWrapper>
447
465
  );
448
- }
466
+ },
467
+ (prev, next) =>
468
+ sameNodePosition(prev.node, next.node) &&
469
+ prev.className === next.className &&
470
+ prev.inline === next.inline
471
+ );
449
472
 
450
- return (
451
- <ContentBlockWrapper
452
- content={validChildrenContent}
453
- getContentToDownload={getContentToDownload}
454
- buttonDisplay="inside"
455
- displayActions="hover"
456
- >
457
- <CodeBlock className={className} inline={inline}>
458
- {children}
459
- </CodeBlock>
460
- </ContentBlockWrapper>
461
- );
462
- }
473
+ MemoCodeBlockWithExtendedSupport.displayName = "CodeBlockWithExtendedSupport";
@@ -0,0 +1,160 @@
1
+ import React, { memo } from "react";
2
+
3
+ import { cn } from "@sparkle/lib/utils";
4
+
5
+ import { MarkdownNode } from "./types";
6
+ import { sameNodePosition, sameTextStyling } from "./utils";
7
+
8
+ export const markdownHeaderClasses = {
9
+ h1: "s-heading-2xl",
10
+ h2: "s-heading-xl",
11
+ h3: "s-heading-lg",
12
+ h4: "s-text-base s-font-semibold",
13
+ h5: "s-text-sm s-font-semibold",
14
+ h6: "s-text-sm s-font-regular s-italic",
15
+ };
16
+
17
+ interface HeaderBlockProps {
18
+ children: React.ReactNode;
19
+ textColor?: string;
20
+ forcedTextSize?: string;
21
+ node?: MarkdownNode;
22
+ }
23
+
24
+ export const MemoH1Block = memo(
25
+ ({ children, textColor, forcedTextSize }: HeaderBlockProps) => {
26
+ return (
27
+ <h1
28
+ className={cn(
29
+ "s-pb-2 s-pt-4",
30
+ forcedTextSize ? forcedTextSize : markdownHeaderClasses.h1,
31
+ textColor
32
+ )}
33
+ >
34
+ {children}
35
+ </h1>
36
+ );
37
+ },
38
+ (prev, next) => {
39
+ return (
40
+ sameNodePosition(prev.node, next.node) && sameTextStyling(prev, next)
41
+ );
42
+ }
43
+ );
44
+
45
+ MemoH1Block.displayName = "H1Block";
46
+
47
+ export const MemoH2Block = memo(
48
+ ({ children, textColor, forcedTextSize }: HeaderBlockProps) => {
49
+ return (
50
+ <h2
51
+ className={cn(
52
+ "s-pb-2 s-pt-4",
53
+ forcedTextSize ? forcedTextSize : markdownHeaderClasses.h2,
54
+ textColor
55
+ )}
56
+ >
57
+ {children}
58
+ </h2>
59
+ );
60
+ },
61
+ (prev, next) => {
62
+ return (
63
+ sameNodePosition(prev.node, next.node) && sameTextStyling(prev, next)
64
+ );
65
+ }
66
+ );
67
+
68
+ MemoH2Block.displayName = "H2Block";
69
+
70
+ export const MemoH3Block = memo(
71
+ ({ children, textColor, forcedTextSize }: HeaderBlockProps) => {
72
+ return (
73
+ <h3
74
+ className={cn(
75
+ "s-pb-2 s-pt-4",
76
+ forcedTextSize ? forcedTextSize : markdownHeaderClasses.h3,
77
+ textColor
78
+ )}
79
+ >
80
+ {children}
81
+ </h3>
82
+ );
83
+ },
84
+ (prev, next) => {
85
+ return (
86
+ sameNodePosition(prev.node, next.node) && sameTextStyling(prev, next)
87
+ );
88
+ }
89
+ );
90
+
91
+ MemoH3Block.displayName = "H3Block";
92
+
93
+ export const MemoH4Block = memo(
94
+ ({ children, textColor, forcedTextSize }: HeaderBlockProps) => {
95
+ return (
96
+ <h4
97
+ className={cn(
98
+ "s-pb-2 s-pt-3",
99
+ forcedTextSize ? forcedTextSize : markdownHeaderClasses.h4,
100
+ textColor
101
+ )}
102
+ >
103
+ {children}
104
+ </h4>
105
+ );
106
+ },
107
+ (prev, next) => {
108
+ return (
109
+ sameNodePosition(prev.node, next.node) && sameTextStyling(prev, next)
110
+ );
111
+ }
112
+ );
113
+
114
+ MemoH4Block.displayName = "H4Block";
115
+
116
+ export const MemoH5Block = memo(
117
+ ({ children, textColor, forcedTextSize }: HeaderBlockProps) => {
118
+ return (
119
+ <h5
120
+ className={cn(
121
+ "s-pb-1.5 s-pt-2.5",
122
+ forcedTextSize ? forcedTextSize : markdownHeaderClasses.h5,
123
+ textColor
124
+ )}
125
+ >
126
+ {children}
127
+ </h5>
128
+ );
129
+ },
130
+ (prev, next) => {
131
+ return (
132
+ sameNodePosition(prev.node, next.node) && sameTextStyling(prev, next)
133
+ );
134
+ }
135
+ );
136
+
137
+ MemoH5Block.displayName = "H5Block";
138
+
139
+ export const MemoH6Block = memo(
140
+ ({ children, textColor, forcedTextSize }: HeaderBlockProps) => {
141
+ return (
142
+ <h6
143
+ className={cn(
144
+ "s-pb-1.5 s-pt-2.5",
145
+ forcedTextSize ? forcedTextSize : markdownHeaderClasses.h6,
146
+ textColor
147
+ )}
148
+ >
149
+ {children}
150
+ </h6>
151
+ );
152
+ },
153
+ (prev, next) => {
154
+ return (
155
+ sameNodePosition(prev.node, next.node) && sameTextStyling(prev, next)
156
+ );
157
+ }
158
+ );
159
+
160
+ MemoH6Block.displayName = "H6Block";
@@ -0,0 +1,60 @@
1
+ import React, { memo } from "react";
2
+ import type { ReactMarkdownProps } from "react-markdown/lib/ast-to-react";
3
+
4
+ import { Checkbox } from "@sparkle/components";
5
+
6
+ import { MarkdownNode } from "./types";
7
+ import { sameNodePosition } from "./utils";
8
+
9
+ type InputBlockProps = Omit<
10
+ React.InputHTMLAttributes<HTMLInputElement>,
11
+ "ref"
12
+ > &
13
+ ReactMarkdownProps & {
14
+ ref?: React.Ref<HTMLInputElement>;
15
+ node?: MarkdownNode;
16
+ };
17
+
18
+ export const MemoInputBlock = memo(
19
+ ({ type, checked, className, onChange, ref, ...props }: InputBlockProps) => {
20
+ const inputRef = React.useRef<HTMLInputElement>(null);
21
+ React.useImperativeHandle(ref, () => inputRef.current!);
22
+
23
+ if (type !== "checkbox") {
24
+ return (
25
+ <input
26
+ ref={inputRef}
27
+ type={type}
28
+ checked={checked}
29
+ className={className}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ const handleCheckedChange = (isChecked: boolean) => {
36
+ onChange?.({
37
+ target: { type: "checkbox", checked: isChecked },
38
+ } as React.ChangeEvent<HTMLInputElement>);
39
+ };
40
+
41
+ return (
42
+ <div className="s-inline-flex s-items-center">
43
+ <Checkbox
44
+ ref={inputRef as React.Ref<HTMLButtonElement>}
45
+ size="xs"
46
+ checked={checked}
47
+ className="s-translate-y-[3px]"
48
+ onCheckedChange={handleCheckedChange}
49
+ />
50
+ </div>
51
+ );
52
+ },
53
+ (prev, next) =>
54
+ sameNodePosition(prev.node, next.node) &&
55
+ prev.type === next.type &&
56
+ prev.checked === next.checked &&
57
+ prev.className === next.className
58
+ );
59
+
60
+ MemoInputBlock.displayName = "InputBlock";
@@ -0,0 +1,36 @@
1
+ import React, { memo } from "react";
2
+
3
+ import { sameNodePosition } from "@sparkle/components/markdown/utils";
4
+ import { cn } from "@sparkle/lib/utils";
5
+
6
+ import { MarkdownNode } from "./types";
7
+
8
+ interface LinkBlockProps {
9
+ href?: string;
10
+ children: React.ReactNode;
11
+ node?: MarkdownNode;
12
+ }
13
+
14
+ export const MemoLinkBlock = memo(
15
+ ({ href, children }: LinkBlockProps) => {
16
+ return (
17
+ <a
18
+ href={href}
19
+ target="_blank"
20
+ rel="noopener noreferrer"
21
+ className={cn(
22
+ "s-break-all s-font-semibold s-transition-all s-duration-200 s-ease-in-out hover:s-underline",
23
+ "s-text-highlight dark:s-text-highlight-night",
24
+ "hover:s-text-highlight-400 dark:hover:s-text-highlight-400-night",
25
+ "active:s-text-highlight-dark dark:active:s-text-highlight-dark-night"
26
+ )}
27
+ >
28
+ {children}
29
+ </a>
30
+ );
31
+ },
32
+ (prev, next) =>
33
+ sameNodePosition(prev.node, next.node) && prev.href === next.href
34
+ );
35
+
36
+ MemoLinkBlock.displayName = "LinkBlock";