@wsxjs/wsx-marked-components 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +223 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +1680 -0
- package/package.json +62 -0
- package/src/Blockquote.css +12 -0
- package/src/Blockquote.wsx +26 -0
- package/src/Code.css +34 -0
- package/src/Code.wsx +46 -0
- package/src/Error.wsx +34 -0
- package/src/Heading.css +37 -0
- package/src/Heading.wsx +44 -0
- package/src/List.wsx +67 -0
- package/src/Markdown.css +66 -0
- package/src/Markdown.wsx +269 -0
- package/src/Paragraph.wsx +36 -0
- package/src/index.ts +16 -0
- package/src/marked-utils.ts +111 -0
- package/src/types/wsx.d.ts +5 -0
- package/src/types.ts +57 -0
package/src/Markdown.wsx
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/** @jsxImportSource @wsxjs/wsx-core */
|
|
2
|
+
/**
|
|
3
|
+
* WSX Markdown Component
|
|
4
|
+
*
|
|
5
|
+
* A reusable component that renders markdown into WSX marked components.
|
|
6
|
+
* Supports customization through custom token renderers.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```html
|
|
10
|
+
* <wsx-markdown markdown="# Hello World"></wsx-markdown>
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { LightComponent, autoRegister, state } from "@wsxjs/wsx-core";
|
|
15
|
+
import { createLogger } from "@wsxjs/wsx-logger";
|
|
16
|
+
import { marked } from "marked";
|
|
17
|
+
import type { Tokens } from "marked";
|
|
18
|
+
import styles from "./Markdown.css?inline";
|
|
19
|
+
import { renderInlineTokens } from "./marked-utils";
|
|
20
|
+
// Import WSX components so they're registered as custom elements
|
|
21
|
+
import "./Heading.wsx";
|
|
22
|
+
import "./Code.wsx";
|
|
23
|
+
import "./Blockquote.wsx";
|
|
24
|
+
import "./Paragraph.wsx";
|
|
25
|
+
import "./List.wsx";
|
|
26
|
+
import "./Error.wsx";
|
|
27
|
+
|
|
28
|
+
const logger = createLogger("Markdown");
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Custom token renderer function type
|
|
32
|
+
* Return null to use default rendering, or return an HTMLElement to override
|
|
33
|
+
*/
|
|
34
|
+
export type TokenRenderer = (
|
|
35
|
+
token: Tokens.Generic,
|
|
36
|
+
defaultRender: () => HTMLElement | null
|
|
37
|
+
) => HTMLElement | null;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Custom renderers configuration
|
|
41
|
+
*/
|
|
42
|
+
export interface CustomRenderers {
|
|
43
|
+
[tokenType: string]: TokenRenderer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Markdown Component
|
|
48
|
+
*
|
|
49
|
+
* Renders markdown content into WSX marked components using marked.lexer()
|
|
50
|
+
* and manual JSX conversion.
|
|
51
|
+
*/
|
|
52
|
+
@autoRegister({ tagName: "wsx-markdown" })
|
|
53
|
+
export default class Markdown extends LightComponent {
|
|
54
|
+
@state private markdown: string = "";
|
|
55
|
+
private customRenderers: CustomRenderers = {};
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
super({
|
|
59
|
+
styles,
|
|
60
|
+
styleName: "wsx-markdown",
|
|
61
|
+
lightDOM: true,
|
|
62
|
+
});
|
|
63
|
+
logger.info("Markdown initialized");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static get observedAttributes() {
|
|
67
|
+
return ["markdown"];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
|
71
|
+
if (name === "markdown") {
|
|
72
|
+
this.markdown = newValue || "";
|
|
73
|
+
this.rerender();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set custom renderers for specific token types
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* markdown.setCustomRenderers({
|
|
83
|
+
* heading: (token, defaultRender) => {
|
|
84
|
+
* // Custom heading rendering
|
|
85
|
+
* return defaultRender();
|
|
86
|
+
* }
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
setCustomRenderers(renderers: CustomRenderers): void {
|
|
91
|
+
this.customRenderers = { ...this.customRenderers, ...renderers };
|
|
92
|
+
this.rerender();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get current custom renderers
|
|
97
|
+
*/
|
|
98
|
+
getCustomRenderers(): CustomRenderers {
|
|
99
|
+
return { ...this.customRenderers };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
render() {
|
|
103
|
+
if (!this.markdown) {
|
|
104
|
+
return <div class="marked-content"></div>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Use marked.lexer to get tokens, then render with WSX JSX
|
|
109
|
+
const tokens = marked.lexer(this.markdown);
|
|
110
|
+
return <div class="marked-content">{this.renderTokens(tokens)}</div>;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
logger.error("Failed to render markdown", error);
|
|
113
|
+
return (
|
|
114
|
+
<div class="marked-content">
|
|
115
|
+
<wsx-marked-error message={`Error: ${error}`} />
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
protected onConnected() {
|
|
122
|
+
// Get initial value from attribute
|
|
123
|
+
const markdownAttr = this.getAttribute("markdown");
|
|
124
|
+
if (markdownAttr) {
|
|
125
|
+
this.markdown = markdownAttr;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Convert marked tokens to WSX JSX elements
|
|
131
|
+
*/
|
|
132
|
+
private renderTokens(tokens: Tokens.Generic[]): (HTMLElement | null)[] {
|
|
133
|
+
return tokens
|
|
134
|
+
.map((token) => this.renderToken(token))
|
|
135
|
+
.filter((el): el is HTMLElement => el !== null);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Render a single token to WSX JSX element
|
|
140
|
+
* Supports custom renderers for extensibility
|
|
141
|
+
*/
|
|
142
|
+
private renderToken(token: Tokens.Generic): HTMLElement | null {
|
|
143
|
+
// Check for custom renderer first
|
|
144
|
+
const customRenderer = this.customRenderers[token.type];
|
|
145
|
+
if (customRenderer) {
|
|
146
|
+
const result = customRenderer(token, () => this.defaultRenderToken(token));
|
|
147
|
+
if (result !== null) {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
// If custom renderer returns null, fall back to default
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return this.defaultRenderToken(token);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Default token rendering logic
|
|
158
|
+
* This is the core rendering implementation
|
|
159
|
+
*/
|
|
160
|
+
private defaultRenderToken(token: Tokens.Generic): HTMLElement | null {
|
|
161
|
+
switch (token.type) {
|
|
162
|
+
case "heading": {
|
|
163
|
+
const headingToken = token as Tokens.Heading;
|
|
164
|
+
return (
|
|
165
|
+
<wsx-marked-heading
|
|
166
|
+
level={headingToken.depth.toString()}
|
|
167
|
+
text={renderInlineTokens(headingToken.tokens)}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
case "code": {
|
|
173
|
+
const codeToken = token as Tokens.Code;
|
|
174
|
+
return <wsx-marked-code code={codeToken.text} language={codeToken.lang || ""} />;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
case "blockquote": {
|
|
178
|
+
const blockquoteToken = token as Tokens.Blockquote;
|
|
179
|
+
return (
|
|
180
|
+
<wsx-marked-blockquote>
|
|
181
|
+
{this.renderTokens(blockquoteToken.tokens)}
|
|
182
|
+
</wsx-marked-blockquote>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
case "paragraph": {
|
|
187
|
+
const paraToken = token as Tokens.Paragraph;
|
|
188
|
+
return <wsx-marked-paragraph content={renderInlineTokens(paraToken.tokens)} />;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
case "list": {
|
|
192
|
+
const listToken = token as Tokens.List;
|
|
193
|
+
// List items can contain block-level tokens (nested lists, blockquotes, etc.)
|
|
194
|
+
// So we need to use renderTokens() instead of renderInlineTokens()
|
|
195
|
+
const items = listToken.items.map((item) => {
|
|
196
|
+
// 防御性检查:确保 item.tokens 存在且是数组
|
|
197
|
+
if (!item.tokens || !Array.isArray(item.tokens)) {
|
|
198
|
+
logger.warn("List item has no tokens or tokens is not an array", { item });
|
|
199
|
+
return "";
|
|
200
|
+
}
|
|
201
|
+
// Render all tokens in the list item (both inline and block-level)
|
|
202
|
+
const renderedElements = this.renderTokens(item.tokens);
|
|
203
|
+
// 如果渲染结果为空,返回空字符串
|
|
204
|
+
if (renderedElements.length === 0) {
|
|
205
|
+
return "";
|
|
206
|
+
}
|
|
207
|
+
// Convert rendered elements to HTML string for the List component
|
|
208
|
+
// Create a temporary container to collect the HTML
|
|
209
|
+
const tempContainer = document.createElement("div");
|
|
210
|
+
renderedElements.forEach((el) => {
|
|
211
|
+
if (el) {
|
|
212
|
+
tempContainer.appendChild(el);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
const html = tempContainer.innerHTML;
|
|
216
|
+
// 如果 innerHTML 为空,记录警告
|
|
217
|
+
if (!html) {
|
|
218
|
+
logger.warn("tempContainer.innerHTML is empty after appending elements", {
|
|
219
|
+
renderedElementsCount: renderedElements.length,
|
|
220
|
+
itemTokens: item.tokens,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return html;
|
|
224
|
+
});
|
|
225
|
+
return (
|
|
226
|
+
<wsx-marked-list
|
|
227
|
+
ordered={listToken.ordered ? "true" : "false"}
|
|
228
|
+
items={JSON.stringify(items)}
|
|
229
|
+
/>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
case "html": {
|
|
234
|
+
const htmlToken = token as Tokens.HTML;
|
|
235
|
+
return <div>{htmlToken.text}</div>;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
case "hr": {
|
|
239
|
+
return <hr />;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
case "space": {
|
|
243
|
+
// Ignore space tokens
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
case "text": {
|
|
248
|
+
// Text tokens are inline tokens, but if they appear as top-level tokens,
|
|
249
|
+
// render them directly as text nodes wrapped in a span
|
|
250
|
+
const textToken = token as Tokens.Text;
|
|
251
|
+
return <span>{textToken.text || ""}</span>;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
default: {
|
|
255
|
+
// For other types, use default marked renderer
|
|
256
|
+
// Note: marked.Renderer() only handles block-level tokens, not inline tokens
|
|
257
|
+
const renderer = new marked.Renderer();
|
|
258
|
+
const renderMethod = (
|
|
259
|
+
renderer as unknown as Record<string, (token: unknown) => string>
|
|
260
|
+
)[token.type];
|
|
261
|
+
const html = renderMethod?.(token) || "";
|
|
262
|
+
if (html) {
|
|
263
|
+
return <div>{html}</div>;
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** @jsxImportSource @wsxjs/wsx-core */
|
|
2
|
+
/**
|
|
3
|
+
* WSX Paragraph Component
|
|
4
|
+
* Custom paragraph component for markdown rendering
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { LightComponent, autoRegister, state } from "@wsxjs/wsx-core";
|
|
8
|
+
|
|
9
|
+
@autoRegister({ tagName: "wsx-marked-paragraph" })
|
|
10
|
+
export default class Paragraph extends LightComponent {
|
|
11
|
+
@state private content: string = "";
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super({
|
|
15
|
+
styleName: "wsx-marked-paragraph",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static get observedAttributes() {
|
|
20
|
+
return ["content"];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
|
24
|
+
if (name === "content") {
|
|
25
|
+
// HTML 属性值会被浏览器自动解码,所以这里得到的是原始的 HTML 字符串
|
|
26
|
+
this.content = newValue || "";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
render() {
|
|
31
|
+
// content 是 HTML 字符串,需要解析为 DOM 节点
|
|
32
|
+
// JSX factory 会自动检测 HTML 字符串并转换
|
|
33
|
+
return <p class="marked-paragraph">{this.content}</p>;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** @jsxImportSource @wsxjs/wsx-core */
|
|
2
|
+
|
|
3
|
+
// Export all marked components
|
|
4
|
+
export { default as Heading } from "./Heading.wsx";
|
|
5
|
+
export { default as Code } from "./Code.wsx";
|
|
6
|
+
export { default as Blockquote } from "./Blockquote.wsx";
|
|
7
|
+
export { default as Paragraph } from "./Paragraph.wsx";
|
|
8
|
+
export { default as List } from "./List.wsx";
|
|
9
|
+
export { default as Error } from "./Error.wsx";
|
|
10
|
+
export { default as Markdown } from "./Markdown.wsx";
|
|
11
|
+
|
|
12
|
+
// Export utilities
|
|
13
|
+
export * from "./marked-utils";
|
|
14
|
+
|
|
15
|
+
// Export types
|
|
16
|
+
export type { TokenRenderer, CustomRenderers, MarkdownOptions } from "./types";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marked utilities for Pattern 1 and Pattern 2
|
|
3
|
+
* Shared utilities for extracting and rendering inline tokens
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Tokens } from "marked";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper to escape HTML for attributes
|
|
10
|
+
*/
|
|
11
|
+
export function escapeHtml(text: string): string {
|
|
12
|
+
const div = document.createElement("div");
|
|
13
|
+
div.textContent = text;
|
|
14
|
+
// Framework utility function for HTML escaping - innerHTML is necessary here
|
|
15
|
+
return div.innerHTML;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Extract inline tokens from a list of tokens
|
|
20
|
+
* Handles paragraph tokens by extracting their inline tokens
|
|
21
|
+
*/
|
|
22
|
+
export function extractInlineTokens(tokens: Tokens.Generic[] | undefined): Tokens.Generic[] {
|
|
23
|
+
// 防御性检查:确保 tokens 存在且是数组
|
|
24
|
+
if (!tokens || !Array.isArray(tokens)) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const inlineTokens: Tokens.Generic[] = [];
|
|
29
|
+
|
|
30
|
+
tokens.forEach((token) => {
|
|
31
|
+
if (token.type === "paragraph") {
|
|
32
|
+
// If it's a paragraph, extract its inline tokens
|
|
33
|
+
const paraToken = token as Tokens.Paragraph;
|
|
34
|
+
// 防御性检查:确保 paraToken.tokens 存在
|
|
35
|
+
if (paraToken.tokens && Array.isArray(paraToken.tokens)) {
|
|
36
|
+
inlineTokens.push(...paraToken.tokens);
|
|
37
|
+
}
|
|
38
|
+
} else if (
|
|
39
|
+
token.type === "text" ||
|
|
40
|
+
token.type === "strong" ||
|
|
41
|
+
token.type === "em" ||
|
|
42
|
+
token.type === "link" ||
|
|
43
|
+
token.type === "code" ||
|
|
44
|
+
token.type === "br"
|
|
45
|
+
) {
|
|
46
|
+
// If it's already an inline token, add it directly
|
|
47
|
+
inlineTokens.push(token);
|
|
48
|
+
}
|
|
49
|
+
// For other block-level tokens, we skip them
|
|
50
|
+
// If needed, they can be handled separately
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return inlineTokens;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Render inline tokens to HTML string
|
|
58
|
+
* Used by both Pattern 1 and Pattern 2
|
|
59
|
+
*/
|
|
60
|
+
export function renderInlineTokens(tokens: Tokens.Generic[] | undefined): string {
|
|
61
|
+
// 防御性检查:确保 tokens 存在且是数组
|
|
62
|
+
if (!tokens || !Array.isArray(tokens)) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return tokens
|
|
67
|
+
.map((token) => {
|
|
68
|
+
switch (token.type) {
|
|
69
|
+
case "text": {
|
|
70
|
+
const textToken = token as Tokens.Text;
|
|
71
|
+
return textToken.text || "";
|
|
72
|
+
}
|
|
73
|
+
case "strong": {
|
|
74
|
+
const strongToken = token as Tokens.Strong;
|
|
75
|
+
// 防御性检查:确保 strongToken.tokens 存在
|
|
76
|
+
const nestedTokens =
|
|
77
|
+
strongToken.tokens && Array.isArray(strongToken.tokens)
|
|
78
|
+
? strongToken.tokens
|
|
79
|
+
: [];
|
|
80
|
+
return `<strong>${renderInlineTokens(nestedTokens)}</strong>`;
|
|
81
|
+
}
|
|
82
|
+
case "em": {
|
|
83
|
+
const emToken = token as Tokens.Em;
|
|
84
|
+
// 防御性检查:确保 emToken.tokens 存在
|
|
85
|
+
const nestedTokens =
|
|
86
|
+
emToken.tokens && Array.isArray(emToken.tokens) ? emToken.tokens : [];
|
|
87
|
+
return `<em>${renderInlineTokens(nestedTokens)}</em>`;
|
|
88
|
+
}
|
|
89
|
+
case "link": {
|
|
90
|
+
const linkToken = token as Tokens.Link;
|
|
91
|
+
const title = linkToken.title ? ` title="${escapeHtml(linkToken.title)}"` : "";
|
|
92
|
+
// 防御性检查:确保 linkToken.tokens 存在
|
|
93
|
+
const nestedTokens =
|
|
94
|
+
linkToken.tokens && Array.isArray(linkToken.tokens) ? linkToken.tokens : [];
|
|
95
|
+
return `<a href="${linkToken.href || "#"}"${title}>${renderInlineTokens(nestedTokens)}</a>`;
|
|
96
|
+
}
|
|
97
|
+
case "code": {
|
|
98
|
+
const codeToken = token as Tokens.Code;
|
|
99
|
+
return `<code>${escapeHtml(codeToken.text || "")}</code>`;
|
|
100
|
+
}
|
|
101
|
+
case "br": {
|
|
102
|
+
return "<br>";
|
|
103
|
+
}
|
|
104
|
+
default: {
|
|
105
|
+
// For unknown token types, return empty string
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.join("");
|
|
111
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for @wsxjs/wsx-marked-components
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Tokens } from "marked";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Custom token renderer function type
|
|
9
|
+
*
|
|
10
|
+
* @param token - The token to render
|
|
11
|
+
* @param defaultRender - Function to call default rendering logic
|
|
12
|
+
* @returns HTMLElement or null (null means use default rendering)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const customHeadingRenderer: TokenRenderer = (token, defaultRender) => {
|
|
17
|
+
* if (token.type === 'heading') {
|
|
18
|
+
* const headingToken = token as Tokens.Heading;
|
|
19
|
+
* // Custom logic here
|
|
20
|
+
* return defaultRender(); // Or return custom element
|
|
21
|
+
* }
|
|
22
|
+
* return null; // Use default for other types
|
|
23
|
+
* };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export type TokenRenderer = (
|
|
27
|
+
token: Tokens.Generic,
|
|
28
|
+
defaultRender: () => HTMLElement | null
|
|
29
|
+
) => HTMLElement | null;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Custom renderers configuration
|
|
33
|
+
* Maps token types to custom renderer functions
|
|
34
|
+
*/
|
|
35
|
+
export interface CustomRenderers {
|
|
36
|
+
[tokenType: string]: TokenRenderer;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Markdown component options
|
|
41
|
+
*/
|
|
42
|
+
export interface MarkdownOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Custom renderers for specific token types
|
|
45
|
+
*/
|
|
46
|
+
customRenderers?: CustomRenderers;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Custom CSS class name for the content container
|
|
50
|
+
*/
|
|
51
|
+
contentClass?: string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether to enable debug logging
|
|
55
|
+
*/
|
|
56
|
+
debug?: boolean;
|
|
57
|
+
}
|