@mastra/playground-ui 49.0.0-alpha.3 → 49.0.0-alpha.5
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/CHANGELOG.md +42 -0
- package/dist/code-BJMC6T9D.cjs +78 -0
- package/dist/code-BJMC6T9D.cjs.map +1 -0
- package/dist/code-DRGJVroV.js +57 -0
- package/dist/code-DRGJVroV.js.map +1 -0
- package/dist/{code-block-Dn0K3DkE.cjs → code-block-BZUY8Fj9.cjs} +2 -2
- package/dist/{code-block-Dn0K3DkE.cjs.map → code-block-BZUY8Fj9.cjs.map} +1 -1
- package/dist/{code-block-DqKXZajq.js → code-block-V4wpmbud.js} +2 -2
- package/dist/{code-block-DqKXZajq.js.map → code-block-V4wpmbud.js.map} +1 -1
- package/dist/components/Code.cjs.js +1 -1
- package/dist/components/Code.es.js +1 -1
- package/dist/components/CodeBlock.cjs.js +1 -1
- package/dist/components/CodeBlock.es.js +1 -1
- package/dist/components/MarkdownRenderer.cjs.js +1 -1
- package/dist/components/MarkdownRenderer.es.js +1 -1
- package/dist/components/ai/plan.cjs.js +1 -1
- package/dist/components/ai/plan.es.js +1 -1
- package/dist/markdown-renderer-BDe_1JLH.js +210 -0
- package/dist/markdown-renderer-BDe_1JLH.js.map +1 -0
- package/dist/markdown-renderer-WHy5Cyu2.cjs +212 -0
- package/dist/markdown-renderer-WHy5Cyu2.cjs.map +1 -0
- package/dist/markdown-renderer.css +32 -1
- package/dist/src/ds/components/Code/code.d.ts +5 -0
- package/dist/src/ds/components/MarkdownRenderer/blocks.d.ts +11 -0
- package/dist/src/ds/components/MarkdownRenderer/markdown-renderer.d.ts +9 -1
- package/dist/src/ds/components/MarkdownRenderer/word-spans.d.ts +26 -0
- package/dist/style.css +461 -0
- package/package.json +8 -7
- package/dist/code-Cj09LVys.js +0 -47
- package/dist/code-Cj09LVys.js.map +0 -1
- package/dist/code-FZwLnf_f.cjs +0 -68
- package/dist/code-FZwLnf_f.cjs.map +0 -1
- package/dist/markdown-renderer-D1vSFfp9.cjs +0 -86
- package/dist/markdown-renderer-D1vSFfp9.cjs.map +0 -1
- package/dist/markdown-renderer-DmYYQHxu.js +0 -84
- package/dist/markdown-renderer-DmYYQHxu.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @mastra/playground-ui
|
|
2
2
|
|
|
3
|
+
## 49.0.0-alpha.5
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Streamed markdown now renders block by block. A growing reply re-parses only the block still being written instead of the whole message on every chunk, so a long reply no longer costs more per chunk as it gets longer, and a reply that finishes streaming keeps every element it already put on screen instead of remounting. ([#21473](https://github.com/mastra-ai/mastra/pull/21473))
|
|
8
|
+
|
|
9
|
+
Half-written markdown also renders as what it is about to become: `**bold` reads as bold while its closing marker is still in flight, and a link shows its text until the URL lands, instead of flashing raw syntax on screen.
|
|
10
|
+
|
|
11
|
+
One caveat: splitting a text into blocks is deliberately conservative rather than a second full parse. Anything it cannot decide — an unclosed fence, an indented continuation, a link reference or footnote definition — is kept whole, so those replies render exactly as before and simply do not get the speedup.
|
|
12
|
+
|
|
13
|
+
- Added a `streaming` prop to `MarkdownRenderer`. A reply marked as still being written fades each word in as it arrives, instead of snapping whole chunks of text into place. ([#21417](https://github.com/mastra-ai/mastra/pull/21417))
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<MarkdownRenderer streaming={part.state === 'streaming'}>{part.text}</MarkdownRenderer>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The fade is CSS on words as they land, so the text already on screen stays put while the reply grows. A word fades in once it is whole rather than one character at a time — the word still being typed is held back until its boundary arrives, so the visible text trails the stream by at most one word. One caveat: when a growing block changes shape, a paragraph turning into a list item as the next character lands, that block fades again.
|
|
20
|
+
|
|
21
|
+
Leave the prop off — the default — for text that is already settled: it renders as plain prose, with no extra markup. The animation is disabled under `prefers-reduced-motion`.
|
|
22
|
+
|
|
23
|
+
Markdown no longer sets `text-wrap: pretty`. It re-broke the last lines of a block to avoid orphans, which reran on every chunk of a streaming reply and jumped words that were already on screen onto another line.
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [[`59d8898`](https://github.com/mastra-ai/mastra/commit/59d8898c8cb48b342fe5bcb5eee803cc8cc95060), [`a40f915`](https://github.com/mastra-ai/mastra/commit/a40f9157690d89ef13ce825cc88e30be581de5d4)]:
|
|
28
|
+
- @mastra/core@1.59.0-alpha.5
|
|
29
|
+
- @mastra/client-js@1.40.0-alpha.5
|
|
30
|
+
- @mastra/react@1.4.3-alpha.5
|
|
31
|
+
|
|
32
|
+
## 49.0.0-alpha.4
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- Fixed code blocks flickering between colored and plain text while an agent streams a fenced snippet. The part already highlighted now keeps its colors, and only the characters that just landed show uncolored until highlighting catches up. ([#21415](https://github.com/mastra-ai/mastra/pull/21415))
|
|
37
|
+
|
|
38
|
+
- Improved chat responsiveness on long conversations. `MarkdownRenderer` is memoized, so a streaming reply no longer re-parses the markdown of every message already on screen on each chunk it receives — only the message actually being written is re-parsed. ([#21416](https://github.com/mastra-ai/mastra/pull/21416))
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [[`be31796`](https://github.com/mastra-ai/mastra/commit/be3179624ad5f77cff5fa342cd08046bf7605283), [`79dd7c2`](https://github.com/mastra-ai/mastra/commit/79dd7c261ee6be1fafedd4651959394db21d2cba), [`b9a28ec`](https://github.com/mastra-ai/mastra/commit/b9a28ecf7acdc0cb7a543d5b660f9fbee301df9a), [`be31796`](https://github.com/mastra-ai/mastra/commit/be3179624ad5f77cff5fa342cd08046bf7605283)]:
|
|
41
|
+
- @mastra/client-js@1.40.0-alpha.4
|
|
42
|
+
- @mastra/core@1.59.0-alpha.4
|
|
43
|
+
- @mastra/react@1.4.3-alpha.4
|
|
44
|
+
|
|
3
45
|
## 49.0.0-alpha.3
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
const React = require('react');
|
|
5
|
+
const highlight = require('./highlight-CEt2vhD8.cjs');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
|
|
9
|
+
if (e) {
|
|
10
|
+
for (const k in e) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => e[k]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
25
|
+
|
|
26
|
+
function tokenStyle(token) {
|
|
27
|
+
if (token.htmlStyle && typeof token.htmlStyle === "object") {
|
|
28
|
+
return token.htmlStyle;
|
|
29
|
+
}
|
|
30
|
+
return token.color ? { color: token.color } : void 0;
|
|
31
|
+
}
|
|
32
|
+
function usableHighlight(highlighted, code, lang) {
|
|
33
|
+
if (!highlighted || highlighted.lang !== lang) return null;
|
|
34
|
+
return code.startsWith(highlighted.code) ? highlighted : null;
|
|
35
|
+
}
|
|
36
|
+
const Code = React__namespace.memo(function Code2({ code, lang, ...props }) {
|
|
37
|
+
const [highlighted, setHighlighted] = React__namespace.useState(null);
|
|
38
|
+
React__namespace.useEffect(() => {
|
|
39
|
+
if (!lang) {
|
|
40
|
+
setHighlighted(null);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
let cancelled = false;
|
|
44
|
+
void highlight.highlight(code, lang).then((tokens) => {
|
|
45
|
+
if (!cancelled && tokens?.length) setHighlighted({ code, lang, tokens });
|
|
46
|
+
}).catch(() => {
|
|
47
|
+
});
|
|
48
|
+
return () => {
|
|
49
|
+
cancelled = true;
|
|
50
|
+
};
|
|
51
|
+
}, [code, lang]);
|
|
52
|
+
const usable = usableHighlight(highlighted, code, lang);
|
|
53
|
+
if (!usable) {
|
|
54
|
+
return /* @__PURE__ */ jsxRuntime.jsx("pre", { ...props, children: code });
|
|
55
|
+
}
|
|
56
|
+
const tail = code.slice(usable.code.length);
|
|
57
|
+
let codeOffset = 0;
|
|
58
|
+
return /* @__PURE__ */ jsxRuntime.jsx("pre", { ...props, children: /* @__PURE__ */ jsxRuntime.jsxs("code", { children: [
|
|
59
|
+
usable.tokens.map((line, lineIndex) => {
|
|
60
|
+
const lineOffset = codeOffset;
|
|
61
|
+
let tokenOffset = lineOffset;
|
|
62
|
+
const tokenSpans = line.map((token) => {
|
|
63
|
+
const key = tokenOffset;
|
|
64
|
+
tokenOffset += token.content.length;
|
|
65
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shiki-token", style: tokenStyle(token), children: token.content }, key);
|
|
66
|
+
});
|
|
67
|
+
codeOffset = tokenOffset + 1;
|
|
68
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
|
|
69
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenSpans }),
|
|
70
|
+
lineIndex !== usable.tokens.length - 1 && "\n"
|
|
71
|
+
] }, lineOffset);
|
|
72
|
+
}),
|
|
73
|
+
tail
|
|
74
|
+
] }) });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
exports.Code = Code;
|
|
78
|
+
//# sourceMappingURL=code-BJMC6T9D.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-BJMC6T9D.cjs","sources":["../src/ds/components/Code/code.tsx"],"sourcesContent":["import * as React from 'react';\nimport type { ThemedToken } from 'shiki/core';\n\nimport { highlight } from '../CodeEditor/highlight';\n\nexport interface CodeProps extends React.HTMLAttributes<HTMLPreElement> {\n code: string;\n lang?: string;\n}\n\nfunction tokenStyle(token: ThemedToken): React.CSSProperties | undefined {\n if (token.htmlStyle && typeof token.htmlStyle === 'object') {\n return token.htmlStyle as React.CSSProperties;\n }\n\n return token.color ? { color: token.color } : undefined;\n}\n\ninterface Highlighted {\n code: string;\n lang: string;\n tokens: ThemedToken[][];\n}\n\n/** Colors from an earlier pass still hold when the new code only appends to the old. */\nfunction usableHighlight(highlighted: Highlighted | null, code: string, lang?: string): Highlighted | null {\n if (!highlighted || highlighted.lang !== lang) return null;\n return code.startsWith(highlighted.code) ? highlighted : null;\n}\n\n/**\n * Low-level shiki token renderer shared by `CodeBlock` and `MarkdownRenderer`.\n * Dual-theme colors stay as `--shiki-light` / `--shiki-dark` CSS variables on\n * each token span; the `.shiki-token` class (index.css) picks the variant from\n * the `.dark` root class, so theme switching is pure CSS — no ThemeProvider\n * required. Renders plain text while highlighting is pending or when the\n * language is missing/unknown.\n *\n * A streaming fence re-renders on every delta while highlighting stays a frame\n * behind. Dropping the previous tokens each time would strobe the whole block\n * between colored and plain, so the settled prefix keeps its colors and only\n * the newly arrived tail waits, uncolored, for the next pass.\n */\nexport const Code = React.memo(function Code({ code, lang, ...props }: CodeProps) {\n const [highlighted, setHighlighted] = React.useState<Highlighted | null>(null);\n\n React.useEffect(() => {\n if (!lang) {\n setHighlighted(null);\n return;\n }\n\n let cancelled = false;\n\n void highlight(code, lang)\n .then(tokens => {\n if (!cancelled && tokens?.length) setHighlighted({ code, lang, tokens });\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [code, lang]);\n\n const usable = usableHighlight(highlighted, code, lang);\n if (!usable) {\n return <pre {...props}>{code}</pre>;\n }\n\n const tail = code.slice(usable.code.length);\n let codeOffset = 0;\n\n return (\n <pre {...props}>\n <code>\n {usable.tokens.map((line, lineIndex) => {\n const lineOffset = codeOffset;\n let tokenOffset = lineOffset;\n const tokenSpans = line.map(token => {\n const key = tokenOffset;\n tokenOffset += token.content.length;\n\n return (\n <span key={key} className=\"shiki-token\" style={tokenStyle(token)}>\n {token.content}\n </span>\n );\n });\n\n codeOffset = tokenOffset + 1;\n\n return (\n <React.Fragment key={lineOffset}>\n <span>{tokenSpans}</span>\n {lineIndex !== usable.tokens.length - 1 && '\\n'}\n </React.Fragment>\n );\n })}\n {tail}\n </code>\n </pre>\n );\n});\n"],"names":["React","Code","highlight","jsx","jsxs"],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { h as highlight } from './highlight-CSJjjoqh.js';
|
|
4
|
+
|
|
5
|
+
function tokenStyle(token) {
|
|
6
|
+
if (token.htmlStyle && typeof token.htmlStyle === "object") {
|
|
7
|
+
return token.htmlStyle;
|
|
8
|
+
}
|
|
9
|
+
return token.color ? { color: token.color } : void 0;
|
|
10
|
+
}
|
|
11
|
+
function usableHighlight(highlighted, code, lang) {
|
|
12
|
+
if (!highlighted || highlighted.lang !== lang) return null;
|
|
13
|
+
return code.startsWith(highlighted.code) ? highlighted : null;
|
|
14
|
+
}
|
|
15
|
+
const Code = React.memo(function Code2({ code, lang, ...props }) {
|
|
16
|
+
const [highlighted, setHighlighted] = React.useState(null);
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (!lang) {
|
|
19
|
+
setHighlighted(null);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let cancelled = false;
|
|
23
|
+
void highlight(code, lang).then((tokens) => {
|
|
24
|
+
if (!cancelled && tokens?.length) setHighlighted({ code, lang, tokens });
|
|
25
|
+
}).catch(() => {
|
|
26
|
+
});
|
|
27
|
+
return () => {
|
|
28
|
+
cancelled = true;
|
|
29
|
+
};
|
|
30
|
+
}, [code, lang]);
|
|
31
|
+
const usable = usableHighlight(highlighted, code, lang);
|
|
32
|
+
if (!usable) {
|
|
33
|
+
return /* @__PURE__ */ jsx("pre", { ...props, children: code });
|
|
34
|
+
}
|
|
35
|
+
const tail = code.slice(usable.code.length);
|
|
36
|
+
let codeOffset = 0;
|
|
37
|
+
return /* @__PURE__ */ jsx("pre", { ...props, children: /* @__PURE__ */ jsxs("code", { children: [
|
|
38
|
+
usable.tokens.map((line, lineIndex) => {
|
|
39
|
+
const lineOffset = codeOffset;
|
|
40
|
+
let tokenOffset = lineOffset;
|
|
41
|
+
const tokenSpans = line.map((token) => {
|
|
42
|
+
const key = tokenOffset;
|
|
43
|
+
tokenOffset += token.content.length;
|
|
44
|
+
return /* @__PURE__ */ jsx("span", { className: "shiki-token", style: tokenStyle(token), children: token.content }, key);
|
|
45
|
+
});
|
|
46
|
+
codeOffset = tokenOffset + 1;
|
|
47
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
48
|
+
/* @__PURE__ */ jsx("span", { children: tokenSpans }),
|
|
49
|
+
lineIndex !== usable.tokens.length - 1 && "\n"
|
|
50
|
+
] }, lineOffset);
|
|
51
|
+
}),
|
|
52
|
+
tail
|
|
53
|
+
] }) });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export { Code as C };
|
|
57
|
+
//# sourceMappingURL=code-DRGJVroV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-DRGJVroV.js","sources":["../src/ds/components/Code/code.tsx"],"sourcesContent":["import * as React from 'react';\nimport type { ThemedToken } from 'shiki/core';\n\nimport { highlight } from '../CodeEditor/highlight';\n\nexport interface CodeProps extends React.HTMLAttributes<HTMLPreElement> {\n code: string;\n lang?: string;\n}\n\nfunction tokenStyle(token: ThemedToken): React.CSSProperties | undefined {\n if (token.htmlStyle && typeof token.htmlStyle === 'object') {\n return token.htmlStyle as React.CSSProperties;\n }\n\n return token.color ? { color: token.color } : undefined;\n}\n\ninterface Highlighted {\n code: string;\n lang: string;\n tokens: ThemedToken[][];\n}\n\n/** Colors from an earlier pass still hold when the new code only appends to the old. */\nfunction usableHighlight(highlighted: Highlighted | null, code: string, lang?: string): Highlighted | null {\n if (!highlighted || highlighted.lang !== lang) return null;\n return code.startsWith(highlighted.code) ? highlighted : null;\n}\n\n/**\n * Low-level shiki token renderer shared by `CodeBlock` and `MarkdownRenderer`.\n * Dual-theme colors stay as `--shiki-light` / `--shiki-dark` CSS variables on\n * each token span; the `.shiki-token` class (index.css) picks the variant from\n * the `.dark` root class, so theme switching is pure CSS — no ThemeProvider\n * required. Renders plain text while highlighting is pending or when the\n * language is missing/unknown.\n *\n * A streaming fence re-renders on every delta while highlighting stays a frame\n * behind. Dropping the previous tokens each time would strobe the whole block\n * between colored and plain, so the settled prefix keeps its colors and only\n * the newly arrived tail waits, uncolored, for the next pass.\n */\nexport const Code = React.memo(function Code({ code, lang, ...props }: CodeProps) {\n const [highlighted, setHighlighted] = React.useState<Highlighted | null>(null);\n\n React.useEffect(() => {\n if (!lang) {\n setHighlighted(null);\n return;\n }\n\n let cancelled = false;\n\n void highlight(code, lang)\n .then(tokens => {\n if (!cancelled && tokens?.length) setHighlighted({ code, lang, tokens });\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [code, lang]);\n\n const usable = usableHighlight(highlighted, code, lang);\n if (!usable) {\n return <pre {...props}>{code}</pre>;\n }\n\n const tail = code.slice(usable.code.length);\n let codeOffset = 0;\n\n return (\n <pre {...props}>\n <code>\n {usable.tokens.map((line, lineIndex) => {\n const lineOffset = codeOffset;\n let tokenOffset = lineOffset;\n const tokenSpans = line.map(token => {\n const key = tokenOffset;\n tokenOffset += token.content.length;\n\n return (\n <span key={key} className=\"shiki-token\" style={tokenStyle(token)}>\n {token.content}\n </span>\n );\n });\n\n codeOffset = tokenOffset + 1;\n\n return (\n <React.Fragment key={lineOffset}>\n <span>{tokenSpans}</span>\n {lineIndex !== usable.tokens.length - 1 && '\\n'}\n </React.Fragment>\n );\n })}\n {tail}\n </code>\n </pre>\n );\n});\n"],"names":["Code"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
const code = require('./code-
|
|
4
|
+
const code = require('./code-BJMC6T9D.cjs');
|
|
5
5
|
const copyButton = require('./copy-button-BfawWA4t.cjs');
|
|
6
6
|
const select = require('./select-OAXqZLkC.cjs');
|
|
7
7
|
const tabsRoot = require('./tabs-root-VtBc6K2q.cjs');
|
|
@@ -85,4 +85,4 @@ function CodeBlock({
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
exports.CodeBlock = CodeBlock;
|
|
88
|
-
//# sourceMappingURL=code-block-
|
|
88
|
+
//# sourceMappingURL=code-block-BZUY8Fj9.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block-
|
|
1
|
+
{"version":3,"file":"code-block-BZUY8Fj9.cjs","sources":["../src/ds/components/CodeBlock/code-block.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Code } from '../Code/code';\nimport { CopyButton } from '../CopyButton/copy-button';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../Select/select';\nimport { TabList } from '../Tabs/tabs-list';\nimport { Tabs } from '../Tabs/tabs-root';\nimport { Tab } from '../Tabs/tabs-tab';\nimport { transitions } from '@/ds/primitives/transitions';\nimport { cn } from '@/lib/utils';\n\nexport type CodeBlockSelector = 'select' | 'tabs';\n\nexport type CodeBlockOverflow = 'wrap' | 'scroll';\n\nexport interface CodeBlockOption {\n label: string;\n value: string;\n}\n\nexport interface CodeBlockProps {\n code: string;\n options?: CodeBlockOption[];\n value?: string;\n onValueChange?: (value: string) => void;\n selector?: CodeBlockSelector;\n fileName?: string;\n lang?: string;\n /** `wrap` (default) breaks long lines — best for commands and snippets.\n * `scroll` preserves columns behind a horizontal scroll — best for source code. */\n overflow?: CodeBlockOverflow;\n copyMessage?: string;\n copyTooltip?: string;\n actions?: ReactNode;\n className?: string;\n}\n\nexport function CodeBlock({\n code,\n options,\n value,\n onValueChange,\n selector = 'select',\n fileName,\n lang,\n overflow = 'wrap',\n copyMessage,\n copyTooltip,\n actions,\n className,\n}: CodeBlockProps) {\n const hasOptions = options && options.length > 0;\n const useTabs = hasOptions && selector === 'tabs';\n const useSelect = hasOptions && selector === 'select';\n const firstOption = options?.[0];\n const activeValue = value ?? firstOption?.value;\n\n return (\n <figure\n className={cn(\n 'group relative flex w-full flex-col overflow-hidden rounded-2xl border border-border2/40 bg-surface2',\n className,\n )}\n >\n {useTabs && firstOption && (\n <Tabs defaultTab={firstOption.value} value={activeValue} onValueChange={onValueChange ?? (() => {})}>\n <div className=\"flex items-stretch\">\n <div className=\"min-w-0 flex-1\">\n <TabList>\n {options.map(opt => (\n <Tab key={opt.value} value={opt.value}>\n {opt.label}\n </Tab>\n ))}\n </TabList>\n </div>\n {actions && <div className=\"border-border1 flex shrink-0 items-center border-b pr-2 pl-3\">{actions}</div>}\n </div>\n </Tabs>\n )}\n\n {useSelect && options && (\n <div className=\"border-border2/40 flex items-center border-b px-2 py-1.5\">\n <Select value={activeValue} onValueChange={onValueChange}>\n <SelectTrigger size=\"sm\" variant=\"ghost\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map(opt => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {actions && <div className=\"ml-auto flex items-center\">{actions}</div>}\n </div>\n )}\n\n {!hasOptions && fileName && (\n <div className=\"border-border2/40 flex items-center border-b px-4 py-2\">\n <figcaption className=\"text-ui-sm text-neutral4 font-mono\">{fileName}</figcaption>\n {actions && <div className=\"ml-auto flex items-center\">{actions}</div>}\n </div>\n )}\n\n {!hasOptions && !fileName && actions && (\n <div className=\"border-border2/40 flex items-center justify-end border-b px-2 py-1.5\">{actions}</div>\n )}\n\n <div className=\"relative\">\n <Code\n code={code}\n lang={lang}\n className={cn(\n 'px-4 py-3 font-mono text-ui-sm text-neutral5',\n overflow === 'scroll' ? 'overflow-x-auto whitespace-pre' : 'break-all whitespace-pre-wrap',\n )}\n />\n <CopyButton\n content={code}\n copyMessage={copyMessage}\n tooltip={copyTooltip}\n size=\"sm\"\n className={cn(\n 'absolute top-2 right-2 opacity-100 group-focus-within:opacity-100 group-hover:opacity-100 pointer-fine:opacity-0',\n transitions.opacity,\n )}\n />\n </div>\n </figure>\n );\n}\n"],"names":["code","jsxs","cn","jsx","Tabs","TabList","Tab","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Code","CopyButton","transitions"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { C as Code } from './code-
|
|
2
|
+
import { C as Code } from './code-DRGJVroV.js';
|
|
3
3
|
import { C as CopyButton } from './copy-button-BLGUkVE8.js';
|
|
4
4
|
import { S as Select, d as SelectTrigger, e as SelectValue, a as SelectContent, c as SelectItem } from './select-B_E0ADD5.js';
|
|
5
5
|
import { a as Tabs, T as TabList } from './tabs-root-BBPou4QC.js';
|
|
@@ -83,4 +83,4 @@ function CodeBlock({
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export { CodeBlock as C };
|
|
86
|
-
//# sourceMappingURL=code-block-
|
|
86
|
+
//# sourceMappingURL=code-block-V4wpmbud.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block-
|
|
1
|
+
{"version":3,"file":"code-block-V4wpmbud.js","sources":["../src/ds/components/CodeBlock/code-block.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Code } from '../Code/code';\nimport { CopyButton } from '../CopyButton/copy-button';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../Select/select';\nimport { TabList } from '../Tabs/tabs-list';\nimport { Tabs } from '../Tabs/tabs-root';\nimport { Tab } from '../Tabs/tabs-tab';\nimport { transitions } from '@/ds/primitives/transitions';\nimport { cn } from '@/lib/utils';\n\nexport type CodeBlockSelector = 'select' | 'tabs';\n\nexport type CodeBlockOverflow = 'wrap' | 'scroll';\n\nexport interface CodeBlockOption {\n label: string;\n value: string;\n}\n\nexport interface CodeBlockProps {\n code: string;\n options?: CodeBlockOption[];\n value?: string;\n onValueChange?: (value: string) => void;\n selector?: CodeBlockSelector;\n fileName?: string;\n lang?: string;\n /** `wrap` (default) breaks long lines — best for commands and snippets.\n * `scroll` preserves columns behind a horizontal scroll — best for source code. */\n overflow?: CodeBlockOverflow;\n copyMessage?: string;\n copyTooltip?: string;\n actions?: ReactNode;\n className?: string;\n}\n\nexport function CodeBlock({\n code,\n options,\n value,\n onValueChange,\n selector = 'select',\n fileName,\n lang,\n overflow = 'wrap',\n copyMessage,\n copyTooltip,\n actions,\n className,\n}: CodeBlockProps) {\n const hasOptions = options && options.length > 0;\n const useTabs = hasOptions && selector === 'tabs';\n const useSelect = hasOptions && selector === 'select';\n const firstOption = options?.[0];\n const activeValue = value ?? firstOption?.value;\n\n return (\n <figure\n className={cn(\n 'group relative flex w-full flex-col overflow-hidden rounded-2xl border border-border2/40 bg-surface2',\n className,\n )}\n >\n {useTabs && firstOption && (\n <Tabs defaultTab={firstOption.value} value={activeValue} onValueChange={onValueChange ?? (() => {})}>\n <div className=\"flex items-stretch\">\n <div className=\"min-w-0 flex-1\">\n <TabList>\n {options.map(opt => (\n <Tab key={opt.value} value={opt.value}>\n {opt.label}\n </Tab>\n ))}\n </TabList>\n </div>\n {actions && <div className=\"border-border1 flex shrink-0 items-center border-b pr-2 pl-3\">{actions}</div>}\n </div>\n </Tabs>\n )}\n\n {useSelect && options && (\n <div className=\"border-border2/40 flex items-center border-b px-2 py-1.5\">\n <Select value={activeValue} onValueChange={onValueChange}>\n <SelectTrigger size=\"sm\" variant=\"ghost\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map(opt => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {actions && <div className=\"ml-auto flex items-center\">{actions}</div>}\n </div>\n )}\n\n {!hasOptions && fileName && (\n <div className=\"border-border2/40 flex items-center border-b px-4 py-2\">\n <figcaption className=\"text-ui-sm text-neutral4 font-mono\">{fileName}</figcaption>\n {actions && <div className=\"ml-auto flex items-center\">{actions}</div>}\n </div>\n )}\n\n {!hasOptions && !fileName && actions && (\n <div className=\"border-border2/40 flex items-center justify-end border-b px-2 py-1.5\">{actions}</div>\n )}\n\n <div className=\"relative\">\n <Code\n code={code}\n lang={lang}\n className={cn(\n 'px-4 py-3 font-mono text-ui-sm text-neutral5',\n overflow === 'scroll' ? 'overflow-x-auto whitespace-pre' : 'break-all whitespace-pre-wrap',\n )}\n />\n <CopyButton\n content={code}\n copyMessage={copyMessage}\n tooltip={copyTooltip}\n size=\"sm\"\n className={cn(\n 'absolute top-2 right-2 opacity-100 group-focus-within:opacity-100 group-hover:opacity-100 pointer-fine:opacity-0',\n transitions.opacity,\n )}\n />\n </div>\n </figure>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { C as Code } from '../code-
|
|
1
|
+
export { C as Code } from '../code-DRGJVroV.js';
|
|
2
2
|
//# sourceMappingURL=Code.es.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { C as CodeBlock } from '../code-block-
|
|
1
|
+
export { C as CodeBlock } from '../code-block-V4wpmbud.js';
|
|
2
2
|
//# sourceMappingURL=CodeBlock.es.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { M as MarkdownRenderer } from '../markdown-renderer-
|
|
1
|
+
export { M as MarkdownRenderer } from '../markdown-renderer-BDe_1JLH.js';
|
|
2
2
|
//# sourceMappingURL=MarkdownRenderer.es.js.map
|
|
@@ -7,7 +7,7 @@ const lucideReact = require('lucide-react');
|
|
|
7
7
|
const React = require('react');
|
|
8
8
|
const Badge = require('../../Badge-BlJpI3fY.cjs');
|
|
9
9
|
const Button = require('../../Button-B4Ixj2aW.cjs');
|
|
10
|
-
const markdownRenderer = require('../../markdown-renderer-
|
|
10
|
+
const markdownRenderer = require('../../markdown-renderer-WHy5Cyu2.cjs');
|
|
11
11
|
const Txt = require('../../Txt-I5lK8zy7.cjs');
|
|
12
12
|
const icons_Icon = require('../../icons/Icon.cjs.js');
|
|
13
13
|
const hooks_useCopyToClipboard = require('../../hooks/use-copy-to-clipboard.cjs.js');
|
|
@@ -3,7 +3,7 @@ import { CheckIcon, CopyIcon, Minimize2, Maximize2, ClipboardList } from 'lucide
|
|
|
3
3
|
import { useState, createContext, useContext } from 'react';
|
|
4
4
|
import { B as Badge } from '../../Badge-woV6OD8k.js';
|
|
5
5
|
import { B as Button } from '../../Button-C-9Pzy-Z.js';
|
|
6
|
-
import { M as MarkdownRenderer } from '../../markdown-renderer-
|
|
6
|
+
import { M as MarkdownRenderer } from '../../markdown-renderer-BDe_1JLH.js';
|
|
7
7
|
import { T as Txt } from '../../Txt-Df42XG5p.js';
|
|
8
8
|
import { Icon } from '../../icons/Icon.es.js';
|
|
9
9
|
import { useCopyToClipboard } from '../../hooks/use-copy-to-clipboard.es.js';
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import Markdown from 'react-markdown';
|
|
4
|
+
import remarkGfm from 'remark-gfm';
|
|
5
|
+
import remend from 'remend';
|
|
6
|
+
import { C as CodeBlock } from './code-block-V4wpmbud.js';
|
|
7
|
+
import { c as cn } from './cn-BakJzhuv.js';
|
|
8
|
+
|
|
9
|
+
import './markdown-renderer.css';const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;
|
|
10
|
+
const FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
|
|
11
|
+
const LIST_ITEM = /^ {0,3}(?:[-*+]|\d{1,9}[.)])(?:[ \t]|$)/;
|
|
12
|
+
const INDENTED = /^(?:\t| {2,})/;
|
|
13
|
+
const DEFINITION = /^ {0,3}\[[^\]]+\]:/;
|
|
14
|
+
function fenceOpenedBy(line) {
|
|
15
|
+
return FENCE_OPEN.exec(line)?.[1];
|
|
16
|
+
}
|
|
17
|
+
function closesFence(line, marker) {
|
|
18
|
+
return FENCE_CLOSE.exec(line)?.[1]?.startsWith(marker) ?? false;
|
|
19
|
+
}
|
|
20
|
+
function startsBlock(line, inList) {
|
|
21
|
+
if (INDENTED.test(line)) return false;
|
|
22
|
+
return !(inList && LIST_ITEM.test(line));
|
|
23
|
+
}
|
|
24
|
+
function splitBlocks(markdown) {
|
|
25
|
+
const lines = markdown.split("\n");
|
|
26
|
+
const starts = [0];
|
|
27
|
+
let fence;
|
|
28
|
+
let started = false;
|
|
29
|
+
let broken = false;
|
|
30
|
+
let inList = false;
|
|
31
|
+
for (const [index, line] of lines.entries()) {
|
|
32
|
+
if (fence) {
|
|
33
|
+
if (closesFence(line, fence)) fence = void 0;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (line.trim() === "") {
|
|
37
|
+
broken = started;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (DEFINITION.test(line)) return [markdown];
|
|
41
|
+
if (broken && startsBlock(line, inList)) {
|
|
42
|
+
starts.push(index);
|
|
43
|
+
started = false;
|
|
44
|
+
}
|
|
45
|
+
if (!started) inList = LIST_ITEM.test(line);
|
|
46
|
+
started = true;
|
|
47
|
+
broken = false;
|
|
48
|
+
fence = fenceOpenedBy(line);
|
|
49
|
+
}
|
|
50
|
+
return starts.map((start, position) => {
|
|
51
|
+
const end = starts[position + 1];
|
|
52
|
+
const block = lines.slice(start, end ?? lines.length).join("\n");
|
|
53
|
+
return end === void 0 ? block : `${block}
|
|
54
|
+
`;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const OPAQUE_TAGS = /* @__PURE__ */ new Set(["code", "pre"]);
|
|
59
|
+
const ANIMATED_TAIL_CHARS = 120;
|
|
60
|
+
function textOf(nodes) {
|
|
61
|
+
let text = "";
|
|
62
|
+
for (const node of nodes) {
|
|
63
|
+
if (node.type === "text") text += node.value;
|
|
64
|
+
else if (node.type === "element") text += textOf(node.children);
|
|
65
|
+
}
|
|
66
|
+
return text;
|
|
67
|
+
}
|
|
68
|
+
function rehypeWordSpans({ complete = false } = {}) {
|
|
69
|
+
return (tree) => {
|
|
70
|
+
const reply = textOf(tree.children);
|
|
71
|
+
const total = reply.length;
|
|
72
|
+
const animateFrom = total - ANIMATED_TAIL_CHARS;
|
|
73
|
+
const pendingFrom = complete ? Number.POSITIVE_INFINITY : total - (/\S*$/.exec(reply)?.[0].length ?? 0);
|
|
74
|
+
let offset = 0;
|
|
75
|
+
const wordProperties = (startsAt, endsAt) => {
|
|
76
|
+
if (startsAt >= pendingFrom) return { className: ["mastra-markdown-word-pending"] };
|
|
77
|
+
if (endsAt > animateFrom) return { className: ["mastra-markdown-word"] };
|
|
78
|
+
return {};
|
|
79
|
+
};
|
|
80
|
+
const wordSpans = (value) => value.split(/(\s+)/).flatMap((chunk) => {
|
|
81
|
+
if (!chunk) return [];
|
|
82
|
+
const startsAt = offset;
|
|
83
|
+
offset += chunk.length;
|
|
84
|
+
if (/^\s+$/.test(chunk)) return [{ type: "text", value: chunk }];
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
type: "element",
|
|
88
|
+
tagName: "span",
|
|
89
|
+
properties: wordProperties(startsAt, offset),
|
|
90
|
+
children: [{ type: "text", value: chunk }]
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
});
|
|
94
|
+
const wrapNodes = (nodes) => nodes.flatMap((node) => {
|
|
95
|
+
if (node.type === "text") return wordSpans(node.value);
|
|
96
|
+
if (node.type === "element") {
|
|
97
|
+
if (OPAQUE_TAGS.has(node.tagName)) offset += textOf(node.children).length;
|
|
98
|
+
else node.children = wrapNodes(node.children);
|
|
99
|
+
}
|
|
100
|
+
return [node];
|
|
101
|
+
});
|
|
102
|
+
tree.children = wrapNodes(tree.children);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const MarkdownRenderer = memo(function MarkdownRenderer2({
|
|
107
|
+
children,
|
|
108
|
+
className,
|
|
109
|
+
externalLinkTarget = "tab",
|
|
110
|
+
streaming = false
|
|
111
|
+
}) {
|
|
112
|
+
const blocks = splitBlocks(decodeEscapedNewlines(children));
|
|
113
|
+
const last = blocks.length - 1;
|
|
114
|
+
const components = externalLinkTarget === "window" ? WINDOW_COMPONENTS : COMPONENTS;
|
|
115
|
+
return /* @__PURE__ */ jsx("div", { className: cn("mastra-markdown", streaming && "mastra-markdown-streaming", className), children: blocks.map((block, index) => {
|
|
116
|
+
const growing = streaming && index === last;
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
MarkdownBlock,
|
|
119
|
+
{
|
|
120
|
+
content: growing ? remend(block, REMEND_OPTIONS) : block,
|
|
121
|
+
rehypePlugins: wordSpansFor(streaming, growing),
|
|
122
|
+
components
|
|
123
|
+
},
|
|
124
|
+
index
|
|
125
|
+
);
|
|
126
|
+
}) });
|
|
127
|
+
});
|
|
128
|
+
const MarkdownBlock = memo(function MarkdownBlock2({
|
|
129
|
+
components,
|
|
130
|
+
content,
|
|
131
|
+
rehypePlugins
|
|
132
|
+
}) {
|
|
133
|
+
return /* @__PURE__ */ jsx(Markdown, { remarkPlugins: REMARK_PLUGINS, rehypePlugins, components, children: content });
|
|
134
|
+
});
|
|
135
|
+
const NO_WORD_SPANS = [];
|
|
136
|
+
const GROWING_WORD_SPANS = [[rehypeWordSpans, { complete: false }]];
|
|
137
|
+
const SETTLED_WORD_SPANS = [[rehypeWordSpans, { complete: true }]];
|
|
138
|
+
function wordSpansFor(streaming, growing) {
|
|
139
|
+
if (!streaming) return NO_WORD_SPANS;
|
|
140
|
+
return growing ? GROWING_WORD_SPANS : SETTLED_WORD_SPANS;
|
|
141
|
+
}
|
|
142
|
+
function decodeEscapedNewlines(text) {
|
|
143
|
+
return text.includes("\n") ? text : text.replace(/\\n/g, "\n");
|
|
144
|
+
}
|
|
145
|
+
function languageOf(node) {
|
|
146
|
+
const classNames = node.properties.className;
|
|
147
|
+
if (!Array.isArray(classNames)) return void 0;
|
|
148
|
+
const language = classNames.find((entry) => typeof entry === "string" && entry.startsWith("language-"));
|
|
149
|
+
return typeof language === "string" ? language.slice("language-".length) : void 0;
|
|
150
|
+
}
|
|
151
|
+
function fencedCode(node) {
|
|
152
|
+
const child = node?.children.find((entry) => entry.type === "element" && entry.tagName === "code");
|
|
153
|
+
if (child?.type !== "element") return void 0;
|
|
154
|
+
const code = child.children.map((entry) => entry.type === "text" ? entry.value : "").join("");
|
|
155
|
+
return { code: code.replace(/\n$/, ""), language: languageOf(child) };
|
|
156
|
+
}
|
|
157
|
+
function MarkdownCodeBlock({ node, children }) {
|
|
158
|
+
const fenced = fencedCode(node);
|
|
159
|
+
if (!fenced) return /* @__PURE__ */ jsx("pre", { children });
|
|
160
|
+
return /* @__PURE__ */ jsx(
|
|
161
|
+
CodeBlock,
|
|
162
|
+
{
|
|
163
|
+
code: fenced.code,
|
|
164
|
+
lang: fenced.language,
|
|
165
|
+
overflow: "scroll",
|
|
166
|
+
className: "bg-surface1 my-3",
|
|
167
|
+
copyMessage: "Copied code to clipboard"
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const POPUP_WINDOW_FEATURES = "popup=yes,width=720,height=800,resizable=yes,scrollbars=yes";
|
|
172
|
+
function isPlainLeftClick(event) {
|
|
173
|
+
return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
|
|
174
|
+
}
|
|
175
|
+
function markdownLink(externalLinkTarget) {
|
|
176
|
+
return function MarkdownLink({ href, title, children }) {
|
|
177
|
+
const isExternal = /^https?:\/\//i.test(href ?? "");
|
|
178
|
+
const openInPopup = (event) => {
|
|
179
|
+
if (!isExternal || externalLinkTarget !== "window" || !isPlainLeftClick(event)) return;
|
|
180
|
+
const popup = window.open(href, "_blank", POPUP_WINDOW_FEATURES);
|
|
181
|
+
if (!popup) return;
|
|
182
|
+
popup.opener = null;
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
};
|
|
185
|
+
return /* @__PURE__ */ jsx(
|
|
186
|
+
"a",
|
|
187
|
+
{
|
|
188
|
+
href,
|
|
189
|
+
title,
|
|
190
|
+
onClick: openInPopup,
|
|
191
|
+
target: isExternal ? "_blank" : void 0,
|
|
192
|
+
rel: isExternal ? "noopener noreferrer" : void 0,
|
|
193
|
+
children
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const REMARK_PLUGINS = [remarkGfm];
|
|
199
|
+
const REMEND_OPTIONS = { katex: false, linkMode: "text-only" };
|
|
200
|
+
const COMPONENTS = {
|
|
201
|
+
pre: MarkdownCodeBlock,
|
|
202
|
+
a: markdownLink("tab")
|
|
203
|
+
};
|
|
204
|
+
const WINDOW_COMPONENTS = {
|
|
205
|
+
...COMPONENTS,
|
|
206
|
+
a: markdownLink("window")
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export { MarkdownRenderer as M };
|
|
210
|
+
//# sourceMappingURL=markdown-renderer-BDe_1JLH.js.map
|