cms-renderer 0.6.4 → 0.6.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.
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface DocsMarkdownProps {
5
+ content: string;
6
+ className?: string;
7
+ renderImage?: (props: {
8
+ src: string;
9
+ alt: string;
10
+ title?: string;
11
+ }) => ReactNode;
12
+ }
13
+ declare function markdownStartsWithHeading(markdown: string): boolean;
14
+ declare function initMarkdown(): Promise<void>;
15
+ declare function DocsMarkdown({ content, className, renderImage, }: DocsMarkdownProps): Promise<React.JSX.Element>;
16
+
17
+ export { DocsMarkdown, type DocsMarkdownProps, initMarkdown, markdownStartsWithHeading };
@@ -0,0 +1,258 @@
1
+ // ../../packages/markdown-wasm/src/index.ts
2
+ import { init as init2, mdToHtml, mdToJSON as mdToJSON2, mdToReadableHtml, ParseFlags } from "md4w";
3
+
4
+ // ../../packages/markdown-wasm/src/components.tsx
5
+ import { NodeType } from "md4w";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function Heading({ level, children }) {
8
+ const Tag = `h${level}`;
9
+ return /* @__PURE__ */ jsx(Tag, { children });
10
+ }
11
+ var defaultComponents = {
12
+ // Block elements
13
+ [NodeType.QUOTE]: (props) => /* @__PURE__ */ jsx("blockquote", { children: props.children }),
14
+ [NodeType.UL]: (props) => /* @__PURE__ */ jsx("ul", { children: props.children }),
15
+ [NodeType.OL]: (props) => /* @__PURE__ */ jsx("ol", { start: props.start, children: props.children }),
16
+ [NodeType.LI]: (props) => {
17
+ const isTask = props.isTask;
18
+ const done = props.done;
19
+ if (isTask) {
20
+ return /* @__PURE__ */ jsxs("li", { children: [
21
+ /* @__PURE__ */ jsx("input", { type: "checkbox", checked: done, disabled: true, readOnly: true }),
22
+ props.children
23
+ ] });
24
+ }
25
+ return /* @__PURE__ */ jsx("li", { children: props.children });
26
+ },
27
+ [NodeType.HR]: () => /* @__PURE__ */ jsx("hr", {}),
28
+ [NodeType.CODE_BLOCK]: (props) => {
29
+ const language = props.lang?.toLowerCase() || "plaintext";
30
+ const languageClass = language ? `language-${language}` : void 0;
31
+ return /* @__PURE__ */ jsx("pre", { className: languageClass, children: /* @__PURE__ */ jsx("code", { className: languageClass, "data-language": language, children: props.children }) });
32
+ },
33
+ [NodeType.HTML]: (props) => {
34
+ const html = getHtmlString(props.children);
35
+ if (html && isLineBreakHtml(html)) {
36
+ return /* @__PURE__ */ jsx("br", {});
37
+ }
38
+ return /* @__PURE__ */ jsx("div", { "data-markdown-html": true, children: props.children });
39
+ },
40
+ [NodeType.P]: (props) => /* @__PURE__ */ jsx("p", { children: props.children }),
41
+ // Table elements
42
+ [NodeType.TABLE]: (props) => /* @__PURE__ */ jsx("table", { children: props.children }),
43
+ [NodeType.THEAD]: (props) => /* @__PURE__ */ jsx("thead", { children: props.children }),
44
+ [NodeType.TBODY]: (props) => /* @__PURE__ */ jsx("tbody", { children: props.children }),
45
+ [NodeType.TR]: (props) => /* @__PURE__ */ jsx("tr", { children: props.children }),
46
+ [NodeType.TH]: (props) => {
47
+ const align = props.align;
48
+ return /* @__PURE__ */ jsx("th", { style: align ? { textAlign: align } : void 0, children: props.children });
49
+ },
50
+ [NodeType.TD]: (props) => {
51
+ const align = props.align;
52
+ return /* @__PURE__ */ jsx("td", { style: align ? { textAlign: align } : void 0, children: props.children });
53
+ },
54
+ // Headings (H1-H6 share the same component with level prop injected by renderer)
55
+ [NodeType.H1]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 1, children: props.children }),
56
+ [NodeType.H2]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 2, children: props.children }),
57
+ [NodeType.H3]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 3, children: props.children }),
58
+ [NodeType.H4]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 4, children: props.children }),
59
+ [NodeType.H5]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 5, children: props.children }),
60
+ [NodeType.H6]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 6, children: props.children }),
61
+ // Inline elements
62
+ [NodeType.EM]: (props) => /* @__PURE__ */ jsx("em", { children: props.children }),
63
+ [NodeType.STRONG]: (props) => /* @__PURE__ */ jsx("strong", { children: props.children }),
64
+ [NodeType.A]: (props) => /* @__PURE__ */ jsx("a", { href: props.href, title: props.title, children: props.children }),
65
+ [NodeType.IMG]: (props) => (
66
+ // biome-ignore lint/performance/noImgElement: Generic markdown package, not Next.js specific. Users can override with next/image.
67
+ /* @__PURE__ */ jsx(
68
+ "img",
69
+ {
70
+ src: props.src,
71
+ alt: props.alt,
72
+ title: props.title
73
+ }
74
+ )
75
+ ),
76
+ [NodeType.CODE_SPAN]: (props) => /* @__PURE__ */ jsx("code", { children: props.children }),
77
+ [NodeType.DEL]: (props) => /* @__PURE__ */ jsx("del", { children: props.children }),
78
+ // Math (LaTeX)
79
+ [NodeType.LATEXMATH]: (props) => /* @__PURE__ */ jsx("span", { "data-math": "inline", children: props.children }),
80
+ [NodeType.LATEXMATH_DISPLAY]: (props) => /* @__PURE__ */ jsx("div", { "data-math": "display", children: props.children }),
81
+ // Wiki links
82
+ [NodeType.WIKILINK]: (props) => {
83
+ const target = props.target;
84
+ return /* @__PURE__ */ jsx("a", { href: `/wiki/${encodeURIComponent(target)}`, "data-wikilink": target, children: props.children });
85
+ },
86
+ // Underline (when UNDERLINE parse flag is enabled)
87
+ [NodeType.U]: (props) => /* @__PURE__ */ jsx("u", { children: props.children })
88
+ };
89
+ function getHtmlString(children) {
90
+ if (typeof children === "string") {
91
+ return children;
92
+ }
93
+ if (Array.isArray(children) && children.length === 1 && typeof children[0] === "string") {
94
+ return children[0];
95
+ }
96
+ return null;
97
+ }
98
+ function isLineBreakHtml(html) {
99
+ const trimmed = html.trim().toLowerCase();
100
+ return trimmed === "<br>" || trimmed === "<br/>" || trimmed === "<br />";
101
+ }
102
+ var minimalComponents = {
103
+ [NodeType.P]: (props) => /* @__PURE__ */ jsx("p", { children: props.children }),
104
+ [NodeType.H1]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 1, children: props.children }),
105
+ [NodeType.H2]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 2, children: props.children }),
106
+ [NodeType.H3]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 3, children: props.children }),
107
+ [NodeType.H4]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 4, children: props.children }),
108
+ [NodeType.H5]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 5, children: props.children }),
109
+ [NodeType.H6]: (props) => /* @__PURE__ */ jsx(Heading, { level: props.level ?? 6, children: props.children }),
110
+ [NodeType.STRONG]: (props) => /* @__PURE__ */ jsx("strong", { children: props.children }),
111
+ [NodeType.EM]: (props) => /* @__PURE__ */ jsx("em", { children: props.children }),
112
+ [NodeType.A]: (props) => /* @__PURE__ */ jsx("a", { href: props.href, children: props.children }),
113
+ [NodeType.CODE_SPAN]: (props) => /* @__PURE__ */ jsx("code", { children: props.children })
114
+ };
115
+
116
+ // ../../packages/markdown-wasm/src/renderer.tsx
117
+ import { init, mdToJSON, NodeType as NodeType2 } from "md4w";
118
+ import { Fragment } from "react";
119
+
120
+ // ../../packages/markdown-wasm/src/types.ts
121
+ function getHeadingLevel(type) {
122
+ const levelMap = {
123
+ 21: 1,
124
+ 22: 2,
125
+ 23: 3,
126
+ 24: 4,
127
+ 25: 5,
128
+ 26: 6
129
+ };
130
+ return levelMap[type];
131
+ }
132
+
133
+ // ../../packages/markdown-wasm/src/renderer.tsx
134
+ import { NodeType as NodeType3 } from "md4w";
135
+ import { Fragment as Fragment2, jsx as jsx2 } from "react/jsx-runtime";
136
+ var initPromise = null;
137
+ async function ensureInitialized() {
138
+ if (!initPromise) {
139
+ initPromise = init("small");
140
+ }
141
+ return initPromise;
142
+ }
143
+ async function initMarkdown() {
144
+ await ensureInitialized();
145
+ }
146
+
147
+ // lib/docs-markdown.tsx
148
+ import { Fragment as Fragment3 } from "react";
149
+ import { codeToTokens } from "shiki";
150
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
151
+ var defaultClassName = "cms-docs-markdown";
152
+ var markdownInitPromise;
153
+ function markdownStartsWithHeading(markdown) {
154
+ return /^\s*#\s+/.test(markdown);
155
+ }
156
+ async function initMarkdown2() {
157
+ markdownInitPromise ??= initMarkdown();
158
+ await markdownInitPromise;
159
+ }
160
+ function isTextNode2(node) {
161
+ return typeof node === "string";
162
+ }
163
+ function buildComponentProps(type, props) {
164
+ const result = { ...props };
165
+ const headingLevel = getHeadingLevel(type);
166
+ if (headingLevel !== void 0) {
167
+ result.level = headingLevel;
168
+ }
169
+ return result;
170
+ }
171
+ function normalizeLanguage(lang) {
172
+ if (typeof lang !== "string") {
173
+ return "text";
174
+ }
175
+ const normalized = lang.trim().toLowerCase();
176
+ return normalized || "text";
177
+ }
178
+ async function renderHighlightedCode(code, lang) {
179
+ const language = normalizeLanguage(lang);
180
+ try {
181
+ const result = await codeToTokens(code, {
182
+ lang: language,
183
+ theme: "github-dark"
184
+ });
185
+ return /* @__PURE__ */ jsx3(
186
+ "pre",
187
+ {
188
+ className: `shiki language-${language}`,
189
+ style: { backgroundColor: result.bg, color: result.fg },
190
+ children: /* @__PURE__ */ jsx3("code", { className: `shiki_code language-${language}`, "data-language": language, children: (() => {
191
+ const lineOccurrences = /* @__PURE__ */ new Map();
192
+ return result.tokens.map((line, lineIndex) => {
193
+ const isLastLine = lineIndex === result.tokens.length - 1;
194
+ const lineSignature = line.map((token) => `${token.offset}:${token.color ?? ""}:${token.content}`).join("|");
195
+ const lineOccurrence = lineOccurrences.get(lineSignature) ?? 0;
196
+ lineOccurrences.set(lineSignature, lineOccurrence + 1);
197
+ let tokenCursor = 0;
198
+ return /* @__PURE__ */ jsxs2(Fragment3, { children: [
199
+ line.map((token) => {
200
+ const tokenKey = `${token.offset}:${token.color ?? ""}:${tokenCursor}:${token.content}`;
201
+ tokenCursor += token.content.length;
202
+ return /* @__PURE__ */ jsx3("span", { style: { color: token.color }, children: token.content }, tokenKey);
203
+ }),
204
+ isLastLine ? null : "\n"
205
+ ] }, `${lineSignature}#${lineOccurrence}`);
206
+ });
207
+ })() })
208
+ }
209
+ );
210
+ } catch {
211
+ return /* @__PURE__ */ jsx3("pre", { className: `language-${language}`, children: /* @__PURE__ */ jsx3("code", { className: `language-${language}`, children: code }) });
212
+ }
213
+ }
214
+ async function renderNode(node, components, renderImage, key) {
215
+ if (isTextNode2(node)) {
216
+ return node;
217
+ }
218
+ const { type, props, children } = node;
219
+ if (type === NodeType3.IMG && renderImage) {
220
+ return /* @__PURE__ */ jsx3(Fragment3, { children: renderImage({
221
+ src: typeof props?.src === "string" ? props.src : "",
222
+ alt: typeof props?.alt === "string" ? props.alt : "",
223
+ title: typeof props?.title === "string" ? props.title : void 0
224
+ }) }, key);
225
+ }
226
+ if (type === NodeType3.CODE_BLOCK) {
227
+ const code = children?.filter(isTextNode2).join("") ?? "";
228
+ return /* @__PURE__ */ jsx3(Fragment3, { children: await renderHighlightedCode(code, props?.lang) }, key);
229
+ }
230
+ const renderedChildren = children ? await Promise.all(
231
+ children.map((child, index) => renderNode(child, components, renderImage, index))
232
+ ) : void 0;
233
+ const Component = components[type];
234
+ if (Component) {
235
+ const componentProps = buildComponentProps(type, props);
236
+ return /* @__PURE__ */ jsx3(Fragment3, { children: Component({ ...componentProps, children: renderedChildren }) }, key);
237
+ }
238
+ return /* @__PURE__ */ jsx3(Fragment3, { children: renderedChildren }, key);
239
+ }
240
+ async function DocsMarkdown({
241
+ content,
242
+ className = defaultClassName,
243
+ renderImage
244
+ }) {
245
+ await initMarkdown2();
246
+ const ast = mdToJSON2(content);
247
+ const rendered = await Promise.all(
248
+ ast.children.map((node, index) => renderNode(node, defaultComponents, renderImage, index))
249
+ );
250
+ const resolvedClassName = className === defaultClassName ? defaultClassName : `${defaultClassName} ${className}`.trim();
251
+ return /* @__PURE__ */ jsx3("div", { className: resolvedClassName, children: rendered });
252
+ }
253
+ export {
254
+ DocsMarkdown,
255
+ initMarkdown2 as initMarkdown,
256
+ markdownStartsWithHeading
257
+ };
258
+ //# sourceMappingURL=docs-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../packages/markdown-wasm/src/index.ts","../../../../packages/markdown-wasm/src/components.tsx","../../../../packages/markdown-wasm/src/renderer.tsx","../../../../packages/markdown-wasm/src/types.ts","../../lib/docs-markdown.tsx"],"sourcesContent":["/**\n * @repo/markdown-wasm\n *\n * High-performance markdown rendering using md4w WASM engine\n * with React component mapping support.\n *\n * @example\n * ```tsx\n * import { MarkdownRenderer, createMarkdownRenderer, NodeType } from \"@repo/markdown-wasm\";\n *\n * // Basic usage\n * <MarkdownRenderer content=\"# Hello **world**\" />\n *\n * // With custom components\n * <MarkdownRenderer\n * content={markdown}\n * components={{\n * [NodeType.H1]: ({ children }) => <MyHeading>{children}</MyHeading>,\n * }}\n * />\n *\n * // Factory pattern for reusable renderer\n * const CustomRenderer = createMarkdownRenderer({\n * components: { ... },\n * className: \"prose\",\n * });\n * <CustomRenderer content={markdown} />\n * ```\n */\n\n// Re-export md4w utilities for advanced usage\nexport { init, mdToHtml, mdToJSON, mdToReadableHtml, ParseFlags as Md4wParseFlags } from 'md4w';\n\n// Component exports\nexport { defaultComponents, minimalComponents } from './components';\n// Main renderer exports\nexport {\n createMarkdownRenderer,\n initMarkdown,\n MarkdownRenderer,\n NodeType,\n renderMarkdown,\n} from './renderer';\n// Type exports\nexport type {\n ComponentMap,\n CreateRendererOptions,\n GenericNodeComponent,\n HeadingLevel,\n MarkdownRendererProps,\n MDNode,\n MDTree,\n NodeComponent,\n NodePropsFor,\n NodeRenderProps,\n Options,\n ParseFlags,\n} from './types';\n// Utility exports\nexport { getHeadingLevel, isElementNode, isTextNode } from './types';\n","/**\n * @repo/markdown-wasm - Default Component Mappings\n *\n * Provides semantic HTML components for all md4w node types.\n * Override any of these by passing custom components to MarkdownRenderer.\n */\n\nimport { NodeType } from 'md4w';\nimport type { ReactNode } from 'react';\n\nimport type { ComponentMap, HeadingLevel } from './types';\n\n/**\n * Default heading component.\n * Renders H1-H6 based on level prop.\n */\nfunction Heading({ level, children }: { level: HeadingLevel; children: ReactNode }) {\n const Tag = `h${level}` as const;\n return <Tag>{children}</Tag>;\n}\n\n/**\n * Default components for all md4w node types.\n * These render semantic HTML elements that can be styled with CSS.\n *\n * Props are accessed from a generic props object to satisfy GenericNodeComponent type.\n * Runtime guarantees from md4w ensure the props exist for each node type.\n */\nexport const defaultComponents: ComponentMap = {\n // Block elements\n [NodeType.QUOTE]: (props) => <blockquote>{props.children}</blockquote>,\n [NodeType.UL]: (props) => <ul>{props.children}</ul>,\n [NodeType.OL]: (props) => <ol start={props.start as number | undefined}>{props.children}</ol>,\n [NodeType.LI]: (props) => {\n const isTask = props.isTask as boolean | undefined;\n const done = props.done as boolean | undefined;\n if (isTask) {\n return (\n <li>\n <input type=\"checkbox\" checked={done} disabled readOnly />\n {props.children}\n </li>\n );\n }\n return <li>{props.children}</li>;\n },\n [NodeType.HR]: () => <hr />,\n [NodeType.CODE_BLOCK]: (props: { lang?: string; children: React.ReactNode }) => {\n const language = props.lang?.toLowerCase() || 'plaintext';\n const languageClass = language ? `language-${language}` : undefined;\n return (\n <pre className={languageClass}>\n <code className={languageClass} data-language={language}>\n {props.children}\n </code>\n </pre>\n );\n },\n [NodeType.HTML]: (props) => {\n const html = getHtmlString(props.children);\n if (html && isLineBreakHtml(html)) {\n return <br />;\n }\n // HTML blocks are rendered as-is inside a div\n // Note: This is raw HTML from markdown, use dangerouslySetInnerHTML if needed\n return <div data-markdown-html>{props.children}</div>;\n },\n [NodeType.P]: (props) => <p>{props.children}</p>,\n\n // Table elements\n [NodeType.TABLE]: (props) => <table>{props.children}</table>,\n [NodeType.THEAD]: (props) => <thead>{props.children}</thead>,\n [NodeType.TBODY]: (props) => <tbody>{props.children}</tbody>,\n [NodeType.TR]: (props) => <tr>{props.children}</tr>,\n [NodeType.TH]: (props) => {\n const align = props.align as 'left' | 'center' | 'right' | '' | undefined;\n return <th style={align ? { textAlign: align } : undefined}>{props.children}</th>;\n },\n [NodeType.TD]: (props) => {\n const align = props.align as 'left' | 'center' | 'right' | '' | undefined;\n return <td style={align ? { textAlign: align } : undefined}>{props.children}</td>;\n },\n\n // Headings (H1-H6 share the same component with level prop injected by renderer)\n [NodeType.H1]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 1}>{props.children}</Heading>\n ),\n [NodeType.H2]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 2}>{props.children}</Heading>\n ),\n [NodeType.H3]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 3}>{props.children}</Heading>\n ),\n [NodeType.H4]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 4}>{props.children}</Heading>\n ),\n [NodeType.H5]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 5}>{props.children}</Heading>\n ),\n [NodeType.H6]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 6}>{props.children}</Heading>\n ),\n\n // Inline elements\n [NodeType.EM]: (props) => <em>{props.children}</em>,\n [NodeType.STRONG]: (props) => <strong>{props.children}</strong>,\n [NodeType.A]: (props) => (\n <a href={props.href as string} title={props.title as string | undefined}>\n {props.children}\n </a>\n ),\n [NodeType.IMG]: (props) => (\n // biome-ignore lint/performance/noImgElement: Generic markdown package, not Next.js specific. Users can override with next/image.\n <img\n src={props.src as string}\n alt={props.alt as string}\n title={props.title as string | undefined}\n />\n ),\n [NodeType.CODE_SPAN]: (props) => <code>{props.children}</code>,\n [NodeType.DEL]: (props) => <del>{props.children}</del>,\n\n // Math (LaTeX)\n [NodeType.LATEXMATH]: (props) => <span data-math=\"inline\">{props.children}</span>,\n [NodeType.LATEXMATH_DISPLAY]: (props) => <div data-math=\"display\">{props.children}</div>,\n\n // Wiki links\n [NodeType.WIKILINK]: (props) => {\n const target = props.target as string;\n return (\n <a href={`/wiki/${encodeURIComponent(target)}`} data-wikilink={target}>\n {props.children}\n </a>\n );\n },\n\n // Underline (when UNDERLINE parse flag is enabled)\n [NodeType.U]: (props) => <u>{props.children}</u>,\n};\n\nfunction getHtmlString(children: ReactNode): string | null {\n if (typeof children === 'string') {\n return children;\n }\n if (Array.isArray(children) && children.length === 1 && typeof children[0] === 'string') {\n return children[0];\n }\n return null;\n}\n\nfunction isLineBreakHtml(html: string): boolean {\n const trimmed = html.trim().toLowerCase();\n return trimmed === '<br>' || trimmed === '<br/>' || trimmed === '<br />';\n}\n\n/**\n * Minimal component set - only essential elements.\n * Use this when you want to style everything yourself.\n */\nexport const minimalComponents: ComponentMap = {\n [NodeType.P]: (props) => <p>{props.children}</p>,\n [NodeType.H1]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 1}>{props.children}</Heading>\n ),\n [NodeType.H2]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 2}>{props.children}</Heading>\n ),\n [NodeType.H3]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 3}>{props.children}</Heading>\n ),\n [NodeType.H4]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 4}>{props.children}</Heading>\n ),\n [NodeType.H5]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 5}>{props.children}</Heading>\n ),\n [NodeType.H6]: (props) => (\n <Heading level={(props.level as HeadingLevel) ?? 6}>{props.children}</Heading>\n ),\n [NodeType.STRONG]: (props) => <strong>{props.children}</strong>,\n [NodeType.EM]: (props) => <em>{props.children}</em>,\n [NodeType.A]: (props) => <a href={props.href as string}>{props.children}</a>,\n [NodeType.CODE_SPAN]: (props) => <code>{props.children}</code>,\n};\n","/**\n * @repo/markdown-wasm - AST to React Renderer\n *\n * Converts md4w JSON AST to React components.\n * Pattern B from research: Direct AST-to-React mapping for maximum performance.\n *\n * IMPORTANT: md4w is a WASM module that requires async initialization.\n * This module uses a singleton pattern with cached promise to ensure\n * init() is called exactly once before any parsing.\n *\n * NOTE: For Vercel/Next.js deployment, ensure `serverExternalPackages: ['md4w']`\n * is set in next.config.ts so md4w can properly resolve its WASM files.\n */\n\nimport { init, mdToJSON, NodeType } from 'md4w';\nimport { Fragment, type ReactNode } from 'react';\n\nimport { defaultComponents } from './components';\nimport type { ComponentMap, CreateRendererOptions, MarkdownRendererProps, MDNode } from './types';\nimport { getHeadingLevel, isTextNode } from './types';\n\n// -----------------------------------------------------------------------------\n// WASM Initialization (Singleton Pattern)\n// -----------------------------------------------------------------------------\n\n/**\n * Cached initialization promise.\n * Ensures init() is called exactly once, even with concurrent calls.\n */\nlet initPromise: Promise<void> | null = null;\n\n/**\n * Ensures md4w WASM is initialized before use.\n * Uses singleton pattern - multiple calls return the same promise.\n *\n * When md4w is listed in `serverExternalPackages`, it will be kept as an\n * external package and can properly resolve its WASM file using import.meta.url.\n *\n * @returns Promise that resolves when WASM is ready\n */\nasync function ensureInitialized(): Promise<void> {\n if (!initPromise) {\n // Use \"small\" variant (28KB gzipped) for faster loading\n initPromise = init('small');\n }\n return initPromise;\n}\n\n// -----------------------------------------------------------------------------\n// Internal Helpers\n// -----------------------------------------------------------------------------\n\n/**\n * Recursively renders an AST node to React elements.\n *\n * @param node - The AST node (MDNode or string)\n * @param components - Component map for rendering\n * @param key - React key for list rendering\n * @returns React element or text\n */\nfunction renderNode(\n node: string | MDNode,\n components: ComponentMap,\n key?: string | number\n): ReactNode {\n // Text nodes render as-is\n if (isTextNode(node)) {\n return node;\n }\n\n const { type, props, children } = node;\n\n // Recursively render children first\n const renderedChildren = children?.map((child, index) => renderNode(child, components, index));\n\n // Get component for this node type\n const Component = components[type as NodeType];\n\n if (Component) {\n // Build props based on node type\n const componentProps = buildComponentProps(type, props);\n\n return (\n <Fragment key={key}>{Component({ ...componentProps, children: renderedChildren })}</Fragment>\n );\n }\n\n // Unmapped node types: warn in development and render children only\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n `[@repo/markdown-wasm] Unmapped node type: ${type} (${NodeType[type] ?? 'unknown'})`\n );\n }\n\n return <Fragment key={key}>{renderedChildren}</Fragment>;\n}\n\n/**\n * Builds component props from AST node props.\n * Handles special cases like heading levels.\n */\nfunction buildComponentProps(\n type: number,\n props?: Record<string, unknown>\n): Record<string, unknown> {\n const result = { ...props };\n\n // Add heading level for H1-H6 nodes\n const headingLevel = getHeadingLevel(type as NodeType);\n if (headingLevel !== undefined) {\n result.level = headingLevel;\n }\n\n return result;\n}\n\n// -----------------------------------------------------------------------------\n// Public API\n// -----------------------------------------------------------------------------\n\n/**\n * Renders markdown content to React elements.\n *\n * This is an async Server Component that initializes the WASM module\n * on first use, then parses and renders markdown content.\n *\n * @example\n * ```tsx\n * import { MarkdownRenderer } from \"@repo/markdown-wasm\";\n *\n * // In a Server Component (async)\n * async function MyComponent() {\n * return <MarkdownRenderer content=\"# Hello **world**\" />;\n * }\n *\n * // Or with await\n * export default async function Page() {\n * return (\n * <article>\n * {await MarkdownRenderer({ content: markdown })}\n * </article>\n * );\n * }\n * ```\n */\nexport async function MarkdownRenderer({\n content,\n components: componentOverrides,\n parseFlags,\n className,\n}: MarkdownRendererProps): Promise<ReactNode> {\n // Ensure WASM is initialized before parsing\n await ensureInitialized();\n\n // Parse markdown to AST\n const ast = mdToJSON(content, { parseFlags });\n\n // Merge default components with overrides\n const components: ComponentMap = {\n ...defaultComponents,\n ...componentOverrides,\n };\n\n // Render AST children\n const rendered = ast.children.map((node, index) => renderNode(node, components, index));\n\n // Wrap in container if className provided, otherwise return fragment\n if (className) {\n return <div className={className}>{rendered}</div>;\n }\n\n return <>{rendered}</>;\n}\n\n/**\n * Creates a pre-configured markdown renderer with default settings.\n *\n * Returns an async function suitable for Server Components.\n *\n * @example\n * ```tsx\n * import { createMarkdownRenderer } from \"@repo/markdown-wasm\";\n * import { MyHeading, MyText, MyLink } from \"./components\";\n *\n * const CustomRenderer = createMarkdownRenderer({\n * components: {\n * [NodeType.H1]: ({ level, children }) => <MyHeading level={level}>{children}</MyHeading>,\n * [NodeType.P]: ({ children }) => <MyText>{children}</MyText>,\n * [NodeType.A]: ({ href, children }) => <MyLink href={href}>{children}</MyLink>,\n * },\n * className: \"prose prose-lg\",\n * });\n *\n * async function Article({ markdown }: { markdown: string }) {\n * return <CustomRenderer content={markdown} />;\n * }\n * ```\n */\nexport function createMarkdownRenderer(options: CreateRendererOptions) {\n const {\n components: defaultOverrides,\n parseFlags: defaultParseFlags,\n className: defaultClassName,\n } = options;\n\n return async function ConfiguredMarkdownRenderer({\n content,\n components: instanceOverrides,\n parseFlags: instanceParseFlags,\n className: instanceClassName,\n }: MarkdownRendererProps): Promise<ReactNode> {\n return MarkdownRenderer({\n content,\n components: { ...defaultOverrides, ...instanceOverrides },\n parseFlags: instanceParseFlags ?? defaultParseFlags,\n className: instanceClassName ?? defaultClassName,\n });\n };\n}\n\n/**\n * Renders markdown to React elements without a wrapper component.\n * Useful for embedding markdown content inline.\n *\n * @example\n * ```tsx\n * import { renderMarkdown } from \"@repo/markdown-wasm\";\n *\n * // In an async Server Component\n * const elements = await renderMarkdown(\"**bold** and *italic*\");\n * ```\n */\nexport async function renderMarkdown(\n content: string,\n options?: Omit<MarkdownRendererProps, 'content'>\n): Promise<ReactNode> {\n return MarkdownRenderer({ content, ...options });\n}\n\n/**\n * Explicitly initialize the md4w WASM module.\n * Useful for pre-warming in middleware or layout components.\n *\n * @example\n * ```tsx\n * // In layout.tsx\n * import { initMarkdown } from \"@repo/markdown-wasm\";\n *\n * export default async function RootLayout({ children }) {\n * // Pre-warm WASM for faster first render\n * await initMarkdown();\n * return <html>{children}</html>;\n * }\n * ```\n */\nexport async function initMarkdown(): Promise<void> {\n await ensureInitialized();\n}\n\n// Re-export for convenience\nexport { NodeType } from 'md4w';\n","/**\n * @repo/markdown-wasm - Type definitions\n *\n * Re-exports md4w types with additional utility types for React rendering.\n */\n\nimport type { MDNode, MDTree, NodeType, Options, ParseFlags } from 'md4w';\nimport type { ReactNode } from 'react';\n\n// Re-export md4w types\nexport type { MDNode, MDTree, NodeType, Options, ParseFlags };\n\n/**\n * Props passed to a custom component for an AST node.\n * Each node type has different props available.\n */\nexport interface NodeRenderProps {\n /** The original AST node */\n node: MDNode;\n /** Rendered children (already converted to React elements) */\n children: ReactNode;\n}\n\n/**\n * A component that renders a specific node type.\n * Receives the node props and pre-rendered children.\n */\nexport type NodeComponent<TProps = Record<string, unknown>> = (\n props: TProps & { children: ReactNode }\n) => ReactNode;\n\n/**\n * Generic component function used internally.\n * Accepts any props and children.\n */\nexport type GenericNodeComponent = (\n props: Record<string, unknown> & { children: ReactNode }\n) => ReactNode;\n\n/**\n * Component overrides map keyed by NodeType.\n * Each entry maps a node type number to a component function.\n * Uses GenericNodeComponent internally for flexibility.\n */\nexport type ComponentMap = Partial<Record<NodeType, GenericNodeComponent>>;\n\n/**\n * Utility type to extract props for a specific node type.\n * This provides type-safe access to node-specific properties.\n */\nexport type NodePropsFor<T extends NodeType> = T extends NodeType.CODE_BLOCK\n ? { lang?: string }\n : T extends NodeType.OL\n ? { start?: number }\n : T extends NodeType.LI\n ? { isTask?: boolean; done?: boolean }\n : T extends NodeType.TH | NodeType.TD\n ? { align?: 'left' | 'center' | 'right' | '' }\n : T extends NodeType.A\n ? { href: string; title?: string }\n : T extends NodeType.IMG\n ? { src: string; alt: string; title?: string }\n : T extends NodeType.WIKILINK\n ? { target: string }\n : T extends\n | NodeType.H1\n | NodeType.H2\n | NodeType.H3\n | NodeType.H4\n | NodeType.H5\n | NodeType.H6\n ? { level: 1 | 2 | 3 | 4 | 5 | 6 }\n : Record<string, unknown>;\n\n/**\n * Props for the MarkdownRenderer component.\n */\nexport interface MarkdownRendererProps {\n /** Markdown content to render */\n content: string;\n /** Optional component overrides */\n components?: ComponentMap;\n /** Optional parse flags for md4w */\n parseFlags?: Options['parseFlags'];\n /** Optional className for the wrapper element */\n className?: string;\n}\n\n/**\n * Factory function options for creating a custom renderer.\n */\nexport interface CreateRendererOptions {\n /** Component overrides */\n components?: ComponentMap;\n /** Default parse flags */\n parseFlags?: Options['parseFlags'];\n /** Default wrapper className */\n className?: string;\n}\n\n/**\n * Heading level type (1-6).\n */\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\n/**\n * Maps NodeType.H1-H6 to numeric heading level.\n */\nexport function getHeadingLevel(type: NodeType): HeadingLevel | undefined {\n const levelMap: Record<number, HeadingLevel> = {\n 21: 1,\n 22: 2,\n 23: 3,\n 24: 4,\n 25: 5,\n 26: 6,\n };\n return levelMap[type];\n}\n\n/**\n * Type guard to check if a node is a text node (string).\n */\nexport function isTextNode(node: string | MDNode): node is string {\n return typeof node === 'string';\n}\n\n/**\n * Type guard to check if a node is an element node (MDNode).\n */\nexport function isElementNode(node: string | MDNode): node is MDNode {\n return typeof node !== 'string' && typeof node === 'object' && 'type' in node;\n}\n","import {\n type ComponentMap,\n defaultComponents,\n getHeadingLevel,\n initMarkdown as initMarkdownWasm,\n type MDNode,\n mdToJSON,\n NodeType,\n} from '@repo/markdown-wasm';\nimport { Fragment, type ReactNode } from 'react';\nimport { type BuiltinLanguage, codeToTokens } from 'shiki';\n\nexport interface DocsMarkdownProps {\n content: string;\n className?: string;\n renderImage?: (props: { src: string; alt: string; title?: string }) => ReactNode;\n}\n\nconst defaultClassName = 'cms-docs-markdown';\nlet markdownInitPromise: Promise<void> | undefined;\n\nexport function markdownStartsWithHeading(markdown: string): boolean {\n return /^\\s*#\\s+/.test(markdown);\n}\n\nexport async function initMarkdown(): Promise<void> {\n markdownInitPromise ??= initMarkdownWasm();\n await markdownInitPromise;\n}\n\nfunction isTextNode(node: string | MDNode): node is string {\n return typeof node === 'string';\n}\n\nfunction buildComponentProps(\n type: number,\n props?: Record<string, unknown>\n): Record<string, unknown> {\n const result = { ...props };\n const headingLevel = getHeadingLevel(type as NodeType);\n\n if (headingLevel !== undefined) {\n result.level = headingLevel;\n }\n\n return result;\n}\n\nfunction normalizeLanguage(lang: unknown): string {\n if (typeof lang !== 'string') {\n return 'text';\n }\n\n const normalized = lang.trim().toLowerCase();\n return normalized || 'text';\n}\n\nasync function renderHighlightedCode(code: string, lang: unknown): Promise<ReactNode> {\n const language = normalizeLanguage(lang);\n\n try {\n const result = await codeToTokens(code, {\n lang: language as BuiltinLanguage,\n theme: 'github-dark',\n });\n\n return (\n <pre\n className={`shiki language-${language}`}\n style={{ backgroundColor: result.bg, color: result.fg }}\n >\n <code className={`shiki_code language-${language}`} data-language={language}>\n {(() => {\n const lineOccurrences = new Map<string, number>();\n\n return result.tokens.map((line, lineIndex) => {\n const isLastLine = lineIndex === result.tokens.length - 1;\n const lineSignature = line\n .map((token) => `${token.offset}:${token.color ?? ''}:${token.content}`)\n .join('|');\n const lineOccurrence = lineOccurrences.get(lineSignature) ?? 0;\n\n lineOccurrences.set(lineSignature, lineOccurrence + 1);\n\n let tokenCursor = 0;\n\n return (\n <Fragment key={`${lineSignature}#${lineOccurrence}`}>\n {line.map((token) => {\n const tokenKey = `${token.offset}:${token.color ?? ''}:${tokenCursor}:${token.content}`;\n tokenCursor += token.content.length;\n\n return (\n <span key={tokenKey} style={{ color: token.color }}>\n {token.content}\n </span>\n );\n })}\n {isLastLine ? null : '\\n'}\n </Fragment>\n );\n });\n })()}\n </code>\n </pre>\n );\n } catch {\n return (\n <pre className={`language-${language}`}>\n <code className={`language-${language}`}>{code}</code>\n </pre>\n );\n }\n}\n\nasync function renderNode(\n node: string | MDNode,\n components: ComponentMap,\n renderImage: DocsMarkdownProps['renderImage'],\n key?: string | number\n): Promise<ReactNode> {\n if (isTextNode(node)) {\n return node;\n }\n\n const { type, props, children } = node;\n\n if (type === NodeType.IMG && renderImage) {\n return (\n <Fragment key={key}>\n {renderImage({\n src: typeof props?.src === 'string' ? props.src : '',\n alt: typeof props?.alt === 'string' ? props.alt : '',\n title: typeof props?.title === 'string' ? props.title : undefined,\n })}\n </Fragment>\n );\n }\n\n if (type === NodeType.CODE_BLOCK) {\n const code = children?.filter(isTextNode).join('') ?? '';\n return <Fragment key={key}>{await renderHighlightedCode(code, props?.lang)}</Fragment>;\n }\n\n const renderedChildren = children\n ? await Promise.all(\n children.map((child, index) => renderNode(child, components, renderImage, index))\n )\n : undefined;\n\n const Component = components[type as NodeType];\n\n if (Component) {\n const componentProps = buildComponentProps(type, props);\n return (\n <Fragment key={key}>{Component({ ...componentProps, children: renderedChildren })}</Fragment>\n );\n }\n\n return <Fragment key={key}>{renderedChildren}</Fragment>;\n}\n\nexport async function DocsMarkdown({\n content,\n className = defaultClassName,\n renderImage,\n}: DocsMarkdownProps) {\n await initMarkdown();\n\n const ast = mdToJSON(content);\n const rendered = await Promise.all(\n ast.children.map((node, index) => renderNode(node, defaultComponents, renderImage, index))\n );\n\n const resolvedClassName =\n className === defaultClassName ? defaultClassName : `${defaultClassName} ${className}`.trim();\n\n return <div className={resolvedClassName}>{rendered}</div>;\n}\n"],"mappings":"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","names":["init","mdToJSON","NodeType","NodeType","Fragment","jsx","Fragment","jsx","jsxs","initMarkdown","isTextNode","NodeType","mdToJSON"]}
@@ -1,6 +1,7 @@
1
1
  import * as _repo_cms_schema_blocks from '@repo/cms-schema/blocks';
