@typefm/react-markdown-viewer 0.1.0
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 +217 -0
- package/dist/ErrorBoundary.d.ts +25 -0
- package/dist/ErrorBoundary.d.ts.map +1 -0
- package/dist/ErrorBoundary.js +29 -0
- package/dist/ErrorBoundary.js.map +1 -0
- package/dist/MarkdownViewer.d.ts +41 -0
- package/dist/MarkdownViewer.d.ts.map +1 -0
- package/dist/MarkdownViewer.js +69 -0
- package/dist/MarkdownViewer.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cache-manager.d.ts +59 -0
- package/dist/lib/cache-manager.d.ts.map +1 -0
- package/dist/lib/cache-manager.js +160 -0
- package/dist/lib/cache-manager.js.map +1 -0
- package/dist/lib/cursor-controller.d.ts +13 -0
- package/dist/lib/cursor-controller.d.ts.map +1 -0
- package/dist/lib/cursor-controller.js +93 -0
- package/dist/lib/cursor-controller.js.map +1 -0
- package/dist/lib/defaults/code-block.d.ts +71 -0
- package/dist/lib/defaults/code-block.d.ts.map +1 -0
- package/dist/lib/defaults/code-block.js +104 -0
- package/dist/lib/defaults/code-block.js.map +1 -0
- package/dist/lib/defaults/image.d.ts +41 -0
- package/dist/lib/defaults/image.d.ts.map +1 -0
- package/dist/lib/defaults/image.js +45 -0
- package/dist/lib/defaults/image.js.map +1 -0
- package/dist/lib/defaults/link.d.ts +45 -0
- package/dist/lib/defaults/link.d.ts.map +1 -0
- package/dist/lib/defaults/link.js +76 -0
- package/dist/lib/defaults/link.js.map +1 -0
- package/dist/lib/defaults/math.d.ts +51 -0
- package/dist/lib/defaults/math.d.ts.map +1 -0
- package/dist/lib/defaults/math.js +119 -0
- package/dist/lib/defaults/math.js.map +1 -0
- package/dist/lib/defaults/table.d.ts +18 -0
- package/dist/lib/defaults/table.d.ts.map +1 -0
- package/dist/lib/defaults/table.js +19 -0
- package/dist/lib/defaults/table.js.map +1 -0
- package/dist/lib/highlighter.d.ts +81 -0
- package/dist/lib/highlighter.d.ts.map +1 -0
- package/dist/lib/highlighter.js +421 -0
- package/dist/lib/highlighter.js.map +1 -0
- package/dist/lib/hook-utils.d.ts +32 -0
- package/dist/lib/hook-utils.d.ts.map +1 -0
- package/dist/lib/hook-utils.js +42 -0
- package/dist/lib/hook-utils.js.map +1 -0
- package/dist/lib/html.d.ts +2 -0
- package/dist/lib/html.d.ts.map +1 -0
- package/dist/lib/html.js +12 -0
- package/dist/lib/html.js.map +1 -0
- package/dist/lib/morph.d.ts +57 -0
- package/dist/lib/morph.d.ts.map +1 -0
- package/dist/lib/morph.js +204 -0
- package/dist/lib/morph.js.map +1 -0
- package/dist/lib/parser.d.ts +32 -0
- package/dist/lib/parser.d.ts.map +1 -0
- package/dist/lib/parser.js +645 -0
- package/dist/lib/parser.js.map +1 -0
- package/dist/lib/wasm-init.d.ts +33 -0
- package/dist/lib/wasm-init.d.ts.map +1 -0
- package/dist/lib/wasm-init.js +69 -0
- package/dist/lib/wasm-init.js.map +1 -0
- package/dist/styles/alerts.css +294 -0
- package/dist/styles/dotted.svg +3 -0
- package/dist/styles/hljs.css +332 -0
- package/dist/styles/index.css +17 -0
- package/dist/styles/katex.css +74 -0
- package/dist/styles/viewer.css +975 -0
- package/dist/types/hooks.d.ts +207 -0
- package/dist/types/hooks.d.ts.map +1 -0
- package/dist/types/hooks.js +7 -0
- package/dist/types/hooks.js.map +1 -0
- package/dist/useMarkdownViewer.d.ts +18 -0
- package/dist/useMarkdownViewer.d.ts.map +1 -0
- package/dist/useMarkdownViewer.js +403 -0
- package/dist/useMarkdownViewer.js.map +1 -0
- package/dist/utils.d.ts +20 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +18 -0
- package/dist/utils.js.map +1 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Janos Veres
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./assets/logo.png" alt="react-markdown-viewer" width="80" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@typefm/react-markdown-viewer</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>High-performance React component for rendering markdown with streaming support</strong>
|
|
9
|
+
<br />
|
|
10
|
+
<em>GitHub-style rendering optimized for LLM chat interfaces</em>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<img src="https://img.shields.io/badge/react-19+-087ea4" alt="React 19+" />
|
|
15
|
+
<img src="https://img.shields.io/badge/typescript-5+-3178c6" alt="TypeScript" />
|
|
16
|
+
<img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License" />
|
|
17
|
+
<img src="https://img.shields.io/badge/styling-GitHub%20Primer-24292f" alt="GitHub Primer" />
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<a href="#features">Features</a> •
|
|
22
|
+
<a href="#installation">Installation</a> •
|
|
23
|
+
<a href="#usage">Usage</a> •
|
|
24
|
+
<a href="#security">Security</a> •
|
|
25
|
+
<a href="#demo">Demo</a>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- **GitHub-Style Rendering** — Visual parity with GitHub's markdown using Primer design tokens
|
|
33
|
+
- **Streaming Support** — Optimized for real-time LLM output with adaptive throttling and DOM morphing
|
|
34
|
+
- **Element-Level Diffing** — Skips unchanged blocks during streaming (~99% skip rate)
|
|
35
|
+
- **Syntax Highlighting** — GitHub Prettylights theme, 40+ languages via highlight.js with dynamic loading
|
|
36
|
+
- **Math Rendering** — KaTeX with lazy loading
|
|
37
|
+
- **Color Previews** — Auto-detects hex, rgb, hsl colors and shows visual swatches
|
|
38
|
+
- **GitHub Flavored Markdown** — Tables, task lists, strikethrough, alerts, footnotes
|
|
39
|
+
- **Smart Caching** — LRU caches for rendered content, syntax highlighting, and KaTeX output
|
|
40
|
+
- **Cursor Animation** — Blinking cursor during streaming with focus-aware styling
|
|
41
|
+
- **Dark Mode** — CSS-based theming (Tailwind `.dark`, `data-theme`, or OS preference)
|
|
42
|
+
- **Error Boundary** — Built-in error handling with customizable fallback UI
|
|
43
|
+
- **Markdown Healing** — `healMarkdown()` closes unclosed delimiters during streaming
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
See **[docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md)** for the full internal architecture:
|
|
48
|
+
|
|
49
|
+
- Rendering pipeline (markdown → WASM parse → sanitize → post-process → DOM)
|
|
50
|
+
- Dual rendering modes (static `dangerouslySetInnerHTML` vs streaming Idiomorph morphing)
|
|
51
|
+
- WASM initialization, streaming throttling, cursor system
|
|
52
|
+
- Caching strategy, hook system, lazy loading
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Markdown → comrak-wasm → HTML → Sanitization → Post-Processing → DOM
|
|
56
|
+
│ │
|
|
57
|
+
GFM Extensions • KaTeX math
|
|
58
|
+
• Tables • Syntax highlighting
|
|
59
|
+
• Task lists • Color previews
|
|
60
|
+
• Alerts • Link security
|
|
61
|
+
• Math syntax • Copy buttons
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pnpm add @typefm/react-markdown-viewer
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Requires React 19:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"react": "^19.0.0",
|
|
76
|
+
"react-dom": "^19.0.0"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Usage
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { initMarkdownViewer, MarkdownViewer } from '@typefm/react-markdown-viewer';
|
|
85
|
+
import '@typefm/react-markdown-viewer/styles.css';
|
|
86
|
+
import 'katex/dist/katex.min.css';
|
|
87
|
+
|
|
88
|
+
// Initialize WASM once at app startup
|
|
89
|
+
await initMarkdownViewer();
|
|
90
|
+
|
|
91
|
+
function ChatMessage({ content, isStreaming }) {
|
|
92
|
+
return (
|
|
93
|
+
<MarkdownViewer
|
|
94
|
+
text={content}
|
|
95
|
+
isStreaming={isStreaming}
|
|
96
|
+
throttleMs={50}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### With Error Boundary
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { MarkdownViewerSafe } from '@typefm/react-markdown-viewer';
|
|
106
|
+
|
|
107
|
+
<MarkdownViewerSafe
|
|
108
|
+
text={content}
|
|
109
|
+
isStreaming={isStreaming}
|
|
110
|
+
fallback={<div>Failed to render markdown</div>}
|
|
111
|
+
onError={(error) => console.error(error)}
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Props
|
|
116
|
+
|
|
117
|
+
| Prop | Type | Default | Description |
|
|
118
|
+
|------|------|---------|-------------|
|
|
119
|
+
| `text` | `string` | `''` | Markdown source text |
|
|
120
|
+
| `isStreaming` | `boolean` | `false` | Enable streaming mode (throttling + cursor) |
|
|
121
|
+
| `throttleMs` | `number` | `50` | Minimum ms between updates during streaming |
|
|
122
|
+
| `className` | `string` | — | Additional CSS class names |
|
|
123
|
+
| `onStreamingEnd` | `() => void` | — | Callback when streaming ends |
|
|
124
|
+
| `hooks` | `RenderHooks` | — | Customize element rendering |
|
|
125
|
+
|
|
126
|
+
## Render Hooks
|
|
127
|
+
|
|
128
|
+
Customize how specific markdown elements are rendered:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { MarkdownViewer, escapeHtml, type RenderHooks } from '@typefm/react-markdown-viewer';
|
|
132
|
+
|
|
133
|
+
const hooks: RenderHooks = {
|
|
134
|
+
onCodeBlock: ({ code, language }) => {
|
|
135
|
+
if (language === 'mermaid') {
|
|
136
|
+
return `<div class="mermaid">${escapeHtml(code)}</div>`;
|
|
137
|
+
}
|
|
138
|
+
return null; // use default
|
|
139
|
+
},
|
|
140
|
+
onLink: ({ href, text }) => {
|
|
141
|
+
if (href.includes('youtube.com')) {
|
|
142
|
+
return `<div class="youtube-embed">...</div>`;
|
|
143
|
+
}
|
|
144
|
+
return null; // use default
|
|
145
|
+
},
|
|
146
|
+
onRender: (html) => html.replace(/TODO/g, '<mark>TODO</mark>'),
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
<MarkdownViewer text={content} hooks={hooks} />
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Return `string`** for custom HTML, **`null`** to use the default processor.
|
|
153
|
+
|
|
154
|
+
Available hooks: `onCodeBlock`, `onInlineCode`, `onMath`, `onTable`, `onLink`, `onImage`, `onHeading`, `onBlockquote`, `onAlert`, `onList`, `onHorizontalRule`, `onFootnoteRef`, `onFootnoteDef`, `onRender`.
|
|
155
|
+
|
|
156
|
+
## Security
|
|
157
|
+
|
|
158
|
+
See **[docs/SECURITY.md](./docs/SECURITY.md)** for complete security documentation.
|
|
159
|
+
|
|
160
|
+
7-layer defense-in-depth against XSS:
|
|
161
|
+
|
|
162
|
+
1. **Comrak tagfilter** — escapes `<script>`, `<iframe>`, `<style>`, etc.
|
|
163
|
+
2. **Tag removal** — removes `<object>`, `<embed>`, `<form>`, `<meta>`, etc.
|
|
164
|
+
3. **Tag unwrapping** — strips `<noscript>`, `<template>` (keeps content)
|
|
165
|
+
4. **Event handler filter** — strips all `on*` attributes
|
|
166
|
+
5. **URL sanitizer** — blocks `javascript:`, `vbscript:`, `data:`, `file:` schemes
|
|
167
|
+
6. **DOM clobbering filter** — removes dangerous `name`/`id` values
|
|
168
|
+
7. **SVG sanitizer** — removes SVG with embedded scripts or event handler injection
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
// Safe to render untrusted content
|
|
172
|
+
<MarkdownViewer text={userInput} />
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Styling
|
|
176
|
+
|
|
177
|
+
See **[docs/STYLING.md](./docs/STYLING.md)** for complete styling documentation.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import '@typefm/react-markdown-viewer/styles.css';
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Dark mode works automatically via `.dark` class, `data-theme="dark"`, or OS preference. Override CSS custom properties on `.markdown-viewer` to customize colors, fonts, and cursor.
|
|
184
|
+
|
|
185
|
+
## Demo
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
cd packages/react-markdown-viewer
|
|
189
|
+
pnpm install
|
|
190
|
+
pnpm dev
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The playground includes:
|
|
194
|
+
|
|
195
|
+
- **Playground** — Full showcase, AI chat simulation, stress test, edge cases with custom markdown input
|
|
196
|
+
- **GitHub Comparison** — Side-by-side rendering comparison with GitHub's Markdown API
|
|
197
|
+
|
|
198
|
+
## Testing
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
pnpm test
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Test coverage includes: parser rendering, XSS prevention, streaming edge cases, DOM morphing, syntax highlighting, KaTeX lazy loading, component API, error boundary, cursor placement, and DoS prevention.
|
|
205
|
+
|
|
206
|
+
## Dependencies
|
|
207
|
+
|
|
208
|
+
| Package | Purpose | Loading |
|
|
209
|
+
|---------|---------|---------|
|
|
210
|
+
| `@typefm/comrak-wasm` | Markdown parser (Rust/WASM) | Requires init |
|
|
211
|
+
| `highlight.js` | Syntax highlighting | Core bundled, languages lazy loaded |
|
|
212
|
+
| `katex` | Math rendering | Lazy loaded |
|
|
213
|
+
| `idiomorph` | DOM morphing | Bundled |
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
MIT
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Component, type ReactNode, type ErrorInfo } from 'react';
|
|
2
|
+
export interface ErrorBoundaryProps {
|
|
3
|
+
/** Content to render */
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
/** Fallback UI when an error occurs */
|
|
6
|
+
fallback?: ReactNode;
|
|
7
|
+
/** Callback when an error is caught */
|
|
8
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
9
|
+
}
|
|
10
|
+
interface ErrorBoundaryState {
|
|
11
|
+
hasError: boolean;
|
|
12
|
+
error: Error | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Error boundary for MarkdownViewer
|
|
16
|
+
* Catches rendering errors and displays fallback UI
|
|
17
|
+
*/
|
|
18
|
+
export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
19
|
+
constructor(props: ErrorBoundaryProps);
|
|
20
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
21
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
22
|
+
render(): ReactNode;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=ErrorBoundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../src/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;CACxD;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;gBACtE,KAAK,EAAE,kBAAkB;IAKrC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB;IAIjE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAI3D,MAAM,IAAI,SAAS;CAgBpB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Component } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Error boundary for MarkdownViewer
|
|
5
|
+
* Catches rendering errors and displays fallback UI
|
|
6
|
+
*/
|
|
7
|
+
export class ErrorBoundary extends Component {
|
|
8
|
+
constructor(props) {
|
|
9
|
+
super(props);
|
|
10
|
+
this.state = { hasError: false, error: null };
|
|
11
|
+
}
|
|
12
|
+
static getDerivedStateFromError(error) {
|
|
13
|
+
return { hasError: true, error };
|
|
14
|
+
}
|
|
15
|
+
componentDidCatch(error, errorInfo) {
|
|
16
|
+
this.props.onError?.(error, errorInfo);
|
|
17
|
+
}
|
|
18
|
+
render() {
|
|
19
|
+
if (this.state.hasError) {
|
|
20
|
+
if (this.props.fallback !== undefined) {
|
|
21
|
+
return this.props.fallback;
|
|
22
|
+
}
|
|
23
|
+
// Default fallback
|
|
24
|
+
return (_jsx("div", { className: "markdown-viewer-error", role: "alert", children: _jsx("p", { children: "Failed to render markdown" }) }));
|
|
25
|
+
}
|
|
26
|
+
return this.props.children;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=ErrorBoundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../src/ErrorBoundary.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAgBlE;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAiD;IAClF,YAAY,KAAyB;QACnC,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACnC,CAAC;IAED,iBAAiB,CAAC,KAAY,EAAE,SAAoB;QAClD,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACxB,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACtC,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC7B,CAAC;YAED,mBAAmB;YACnB,OAAO,CACL,cAAK,SAAS,EAAC,uBAAuB,EAAC,IAAI,EAAC,OAAO,YACjD,oDAAgC,GAC5B,CACP,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type ReactNode, type ComponentProps, type Ref } from 'react';
|
|
2
|
+
import type { RenderHooks } from './types/hooks';
|
|
3
|
+
export interface MarkdownViewerRef {
|
|
4
|
+
/** Reset the component state (call when switching content sources) */
|
|
5
|
+
reset: () => void;
|
|
6
|
+
/** Get the container element */
|
|
7
|
+
getContainer: () => HTMLDivElement | null;
|
|
8
|
+
/** Focus the viewer (activates cursor) */
|
|
9
|
+
focus: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface MarkdownViewerProps extends Omit<ComponentProps<'div'>, 'children' | 'ref'> {
|
|
12
|
+
/** Markdown source text */
|
|
13
|
+
text: string;
|
|
14
|
+
/** Enable streaming mode (throttling + cursor) */
|
|
15
|
+
isStreaming?: boolean;
|
|
16
|
+
/** Minimum ms between updates during streaming (default: 50) */
|
|
17
|
+
throttleMs?: number;
|
|
18
|
+
/** Callback when streaming ends */
|
|
19
|
+
onStreamingEnd?: () => void;
|
|
20
|
+
/** Ref to access imperative methods (React 19+) */
|
|
21
|
+
ref?: Ref<MarkdownViewerRef>;
|
|
22
|
+
/** Hooks for customizing markdown rendering */
|
|
23
|
+
hooks?: RenderHooks;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* High-performance React component for rendering markdown with streaming support,
|
|
27
|
+
* optimized for LLM chat interfaces.
|
|
28
|
+
*/
|
|
29
|
+
export declare const MarkdownViewer: import("react").NamedExoticComponent<MarkdownViewerProps>;
|
|
30
|
+
export interface MarkdownViewerSafeProps extends Omit<MarkdownViewerProps, 'onError'> {
|
|
31
|
+
/** Fallback UI when an error occurs */
|
|
32
|
+
fallback?: ReactNode;
|
|
33
|
+
/** Callback when a rendering error is caught by the error boundary */
|
|
34
|
+
onError?: (error: Error) => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* MarkdownViewer wrapped with an error boundary.
|
|
38
|
+
* Catches rendering errors and displays fallback UI instead of crashing.
|
|
39
|
+
*/
|
|
40
|
+
export declare const MarkdownViewerSafe: import("react").NamedExoticComponent<MarkdownViewerSafeProps>;
|
|
41
|
+
//# sourceMappingURL=MarkdownViewer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownViewer.d.ts","sourceRoot":"","sources":["../src/MarkdownViewer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqC,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAGzG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,gCAAgC;IAChC,YAAY,EAAE,MAAM,cAAc,GAAG,IAAI,CAAC;IAC1C,0CAA0C;IAC1C,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;IAC1F,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,mDAAmD;IACnD,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC7B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,2DAuFzB,CAAC;AAEH,MAAM,WAAW,uBAAwB,SAAQ,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC;IACnF,uCAAuC;IACvC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,sEAAsE;IACtE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,+DAU7B,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useImperativeHandle, memo, useRef } from 'react';
|
|
3
|
+
import { useMarkdownViewer } from './useMarkdownViewer';
|
|
4
|
+
import { ErrorBoundary } from './ErrorBoundary';
|
|
5
|
+
/**
|
|
6
|
+
* High-performance React component for rendering markdown with streaming support,
|
|
7
|
+
* optimized for LLM chat interfaces.
|
|
8
|
+
*/
|
|
9
|
+
export const MarkdownViewer = memo(function MarkdownViewer({ text, isStreaming = false, throttleMs = 50, className, onStreamingEnd, ref, onClick, hooks, ...props }) {
|
|
10
|
+
const { containerRef, syncMorphEnabled, getRenderedContent, handleClick, reset, } = useMarkdownViewer({
|
|
11
|
+
text,
|
|
12
|
+
isStreaming,
|
|
13
|
+
throttleMs,
|
|
14
|
+
onStreamingEnd,
|
|
15
|
+
hooks,
|
|
16
|
+
});
|
|
17
|
+
const wrapperRef = useRef(null);
|
|
18
|
+
// Expose imperative methods
|
|
19
|
+
useImperativeHandle(ref, () => ({
|
|
20
|
+
reset,
|
|
21
|
+
getContainer: () => containerRef.current,
|
|
22
|
+
focus: () => wrapperRef.current?.focus(),
|
|
23
|
+
}), [reset]);
|
|
24
|
+
// Build className without array allocation
|
|
25
|
+
let rootClassName = 'markdown-viewer';
|
|
26
|
+
if (className)
|
|
27
|
+
rootClassName += ' ' + className;
|
|
28
|
+
// Merge click handlers
|
|
29
|
+
const handleRootClick = (e) => {
|
|
30
|
+
handleClick(e);
|
|
31
|
+
onClick?.(e);
|
|
32
|
+
};
|
|
33
|
+
// Shared props for both render paths
|
|
34
|
+
const rootProps = {
|
|
35
|
+
ref: wrapperRef,
|
|
36
|
+
className: rootClassName,
|
|
37
|
+
onClick: handleRootClick,
|
|
38
|
+
tabIndex: 0,
|
|
39
|
+
// Data attributes for state-based styling
|
|
40
|
+
'data-slot': 'markdown-viewer',
|
|
41
|
+
'data-state': isStreaming ? 'streaming' : 'idle',
|
|
42
|
+
// ARIA attributes for accessibility
|
|
43
|
+
role: 'article',
|
|
44
|
+
'aria-label': props['aria-label'] ?? 'Markdown content',
|
|
45
|
+
'aria-busy': isStreaming,
|
|
46
|
+
...props,
|
|
47
|
+
};
|
|
48
|
+
/*
|
|
49
|
+
* If `syncMorphEnabled` is TRUE (Streaming or Finished Streaming):
|
|
50
|
+
* We use DOM morphing via `morphContent`. This patches the DOM intelligently.
|
|
51
|
+
* It prevents full re-renders and preserves selection.
|
|
52
|
+
* The content is rendered empty initially and morphed in the hook.
|
|
53
|
+
*
|
|
54
|
+
* If `syncMorphEnabled` is FALSE (Initial Static Load):
|
|
55
|
+
* We use dangerouslySetInnerHTML for speed.
|
|
56
|
+
*/
|
|
57
|
+
if (syncMorphEnabled) {
|
|
58
|
+
return (_jsx("div", { ...rootProps, children: _jsx("div", { ref: containerRef, className: "markdown", "data-slot": "markdown-content" }) }));
|
|
59
|
+
}
|
|
60
|
+
return (_jsx("div", { ...rootProps, children: _jsx("div", { ref: containerRef, className: "markdown", "data-slot": "markdown-content", dangerouslySetInnerHTML: { __html: getRenderedContent() } }) }));
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* MarkdownViewer wrapped with an error boundary.
|
|
64
|
+
* Catches rendering errors and displays fallback UI instead of crashing.
|
|
65
|
+
*/
|
|
66
|
+
export const MarkdownViewerSafe = memo(function MarkdownViewerSafe({ fallback, onError, ...props }) {
|
|
67
|
+
return (_jsx(ErrorBoundary, { fallback: fallback, onError: onError, children: _jsx(MarkdownViewer, { ...props }) }));
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=MarkdownViewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownViewer.js","sourceRoot":"","sources":["../src/MarkdownViewer.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,EAAiD,MAAM,OAAO,CAAC;AACzG,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA2BhD;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,cAAc,CAAC,EACzD,IAAI,EACJ,WAAW,GAAG,KAAK,EACnB,UAAU,GAAG,EAAE,EACf,SAAS,EACT,cAAc,EACd,GAAG,EACH,OAAO,EACP,KAAK,EACL,GAAG,KAAK,EACY;IACpB,MAAM,EACJ,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,KAAK,GACN,GAAG,iBAAiB,CAAC;QACpB,IAAI;QACJ,WAAW;QACX,UAAU;QACV,cAAc;QACd,KAAK;KACN,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAEhD,4BAA4B;IAC5B,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9B,KAAK;QACL,YAAY,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO;QACxC,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE;KACzC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEb,2CAA2C;IAC3C,IAAI,aAAa,GAAG,iBAAiB,CAAC;IACtC,IAAI,SAAS;QAAE,aAAa,IAAI,GAAG,GAAG,SAAS,CAAC;IAEhD,uBAAuB;IACvB,MAAM,eAAe,GAAG,CAAC,CAAmC,EAAE,EAAE;QAC9D,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACf,CAAC,CAAC;IAEF,qCAAqC;IACrC,MAAM,SAAS,GAAG;QAChB,GAAG,EAAE,UAAU;QACf,SAAS,EAAE,aAAa;QACxB,OAAO,EAAE,eAAe;QACxB,QAAQ,EAAE,CAAC;QACX,0CAA0C;QAC1C,WAAW,EAAE,iBAAiB;QAC9B,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM;QAChD,oCAAoC;QACpC,IAAI,EAAE,SAAkB;QACxB,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,IAAI,kBAAkB;QACvD,WAAW,EAAE,WAAW;QACxB,GAAG,KAAK;KACT,CAAC;IAEF;;;;;;;;OAQG;IACH,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO,CACL,iBAAS,SAAS,YAChB,cAAK,GAAG,EAAE,YAAY,EAAE,SAAS,EAAC,UAAU,eAAW,kBAAkB,GAAG,GACxE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,iBAAS,SAAS,YAChB,cACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAC,UAAU,eACV,kBAAkB,EAC5B,uBAAuB,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,GACzD,GACE,CACP,CAAC;AACJ,CAAC,CAAC,CAAC;AASH;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,kBAAkB,CAAC,EACjE,QAAQ,EACR,OAAO,EACP,GAAG,KAAK,EACgB;IACxB,OAAO,CACL,KAAC,aAAa,IAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,YACjD,KAAC,cAAc,OAAK,KAAK,GAAI,GACf,CACjB,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { MarkdownViewer, MarkdownViewerSafe } from "./MarkdownViewer";
|
|
2
|
+
export type { MarkdownViewerProps, MarkdownViewerRef, MarkdownViewerSafeProps, } from "./MarkdownViewer";
|
|
3
|
+
export { ErrorBoundary } from "./ErrorBoundary";
|
|
4
|
+
export type { ErrorBoundaryProps } from "./ErrorBoundary";
|
|
5
|
+
export { useMarkdownViewer } from "./useMarkdownViewer";
|
|
6
|
+
export type { UseMarkdownViewerOptions } from "./useMarkdownViewer";
|
|
7
|
+
export { initMarkdownViewer, initMarkdownViewerSync, isWasmReady, } from "./lib/wasm-init";
|
|
8
|
+
export type { RenderHooks, HookResult, CodeBlockData, InlineCodeData, MathData, TableData, LinkData, ImageData, HeadingData, BlockquoteData, AlertData, AlertType, ListData, HorizontalRuleData, FootnoteRefData, FootnoteDefData, } from "./types/hooks";
|
|
9
|
+
export { processCodeBlock, processInlineCode, injectColorPreviews, type CodeBlockOptions, type InlineCodeOptions, } from "./lib/defaults/code-block";
|
|
10
|
+
export { processMathBlock } from "./lib/defaults/math";
|
|
11
|
+
export { processTable } from "./lib/defaults/table";
|
|
12
|
+
export { processLink, isDangerousUrl, isExternalUrl, } from "./lib/defaults/link";
|
|
13
|
+
export { processImage, type ImageOptions } from "./lib/defaults/image";
|
|
14
|
+
export { preloadKaTeX } from "./lib/parser";
|
|
15
|
+
export { escapeHtml } from "./lib/hook-utils";
|
|
16
|
+
export { loadLanguage, loadLanguages, registerLanguage, isLanguageReady, isLanguageSupported, getRegisteredLanguages, getSupportedLanguages, } from "./lib/highlighter";
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtE,YAAY,EACX,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,GACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAGpE,OAAO,EACN,kBAAkB,EAClB,sBAAsB,EACtB,WAAW,GACX,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,cAAc,EACd,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,EACT,WAAW,EACX,cAAc,EACd,SAAS,EACT,SAAS,EACT,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,eAAe,GACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EACN,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EACN,WAAW,EACX,cAAc,EACd,aAAa,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EACN,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACrB,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Main component
|
|
2
|
+
export { MarkdownViewer, MarkdownViewerSafe } from "./MarkdownViewer";
|
|
3
|
+
// Error boundary (for custom error handling)
|
|
4
|
+
export { ErrorBoundary } from "./ErrorBoundary";
|
|
5
|
+
// Hook for custom implementations
|
|
6
|
+
export { useMarkdownViewer } from "./useMarkdownViewer";
|
|
7
|
+
// WASM initialization
|
|
8
|
+
export { initMarkdownViewer, initMarkdownViewerSync, isWasmReady, } from "./lib/wasm-init";
|
|
9
|
+
// Default processors (for extending/wrapping)
|
|
10
|
+
export { processCodeBlock, processInlineCode, injectColorPreviews, } from "./lib/defaults/code-block";
|
|
11
|
+
export { processMathBlock } from "./lib/defaults/math";
|
|
12
|
+
export { processTable } from "./lib/defaults/table";
|
|
13
|
+
export { processLink, isDangerousUrl, isExternalUrl, } from "./lib/defaults/link";
|
|
14
|
+
export { processImage } from "./lib/defaults/image";
|
|
15
|
+
// Preload utilities for performance optimization
|
|
16
|
+
export { preloadKaTeX } from "./lib/parser";
|
|
17
|
+
// Hook utilities
|
|
18
|
+
export { escapeHtml } from "./lib/hook-utils";
|
|
19
|
+
// Syntax highlighting utilities
|
|
20
|
+
export { loadLanguage, loadLanguages, registerLanguage, isLanguageReady, isLanguageSupported, getRegisteredLanguages, getSupportedLanguages, } from "./lib/highlighter";
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iBAAiB;AACjB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAOtE,6CAA6C;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,kCAAkC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,sBAAsB;AACtB,OAAO,EACN,kBAAkB,EAClB,sBAAsB,EACtB,WAAW,GACX,MAAM,iBAAiB,CAAC;AAsBzB,8CAA8C;AAC9C,OAAO,EACN,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GAGnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EACN,WAAW,EACX,cAAc,EACd,aAAa,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,YAAY,EAAqB,MAAM,sBAAsB,CAAC;AAEvE,iDAAiD;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,iBAAiB;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,gCAAgC;AAChC,OAAO,EACN,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACrB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified LRU Cache Manager
|
|
3
|
+
* Coordinates memory usage across all caches in the markdown-viewer
|
|
4
|
+
*/
|
|
5
|
+
export interface CacheStats {
|
|
6
|
+
entries: number;
|
|
7
|
+
estimatedBytes: number;
|
|
8
|
+
}
|
|
9
|
+
declare class LRUCache<V> {
|
|
10
|
+
private cache;
|
|
11
|
+
private sizeEstimates;
|
|
12
|
+
private totalBytes;
|
|
13
|
+
private maxEntries;
|
|
14
|
+
private maxBytes;
|
|
15
|
+
constructor(maxEntries: number, maxBytes?: number);
|
|
16
|
+
get(key: string): V | undefined;
|
|
17
|
+
set(key: string, value: V, sizeBytes?: number): void;
|
|
18
|
+
has(key: string): boolean;
|
|
19
|
+
clear(): void;
|
|
20
|
+
evictOldest(count: number): void;
|
|
21
|
+
get stats(): CacheStats;
|
|
22
|
+
private estimateSize;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Global cache manager instance
|
|
26
|
+
* Coordinates memory across render cache, katex cache, and morph cache
|
|
27
|
+
*/
|
|
28
|
+
declare class CacheManager {
|
|
29
|
+
private memoryBudget;
|
|
30
|
+
readonly renderCacheSync: LRUCache<string>;
|
|
31
|
+
readonly renderCacheAsync: LRUCache<string>;
|
|
32
|
+
readonly katexCacheDisplay: LRUCache<string>;
|
|
33
|
+
readonly katexCacheInline: LRUCache<string>;
|
|
34
|
+
readonly highlightCache: LRUCache<string>;
|
|
35
|
+
constructor(memoryBudget?: number);
|
|
36
|
+
/**
|
|
37
|
+
* Get combined stats for all caches
|
|
38
|
+
*/
|
|
39
|
+
get stats(): {
|
|
40
|
+
renderSync: CacheStats;
|
|
41
|
+
renderAsync: CacheStats;
|
|
42
|
+
katexDisplay: CacheStats;
|
|
43
|
+
katexInline: CacheStats;
|
|
44
|
+
highlight: CacheStats;
|
|
45
|
+
total: CacheStats;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Clear all caches
|
|
49
|
+
*/
|
|
50
|
+
clearAll(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Trim caches if under memory pressure
|
|
53
|
+
* Evicts ~25% of entries from each cache when budget exceeded
|
|
54
|
+
*/
|
|
55
|
+
trimIfNeeded(): void;
|
|
56
|
+
}
|
|
57
|
+
export declare const cacheManager: CacheManager;
|
|
58
|
+
export type { LRUCache };
|
|
59
|
+
//# sourceMappingURL=cache-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-manager.d.ts","sourceRoot":"","sources":["../../src/lib/cache-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,cAAM,QAAQ,CAAC,CAAC;IACd,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;gBAEb,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAiB;IAK3D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAU/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IA6BpD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,KAAK,IAAI,IAAI;IAMb,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAWhC,IAAI,KAAK,IAAI,UAAU,CAKtB;IAED,OAAO,CAAC,YAAY;CAMrB;AAKD;;;GAGG;AACH,cAAM,YAAY;IAChB,OAAO,CAAC,YAAY,CAAS;IAI7B,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,QAAQ,CAAC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE5C,QAAQ,CAAC,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE5C,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAE9B,YAAY,SAAwB;IAYhD;;OAEG;IACH,IAAI,KAAK,IAAI;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,WAAW,EAAE,UAAU,CAAC;QAAC,YAAY,EAAE,UAAU,CAAC;QAAC,WAAW,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAkB5J;IAED;;OAEG;IACH,QAAQ,IAAI,IAAI;IAQhB;;;OAGG;IACH,YAAY,IAAI,IAAI;CAiBrB;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAC;AAG/C,YAAY,EAAE,QAAQ,EAAE,CAAC"}
|