@dxos/react-ui-markdown 0.8.4-main.03d5cd7b56
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/LICENSE +8 -0
- package/README.md +1 -0
- package/dist/lib/browser/index.mjs +608 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/types/src/MarkdownBlock/MarkdownBlock.d.ts +15 -0
- package/dist/types/src/MarkdownBlock/MarkdownBlock.d.ts.map +1 -0
- package/dist/types/src/MarkdownBlock/MarkdownBlock.stories.d.ts +11 -0
- package/dist/types/src/MarkdownBlock/MarkdownBlock.stories.d.ts.map +1 -0
- package/dist/types/src/MarkdownBlock/index.d.ts +2 -0
- package/dist/types/src/MarkdownBlock/index.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/MarkdownStream.d.ts +101 -0
- package/dist/types/src/MarkdownStream/MarkdownStream.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/MarkdownStream.stories.d.ts +23 -0
- package/dist/types/src/MarkdownStream/MarkdownStream.stories.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/footer.d.ts +23 -0
- package/dist/types/src/MarkdownStream/footer.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/index.d.ts +4 -0
- package/dist/types/src/MarkdownStream/index.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/stream.d.ts +39 -0
- package/dist/types/src/MarkdownStream/stream.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/stream.test.d.ts +2 -0
- package/dist/types/src/MarkdownStream/stream.test.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/testing/index.d.ts +2 -0
- package/dist/types/src/MarkdownStream/testing/index.d.ts.map +1 -0
- package/dist/types/src/MarkdownStream/testing/testing.d.ts +16 -0
- package/dist/types/src/MarkdownStream/testing/testing.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +82 -0
- package/src/MarkdownBlock/MarkdownBlock.stories.tsx +75 -0
- package/src/MarkdownBlock/MarkdownBlock.tsx +100 -0
- package/src/MarkdownBlock/index.ts +5 -0
- package/src/MarkdownStream/MarkdownStream.stories.tsx +215 -0
- package/src/MarkdownStream/MarkdownStream.tsx +444 -0
- package/src/MarkdownStream/footer.ts +119 -0
- package/src/MarkdownStream/index.ts +8 -0
- package/src/MarkdownStream/stream.test.ts +126 -0
- package/src/MarkdownStream/stream.ts +229 -0
- package/src/MarkdownStream/testing/index.ts +5 -0
- package/src/MarkdownStream/testing/testing.ts +56 -0
- package/src/MarkdownStream/testing/text.md +67 -0
- package/src/index.ts +6 -0
- package/src/typings.d.ts +8 -0
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/react-ui-markdown",
|
|
3
|
+
"version": "0.8.4-main.03d5cd7b56",
|
|
4
|
+
"description": "Markdown components.",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "DXOS.org",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"source": "./src/index.ts",
|
|
17
|
+
"types": "./dist/types/src/index.d.ts",
|
|
18
|
+
"browser": "./dist/lib/browser/index.mjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"types": "dist/types/src/index.d.ts",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@codemirror/autocomplete": "^6.19.0",
|
|
28
|
+
"@codemirror/language": "^6.11.3",
|
|
29
|
+
"@codemirror/state": "^6.5.2",
|
|
30
|
+
"@codemirror/view": "^6.38.5",
|
|
31
|
+
"@lezer/common": "^1.2.2",
|
|
32
|
+
"@lezer/highlight": "^1.2.1",
|
|
33
|
+
"@lezer/markdown": "^1.3.1",
|
|
34
|
+
"@lezer/xml": "^1.0.6",
|
|
35
|
+
"@radix-ui/react-compose-refs": "1.1.1",
|
|
36
|
+
"@radix-ui/react-context": "1.1.1",
|
|
37
|
+
"effect": "3.20.0",
|
|
38
|
+
"json5": "^2.2.3",
|
|
39
|
+
"react-markdown": "^10.1.0",
|
|
40
|
+
"react-resize-detector": "^11.0.1",
|
|
41
|
+
"remark-gfm": "^4.0.1",
|
|
42
|
+
"@dxos/async": "0.8.4-main.03d5cd7b56",
|
|
43
|
+
"@dxos/echo": "0.8.4-main.03d5cd7b56",
|
|
44
|
+
"@dxos/effect": "0.8.4-main.03d5cd7b56",
|
|
45
|
+
"@dxos/invariant": "0.8.4-main.03d5cd7b56",
|
|
46
|
+
"@dxos/log": "0.8.4-main.03d5cd7b56",
|
|
47
|
+
"@dxos/react-ui": "0.8.4-main.03d5cd7b56",
|
|
48
|
+
"@dxos/react-ui-editor": "0.8.4-main.03d5cd7b56",
|
|
49
|
+
"@dxos/ui": "0.8.4-main.03d5cd7b56",
|
|
50
|
+
"@dxos/ui-editor": "0.8.4-main.03d5cd7b56",
|
|
51
|
+
"@dxos/ui-theme": "0.8.4-main.03d5cd7b56",
|
|
52
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.03d5cd7b56",
|
|
53
|
+
"@dxos/util": "0.8.4-main.03d5cd7b56"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@effect/vitest": "0.29.0",
|
|
57
|
+
"@types/react": "~19.2.7",
|
|
58
|
+
"@types/react-dom": "~19.2.3",
|
|
59
|
+
"react": "~19.2.3",
|
|
60
|
+
"react-dom": "~19.2.3",
|
|
61
|
+
"vite": "^8.0.10",
|
|
62
|
+
"@dxos/echo": "0.8.4-main.03d5cd7b56",
|
|
63
|
+
"@dxos/keys": "0.8.4-main.03d5cd7b56",
|
|
64
|
+
"@dxos/random": "0.8.4-main.03d5cd7b56",
|
|
65
|
+
"@dxos/lit-ui": "0.8.4-main.03d5cd7b56",
|
|
66
|
+
"@dxos/react-ui": "0.8.4-main.03d5cd7b56",
|
|
67
|
+
"@dxos/react-client": "0.8.4-main.03d5cd7b56",
|
|
68
|
+
"@dxos/schema": "0.8.4-main.03d5cd7b56",
|
|
69
|
+
"@dxos/ui-theme": "0.8.4-main.03d5cd7b56",
|
|
70
|
+
"@dxos/storybook-utils": "0.8.4-main.03d5cd7b56"
|
|
71
|
+
},
|
|
72
|
+
"peerDependencies": {
|
|
73
|
+
"effect": "3.20.0",
|
|
74
|
+
"react": "~19.2.3",
|
|
75
|
+
"react-dom": "~19.2.3",
|
|
76
|
+
"@dxos/ui-theme": "0.8.4-main.03d5cd7b56",
|
|
77
|
+
"@dxos/react-ui": "0.8.4-main.03d5cd7b56"
|
|
78
|
+
},
|
|
79
|
+
"publishConfig": {
|
|
80
|
+
"access": "public"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
|
|
7
|
+
import { random } from '@dxos/random';
|
|
8
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
9
|
+
import { trim } from '@dxos/util';
|
|
10
|
+
|
|
11
|
+
import { MarkdownBlock } from './MarkdownBlock';
|
|
12
|
+
|
|
13
|
+
random.seed(0);
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
title: 'ui/react-ui-markdown/MarkdownBlock',
|
|
17
|
+
component: MarkdownBlock,
|
|
18
|
+
decorators: [withTheme(), withLayout({ layout: 'column' })],
|
|
19
|
+
} satisfies Meta<typeof MarkdownBlock>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
|
|
23
|
+
type Story = StoryObj<typeof MarkdownBlock>;
|
|
24
|
+
|
|
25
|
+
const content = trim`
|
|
26
|
+
# Hello World!
|
|
27
|
+
|
|
28
|
+
> An example of the MarkdownBlock component.
|
|
29
|
+
|
|
30
|
+
${random.lorem.paragraphs(1)}
|
|
31
|
+
|
|
32
|
+
Here's a JSON block:
|
|
33
|
+
|
|
34
|
+
~~~json
|
|
35
|
+
{
|
|
36
|
+
"hello": "world"
|
|
37
|
+
}
|
|
38
|
+
~~~
|
|
39
|
+
|
|
40
|
+
And some code:
|
|
41
|
+
|
|
42
|
+
~~~tsx
|
|
43
|
+
import React from 'react'
|
|
44
|
+
|
|
45
|
+
const App = () => {
|
|
46
|
+
const title = 'Hello, world!'
|
|
47
|
+
return <div>{title}</div>
|
|
48
|
+
}
|
|
49
|
+
~~~
|
|
50
|
+
|
|
51
|
+
## Task lists
|
|
52
|
+
|
|
53
|
+
- [ ] Task one
|
|
54
|
+
- [x] Task two
|
|
55
|
+
- [ ] Task three
|
|
56
|
+
|
|
57
|
+
## Tables
|
|
58
|
+
|
|
59
|
+
| Column 1 | Column 2 | Column 3 |
|
|
60
|
+
| -------- | -------- | -------- |
|
|
61
|
+
| Cell 1 | Cell 2 | Cell 3 |
|
|
62
|
+
| Cell 4 | Cell 5 | Cell 6 |
|
|
63
|
+
| Cell 7 | Cell 8 | Cell 9 |
|
|
64
|
+
|
|
65
|
+
## Examples
|
|
66
|
+
|
|
67
|
+
${random.lorem.paragraphs(1)}
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
export const Default: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
classNames: 'p-4 border border-border rounded-md overflow-y-auto bg-base-surface',
|
|
73
|
+
content,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type PropsWithChildren } from 'react';
|
|
6
|
+
import ReactMarkdown, { type Options as ReactMarkdownOptions } from 'react-markdown';
|
|
7
|
+
import remarkGfm from 'remark-gfm';
|
|
8
|
+
|
|
9
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
+
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
+
import { mx } from '@dxos/ui-theme';
|
|
12
|
+
|
|
13
|
+
export type MarkdownBlockProps = ThemedClassName<
|
|
14
|
+
PropsWithChildren<{
|
|
15
|
+
content?: string;
|
|
16
|
+
components?: ReactMarkdownOptions['components'];
|
|
17
|
+
}>
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Transforms markdown text into react elements.
|
|
22
|
+
* https://github.com/remarkjs/react-markdown
|
|
23
|
+
* markdown -> remark -> [mdast -> remark plugins] -> [hast -> rehype plugins] -> components -> react elements.
|
|
24
|
+
* Consider using @dxos/react-ui-editor.
|
|
25
|
+
*/
|
|
26
|
+
export const MarkdownBlock = ({ classNames, children, components, content = '' }: MarkdownBlockProps) => {
|
|
27
|
+
return (
|
|
28
|
+
<div className={mx(classNames)}>
|
|
29
|
+
<ReactMarkdown remarkPlugins={[remarkGfm]} skipHtml components={{ ...defaultComponents, ...components }}>
|
|
30
|
+
{content}
|
|
31
|
+
</ReactMarkdown>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const defaultComponents: ReactMarkdownOptions['components'] = {
|
|
38
|
+
h1: ({ children }) => {
|
|
39
|
+
return <h1 className='pt-1 pb-1 text-accent-text text-xl'>{children}</h1>;
|
|
40
|
+
},
|
|
41
|
+
h2: ({ children }) => {
|
|
42
|
+
return <h2 className='pt-1 pb-1 text-accent-text text-lg'>{children}</h2>;
|
|
43
|
+
},
|
|
44
|
+
h3: ({ children }) => {
|
|
45
|
+
return <h3 className='pt-1 pb-1 text-accent-text text-base'>{children}</h3>;
|
|
46
|
+
},
|
|
47
|
+
blockquote: ({ children, ...props }) => (
|
|
48
|
+
<blockquote className='my-2 py-2 ps-4 border-l-4 border-accent-text text-accent-text' {...props}>
|
|
49
|
+
{children}
|
|
50
|
+
</blockquote>
|
|
51
|
+
),
|
|
52
|
+
p: ({ children }) => {
|
|
53
|
+
return <div className='pt-1 pb-1'>{children}</div>;
|
|
54
|
+
},
|
|
55
|
+
a: ({ children, href, ...props }) => (
|
|
56
|
+
<a
|
|
57
|
+
href={href}
|
|
58
|
+
className='text-primary-500 hover:text-primary-500' // TODO(burdon): Use link token.
|
|
59
|
+
target='_blank'
|
|
60
|
+
rel='noopener noreferrer'
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
</a>
|
|
65
|
+
),
|
|
66
|
+
ol: ({ children, ...props }) => (
|
|
67
|
+
<ol className='pt-1 pb-1 ps-6 leading-tight list-decimal' {...props}>
|
|
68
|
+
{children}
|
|
69
|
+
</ol>
|
|
70
|
+
),
|
|
71
|
+
ul: ({ children, ...props }) => (
|
|
72
|
+
<ul className='pt-1 pb-1 ps-6 leading-tight list-disc' {...props}>
|
|
73
|
+
{children}
|
|
74
|
+
</ul>
|
|
75
|
+
),
|
|
76
|
+
li: ({ children, ...props }) => (
|
|
77
|
+
<li className='' {...props}>
|
|
78
|
+
{children}
|
|
79
|
+
</li>
|
|
80
|
+
),
|
|
81
|
+
pre: ({ children }) => children,
|
|
82
|
+
code: ({ children, className, node }) => {
|
|
83
|
+
const [, language] = /language-(\w+)/.exec(className || '') || [];
|
|
84
|
+
const inline = !className && node?.position?.start.line === node?.position?.end.line;
|
|
85
|
+
if (inline) {
|
|
86
|
+
return <code className='rounded-xs bg-group-surface px-1 py-0.5 text-sm text-info-text'>{children}</code>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<SyntaxHighlighter
|
|
91
|
+
language={language}
|
|
92
|
+
classNames='mt-2 mb-2 p-2 border border-separator rounded-xs text-sm bg-group-surface'
|
|
93
|
+
copyButton
|
|
94
|
+
PreTag='pre'
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
</SyntaxHighlighter>
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { WidgetType } from '@codemirror/view';
|
|
6
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import '@dxos/lit-ui';
|
|
10
|
+
import { PublicKey } from '@dxos/keys';
|
|
11
|
+
import { random } from '@dxos/random';
|
|
12
|
+
import { Input, Toolbar } from '@dxos/react-ui';
|
|
13
|
+
import { Panel } from '@dxos/react-ui';
|
|
14
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
15
|
+
import { Domino } from '@dxos/ui';
|
|
16
|
+
import { type XmlWidgetRegistry, getXmlTextChild } from '@dxos/ui-editor';
|
|
17
|
+
import { mx } from '@dxos/ui-theme';
|
|
18
|
+
import { keyToFallback, trim } from '@dxos/util';
|
|
19
|
+
|
|
20
|
+
import { MarkdownStream, type MarkdownStreamController, type MarkdownStreamProps } from './MarkdownStream';
|
|
21
|
+
import { type TextStreamOptions, textStream } from './testing';
|
|
22
|
+
import TEXT from './testing/text.md?raw';
|
|
23
|
+
|
|
24
|
+
random.seed(123);
|
|
25
|
+
|
|
26
|
+
const userHue = keyToFallback(PublicKey.random()).hue;
|
|
27
|
+
|
|
28
|
+
const defaultStreamOptions: TextStreamOptions = {
|
|
29
|
+
wordsPerChunk: 5,
|
|
30
|
+
chunkDelay: 200,
|
|
31
|
+
variance: 0.5,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
class DOMWidget extends WidgetType {
|
|
35
|
+
constructor(private text: string) {
|
|
36
|
+
super();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override eq(other: this) {
|
|
40
|
+
return this.text === other.text;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
override toDOM() {
|
|
44
|
+
return Domino.of('span').classNames(mx('flex m-2 p-2 border border-separator rounded')).text(this.text).root;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const ReactWidget = ({ children }: { children: string }) => {
|
|
49
|
+
return <div className='m-2 p-2 border border-separator rounded'>{children}</div>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const registry: XmlWidgetRegistry = {
|
|
53
|
+
'dom-widget': {
|
|
54
|
+
block: true,
|
|
55
|
+
streaming: true,
|
|
56
|
+
factory: ({ children }) => {
|
|
57
|
+
const text = getXmlTextChild(children);
|
|
58
|
+
return text ? new DOMWidget(text) : null;
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
'react-widget': {
|
|
63
|
+
block: true,
|
|
64
|
+
streaming: true,
|
|
65
|
+
Component: ReactWidget,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type DefaultStoryProps = MarkdownStreamProps & {
|
|
70
|
+
initialContent?: string;
|
|
71
|
+
streamOptions?: TextStreamOptions;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const DefaultStory = ({
|
|
75
|
+
initialContent,
|
|
76
|
+
content,
|
|
77
|
+
streamOptions = defaultStreamOptions,
|
|
78
|
+
debug: debugProp,
|
|
79
|
+
...props
|
|
80
|
+
}: DefaultStoryProps) => {
|
|
81
|
+
const [controller, setController] = useState<MarkdownStreamController | null>(null);
|
|
82
|
+
const [streaming, setStreaming] = useState(false);
|
|
83
|
+
const [debug, setDebug] = useState(debugProp);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (initialContent) {
|
|
87
|
+
void controller?.append(initialContent);
|
|
88
|
+
}
|
|
89
|
+
}, [controller, initialContent]);
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!controller || !streaming || !content) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let cancelled = false;
|
|
97
|
+
void (async () => {
|
|
98
|
+
for await (const chunk of textStream(content + '\n', streamOptions)) {
|
|
99
|
+
if (cancelled) {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await controller.append(chunk);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setStreaming(false);
|
|
107
|
+
})();
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
cancelled = true;
|
|
111
|
+
};
|
|
112
|
+
}, [controller, content, streaming]);
|
|
113
|
+
|
|
114
|
+
const handleReset = useCallback(() => {
|
|
115
|
+
setStreaming(false);
|
|
116
|
+
void controller?.setContent('');
|
|
117
|
+
}, [controller]);
|
|
118
|
+
|
|
119
|
+
const handleAppend = useCallback(() => {
|
|
120
|
+
void controller?.append(
|
|
121
|
+
[
|
|
122
|
+
random.lorem.paragraph(),
|
|
123
|
+
`<dom-widget>${random.lorem.paragraphs(3)}</dom-widget>`,
|
|
124
|
+
random.lorem.paragraph(),
|
|
125
|
+
`<react-widget>${random.lorem.paragraphs(3)}</react-widget>`,
|
|
126
|
+
'',
|
|
127
|
+
].join('\n\n'),
|
|
128
|
+
);
|
|
129
|
+
}, [controller]);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<Panel.Root data-hue={userHue}>
|
|
133
|
+
<Panel.Toolbar asChild>
|
|
134
|
+
<Toolbar.Root>
|
|
135
|
+
<Toolbar.IconButton
|
|
136
|
+
disabled={streaming}
|
|
137
|
+
icon='ph--play--regular'
|
|
138
|
+
iconOnly
|
|
139
|
+
label='Start'
|
|
140
|
+
onClick={() => setStreaming(true)}
|
|
141
|
+
/>
|
|
142
|
+
<Toolbar.IconButton
|
|
143
|
+
disabled={!streaming}
|
|
144
|
+
icon='ph--stop--regular'
|
|
145
|
+
iconOnly
|
|
146
|
+
label='Stop'
|
|
147
|
+
onClick={() => setStreaming(false)}
|
|
148
|
+
/>
|
|
149
|
+
<Toolbar.IconButton icon='ph--trash--regular' iconOnly label='Reset' onClick={handleReset} />
|
|
150
|
+
<Toolbar.IconButton
|
|
151
|
+
disabled={streaming}
|
|
152
|
+
icon='ph--plus--regular'
|
|
153
|
+
iconOnly
|
|
154
|
+
label='Append'
|
|
155
|
+
onClick={handleAppend}
|
|
156
|
+
/>
|
|
157
|
+
<Toolbar.Separator />
|
|
158
|
+
<Input.Root>
|
|
159
|
+
<Input.Label classNames='pr-1'>Debug</Input.Label>
|
|
160
|
+
<Input.Switch checked={debug} onCheckedChange={setDebug} />
|
|
161
|
+
</Input.Root>
|
|
162
|
+
</Toolbar.Root>
|
|
163
|
+
</Panel.Toolbar>
|
|
164
|
+
<Panel.Content>
|
|
165
|
+
<MarkdownStream {...props} debug={debug} ref={setController} />
|
|
166
|
+
</Panel.Content>
|
|
167
|
+
</Panel.Root>
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const meta = {
|
|
172
|
+
title: 'ui/react-ui-markdown/MarkdownStream',
|
|
173
|
+
render: DefaultStory,
|
|
174
|
+
decorators: [withTheme(), withLayout({ layout: 'column' })],
|
|
175
|
+
parameters: { layout: 'fullscreen' },
|
|
176
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
177
|
+
|
|
178
|
+
export default meta;
|
|
179
|
+
|
|
180
|
+
type Story = StoryObj<typeof meta>;
|
|
181
|
+
|
|
182
|
+
export const Default: Story = {
|
|
183
|
+
args: {
|
|
184
|
+
initialContent: TEXT,
|
|
185
|
+
options: {
|
|
186
|
+
autoScroll: true,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export const Streaming: Story = {
|
|
192
|
+
args: {
|
|
193
|
+
registry,
|
|
194
|
+
content: TEXT,
|
|
195
|
+
options: {
|
|
196
|
+
autoScroll: true,
|
|
197
|
+
typewriter: true,
|
|
198
|
+
fader: true,
|
|
199
|
+
cursor: true,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export const Widgets: Story = {
|
|
205
|
+
args: {
|
|
206
|
+
registry,
|
|
207
|
+
initialContent: trim`
|
|
208
|
+
# DOM Widget
|
|
209
|
+
<dom-widget>${random.lorem.paragraph()}</dom-widget>
|
|
210
|
+
|
|
211
|
+
# React Widget
|
|
212
|
+
<react-widget>${random.lorem.paragraph()}</react-widget>
|
|
213
|
+
`,
|
|
214
|
+
},
|
|
215
|
+
};
|