2
2
  import { ComponentType } from 'react';
3
3
 
4
+ declare const __cmsRendererTypesModule = true;
4
5
  /**
5
6
  * Valid block type strings.
6
7
  * Must match the schema_name field in block data from the tRPC API.
@@ -196,4 +197,4 @@ interface BlockComponentRegistry {
196
197
  [schemaName: string]: BlockComponent<any>;
197
198
  }
198
199
 
199
- export type { ArticleContent, BlockComponent, BlockComponentProps, BlockComponentRegistry, BlockData, BlockType, CTABlockContent, FeaturesBlockContent, HeaderContent, HeroBlockContent, Level1Link, Level2Link, Level3Link, LogoTrustBlockContent, NavigationButton, NavigationContent, NavigationLink, ResolvedRouteParams, RouteParamInfo };
200
+ export { type ArticleContent, type BlockComponent, type BlockComponentProps, type BlockComponentRegistry, type BlockData, type BlockType, type CTABlockContent, type FeaturesBlockContent, type HeaderContent, type HeroBlockContent, type Level1Link, type Level2Link, type Level3Link, type LogoTrustBlockContent, type NavigationButton, type NavigationContent, type NavigationLink, type ResolvedRouteParams, type RouteParamInfo, __cmsRendererTypesModule };
package/dist/lib/types.js CHANGED
@@ -1 +1,6 @@
1
+ // lib/types.ts
2
+ var __cmsRendererTypesModule = true;
3
+ export {
4
+ __cmsRendererTypesModule
5
+ };
1
6
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../../lib/types.ts"],"sourcesContent":["/**\n * Block Rendering Types\n *\n * Type definitions for the ComponentMap pattern.\n * Block types map to React components via the blockComponents registry.\n */\n\nimport type { ComponentType } from 'react';\n\n// Ensures the emitted JS stays a module for consumers that import this entry for types.\nexport const __cmsRendererTypesModule = true;\n\n// -----------------------------------------------------------------------------\n// Block Type Discriminant\n// -----------------------------------------------------------------------------\n\n/**\n * Valid block type strings.\n * Must match the schema_name field in block data from the tRPC API.\n */\nexport type BlockType =\n | 'navigation'\n | 'header'\n | 'article'\n | 'hero-block'\n | 'features-block'\n | 'cta-block'\n | 'logo-trust-block';\n\n// -----------------------------------------------------------------------------\n// Content Types (inferred from seed data)\n// -----------------------------------------------------------------------------\n\n/**\n * Navigation link button structure.\n */\nexport interface NavigationButton {\n label: string;\n href: string;\n ariaLabel: string;\n}\n\n/**\n * Navigation link with type and button.\n */\nexport interface NavigationLink {\n button: NavigationButton;\n type: 'Default' | 'Flyout';\n}\n\n/**\n * Level 3 navigation item (leaf node).\n */\nexport interface Level3Link {\n link: NavigationLink;\n}\n\n/**\n * Level 2 navigation item with optional Level 3 children.\n */\nexport interface Level2Link {\n link: NavigationLink;\n children?: Level3Link[];\n}\n\n/**\n * Level 1 navigation item with optional Level 2 children.\n */\nexport interface Level1Link {\n link: NavigationLink;\n children?: Level2Link[];\n}\n\n/**\n * Navigation block content.\n */\nexport interface NavigationContent {\n logo?: {\n url: string;\n alt: string;\n };\n ariaLabel: string;\n links: Level1Link[];\n}\n\n/**\n * Header block content.\n */\nexport interface HeaderContent {\n headline: string;\n subheadline?: string;\n backgroundImage?: {\n url: string;\n alt: string;\n };\n ctaButton?: {\n label: string;\n href: string;\n };\n alignment: 'left' | 'center' | 'right';\n}\n\n/**\n * Article block content.\n */\nexport interface ArticleContent {\n headline: string;\n author?: string;\n publishedAt?: string;\n body: string;\n tags?: readonly string[] | string[];\n status?: string;\n}\n\n/**\n * Hero block content (from CMS schema).\n */\nexport type HeroBlockContent = import('@repo/cms-schema/blocks').HeroBlockContent;\n\n/**\n * Features block content (from CMS schema).\n */\nexport type FeaturesBlockContent = import('@repo/cms-schema/blocks').FeaturesBlockContent;\n\n/**\n * CTA block content (from CMS schema).\n */\nexport type CTABlockContent = import('@repo/cms-schema/blocks').CTABlockContent;\n\n/**\n * Logo Trust block content (from CMS schema).\n */\nexport type LogoTrustBlockContent = import('@repo/cms-schema/blocks').LogoTrustBlockContent;\n\n// -----------------------------------------------------------------------------\n// Block Data Union\n// -----------------------------------------------------------------------------\n\n/**\n * Discriminated union of all block types.\n * Use the `type` field to narrow to specific content types.\n *\n * Each block also carries its stable CMS `id`, which should be used as the\n * React `key` when rendering lists of blocks.\n *\n * @example\n * ```tsx\n * function renderBlock(block: BlockData) {\n * if (block.type === 'header') {\n * // TypeScript knows block.content is HeaderContent\n * return <h1>{block.content.headline}</h1>;\n * }\n * }\n * ```\n */\nexport type BlockData =\n | { id: string; type: 'navigation'; content: NavigationContent }\n | { id: string; type: 'header'; content: HeaderContent }\n | { id: string; type: 'article'; content: ArticleContent }\n | { id: string; type: 'hero-block'; content: HeroBlockContent }\n | { id: string; type: 'features-block'; content: FeaturesBlockContent }\n | { id: string; type: 'cta-block'; content: CTABlockContent }\n | { id: string; type: 'logo-trust-block'; content: LogoTrustBlockContent }\n | { id: string; type: string; content: Record<string, unknown> };\n\n// -----------------------------------------------------------------------------\n// Route Parameter Types\n// -----------------------------------------------------------------------------\n\n/**\n * Resolved route parameter info exposed to block components.\n * Contains the parameter value, its schema name, and the resolved document.\n */\nexport interface RouteParamInfo {\n value: string;\n schemaName: string;\n document: {\n id: string;\n title: string;\n content: Record<string, unknown>;\n schema_name: string;\n };\n}\n\n/**\n * Map of route parameter names to their resolved info.\n * E.g., `{ country: { value: \"us\", schemaName: \"country\", document: {...} } }`\n */\nexport type ResolvedRouteParams = Record<string, RouteParamInfo>;\n\n// -----------------------------------------------------------------------------\n// Component Types\n// -----------------------------------------------------------------------------\n\n/**\n * Props for a block component.\n * Each block component receives its typed content.\n */\nexport interface BlockComponentProps<T> {\n content: T;\n routeParams?: ResolvedRouteParams;\n /** ISO 639-1 language code extracted from the route's language param, if present. */\n language?: string;\n}\n\n/**\n * A React component that renders a specific block type.\n */\nexport type BlockComponent<T> = ComponentType<BlockComponentProps<T>>;\n\n/**\n * Registry of block type to component mappings.\n * This is the core of the ComponentMap pattern.\n *\n * Predefined block types have strongly-typed content.\n * Custom blocks (keyed by their schema_name) receive `Record<string, unknown>`.\n */\nexport interface BlockComponentRegistry {\n navigation: BlockComponent<NavigationContent>;\n header: BlockComponent<HeaderContent>;\n article: BlockComponent<ArticleContent>;\n 'hero-block': BlockComponent<HeroBlockContent>;\n 'features-block': BlockComponent<FeaturesBlockContent>;\n 'cta-block': BlockComponent<CTABlockContent>;\n 'logo-trust-block': BlockComponent<LogoTrustBlockContent>;\n // Custom blocks: register by schema_name\n // biome-ignore lint/suspicious/noExplicitAny: Custom block content is dynamic\n [schemaName: string]: BlockComponent<any>;\n}\n"],"mappings":";AAUO,IAAM,2BAA2B;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cms-renderer",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -56,13 +56,22 @@
56
56
  "types": "./dist/lib/custom-schemas.d.ts",
