@gridui/markdown 0.0.0-stage → 1.0.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.
- package/README.md +37 -2
- package/dist/index.d.ts +8 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +7 -0
- package/dist/markdown/markdown.d.ts +22 -0
- package/dist/styles.css +85 -0
- package/docs/catalog.json +40 -0
- package/package.json +51 -3
package/README.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gridui/markdown
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Render Markdown content using the existing GridUI theme. Import `Markdown` from
|
|
4
|
+
`@gridui/markdown`; load `@gridui/theme/styles.css` before
|
|
5
|
+
`@gridui/markdown/styles.css`. Consumers do not need a styling compiler.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import "@gridui/theme/styles.css";
|
|
9
|
+
import "@gridui/markdown/styles.css";
|
|
10
|
+
import { Markdown } from "@gridui/markdown";
|
|
11
|
+
|
|
12
|
+
<Markdown streaming={isReceiving}>{responseText}</Markdown>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Pass the complete current text snapshot as `children`. Updates support unfinished
|
|
16
|
+
fenced code and other CommonMark syntax through `react-markdown`; GFM tables,
|
|
17
|
+
task lists, strikethrough and autolinks are enabled. `streaming` exposes
|
|
18
|
+
`aria-busy` without announcing every token. Applications own transport, buffering,
|
|
19
|
+
throttling, completion announcements, and scroll following. Parsing is per
|
|
20
|
+
snapshot, not an incremental parser or a network stream implementation.
|
|
21
|
+
|
|
22
|
+
Raw HTML is skipped. URLs pass through react-markdown's safe default transform;
|
|
23
|
+
there is no raw-HTML or unsafe URL-transform escape hatch. `components` can
|
|
24
|
+
replace semantic renderers, including code, links and images. Those trusted
|
|
25
|
+
renderers own their output and accessibility; never inject untrusted HTML.
|
|
26
|
+
Images use the supplied URLs, so the application owns remote-media policy.
|
|
27
|
+
|
|
28
|
+
Use `[1](#citation:guide)` with
|
|
29
|
+
`citations={[{ id: "guide", title: "Guide", href: "https://example.com" }]}`
|
|
30
|
+
to resolve application-supplied citations. IDs must be nonempty and unique.
|
|
31
|
+
Unknown citations and rejected URLs render without an active link. A custom
|
|
32
|
+
link renderer can compose the application's existing citation presentation.
|
|
33
|
+
|
|
34
|
+
Compose messages with native `article`/heading elements and `@gridui/follow-scroll`.
|
|
35
|
+
Keep author identity, timestamps, message actions, retries and persistence in the
|
|
36
|
+
application. The Streaming story demonstrates successive response snapshots.
|
|
37
|
+
|
|
38
|
+
See [release history](https://github.com/sudo-ui/GridUI-React-Monorepo/tree/main/packages-core/markdown/.changeset/history).
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// src/markdown/markdown.tsx
|
|
2
|
+
import { cn } from "@gridui/style-utils";
|
|
3
|
+
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
|
|
4
|
+
import remarkGfm from "remark-gfm";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
var defaultComponents = {
|
|
7
|
+
a: ({ node: _node, href, children, ...anchorProps }) => {
|
|
8
|
+
return href ? /* @__PURE__ */ jsx(
|
|
9
|
+
"a",
|
|
10
|
+
{
|
|
11
|
+
...anchorProps,
|
|
12
|
+
href,
|
|
13
|
+
children
|
|
14
|
+
}
|
|
15
|
+
) : /* @__PURE__ */ jsx("span", { children });
|
|
16
|
+
},
|
|
17
|
+
img: ({ node: _node, src, alt, ...imageProps }) => {
|
|
18
|
+
return src ? /* @__PURE__ */ jsx(
|
|
19
|
+
"img",
|
|
20
|
+
{
|
|
21
|
+
...imageProps,
|
|
22
|
+
src,
|
|
23
|
+
alt: alt ?? ""
|
|
24
|
+
}
|
|
25
|
+
) : /* @__PURE__ */ jsx("span", { children: alt });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var Markdown = (props) => {
|
|
29
|
+
const { children, streaming = false, components, citations = [], className, ...restProps } = props;
|
|
30
|
+
const sources = new Map(citations.map((citation) => {
|
|
31
|
+
return [
|
|
32
|
+
citation.id,
|
|
33
|
+
citation
|
|
34
|
+
];
|
|
35
|
+
}));
|
|
36
|
+
if (sources.size !== citations.length || citations.some((citation) => {
|
|
37
|
+
return !citation.id;
|
|
38
|
+
})) {
|
|
39
|
+
throw new Error("Markdown citation IDs must be nonempty and unique.");
|
|
40
|
+
}
|
|
41
|
+
return /* @__PURE__ */ jsx(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
...restProps,
|
|
45
|
+
className: cn("gridui-markdown", className),
|
|
46
|
+
"aria-busy": streaming || void 0,
|
|
47
|
+
"data-streaming": streaming || void 0,
|
|
48
|
+
children: /* @__PURE__ */ jsx(
|
|
49
|
+
ReactMarkdown,
|
|
50
|
+
{
|
|
51
|
+
skipHtml: true,
|
|
52
|
+
remarkPlugins: [remarkGfm],
|
|
53
|
+
urlTransform: (url, key, node) => {
|
|
54
|
+
if (url.startsWith("#citation:")) {
|
|
55
|
+
const citation = sources.get(url.slice("#citation:".length));
|
|
56
|
+
if (citation && key === "href") {
|
|
57
|
+
node.properties.title = citation.title;
|
|
58
|
+
}
|
|
59
|
+
return citation ? defaultUrlTransform(citation.href) : "";
|
|
60
|
+
}
|
|
61
|
+
return defaultUrlTransform(url);
|
|
62
|
+
},
|
|
63
|
+
components: {
|
|
64
|
+
...defaultComponents,
|
|
65
|
+
...components
|
|
66
|
+
},
|
|
67
|
+
children
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
export {
|
|
74
|
+
Markdown
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/markdown/markdown.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * @author WMXPY\n * @package Markdown\n * @namespace Markdown\n * @description Markdown\n */\n\nimport { cn } from \"@gridui/style-utils\";\nimport type { ComponentPropsWithRef, FC } from \"react\";\nimport ReactMarkdown, { type Components, defaultUrlTransform } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\n\nexport type MarkdownComponents = Components;\n\nexport type MarkdownCitation = {\n readonly id: string;\n readonly title: string;\n readonly href: string;\n};\n\nexport type MarkdownProps = Omit<ComponentPropsWithRef<\"div\">, \"children\" | \"dangerouslySetInnerHTML\"> & {\n readonly children: string;\n readonly streaming?: boolean;\n readonly components?: MarkdownComponents;\n readonly citations?: readonly MarkdownCitation[];\n};\n\nconst defaultComponents: MarkdownComponents = {\n a: ({ node: _node, href, children, ...anchorProps }) => {\n return href ? (<a\n {...anchorProps}\n href={href}\n >\n {children}\n </a>) : (<span>\n {children}\n </span>);\n },\n img: ({ node: _node, src, alt, ...imageProps }) => {\n return src ? (<img\n {...imageProps}\n src={src}\n alt={alt ?? \"\"}\n />) : (<span>\n {alt}\n </span>);\n },\n};\n\n/** Renders the current document snapshot; transport and update scheduling stay with the host. */\nexport const Markdown: FC<MarkdownProps> = (props: MarkdownProps) => {\n const { children, streaming = false, components, citations = [], className, ...restProps } = props;\n const sources = new Map(citations.map((citation) => {\n return [\n citation.id,\n citation,\n ];\n }));\n if (sources.size !== citations.length || citations.some((citation) => {\n return !citation.id;\n })) {\n throw new Error(\"Markdown citation IDs must be nonempty and unique.\");\n }\n return (<div\n {...restProps}\n className={cn(\"gridui-markdown\", className)}\n aria-busy={streaming || undefined}\n data-streaming={streaming || undefined}\n >\n <ReactMarkdown\n skipHtml\n remarkPlugins={[remarkGfm]}\n urlTransform={(url, key, node) => {\n if (url.startsWith(\"#citation:\")) {\n const citation = sources.get(url.slice(\"#citation:\".length));\n if (citation && key === \"href\") {\n node.properties.title = citation.title;\n }\n return citation ? defaultUrlTransform(citation.href) : \"\";\n }\n return defaultUrlTransform(url);\n }}\n components={{\n ...defaultComponents,\n ...components,\n }}\n >\n {children}\n </ReactMarkdown>\n </div>);\n};\n"],
|
|
5
|
+
"mappings": ";AAOA,SAAS,UAAU;AAEnB,OAAO,iBAAkC,2BAA2B;AACpE,OAAO,eAAe;AAmBC;AAFvB,IAAM,oBAAwC;AAAA,EAC1C,GAAG,CAAC,EAAE,MAAM,OAAO,MAAM,UAAU,GAAG,YAAY,MAAM;AACpD,WAAO,OAAQ;AAAA,MAAC;AAAA;AAAA,QACX,GAAG;AAAA,QACJ;AAAA,QAEC;AAAA;AAAA,IACL,IAAS,oBAAC,UACL,UACL;AAAA,EACJ;AAAA,EACA,KAAK,CAAC,EAAE,MAAM,OAAO,KAAK,KAAK,GAAG,WAAW,MAAM;AAC/C,WAAO,MAAO;AAAA,MAAC;AAAA;AAAA,QACV,GAAG;AAAA,QACJ;AAAA,QACA,KAAK,OAAO;AAAA;AAAA,IAChB,IAAO,oBAAC,UACH,eACL;AAAA,EACJ;AACJ;AAGO,IAAM,WAA8B,CAAC,UAAyB;AACjE,QAAM,EAAE,UAAU,YAAY,OAAO,YAAY,YAAY,CAAC,GAAG,WAAW,GAAG,UAAU,IAAI;AAC7F,QAAM,UAAU,IAAI,IAAI,UAAU,IAAI,CAAC,aAAa;AAChD,WAAO;AAAA,MACH,SAAS;AAAA,MACT;AAAA,IACJ;AAAA,EACJ,CAAC,CAAC;AACF,MAAI,QAAQ,SAAS,UAAU,UAAU,UAAU,KAAK,CAAC,aAAa;AAClE,WAAO,CAAC,SAAS;AAAA,EACrB,CAAC,GAAG;AACA,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACxE;AACA,SAAQ;AAAA,IAAC;AAAA;AAAA,MACJ,GAAG;AAAA,MACJ,WAAW,GAAG,mBAAmB,SAAS;AAAA,MAC1C,aAAW,aAAa;AAAA,MACxB,kBAAgB,aAAa;AAAA,MAE7B;AAAA,QAAC;AAAA;AAAA,UACG,UAAQ;AAAA,UACR,eAAe,CAAC,SAAS;AAAA,UACzB,cAAc,CAAC,KAAK,KAAK,SAAS;AAC9B,gBAAI,IAAI,WAAW,YAAY,GAAG;AAC9B,oBAAM,WAAW,QAAQ,IAAI,IAAI,MAAM,aAAa,MAAM,CAAC;AAC3D,kBAAI,YAAY,QAAQ,QAAQ;AAC5B,qBAAK,WAAW,QAAQ,SAAS;AAAA,cACrC;AACA,qBAAO,WAAW,oBAAoB,SAAS,IAAI,IAAI;AAAA,YAC3D;AACA,mBAAO,oBAAoB,GAAG;AAAA,UAClC;AAAA,UACA,YAAY;AAAA,YACR,GAAG;AAAA,YACH,GAAG;AAAA,UACP;AAAA,UAEC;AAAA;AAAA,MACL;AAAA;AAAA,EACJ;AACJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author WMXPY
|
|
3
|
+
* @package Markdown
|
|
4
|
+
* @namespace Markdown
|
|
5
|
+
* @description Markdown
|
|
6
|
+
*/
|
|
7
|
+
import type { ComponentPropsWithRef, FC } from "react";
|
|
8
|
+
import { type Components } from "react-markdown";
|
|
9
|
+
export type MarkdownComponents = Components;
|
|
10
|
+
export type MarkdownCitation = {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly title: string;
|
|
13
|
+
readonly href: string;
|
|
14
|
+
};
|
|
15
|
+
export type MarkdownProps = Omit<ComponentPropsWithRef<"div">, "children" | "dangerouslySetInnerHTML"> & {
|
|
16
|
+
readonly children: string;
|
|
17
|
+
readonly streaming?: boolean;
|
|
18
|
+
readonly components?: MarkdownComponents;
|
|
19
|
+
readonly citations?: readonly MarkdownCitation[];
|
|
20
|
+
};
|
|
21
|
+
/** Renders the current document snapshot; transport and update scheduling stay with the host. */
|
|
22
|
+
export declare const Markdown: FC<MarkdownProps>;
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.4 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer theme, base, components, utilities;
|
|
3
|
+
@layer utilities;
|
|
4
|
+
@layer components {
|
|
5
|
+
.gridui-markdown {
|
|
6
|
+
overflow-wrap: anywhere;
|
|
7
|
+
color: var(--gridui-foreground);
|
|
8
|
+
line-height: 1.6;
|
|
9
|
+
}
|
|
10
|
+
.gridui-markdown > :first-child {
|
|
11
|
+
margin-block-start: 0;
|
|
12
|
+
}
|
|
13
|
+
.gridui-markdown > :last-child {
|
|
14
|
+
margin-block-end: 0;
|
|
15
|
+
}
|
|
16
|
+
.gridui-markdown :where(p, ul, ol, pre, blockquote, table) {
|
|
17
|
+
margin-block: var(--gridui-space-16);
|
|
18
|
+
}
|
|
19
|
+
.gridui-markdown :where(ul, ol) {
|
|
20
|
+
padding-inline-start: var(--gridui-space-24);
|
|
21
|
+
}
|
|
22
|
+
.gridui-markdown ul {
|
|
23
|
+
list-style: disc;
|
|
24
|
+
}
|
|
25
|
+
.gridui-markdown ol {
|
|
26
|
+
list-style: decimal;
|
|
27
|
+
}
|
|
28
|
+
.gridui-markdown :where(h1, h2, h3, h4, h5, h6) {
|
|
29
|
+
margin-block: var(--gridui-space-24) var(--gridui-space-8);
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
line-height: 1.3;
|
|
32
|
+
}
|
|
33
|
+
.gridui-markdown h1 {
|
|
34
|
+
font-size: 1.75em;
|
|
35
|
+
}
|
|
36
|
+
.gridui-markdown h2 {
|
|
37
|
+
font-size: 1.5em;
|
|
38
|
+
}
|
|
39
|
+
.gridui-markdown h3 {
|
|
40
|
+
font-size: 1.25em;
|
|
41
|
+
}
|
|
42
|
+
.gridui-markdown a {
|
|
43
|
+
color: var(--gridui-link);
|
|
44
|
+
text-decoration: underline;
|
|
45
|
+
text-underline-offset: 0.15em;
|
|
46
|
+
}
|
|
47
|
+
.gridui-markdown :where(pre, code) {
|
|
48
|
+
font-family: monospace;
|
|
49
|
+
background: var(--gridui-surface);
|
|
50
|
+
}
|
|
51
|
+
.gridui-markdown pre {
|
|
52
|
+
overflow: auto;
|
|
53
|
+
padding: var(--gridui-space-16);
|
|
54
|
+
border: 1px solid var(--gridui-border);
|
|
55
|
+
white-space: pre;
|
|
56
|
+
}
|
|
57
|
+
.gridui-markdown :not(pre) > code {
|
|
58
|
+
padding-inline: var(--gridui-space-4);
|
|
59
|
+
}
|
|
60
|
+
.gridui-markdown blockquote {
|
|
61
|
+
padding-inline-start: var(--gridui-space-16);
|
|
62
|
+
border-inline-start: 2px solid var(--gridui-border);
|
|
63
|
+
color: var(--gridui-muted);
|
|
64
|
+
}
|
|
65
|
+
.gridui-markdown table {
|
|
66
|
+
display: block;
|
|
67
|
+
max-width: 100%;
|
|
68
|
+
overflow: auto;
|
|
69
|
+
border-collapse: collapse;
|
|
70
|
+
}
|
|
71
|
+
.gridui-markdown :where(th, td) {
|
|
72
|
+
padding: var(--gridui-space-8) var(--gridui-space-12);
|
|
73
|
+
border: 1px solid var(--gridui-border);
|
|
74
|
+
text-align: start;
|
|
75
|
+
}
|
|
76
|
+
.gridui-markdown img {
|
|
77
|
+
max-width: 100%;
|
|
78
|
+
height: auto;
|
|
79
|
+
}
|
|
80
|
+
.gridui-markdown hr {
|
|
81
|
+
border: 0;
|
|
82
|
+
border-block-start: 1px solid var(--gridui-border);
|
|
83
|
+
margin-block: var(--gridui-space-24);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"name": "@gridui/markdown",
|
|
4
|
+
"version": "1.0.1",
|
|
5
|
+
"category": "core",
|
|
6
|
+
"description": "GridUI React Markdown content with streaming updates",
|
|
7
|
+
"usage": "Render Markdown content using the existing GridUI theme. Import `Markdown` from `@gridui/markdown`; load `@gridui/theme/styles.css` before `@gridui/markdown/styles.css`. Consumers do not need a styling compiler.",
|
|
8
|
+
"documentation": [
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"exports": [
|
|
12
|
+
{
|
|
13
|
+
"name": "Markdown",
|
|
14
|
+
"kind": "component",
|
|
15
|
+
"importPath": "@gridui/markdown"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "MarkdownCitation",
|
|
19
|
+
"kind": "type",
|
|
20
|
+
"importPath": "@gridui/markdown"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "MarkdownComponents",
|
|
24
|
+
"kind": "type",
|
|
25
|
+
"importPath": "@gridui/markdown"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "MarkdownProps",
|
|
29
|
+
"kind": "type",
|
|
30
|
+
"importPath": "@gridui/markdown"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"styles": [
|
|
34
|
+
"@gridui/theme/styles.css",
|
|
35
|
+
"@gridui/markdown/styles.css"
|
|
36
|
+
],
|
|
37
|
+
"examples": [
|
|
38
|
+
"https://github.com/sudo-ui/GridUI-React-Monorepo/blob/main/packages-core/markdown/stories/markdown.stories.tsx"
|
|
39
|
+
]
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gridui/markdown",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "GridUI React Markdown content with streaming updates",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"docs"
|
|
11
|
+
],
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"**/*.css"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./styles.css": "./dist/styles.css"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^19.2.0",
|
|
27
|
+
"react-dom": "^19.2.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@gridui/style-utils": "^2.2.2",
|
|
31
|
+
"@gridui/theme": "^2.5.3",
|
|
32
|
+
"react-markdown": "10.1.0",
|
|
33
|
+
"remark-gfm": "4.0.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@gridui-internal/configuration": "0.0.0-internal",
|
|
37
|
+
"@jest/globals": "30.5.2",
|
|
38
|
+
"@storybook/react-vite": "10.6.6",
|
|
39
|
+
"@types/jest": "30.0.0",
|
|
40
|
+
"@types/node": "26.6.6",
|
|
41
|
+
"@types/react": "19.3.0",
|
|
42
|
+
"@types/react-dom": "19.3.0",
|
|
43
|
+
"jest": "30.5.2",
|
|
44
|
+
"react": "19.3.1",
|
|
45
|
+
"react-dom": "19.3.1",
|
|
46
|
+
"typescript": "6.0.3"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "node ../../scripts/compilation/package.ts",
|
|
50
|
+
"compile": "tsc --project tsconfig.json",
|
|
51
|
+
"test": "jest",
|
|
52
|
+
"coverage": "jest --coverage"
|
|
53
|
+
}
|
|
6
54
|
}
|