57
57
  "import": "./dist/lib/custom-schemas.js"
58
58
  },
59
+ "./lib/docs-markdown": {
60
+ "types": "./dist/lib/docs-markdown.d.ts",
61
+ "import": "./dist/lib/docs-markdown.js"
62
+ },
63
+ "./styles/docs-markdown.css": "./styles/docs-markdown.css",
59
64
  "./lib/refresher": {
60
65
  "types": "./dist/lib/refresher.d.ts",
61
66
  "import": "./dist/lib/refresher.js"
62
67
  }
63
68
  },
64
69
  "files": [
65
- "dist"
70
+ "dist",
71
+ "styles"
72
+ ],
73
+ "sideEffects": [
74
+ "./styles/*.css"
66
75
  ],
67
76
  "publishConfig": {
68
77
  "access": "public"
@@ -78,6 +87,7 @@
78
87
  "clean": "rm -rf .next .turbo node_modules dist"
79
88
  },
80
89
  "dependencies": {
90
+ "@heroicons/react": "^2.2.0",
81
91
  "@hookform/resolvers": "^5.1.0",
82
92
  "@lexical/code": "0.39.0",
83
93
  "@lexical/link": "0.39.0",
@@ -94,19 +104,20 @@
94
104
  "@trpc/client": "^11.8.1",
95
105
  "@trpc/react-query": "^11.8.1",
96
106
  "@trpc/server": "^11.8.1",
97
- "lucide-react": "0.562.0",
107
+ "escape-string-regexp": "^5.0.0",
98
108
  "md4w": "^0.2.7",
99
- "object-hash": "^3.0.0",
100
109
  "next": "^16.1.1",
110
+ "object-hash": "^3.0.0",
111
+ "shiki": "^4.0.2",
101
112
  "react": "^19.1.0",
102
113
  "react-dom": "^19.1.0",
103
114
  "superjson": "2.2.6",
104
- "escape-string-regexp": "^5.0.0",
105
115
  "zod": "4.3.5"
106
116
  },
107
117
  "devDependencies": {
108
118
  "@happy-dom/global-registrator": "20.1.0",
109
119
  "@repo/cms-schema": "0.0.0",
120
+ "@repo/markdown-wasm": "0.0.0",
110
121
  "@repo/typescript-config": "0.0.0",
111
122
  "tsup": "^8.3.5",
112
123
  "@tailwindcss/postcss": "4",
@@ -0,0 +1,181 @@
1
+ .cms-docs-markdown {
2
+ max-width: none;
3
+ min-width: 0;
4
+ overflow-x: hidden;
5
+ overflow-wrap: anywhere;
6
+ word-break: break-word;
7
+ color: #d1d5db;
8
+ font-size: 15px;
9
+ line-height: 1.75;
10
+ }
11
+
12
+ .cms-docs-markdown h1,
13
+ .cms-docs-markdown h2,
14
+ .cms-docs-markdown h3,
15
+ .cms-docs-markdown h4 {
16
+ color: #ffffff;
17
+ font-weight: 600;
18
+ }
19
+
20
+ .cms-docs-markdown h1 {
21
+ margin: 0 0 1.5rem;
22
+ letter-spacing: -0.025em;
23
+ font-size: 2.25rem;
24
+ line-height: 1.1;
25
+ }
26
+
27
+ .cms-docs-markdown h2 {
28
+ margin: 2.5rem 0 1rem;
29
+ font-size: 1.5rem;
30
+ line-height: 1.3;
31
+ }
32
+
33
+ .cms-docs-markdown h3 {
34
+ margin: 2rem 0 0.75rem;
35
+ font-size: 1.25rem;
36
+ line-height: 1.35;
37
+ }
38
+
39
+ .cms-docs-markdown h4 {
40
+ margin: 1.5rem 0 0.5rem;
41
+ font-size: 1.125rem;
42
+ line-height: 1.4;
43
+ }
44
+
45
+ .cms-docs-markdown > :first-child {
46
+ margin-top: 0;
47
+ }
48
+
49
+ .cms-docs-markdown p,
50
+ .cms-docs-markdown ul,
51
+ .cms-docs-markdown ol,
52
+ .cms-docs-markdown blockquote,
53
+ .cms-docs-markdown pre,
54
+ .cms-docs-markdown table,
55
+ .cms-docs-markdown img {
56
+ margin: 1rem 0;
57
+ }
58
+
59
+ .cms-docs-markdown a {
60
+ color: #93c5fd;
61
+ text-decoration: underline;
62
+ text-underline-offset: 4px;
63
+ }
64
+
65
+ .cms-docs-markdown strong {
66
+ color: #ffffff;
67
+ font-weight: 600;
68
+ }
69
+
70
+ .cms-docs-markdown em {
71
+ font-style: italic;
72
+ }
73
+
74
+ .cms-docs-markdown ul,
75
+ .cms-docs-markdown ol {
76
+ padding-left: 1.5rem;
77
+ }
78
+
79
+ .cms-docs-markdown ul {
80
+ list-style: disc;
81
+ }
82
+
83
+ .cms-docs-markdown ol {
84
+ list-style: decimal;
85
+ }
86
+
87
+ .cms-docs-markdown li {
88
+ margin: 0.25rem 0;
89
+ overflow-wrap: anywhere;
90
+ }
91
+
92
+ .cms-docs-markdown blockquote {
93
+ border-left: 2px solid #374151;
94
+ padding-left: 1rem;
95
+ color: #9ca3af;
96
+ font-style: italic;
97
+ }
98
+
99
+ .cms-docs-markdown :not(pre) > code {
100
+ border-radius: 0.25rem;
101
+ background: #1f2937;
102
+ padding: 0.125rem 0.375rem;
103
+ color: #f9fafb;
104
+ font-size: 0.9em;
105
+ }
106
+
107
+ .cms-docs-markdown pre,
108
+ .cms-docs-markdown .shiki {
109
+ overflow-x: auto;
110
+ border: 1px solid #1f2937;
111
+ border-radius: 0.75rem;
112
+ background: #0b0f19;
113
+ padding: 1rem;
114
+ }
115
+
116
+ .cms-docs-markdown pre code,
117
+ .cms-docs-markdown .shiki code {
118
+ background: transparent;
119
+ padding: 0;
120
+ color: #e5e7eb;
121
+ }
122
+
123
+ .cms-docs-markdown hr {
124
+ margin: 2rem 0;
125
+ border: 0;
126
+ border-top: 1px solid #1f2937;
127
+ }
128
+
129
+ .cms-docs-markdown table {
130
+ display: block;
131
+ max-width: 100%;
132
+ overflow-x: auto;
133
+ border-collapse: collapse;
134
+ }
135
+
136
+ .cms-docs-markdown th,
137
+ .cms-docs-markdown td {
138
+ border: 1px solid #374151;
139
+ padding: 0.5rem 0.75rem;
140
+ }
141
+
142
+ .cms-docs-markdown th {
143
+ background: #111827;
144
+ color: #ffffff;
145
+ text-align: left;
146
+ }
147
+
148
+ .cms-docs-markdown img {
149
+ display: block;
150
+ max-width: 100%;
151
+ height: auto;
152
+ border-radius: 0.75rem;
153
+ }
154
+
155
+ @media (min-width: 640px) {
156
+ .cms-docs-markdown h1 {
157
+ font-size: 2.5rem;
158
+ }
159
+
160
+ .cms-docs-markdown h2 {
161
+ font-size: 1.75rem;
162
+ }
163
+
164
+ .cms-docs-markdown h3 {
165
+ font-size: 1.5rem;
166
+ }
167
+
168
+ .cms-docs-markdown h4 {
169
+ font-size: 1.125rem;
170
+ }
171
+
172
+ .cms-docs-markdown ul,
173
+ .cms-docs-markdown ol {
174
+ padding-left: 1.75rem;
175
+ }
176
+
177
+ .cms-docs-markdown table {
178
+ display: table;
179
+ width: 100%;
180
+ }
181
+ }