docsui-cli 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +279 -0
- package/dist/commands/add.d.ts +5 -0
- package/dist/commands/add.js +254 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.js +250 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +548 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.js +84 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.js +1562 -0
- package/dist/commands/new.d.ts +5 -0
- package/dist/commands/new.js +113 -0
- package/dist/commands/remove.d.ts +5 -0
- package/dist/commands/remove.js +134 -0
- package/dist/commands/save.d.ts +5 -0
- package/dist/commands/save.js +60 -0
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.js +247 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +88 -0
- package/dist/latex-to-primitives.d.ts +59 -0
- package/dist/latex-to-primitives.js +1019 -0
- package/dist/lib/component-registry.d.ts +33 -0
- package/dist/lib/component-registry.js +843 -0
- package/dist/lib/css-tokens.d.ts +8 -0
- package/dist/lib/css-tokens.js +294 -0
- package/dist/metadata.d.ts +1 -0
- package/dist/metadata.js +4 -0
- package/dist/symbol-map.d.ts +30 -0
- package/dist/symbol-map.js +1607 -0
- package/dist/utils/detect-structure.d.ts +16 -0
- package/dist/utils/detect-structure.js +58 -0
- package/dist/utils/fetch-component.d.ts +13 -0
- package/dist/utils/fetch-component.js +81 -0
- package/dist/utils/get-config.d.ts +14 -0
- package/dist/utils/get-config.js +19 -0
- package/dist/utils/install-deps.d.ts +3 -0
- package/dist/utils/install-deps.js +23 -0
- package/dist/utils/save-mdx-page.d.ts +35 -0
- package/dist/utils/save-mdx-page.js +44 -0
- package/dist/utils/scan-mdx.d.ts +20 -0
- package/dist/utils/scan-mdx.js +106 -0
- package/dist/utils/telemetry.d.ts +3 -0
- package/dist/utils/telemetry.js +42 -0
- package/dist/utils/write-component.d.ts +7 -0
- package/dist/utils/write-component.js +25 -0
- package/dist/webview-bundle.js +3478 -0
- package/package.json +94 -0
|
@@ -0,0 +1,1562 @@
|
|
|
1
|
+
import {
|
|
2
|
+
McpServer,
|
|
3
|
+
ResourceTemplate
|
|
4
|
+
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import axios from "axios";
|
|
8
|
+
import fs from "fs-extra";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { readFileSync } from "fs";
|
|
11
|
+
import {
|
|
12
|
+
SYMBOL_MAP,
|
|
13
|
+
searchSymbols,
|
|
14
|
+
symbolsByCategory,
|
|
15
|
+
buildMathStandard,
|
|
16
|
+
formatEntry,
|
|
17
|
+
CATEGORIES_LIST
|
|
18
|
+
} from "../symbol-map.js";
|
|
19
|
+
import {
|
|
20
|
+
latexToPrimitives,
|
|
21
|
+
convertMarkdownMath,
|
|
22
|
+
hasLatex,
|
|
23
|
+
parseSolution
|
|
24
|
+
} from "../latex-to-primitives.js";
|
|
25
|
+
import { fileURLToPath } from "url";
|
|
26
|
+
function getCliVersion() {
|
|
27
|
+
try {
|
|
28
|
+
const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const pkg = JSON.parse(
|
|
30
|
+
readFileSync(path.join(__dirname2, "../../package.json"), "utf-8")
|
|
31
|
+
);
|
|
32
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
33
|
+
} catch {
|
|
34
|
+
return "0.0.0";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const REGISTRY_URL = "https://raw.githubusercontent.com/suryaravikumar-space/mdx-ui/main/registry/registry.json";
|
|
38
|
+
const CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
39
|
+
let cache = null;
|
|
40
|
+
async function loadLocalRegistry() {
|
|
41
|
+
try {
|
|
42
|
+
const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
43
|
+
const candidates = [
|
|
44
|
+
path.join(__dirname2, "../../../../registry/registry.json"),
|
|
45
|
+
path.join(__dirname2, "../../../registry/registry.json"),
|
|
46
|
+
path.join(__dirname2, "../../registry/registry.json")
|
|
47
|
+
];
|
|
48
|
+
for (const p of candidates) {
|
|
49
|
+
if (await fs.pathExists(p)) {
|
|
50
|
+
return await fs.readJSON(p);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
55
|
+
process.stderr.write(
|
|
56
|
+
`[docsui mcp] Failed to load local registry: ${msg}
|
|
57
|
+
`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
async function fetchRegistry() {
|
|
63
|
+
if (cache && Date.now() - cache.fetchedAt < CACHE_TTL_MS) {
|
|
64
|
+
return cache.data;
|
|
65
|
+
}
|
|
66
|
+
const local = await loadLocalRegistry();
|
|
67
|
+
if (local) {
|
|
68
|
+
if (!Array.isArray(local.components)) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
"Local registry.json is malformed \u2014 missing components array"
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
cache = { data: local, fetchedAt: Date.now() };
|
|
74
|
+
return local;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const res = await axios.get(REGISTRY_URL, { timeout: 8e3 });
|
|
78
|
+
const data = res.data;
|
|
79
|
+
if (!data || !Array.isArray(data.components)) {
|
|
80
|
+
throw new Error("Remote registry returned malformed data");
|
|
81
|
+
}
|
|
82
|
+
cache = { data, fetchedAt: Date.now() };
|
|
83
|
+
return data;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (cache) return cache.data;
|
|
86
|
+
const reason = err instanceof Error ? err.message : "network error";
|
|
87
|
+
throw new Error(`Could not load component registry \u2014 ${reason}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function levenshtein(a, b) {
|
|
91
|
+
const m = a.length;
|
|
92
|
+
const n = b.length;
|
|
93
|
+
const dp = Array.from(
|
|
94
|
+
{ length: m + 1 },
|
|
95
|
+
(_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
|
|
96
|
+
);
|
|
97
|
+
for (let i = 1; i <= m; i++) {
|
|
98
|
+
for (let j = 1; j <= n; j++) {
|
|
99
|
+
dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return dp[m][n];
|
|
103
|
+
}
|
|
104
|
+
const normalize = (s) => s.toLowerCase().replace(/[-_\s]+/g, "");
|
|
105
|
+
function registryError(msg) {
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: "text", text: `\u26A0\uFE0F ${msg}` }],
|
|
108
|
+
isError: true
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function formatComponent(c) {
|
|
112
|
+
const lines = [
|
|
113
|
+
`## ${c.name}`,
|
|
114
|
+
`**Type:** ${c.type}`,
|
|
115
|
+
`**Description:** ${c.description}`
|
|
116
|
+
];
|
|
117
|
+
if (c.whenToUse) lines.push(`
|
|
118
|
+
**When to use:** ${c.whenToUse}`);
|
|
119
|
+
if (c.whenNotToUse) lines.push(`**When NOT to use:** ${c.whenNotToUse}`);
|
|
120
|
+
if (c.dependencies?.length)
|
|
121
|
+
lines.push(`
|
|
122
|
+
**npm dependencies:** ${c.dependencies.join(", ")}`);
|
|
123
|
+
if (c.registryDependencies?.length)
|
|
124
|
+
lines.push(`**Requires:** ${c.registryDependencies.join(", ")}`);
|
|
125
|
+
if (c.example) lines.push(`
|
|
126
|
+
**Example:**
|
|
127
|
+
\`\`\`mdx
|
|
128
|
+
${c.example}
|
|
129
|
+
\`\`\``);
|
|
130
|
+
return lines.join("\n");
|
|
131
|
+
}
|
|
132
|
+
const STRUCTURE_STANDARD = `You generate content as MDX \u2014 Markdown with a small set of JSX components.
|
|
133
|
+
|
|
134
|
+
STRUCTURE
|
|
135
|
+
- Start sections with ## headings, subsections with ###, never go deeper
|
|
136
|
+
- Separate major sections with ---
|
|
137
|
+
- One blank line between block elements
|
|
138
|
+
|
|
139
|
+
STANDARD MARKDOWN (write these normally \u2014 they map to styled components automatically)
|
|
140
|
+
- Headings: ## and ###
|
|
141
|
+
- Bold: **text**, Italic: *text*
|
|
142
|
+
- Inline code: \`code\`
|
|
143
|
+
- Code blocks with language tag: \`\`\`lang
|
|
144
|
+
- Lists: - for unordered, 1. for ordered
|
|
145
|
+
- Tables: standard markdown | col | syntax
|
|
146
|
+
- Blockquotes: > text
|
|
147
|
+
- Horizontal rule: ---
|
|
148
|
+
|
|
149
|
+
CUSTOM COMPONENTS (use JSX for these only)
|
|
150
|
+
|
|
151
|
+
CALLOUT \u2014 notes, warnings, tips:
|
|
152
|
+
<Callout variant="info" title="Title">Content here.</Callout>
|
|
153
|
+
<Callout variant="warning" title="Title">Content here.</Callout>
|
|
154
|
+
<Callout variant="success" title="Title">Content here.</Callout>
|
|
155
|
+
<Callout variant="error" title="Title">Content here.</Callout>
|
|
156
|
+
|
|
157
|
+
STEPS \u2014 sequential procedures:
|
|
158
|
+
<Steps>
|
|
159
|
+
### Step 1: Title
|
|
160
|
+
Step content here.
|
|
161
|
+
### Step 2: Title
|
|
162
|
+
Step content here.
|
|
163
|
+
</Steps>
|
|
164
|
+
|
|
165
|
+
ACCORDION \u2014 optional depth, proofs, extra examples:
|
|
166
|
+
<Accordion>
|
|
167
|
+
<AccordionItem value="unique-id">
|
|
168
|
+
<AccordionTrigger>Label</AccordionTrigger>
|
|
169
|
+
<AccordionContent>Content here.</AccordionContent>
|
|
170
|
+
</AccordionItem>
|
|
171
|
+
</Accordion>
|
|
172
|
+
|
|
173
|
+
TABS \u2014 alternative explanations:
|
|
174
|
+
<Tabs>
|
|
175
|
+
<TabsList>
|
|
176
|
+
<TabsTrigger value="a">Label A</TabsTrigger>
|
|
177
|
+
<TabsTrigger value="b">Label B</TabsTrigger>
|
|
178
|
+
</TabsList>
|
|
179
|
+
<TabsContent value="a">Content A</TabsContent>
|
|
180
|
+
<TabsContent value="b">Content B</TabsContent>
|
|
181
|
+
</Tabs>
|
|
182
|
+
|
|
183
|
+
TOOLS \u2014 call these before writing math or solutions:
|
|
184
|
+
|
|
185
|
+
convert_latex(latex, block?)
|
|
186
|
+
\u2192 converts any LaTeX expression to primitive JSX deterministically
|
|
187
|
+
\u2192 USE THIS for any complex expression \u2014 do not guess the primitives yourself
|
|
188
|
+
\u2192 example: convert_latex("\\int_{-\\infty}^{\\infty} f(x) e^{-2\\pi i x t}\\,dx")
|
|
189
|
+
|
|
190
|
+
parse_solution(text)
|
|
191
|
+
\u2192 converts a plain-text step-by-step solution (with \u21D2 steps, parenthetical reasons)
|
|
192
|
+
into <Solution><SolutionStep>...</SolutionStep><SolutionAnswer/></Solution> MDX
|
|
193
|
+
\u2192 USE THIS whenever you write a worked example or derivation
|
|
194
|
+
|
|
195
|
+
convert_mdx_math(content)
|
|
196
|
+
\u2192 fixes an entire MDX string \u2014 replaces all $...$ and $$...$$ with primitives
|
|
197
|
+
\u2192 USE THIS if you accidentally produced dollar-sign math
|
|
198
|
+
|
|
199
|
+
search_symbols(query)
|
|
200
|
+
\u2192 find the right primitive component for any symbol or concept
|
|
201
|
+
\u2192 example: search_symbols("fourier") \u2192 ScriptF, Integral usage
|
|
202
|
+
|
|
203
|
+
MATH \u2014 use primitive components only, never $ signs or LaTeX strings.
|
|
204
|
+
Each primitive maps directly to readable English: <Frac num="a" den="b" /> means "a over b".
|
|
205
|
+
|
|
206
|
+
\u2500\u2500 BUILDING BLOCKS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
207
|
+
|
|
208
|
+
Fraction \u2192 <Frac num="1" den="2" /> (1/2)
|
|
209
|
+
Power / exponent \u2192 <Pow exp="2">x</Pow> (x\xB2)
|
|
210
|
+
Subscript \u2192 <Sub sub="n">a</Sub> (a\u2099)
|
|
211
|
+
Square root \u2192 <Sqrt>b\xB2 \u2212 4ac</Sqrt> (\u221A(b\xB2\u22124ac))
|
|
212
|
+
Nth root \u2192 <Sqrt n="3">x</Sqrt> (\u221Bx)
|
|
213
|
+
Absolute value \u2192 <Abs>x \u2212 y</Abs> (|x\u2212y|)
|
|
214
|
+
Parentheses \u2192 <Paren>a + b</Paren> ((a+b))
|
|
215
|
+
Infinity \u2192 <Inf /> (\u221E)
|
|
216
|
+
Degree symbol \u2192 <Deg>90</Deg> (90\xB0)
|
|
217
|
+
|
|
218
|
+
\u2500\u2500 CALCULUS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
219
|
+
|
|
220
|
+
Indefinite integral \u2192 <Integral>f(x) dx</Integral>
|
|
221
|
+
Definite integral \u2192 <Integral from="a" to="b">f(x) dx</Integral>
|
|
222
|
+
To infinity \u2192 <Integral from="0" to={<Inf />}>f(x) dx</Integral>
|
|
223
|
+
Summation \u2192 <Sum from="i=1" to="n"><Pow exp="2">i</Pow></Sum>
|
|
224
|
+
Product \u2192 <Prod from="i=1" to="n">i</Prod>
|
|
225
|
+
Limit \u2192 <Lim sub="x \u2192 0"><Frac num="sin x" den="x" /></Lim>
|
|
226
|
+
|
|
227
|
+
\u2500\u2500 GREEK LETTERS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
228
|
+
|
|
229
|
+
Lowercase: <Alpha /> <Beta /> <Gamma /> <Epsilon /> <Theta /> <Lambda />
|
|
230
|
+
<Mu /> <PiSym /> <Rho /> <SigmaSym /> <Tau /> <Phi /> <Omega />
|
|
231
|
+
Uppercase: <GammaU /> <DeltaU /> <ThetaU /> <LambdaU /> <SigmaU /> <PhiU /> <OmegaU />
|
|
232
|
+
|
|
233
|
+
\u2500\u2500 REAL EXAMPLES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
234
|
+
|
|
235
|
+
Quadratic formula:
|
|
236
|
+
x = <Frac num={<span>\u2212b \xB1 <Sqrt>b\xB2 \u2212 4ac</Sqrt></span>} den="2a" />
|
|
237
|
+
|
|
238
|
+
sin\xB2\u03B8 + cos\xB2\u03B8 = 1 (Pythagorean identity \u2014 write in plain text, use Greek inline):
|
|
239
|
+
sin\xB2<Theta /> + cos\xB2<Theta /> = 1
|
|
240
|
+
|
|
241
|
+
Euler's formula:
|
|
242
|
+
<Pow exp={<span>i<Theta /></span>}>e</Pow> = cos<Theta /> + i sin<Theta />
|
|
243
|
+
|
|
244
|
+
Derivative definition:
|
|
245
|
+
f\u2032(x) = <Lim sub="h \u2192 0"><Frac num={<span>f(x + h) \u2212 f(x)</span>} den="h" /></Lim>
|
|
246
|
+
|
|
247
|
+
Geometric series:
|
|
248
|
+
<Sum from="k=0" to={<Inf />}><Pow exp="k">r</Pow></Sum> = <Frac num="1" den="1 \u2212 r" /> when |r| < 1
|
|
249
|
+
|
|
250
|
+
Gaussian integral:
|
|
251
|
+
<Integral from={<span>\u2212<Inf /></span>} to={<Inf />}><Pow exp={<span>\u2212<Pow exp="2">x</Pow></span>}>e</Pow> dx</Integral> = <Sqrt><PiSym /></Sqrt>
|
|
252
|
+
|
|
253
|
+
Trig table cell (use in markdown table columns):
|
|
254
|
+
| <Theta /> | sin <Theta /> | cos <Theta /> | tan <Theta /> |
|
|
255
|
+
| 0\xB0 | 0 | 1 | 0 |
|
|
256
|
+
| 30\xB0 | <Frac num="1" den="2" /> | <Frac num={<Sqrt>3</Sqrt>} den="2" /> | <Frac num="1" den={<Sqrt>3</Sqrt>} /> |
|
|
257
|
+
| 45\xB0 | <Frac num={<Sqrt>2</Sqrt>} den="2" /> | <Frac num={<Sqrt>2</Sqrt>} den="2" /> | 1 |
|
|
258
|
+
| 60\xB0 | <Frac num={<Sqrt>3</Sqrt>} den="2" /> | <Frac num="1" den="2" /> | <Sqrt>3</Sqrt> |
|
|
259
|
+
| 90\xB0 | 1 | 0 | undefined |
|
|
260
|
+
|
|
261
|
+
Binomial coefficient (n choose k):
|
|
262
|
+
<Frac num={<span>n!</span>} den={<span>k!(n \u2212 k)!</span>} />
|
|
263
|
+
|
|
264
|
+
Integration by parts:
|
|
265
|
+
<Integral>u dv</Integral> = uv \u2212 <Integral>v du</Integral>
|
|
266
|
+
|
|
267
|
+
\u2500\u2500 COMPOSING NESTED EXPRESSIONS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
268
|
+
|
|
269
|
+
For complex numerators/denominators, wrap in {<span>...</span>}:
|
|
270
|
+
<Frac num={<span><Pow exp="2">x</Pow> + 2x + 1</span>} den={<span>x \u2212 1</span>} />
|
|
271
|
+
|
|
272
|
+
For exponents containing expressions:
|
|
273
|
+
<Pow exp={<span>\u2212<Frac num="x" den="2" /></span>}>e</Pow>
|
|
274
|
+
|
|
275
|
+
\u2500\u2500 SOLUTION BLOCKS \u2014 step-by-step worked math \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
276
|
+
|
|
277
|
+
<Solution title="Problem statement in plain English">
|
|
278
|
+
<SolutionStep reason="What this step does">expression with primitives</SolutionStep>
|
|
279
|
+
<SolutionStep reason="Key insight" highlight>key transformation</SolutionStep>
|
|
280
|
+
<SolutionAnswer>final result with primitives</SolutionAnswer>
|
|
281
|
+
<SolutionNote>Optional caveat or note.</SolutionNote>
|
|
282
|
+
</Solution>
|
|
283
|
+
|
|
284
|
+
`;
|
|
285
|
+
const OUTPUT_STANDARD = STRUCTURE_STANDARD + "\n\n" + buildMathStandard();
|
|
286
|
+
const CATEGORIES = {
|
|
287
|
+
"Layout & Structure": [
|
|
288
|
+
"accordion",
|
|
289
|
+
"callout",
|
|
290
|
+
"card",
|
|
291
|
+
"steps",
|
|
292
|
+
"tabs",
|
|
293
|
+
"reveal",
|
|
294
|
+
"spoiler"
|
|
295
|
+
],
|
|
296
|
+
"Typography & Text": [
|
|
297
|
+
"blockquote",
|
|
298
|
+
"emphasis",
|
|
299
|
+
"heading",
|
|
300
|
+
"headings",
|
|
301
|
+
"highlight",
|
|
302
|
+
"horizontal-rule",
|
|
303
|
+
"inline-code",
|
|
304
|
+
"kbd",
|
|
305
|
+
"link",
|
|
306
|
+
"list",
|
|
307
|
+
"paragraph"
|
|
308
|
+
],
|
|
309
|
+
Code: ["code-block", "code-group", "diff-block", "terminal"],
|
|
310
|
+
Math: ["math-equation", "math-primitives", "math-solution"],
|
|
311
|
+
"Data & Tables": [
|
|
312
|
+
"complexity-table",
|
|
313
|
+
"data-table",
|
|
314
|
+
"data-type-table",
|
|
315
|
+
"hardware-spec",
|
|
316
|
+
"pin-table",
|
|
317
|
+
"privacy-table",
|
|
318
|
+
"register-map",
|
|
319
|
+
"table"
|
|
320
|
+
],
|
|
321
|
+
"Diagrams & Visualization": ["ds", "ds-tree", "file-tree", "mermaid", "tree"],
|
|
322
|
+
Media: ["image", "video"],
|
|
323
|
+
"Annotation & Reference": ["annotation", "glossary"],
|
|
324
|
+
"Metadata & Utility": [
|
|
325
|
+
"alert",
|
|
326
|
+
"badge",
|
|
327
|
+
"certification-badge",
|
|
328
|
+
"changelog",
|
|
329
|
+
"definition",
|
|
330
|
+
"invariant",
|
|
331
|
+
"json-ld",
|
|
332
|
+
"security-note"
|
|
333
|
+
]
|
|
334
|
+
};
|
|
335
|
+
const ALLOWED_COMPONENTS = /* @__PURE__ */ new Set([
|
|
336
|
+
// layout & structure
|
|
337
|
+
"Callout",
|
|
338
|
+
"Steps",
|
|
339
|
+
"Step",
|
|
340
|
+
"Accordion",
|
|
341
|
+
"AccordionItem",
|
|
342
|
+
"AccordionTrigger",
|
|
343
|
+
"AccordionContent",
|
|
344
|
+
"Tabs",
|
|
345
|
+
"TabsList",
|
|
346
|
+
"TabsTrigger",
|
|
347
|
+
"TabsContent",
|
|
348
|
+
"CodeGroup",
|
|
349
|
+
"Solution",
|
|
350
|
+
"SolutionStep",
|
|
351
|
+
"SolutionAnswer",
|
|
352
|
+
"SolutionNote",
|
|
353
|
+
"Equation",
|
|
354
|
+
"EqSystem",
|
|
355
|
+
// math-primitives — structural
|
|
356
|
+
"Frac",
|
|
357
|
+
"Pow",
|
|
358
|
+
"Sub",
|
|
359
|
+
"Sqrt",
|
|
360
|
+
"Abs",
|
|
361
|
+
"Paren",
|
|
362
|
+
"Deg",
|
|
363
|
+
"Inf",
|
|
364
|
+
// math-primitives — calculus
|
|
365
|
+
"Integral",
|
|
366
|
+
"ContourIntegral",
|
|
367
|
+
"DoubleInt",
|
|
368
|
+
"TripleInt",
|
|
369
|
+
"SurfaceInt",
|
|
370
|
+
"VolumeInt",
|
|
371
|
+
"Sum",
|
|
372
|
+
"Prod",
|
|
373
|
+
"Lim",
|
|
374
|
+
"Limsup",
|
|
375
|
+
"Liminf",
|
|
376
|
+
"Deriv",
|
|
377
|
+
"PDeriv",
|
|
378
|
+
"Nabla",
|
|
379
|
+
"Laplacian",
|
|
380
|
+
"Overbrace",
|
|
381
|
+
"Underbrace",
|
|
382
|
+
// math-primitives — trig & functions
|
|
383
|
+
"Sin",
|
|
384
|
+
"Cos",
|
|
385
|
+
"Tan",
|
|
386
|
+
"Cot",
|
|
387
|
+
"Sec",
|
|
388
|
+
"Csc",
|
|
389
|
+
"ArcSin",
|
|
390
|
+
"ArcCos",
|
|
391
|
+
"ArcTan",
|
|
392
|
+
"Sinh",
|
|
393
|
+
"Cosh",
|
|
394
|
+
"Tanh",
|
|
395
|
+
"Log",
|
|
396
|
+
"Ln",
|
|
397
|
+
"Exp",
|
|
398
|
+
// math-primitives — algebra & combinatorics
|
|
399
|
+
"Factorial",
|
|
400
|
+
"Choose",
|
|
401
|
+
"Perm",
|
|
402
|
+
"Mod",
|
|
403
|
+
"GCD",
|
|
404
|
+
"LCM",
|
|
405
|
+
"Floor",
|
|
406
|
+
"Ceil",
|
|
407
|
+
// math-primitives — set theory
|
|
408
|
+
"SetOf",
|
|
409
|
+
"Cardinality",
|
|
410
|
+
"PowerSet",
|
|
411
|
+
"In",
|
|
412
|
+
"NotIn",
|
|
413
|
+
"Subset",
|
|
414
|
+
"SubsetEq",
|
|
415
|
+
"Supset",
|
|
416
|
+
"SupsetEq",
|
|
417
|
+
"ProperSubset",
|
|
418
|
+
"ProperSupset",
|
|
419
|
+
"NotSubset",
|
|
420
|
+
"NotSupset",
|
|
421
|
+
"Union",
|
|
422
|
+
"Intersect",
|
|
423
|
+
"Empty",
|
|
424
|
+
"SetMinus",
|
|
425
|
+
"BigUnion",
|
|
426
|
+
"BigIntersect",
|
|
427
|
+
"BigAnd",
|
|
428
|
+
"BigOr",
|
|
429
|
+
"NN",
|
|
430
|
+
"ZZ",
|
|
431
|
+
"QQ",
|
|
432
|
+
"RR",
|
|
433
|
+
"CC",
|
|
434
|
+
"PP",
|
|
435
|
+
"FF",
|
|
436
|
+
// math-primitives — logic & proof
|
|
437
|
+
"And",
|
|
438
|
+
"Or",
|
|
439
|
+
"Not",
|
|
440
|
+
"Xor",
|
|
441
|
+
"Nand",
|
|
442
|
+
"Nor",
|
|
443
|
+
"ForAll",
|
|
444
|
+
"Exists",
|
|
445
|
+
"NotExists",
|
|
446
|
+
"Therefore",
|
|
447
|
+
"Because",
|
|
448
|
+
"Turnstile",
|
|
449
|
+
"QED",
|
|
450
|
+
"Implies",
|
|
451
|
+
"Iff",
|
|
452
|
+
"Models",
|
|
453
|
+
"NotTurnstile",
|
|
454
|
+
"NotModels",
|
|
455
|
+
"Top",
|
|
456
|
+
"Bot",
|
|
457
|
+
"Contradiction",
|
|
458
|
+
"Tombstone",
|
|
459
|
+
// math-primitives — linear algebra
|
|
460
|
+
"Vec",
|
|
461
|
+
"Norm",
|
|
462
|
+
"Dot",
|
|
463
|
+
"Cross",
|
|
464
|
+
"Transpose",
|
|
465
|
+
"Det",
|
|
466
|
+
"Matrix",
|
|
467
|
+
"SpanOp",
|
|
468
|
+
"Rank",
|
|
469
|
+
"Dim",
|
|
470
|
+
"NullOp",
|
|
471
|
+
"Img",
|
|
472
|
+
"Trace",
|
|
473
|
+
// math-primitives — probability & statistics
|
|
474
|
+
"Prob",
|
|
475
|
+
"CondProb",
|
|
476
|
+
"Expected",
|
|
477
|
+
"Variance",
|
|
478
|
+
"StdDev",
|
|
479
|
+
"Cov",
|
|
480
|
+
"Corr",
|
|
481
|
+
"Dist",
|
|
482
|
+
// math-primitives — complex numbers
|
|
483
|
+
"Complex",
|
|
484
|
+
"Conj",
|
|
485
|
+
// math-primitives — Greek lowercase
|
|
486
|
+
"Greek",
|
|
487
|
+
"Alpha",
|
|
488
|
+
"Beta",
|
|
489
|
+
"Gamma",
|
|
490
|
+
"GDelta",
|
|
491
|
+
"Epsilon",
|
|
492
|
+
"Zeta",
|
|
493
|
+
"Eta",
|
|
494
|
+
"Theta",
|
|
495
|
+
"Iota",
|
|
496
|
+
"Kappa",
|
|
497
|
+
"Lambda",
|
|
498
|
+
"Mu",
|
|
499
|
+
"Nu",
|
|
500
|
+
"Xi",
|
|
501
|
+
"PiSym",
|
|
502
|
+
"Rho",
|
|
503
|
+
"SigmaSym",
|
|
504
|
+
"Tau",
|
|
505
|
+
"Upsilon",
|
|
506
|
+
"Phi",
|
|
507
|
+
"Chi",
|
|
508
|
+
"Psi",
|
|
509
|
+
"Omega",
|
|
510
|
+
// math-primitives — Greek uppercase
|
|
511
|
+
"GammaU",
|
|
512
|
+
"DeltaU",
|
|
513
|
+
"ThetaU",
|
|
514
|
+
"LambdaU",
|
|
515
|
+
"XiU",
|
|
516
|
+
"PiU",
|
|
517
|
+
"SigmaU",
|
|
518
|
+
"PhiU",
|
|
519
|
+
"PsiU",
|
|
520
|
+
"OmegaU",
|
|
521
|
+
// math-primitives — Greek variants
|
|
522
|
+
"Varsigma",
|
|
523
|
+
"Varepsilon",
|
|
524
|
+
"Varphi",
|
|
525
|
+
"Vartheta",
|
|
526
|
+
"Varpi",
|
|
527
|
+
"Varrho",
|
|
528
|
+
"Digamma",
|
|
529
|
+
// math-primitives — relations & operators
|
|
530
|
+
"Eq",
|
|
531
|
+
"Neq",
|
|
532
|
+
"NotEq",
|
|
533
|
+
"Approx",
|
|
534
|
+
"Equiv",
|
|
535
|
+
"Cong",
|
|
536
|
+
"NotCong",
|
|
537
|
+
"NotSim",
|
|
538
|
+
"Leq",
|
|
539
|
+
"Geq",
|
|
540
|
+
"Ll",
|
|
541
|
+
"Gg",
|
|
542
|
+
"Propto",
|
|
543
|
+
"Sim",
|
|
544
|
+
"PlusMinus",
|
|
545
|
+
"MinusPlus",
|
|
546
|
+
"Divides",
|
|
547
|
+
"NotDivides",
|
|
548
|
+
"Arrow",
|
|
549
|
+
"MapsTo",
|
|
550
|
+
"Compose",
|
|
551
|
+
"OTimes",
|
|
552
|
+
"DegNum",
|
|
553
|
+
"Prec",
|
|
554
|
+
"Succ",
|
|
555
|
+
"PrecEq",
|
|
556
|
+
"SuccEq",
|
|
557
|
+
"LessGreater",
|
|
558
|
+
"GreaterLess",
|
|
559
|
+
// math-primitives — equality & definition
|
|
560
|
+
"Approaches",
|
|
561
|
+
"DefinedAs",
|
|
562
|
+
"EqDef",
|
|
563
|
+
"Corresponds",
|
|
564
|
+
"Equiangular",
|
|
565
|
+
"AsympEq",
|
|
566
|
+
"NotAsympEq",
|
|
567
|
+
"DefEq",
|
|
568
|
+
// math-primitives — accents
|
|
569
|
+
"Bar",
|
|
570
|
+
"Hat",
|
|
571
|
+
"Tilde",
|
|
572
|
+
"DotAccent",
|
|
573
|
+
"DDot",
|
|
574
|
+
// math-primitives — prime notation
|
|
575
|
+
"Prime",
|
|
576
|
+
"DoublePrime",
|
|
577
|
+
"TriplePrime",
|
|
578
|
+
"PrimeOf",
|
|
579
|
+
// math-primitives — arrows
|
|
580
|
+
"LeftArrow",
|
|
581
|
+
"UpArrow",
|
|
582
|
+
"DownArrow",
|
|
583
|
+
"LeftRightArrow",
|
|
584
|
+
"UpDownArrow",
|
|
585
|
+
"NearArrow",
|
|
586
|
+
"SeArrow",
|
|
587
|
+
"SwArrow",
|
|
588
|
+
"NwArrow",
|
|
589
|
+
"HookRightArrow",
|
|
590
|
+
"HookLeftArrow",
|
|
591
|
+
"TwoHeadRight",
|
|
592
|
+
"TwoHeadLeft",
|
|
593
|
+
"DoubleLeftArrow",
|
|
594
|
+
"DoubleRightArrow",
|
|
595
|
+
"DoubleLeftRightArrow",
|
|
596
|
+
"DoubleUpArrow",
|
|
597
|
+
"DoubleDownArrow",
|
|
598
|
+
"DoubleUpDownArrow",
|
|
599
|
+
"LongRightArrow",
|
|
600
|
+
"LongLeftArrow",
|
|
601
|
+
"LongLeftRightArrow",
|
|
602
|
+
"LongMapsTo",
|
|
603
|
+
"RightHarpoonUp",
|
|
604
|
+
"RightHarpoonDown",
|
|
605
|
+
"LeftHarpoonUp",
|
|
606
|
+
"LeftHarpoonDown",
|
|
607
|
+
"EquilibriumArrow",
|
|
608
|
+
"DoubleHarpoon",
|
|
609
|
+
"CircleArrow",
|
|
610
|
+
"CircleArrowLeft",
|
|
611
|
+
// math-primitives — dots & ellipsis
|
|
612
|
+
"CDots",
|
|
613
|
+
"VDots",
|
|
614
|
+
"DDots",
|
|
615
|
+
"LDots",
|
|
616
|
+
"UpDots",
|
|
617
|
+
// math-primitives — brackets & intervals
|
|
618
|
+
"AngleBracket",
|
|
619
|
+
"DoubleBracket",
|
|
620
|
+
"Interval",
|
|
621
|
+
"Brace",
|
|
622
|
+
"Expr",
|
|
623
|
+
// math-primitives — piecewise
|
|
624
|
+
"Case",
|
|
625
|
+
"Cases",
|
|
626
|
+
// math-primitives — script letters
|
|
627
|
+
"ScriptA",
|
|
628
|
+
"ScriptB",
|
|
629
|
+
"ScriptC",
|
|
630
|
+
"ScriptD",
|
|
631
|
+
"ScriptE",
|
|
632
|
+
"ScriptF",
|
|
633
|
+
"ScriptG",
|
|
634
|
+
"ScriptH",
|
|
635
|
+
"ScriptI",
|
|
636
|
+
"ScriptJ",
|
|
637
|
+
"ScriptK",
|
|
638
|
+
"ScriptL",
|
|
639
|
+
"ScriptM",
|
|
640
|
+
"ScriptN",
|
|
641
|
+
"ScriptO",
|
|
642
|
+
"ScriptP",
|
|
643
|
+
"ScriptQ",
|
|
644
|
+
"ScriptR",
|
|
645
|
+
"ScriptS",
|
|
646
|
+
"ScriptT",
|
|
647
|
+
"ScriptU",
|
|
648
|
+
"ScriptV",
|
|
649
|
+
"ScriptW",
|
|
650
|
+
"ScriptX",
|
|
651
|
+
"ScriptY",
|
|
652
|
+
"ScriptZ",
|
|
653
|
+
"ScriptEll",
|
|
654
|
+
// math-primitives — physics
|
|
655
|
+
"HBar",
|
|
656
|
+
"Angstrom",
|
|
657
|
+
"Bra",
|
|
658
|
+
"Ket",
|
|
659
|
+
"BraKet",
|
|
660
|
+
"FrakR",
|
|
661
|
+
"FrakI",
|
|
662
|
+
"Weierstrass",
|
|
663
|
+
// math-primitives — extra operators
|
|
664
|
+
"DirectSum",
|
|
665
|
+
"Hadamard",
|
|
666
|
+
"CircledDiv",
|
|
667
|
+
"CircledStar",
|
|
668
|
+
"CircledPlus",
|
|
669
|
+
"CircledMinus",
|
|
670
|
+
"CircledTimes",
|
|
671
|
+
"WreathProduct",
|
|
672
|
+
"Star",
|
|
673
|
+
"Bullet",
|
|
674
|
+
"Dagger",
|
|
675
|
+
"DoubleDagger",
|
|
676
|
+
"Diamond",
|
|
677
|
+
"Bowtie",
|
|
678
|
+
"Amalg",
|
|
679
|
+
"SmallInt",
|
|
680
|
+
"Convo",
|
|
681
|
+
// math-primitives — school arithmetic
|
|
682
|
+
"Division",
|
|
683
|
+
"Times",
|
|
684
|
+
"Percent",
|
|
685
|
+
"Permille",
|
|
686
|
+
"Proportion",
|
|
687
|
+
"Ratio",
|
|
688
|
+
// math-primitives — geometry
|
|
689
|
+
"Angle",
|
|
690
|
+
"Triangle",
|
|
691
|
+
"Segment",
|
|
692
|
+
"Ray",
|
|
693
|
+
"Arc",
|
|
694
|
+
"Parallel",
|
|
695
|
+
"NotParallel",
|
|
696
|
+
"Perpendicular",
|
|
697
|
+
"RightAngle",
|
|
698
|
+
"RightAngleCorner",
|
|
699
|
+
"RightTriangle",
|
|
700
|
+
"Diameter",
|
|
701
|
+
"MeasuredAngle",
|
|
702
|
+
"SphericalAngle",
|
|
703
|
+
"GeoCong",
|
|
704
|
+
"GeoSim",
|
|
705
|
+
// geometry-2d (Fig* visual SVG components)
|
|
706
|
+
"FigScene",
|
|
707
|
+
"FigPoint",
|
|
708
|
+
"FigVector",
|
|
709
|
+
"FigLine",
|
|
710
|
+
"FigSegment",
|
|
711
|
+
"FigCircle",
|
|
712
|
+
"FigArc",
|
|
713
|
+
"FigAngle",
|
|
714
|
+
"FigPolygon",
|
|
715
|
+
"FigLabel",
|
|
716
|
+
"FigVisibleLine",
|
|
717
|
+
"FigHiddenLine",
|
|
718
|
+
"FigCenterLine",
|
|
719
|
+
"FigDimensionLine",
|
|
720
|
+
"FigExtensionLine",
|
|
721
|
+
"FigLeaderLine",
|
|
722
|
+
"FigSectionHatch",
|
|
723
|
+
"FigCuttingPlane",
|
|
724
|
+
"FigBreakLine",
|
|
725
|
+
"FigPhantomLine",
|
|
726
|
+
"FigEllipse",
|
|
727
|
+
"FigSemicircle",
|
|
728
|
+
"FigOval",
|
|
729
|
+
"FigCrescent",
|
|
730
|
+
"FigLens",
|
|
731
|
+
"FigLune",
|
|
732
|
+
"FigParabola",
|
|
733
|
+
"FigHyperbola",
|
|
734
|
+
"FigCardioid",
|
|
735
|
+
"FigLimacon",
|
|
736
|
+
"FigLemniscate",
|
|
737
|
+
"FigRightAngleMarker",
|
|
738
|
+
"FigEquilateralTriangle",
|
|
739
|
+
"FigIsoscelesTriangle",
|
|
740
|
+
"FigScaleneTriangle",
|
|
741
|
+
"FigRightTriangle",
|
|
742
|
+
"FigRect",
|
|
743
|
+
"FigSquare",
|
|
744
|
+
"FigRhombus",
|
|
745
|
+
"FigParallelogram",
|
|
746
|
+
"FigTrapezoid",
|
|
747
|
+
"FigKite",
|
|
748
|
+
"FigDart",
|
|
749
|
+
"FigCrossQuad",
|
|
750
|
+
"FigAntiparallelogram",
|
|
751
|
+
"FigCyclicQuad",
|
|
752
|
+
"FigTangentialQuad",
|
|
753
|
+
"FigRegularPolygon",
|
|
754
|
+
"FigPentagon",
|
|
755
|
+
"FigHexagon",
|
|
756
|
+
"FigHeptagon",
|
|
757
|
+
"FigOctagon",
|
|
758
|
+
"FigNonagon",
|
|
759
|
+
"FigDecagon",
|
|
760
|
+
"FigHendecagon",
|
|
761
|
+
"FigDodecagon",
|
|
762
|
+
"FigStarPolygon",
|
|
763
|
+
"FigPentagram",
|
|
764
|
+
"FigHexagram",
|
|
765
|
+
"FigOctagram",
|
|
766
|
+
"FigKochSnowflake",
|
|
767
|
+
"FigSierpinskiTriangle",
|
|
768
|
+
"FigMeasure",
|
|
769
|
+
"FigCirclePi",
|
|
770
|
+
"FigContour",
|
|
771
|
+
"FigFunnel",
|
|
772
|
+
"FigCuboid",
|
|
773
|
+
"FigFunction",
|
|
774
|
+
"FigDraggablePoint",
|
|
775
|
+
"FigParabolaExplorer",
|
|
776
|
+
"FigLineExplorer",
|
|
777
|
+
"FigTangentExplorer",
|
|
778
|
+
"FigSineExplorer",
|
|
779
|
+
"FigCircleExplorer",
|
|
780
|
+
"FigHyperbolicTangentExplorer",
|
|
781
|
+
"ElecWire",
|
|
782
|
+
"ElecNode",
|
|
783
|
+
"ElecGround",
|
|
784
|
+
"ElecResistor",
|
|
785
|
+
"ElecCapacitor",
|
|
786
|
+
"ElecInductor",
|
|
787
|
+
"ElecFuse",
|
|
788
|
+
"ElecBattery",
|
|
789
|
+
"ElecVoltageSource",
|
|
790
|
+
"ElecACSource",
|
|
791
|
+
"ElecCurrentSource",
|
|
792
|
+
"ElecSwitch",
|
|
793
|
+
"ElecDiode",
|
|
794
|
+
"ElecLED",
|
|
795
|
+
"ElecLamp",
|
|
796
|
+
"ElecVoltmeter",
|
|
797
|
+
"ElecAmmeter",
|
|
798
|
+
"ElecLabel",
|
|
799
|
+
// math-primitives — shapes
|
|
800
|
+
"Circle",
|
|
801
|
+
"FilledCircle",
|
|
802
|
+
"Square",
|
|
803
|
+
"FilledSquare",
|
|
804
|
+
"Rhombus",
|
|
805
|
+
"FilledRhombus",
|
|
806
|
+
"Pentagon",
|
|
807
|
+
"Hexagon",
|
|
808
|
+
"Ellipse",
|
|
809
|
+
// math-primitives — number theory operators
|
|
810
|
+
"Lcm",
|
|
811
|
+
"Gcd",
|
|
812
|
+
"Ord",
|
|
813
|
+
"Sgn",
|
|
814
|
+
"Arg",
|
|
815
|
+
"Re",
|
|
816
|
+
"Im",
|
|
817
|
+
"Res",
|
|
818
|
+
"Sup",
|
|
819
|
+
"Inf2",
|
|
820
|
+
"Max",
|
|
821
|
+
"Min",
|
|
822
|
+
"Ker",
|
|
823
|
+
"Hom",
|
|
824
|
+
"End",
|
|
825
|
+
"Aut",
|
|
826
|
+
"Der",
|
|
827
|
+
// math-primitives — chemistry
|
|
828
|
+
"ReactionArrow",
|
|
829
|
+
"GasMarker",
|
|
830
|
+
"PrecipitateMarker",
|
|
831
|
+
"ChemEquilibrium",
|
|
832
|
+
"SingleBond",
|
|
833
|
+
"DoubleBond",
|
|
834
|
+
"TripleBond",
|
|
835
|
+
// math-primitives — misc symbols
|
|
836
|
+
"Aleph",
|
|
837
|
+
"Beth",
|
|
838
|
+
"Gimel",
|
|
839
|
+
"Daleth",
|
|
840
|
+
"PartialDiff",
|
|
841
|
+
"FlatSymbol",
|
|
842
|
+
"SharpSymbol",
|
|
843
|
+
"NaturalSymbol",
|
|
844
|
+
"Differential",
|
|
845
|
+
// math-primitives — shorthands
|
|
846
|
+
"Squared",
|
|
847
|
+
"Cubed",
|
|
848
|
+
"Inverse",
|
|
849
|
+
"SubZero",
|
|
850
|
+
"SubOne",
|
|
851
|
+
"SubTwo",
|
|
852
|
+
// math-primitives — CS asymptotic notation
|
|
853
|
+
"BigO",
|
|
854
|
+
"BigTheta",
|
|
855
|
+
"BigOmega",
|
|
856
|
+
"LittleO",
|
|
857
|
+
"LittleOmega",
|
|
858
|
+
// math-primitives — basic arithmetic
|
|
859
|
+
"Plus",
|
|
860
|
+
"Minus",
|
|
861
|
+
"Mul",
|
|
862
|
+
"Div",
|
|
863
|
+
"Modulus"
|
|
864
|
+
]);
|
|
865
|
+
const BANNED_HTML = /^<(div|span|p\b|b\b|i\b|strong|em|br|hr|section|article|main|aside|header|footer|nav)\s*[\s/>]/i;
|
|
866
|
+
const INVENTED_JSX = /^<([A-Z][a-zA-Z0-9]*)/;
|
|
867
|
+
const BANNED_MATH_COMPONENTS = /^<(InlineMath|BlockMath|Math\b|ME\b|BME\b|BM\b)\s/;
|
|
868
|
+
const INLINE_MATH_DOLLAR = /(?<!\d)\$(?!\$|\d)[^$\n]{2,}\$/;
|
|
869
|
+
const BLOCK_MATH_DOLLAR = /\$\$/;
|
|
870
|
+
function validateMdxContent(content) {
|
|
871
|
+
const issues = [];
|
|
872
|
+
const lines = content.split("\n");
|
|
873
|
+
let insideCodeFence = false;
|
|
874
|
+
let codeFenceMarker = "";
|
|
875
|
+
for (let i = 0; i < lines.length; i++) {
|
|
876
|
+
const line = lines[i];
|
|
877
|
+
const lineNo = i + 1;
|
|
878
|
+
const trimmed = line.trim();
|
|
879
|
+
const fenceMatch = trimmed.match(/^(`{3,}|~{3,})/);
|
|
880
|
+
if (fenceMatch) {
|
|
881
|
+
if (!insideCodeFence) {
|
|
882
|
+
insideCodeFence = true;
|
|
883
|
+
codeFenceMarker = fenceMatch[1];
|
|
884
|
+
} else if (trimmed.startsWith(codeFenceMarker)) {
|
|
885
|
+
insideCodeFence = false;
|
|
886
|
+
codeFenceMarker = "";
|
|
887
|
+
}
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (insideCodeFence) continue;
|
|
891
|
+
if (BLOCK_MATH_DOLLAR.test(line) || INLINE_MATH_DOLLAR.test(line)) {
|
|
892
|
+
issues.push({
|
|
893
|
+
line: lineNo,
|
|
894
|
+
rule: "no-dollar-math",
|
|
895
|
+
text: `Dollar-sign math detected \u2014 use math primitive components instead (Frac, Pow, Integral, etc.)`
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
if (BANNED_MATH_COMPONENTS.test(trimmed)) {
|
|
899
|
+
issues.push({
|
|
900
|
+
line: lineNo,
|
|
901
|
+
rule: "no-katex-components",
|
|
902
|
+
text: `KaTeX component not allowed \u2014 use math primitives: <Frac>, <Pow>, <Integral>, <Sum>, Greek letters, etc.`
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
if (/^# [^#]/.test(line)) {
|
|
906
|
+
issues.push({
|
|
907
|
+
line: lineNo,
|
|
908
|
+
rule: "no-h1",
|
|
909
|
+
text: `H1 heading not allowed \u2014 start sections with ## minimum`
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
if (/^#{4,} /.test(line)) {
|
|
913
|
+
issues.push({
|
|
914
|
+
line: lineNo,
|
|
915
|
+
rule: "max-heading-depth",
|
|
916
|
+
text: `Heading depth exceeds ### \u2014 maximum allowed is ###`
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
if (BANNED_HTML.test(trimmed)) {
|
|
920
|
+
issues.push({
|
|
921
|
+
line: lineNo,
|
|
922
|
+
rule: "no-raw-html",
|
|
923
|
+
text: `Raw HTML tag not allowed \u2014 use Markdown or docsui components`
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
const jsxMatch = trimmed.match(INVENTED_JSX);
|
|
927
|
+
if (jsxMatch) {
|
|
928
|
+
const componentName = jsxMatch[1];
|
|
929
|
+
if (!ALLOWED_COMPONENTS.has(componentName)) {
|
|
930
|
+
issues.push({
|
|
931
|
+
line: lineNo,
|
|
932
|
+
rule: "no-invented-components",
|
|
933
|
+
text: `Unknown component <${componentName}> \u2014 only use components listed in the output standard`
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return issues;
|
|
939
|
+
}
|
|
940
|
+
async function startMcpServer() {
|
|
941
|
+
const server = new McpServer({
|
|
942
|
+
name: "docsui",
|
|
943
|
+
version: getCliVersion()
|
|
944
|
+
});
|
|
945
|
+
server.registerResource(
|
|
946
|
+
"component-registry",
|
|
947
|
+
"registry://components",
|
|
948
|
+
{
|
|
949
|
+
title: "docsui Component Registry",
|
|
950
|
+
description: "Full list of all docsui components with metadata \u2014 name, description, whenToUse, whenNotToUse, example",
|
|
951
|
+
mimeType: "application/json"
|
|
952
|
+
},
|
|
953
|
+
async () => {
|
|
954
|
+
let registry;
|
|
955
|
+
try {
|
|
956
|
+
registry = await fetchRegistry();
|
|
957
|
+
} catch (err) {
|
|
958
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
959
|
+
return {
|
|
960
|
+
contents: [
|
|
961
|
+
{
|
|
962
|
+
uri: "registry://components",
|
|
963
|
+
mimeType: "application/json",
|
|
964
|
+
text: JSON.stringify({ error: msg })
|
|
965
|
+
}
|
|
966
|
+
]
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
return {
|
|
970
|
+
contents: [
|
|
971
|
+
{
|
|
972
|
+
uri: "registry://components",
|
|
973
|
+
mimeType: "application/json",
|
|
974
|
+
text: JSON.stringify(registry, null, 2)
|
|
975
|
+
}
|
|
976
|
+
]
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
);
|
|
980
|
+
server.registerResource(
|
|
981
|
+
"component",
|
|
982
|
+
new ResourceTemplate("registry://component/{name}", {
|
|
983
|
+
list: async () => {
|
|
984
|
+
try {
|
|
985
|
+
const registry = await fetchRegistry();
|
|
986
|
+
return {
|
|
987
|
+
resources: registry.components.map((c) => ({
|
|
988
|
+
uri: `registry://component/${c.name}`,
|
|
989
|
+
name: c.name,
|
|
990
|
+
description: c.description,
|
|
991
|
+
mimeType: "text/plain"
|
|
992
|
+
}))
|
|
993
|
+
};
|
|
994
|
+
} catch {
|
|
995
|
+
return { resources: [] };
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}),
|
|
999
|
+
{
|
|
1000
|
+
title: "docsui Component",
|
|
1001
|
+
description: "Full schema for a single docsui component",
|
|
1002
|
+
mimeType: "text/plain"
|
|
1003
|
+
},
|
|
1004
|
+
async (uri, { name }) => {
|
|
1005
|
+
let registry;
|
|
1006
|
+
try {
|
|
1007
|
+
registry = await fetchRegistry();
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1010
|
+
return {
|
|
1011
|
+
contents: [
|
|
1012
|
+
{
|
|
1013
|
+
uri: uri.href,
|
|
1014
|
+
mimeType: "text/plain",
|
|
1015
|
+
text: `Error: ${msg}`
|
|
1016
|
+
}
|
|
1017
|
+
]
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
const componentName = Array.isArray(name) ? name[0] : name;
|
|
1021
|
+
const component = registry.components.find((c) => c.name === componentName) ?? registry.components.find(
|
|
1022
|
+
(c) => normalize(c.name) === normalize(componentName ?? "")
|
|
1023
|
+
);
|
|
1024
|
+
return {
|
|
1025
|
+
contents: [
|
|
1026
|
+
{
|
|
1027
|
+
uri: uri.href,
|
|
1028
|
+
mimeType: "text/plain",
|
|
1029
|
+
text: component ? formatComponent(component) : `Component "${componentName}" not found.`
|
|
1030
|
+
}
|
|
1031
|
+
]
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
server.registerResource(
|
|
1036
|
+
"output-standard",
|
|
1037
|
+
"registry://standard",
|
|
1038
|
+
{
|
|
1039
|
+
title: "MDX AI Output Standard",
|
|
1040
|
+
description: "The standard MDX output format for LLMs \u2014 inject this into your system prompt",
|
|
1041
|
+
mimeType: "text/plain"
|
|
1042
|
+
},
|
|
1043
|
+
async () => ({
|
|
1044
|
+
contents: [
|
|
1045
|
+
{
|
|
1046
|
+
uri: "registry://standard",
|
|
1047
|
+
mimeType: "text/plain",
|
|
1048
|
+
text: OUTPUT_STANDARD
|
|
1049
|
+
}
|
|
1050
|
+
]
|
|
1051
|
+
})
|
|
1052
|
+
);
|
|
1053
|
+
server.registerPrompt(
|
|
1054
|
+
"generate_mdx",
|
|
1055
|
+
{
|
|
1056
|
+
title: "Generate MDX Content",
|
|
1057
|
+
description: "Generate valid docsui MDX content for a topic \u2014 injects the output standard and relevant components automatically",
|
|
1058
|
+
argsSchema: {
|
|
1059
|
+
topic: z.string().min(3, "Topic must be at least 3 characters").max(300, "Topic must be 300 characters or fewer").describe("The subject to generate content about"),
|
|
1060
|
+
level: z.enum(["beginner", "intermediate", "advanced"]).optional().describe("Audience level (default: intermediate)"),
|
|
1061
|
+
type: z.enum(["lesson", "reference", "exercise", "explanation"]).optional().describe("Content type (default: lesson)")
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
async ({ topic, level = "intermediate", type = "lesson" }) => {
|
|
1065
|
+
let componentHint = "";
|
|
1066
|
+
try {
|
|
1067
|
+
const registry = await fetchRegistry();
|
|
1068
|
+
const contentComponents = registry.components.filter(
|
|
1069
|
+
(c) => [
|
|
1070
|
+
"callout",
|
|
1071
|
+
"steps",
|
|
1072
|
+
"accordion",
|
|
1073
|
+
"tabs",
|
|
1074
|
+
"math-primitives",
|
|
1075
|
+
"math-solution",
|
|
1076
|
+
"code-block",
|
|
1077
|
+
"code-group"
|
|
1078
|
+
].includes(c.name)
|
|
1079
|
+
).map((c) => `- **${c.name}**: ${c.description}`).join("\n");
|
|
1080
|
+
if (contentComponents) {
|
|
1081
|
+
componentHint = `
|
|
1082
|
+
|
|
1083
|
+
KEY COMPONENTS FOR THIS CONTENT:
|
|
1084
|
+
${contentComponents}`;
|
|
1085
|
+
}
|
|
1086
|
+
} catch {
|
|
1087
|
+
}
|
|
1088
|
+
const typeInstructions = {
|
|
1089
|
+
lesson: "Structure as a complete lesson: introduction, core concept, examples, key points to remember.",
|
|
1090
|
+
reference: "Structure as a reference: concise definitions, syntax, parameters, and quick examples.",
|
|
1091
|
+
exercise: "Structure as a guided exercise: goal statement, steps to follow, expected outcome.",
|
|
1092
|
+
explanation: "Structure as a focused explanation: one concept, multiple perspectives, concrete analogies."
|
|
1093
|
+
};
|
|
1094
|
+
return {
|
|
1095
|
+
messages: [
|
|
1096
|
+
{
|
|
1097
|
+
role: "user",
|
|
1098
|
+
content: {
|
|
1099
|
+
type: "text",
|
|
1100
|
+
text: `${OUTPUT_STANDARD}${componentHint}
|
|
1101
|
+
|
|
1102
|
+
---
|
|
1103
|
+
|
|
1104
|
+
Generate a ${level} ${type} about: **${topic}**
|
|
1105
|
+
|
|
1106
|
+
${typeInstructions[type]}
|
|
1107
|
+
|
|
1108
|
+
BEFORE WRITING MATH:
|
|
1109
|
+
- Call convert_latex(latex) for any complex expression \u2014 never guess the primitives
|
|
1110
|
+
- Call parse_solution(text) for any worked example or step-by-step derivation
|
|
1111
|
+
- Call search_symbols(query) if unsure which component to use for a symbol
|
|
1112
|
+
|
|
1113
|
+
Output only the MDX content \u2014 no explanation, no code fences wrapping the whole thing.
|
|
1114
|
+
|
|
1115
|
+
MANDATORY SELF-CHECK BEFORE RESPONDING:
|
|
1116
|
+
1. Call validate_mdx on your output.
|
|
1117
|
+
2. If validate_mdx reports any dollar-math issues \u2192 call convert_mdx_math to fix them.
|
|
1118
|
+
3. If validate_mdx reports unknown components \u2192 replace them with allowed primitives.
|
|
1119
|
+
4. Return the corrected MDX \u2014 never return content that fails validate_mdx.`
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
]
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
);
|
|
1126
|
+
server.registerPrompt(
|
|
1127
|
+
"review_mdx",
|
|
1128
|
+
{
|
|
1129
|
+
title: "Review MDX Content",
|
|
1130
|
+
description: "Review MDX content against the AI Output Standard and suggest fixes",
|
|
1131
|
+
argsSchema: {
|
|
1132
|
+
content: z.string().min(1, "Content cannot be empty").max(5e4, "Content must be 50,000 characters or fewer").describe("The MDX content to review")
|
|
1133
|
+
}
|
|
1134
|
+
},
|
|
1135
|
+
async ({ content }) => {
|
|
1136
|
+
const issues = validateMdxContent(content);
|
|
1137
|
+
const issueBlock = issues.length > 0 ? `
|
|
1138
|
+
|
|
1139
|
+
PRE-DETECTED ISSUES (${issues.length}):
|
|
1140
|
+
${issues.map((i) => `- Line ${i.line}: [${i.rule}] ${i.text}`).join("\n")}` : "\n\nNo structural issues auto-detected \u2014 check for semantic and style problems.";
|
|
1141
|
+
return {
|
|
1142
|
+
messages: [
|
|
1143
|
+
{
|
|
1144
|
+
role: "user",
|
|
1145
|
+
content: {
|
|
1146
|
+
type: "text",
|
|
1147
|
+
text: `You are reviewing MDX content against the docsui AI Output Standard.
|
|
1148
|
+
|
|
1149
|
+
OUTPUT STANDARD:
|
|
1150
|
+
${OUTPUT_STANDARD}
|
|
1151
|
+
${issueBlock}
|
|
1152
|
+
|
|
1153
|
+
---
|
|
1154
|
+
|
|
1155
|
+
CONTENT TO REVIEW:
|
|
1156
|
+
${content}
|
|
1157
|
+
|
|
1158
|
+
---
|
|
1159
|
+
|
|
1160
|
+
Review the content above. For each problem found:
|
|
1161
|
+
1. Quote the problematic line
|
|
1162
|
+
2. State which rule it breaks
|
|
1163
|
+
3. Provide the corrected version
|
|
1164
|
+
|
|
1165
|
+
If the content contains $...$ or $$...$$ math, call convert_mdx_math(content) to fix it automatically.
|
|
1166
|
+
If the content has step-by-step solutions in plain text, call parse_solution(text) to convert them.
|
|
1167
|
+
If any LaTeX expression needs converting, call convert_latex(latex) first.
|
|
1168
|
+
|
|
1169
|
+
Then provide the fully corrected MDX at the end.`
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
]
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
);
|
|
1176
|
+
server.registerTool(
|
|
1177
|
+
"list_components",
|
|
1178
|
+
{
|
|
1179
|
+
description: "List all available docsui components with descriptions. For a grouped view by category, use list_categories instead."
|
|
1180
|
+
},
|
|
1181
|
+
async () => {
|
|
1182
|
+
let registry;
|
|
1183
|
+
try {
|
|
1184
|
+
registry = await fetchRegistry();
|
|
1185
|
+
} catch (err) {
|
|
1186
|
+
return registryError(err instanceof Error ? err.message : String(err));
|
|
1187
|
+
}
|
|
1188
|
+
const lines = registry.components.map(
|
|
1189
|
+
(c) => `- **${c.name}** (${c.type}): ${c.description}`
|
|
1190
|
+
);
|
|
1191
|
+
return {
|
|
1192
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1196
|
+
server.registerTool(
|
|
1197
|
+
"get_component",
|
|
1198
|
+
{
|
|
1199
|
+
description: "Get the full schema for a component \u2014 props, when to use, when not to use, and an MDX usage example",
|
|
1200
|
+
inputSchema: {
|
|
1201
|
+
name: z.string().min(1, "Component name cannot be empty").describe("Component name, e.g. accordion, complexity-table, dsbst")
|
|
1202
|
+
}
|
|
1203
|
+
},
|
|
1204
|
+
async ({ name }) => {
|
|
1205
|
+
let registry;
|
|
1206
|
+
try {
|
|
1207
|
+
registry = await fetchRegistry();
|
|
1208
|
+
} catch (err) {
|
|
1209
|
+
return registryError(err instanceof Error ? err.message : String(err));
|
|
1210
|
+
}
|
|
1211
|
+
const normalizedInput = normalize(name);
|
|
1212
|
+
const component = registry.components.find((c) => c.name === name.toLowerCase()) ?? registry.components.find((c) => normalize(c.name) === normalizedInput);
|
|
1213
|
+
if (!component) {
|
|
1214
|
+
const scored = registry.components.map((c) => ({
|
|
1215
|
+
name: c.name,
|
|
1216
|
+
dist: levenshtein(normalizedInput, normalize(c.name))
|
|
1217
|
+
})).sort((a, b) => a.dist - b.dist).slice(0, 3);
|
|
1218
|
+
const suggestions = scored[0].dist <= 4 ? `
|
|
1219
|
+
|
|
1220
|
+
Did you mean: ${scored.map((s) => `**${s.name}**`).join(", ")}?` : `
|
|
1221
|
+
|
|
1222
|
+
Use list_categories to browse all available components.`;
|
|
1223
|
+
return {
|
|
1224
|
+
content: [
|
|
1225
|
+
{
|
|
1226
|
+
type: "text",
|
|
1227
|
+
text: `Component "${name}" not found.${suggestions}`
|
|
1228
|
+
}
|
|
1229
|
+
]
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
return {
|
|
1233
|
+
content: [{ type: "text", text: formatComponent(component) }]
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
);
|
|
1237
|
+
server.registerTool(
|
|
1238
|
+
"search_components",
|
|
1239
|
+
{
|
|
1240
|
+
description: "Search components by keyword or use case \u2014 e.g. 'math', 'tree', 'security', 'table'",
|
|
1241
|
+
inputSchema: {
|
|
1242
|
+
query: z.string().min(2, "Query must be at least 2 characters").max(200, "Query must be 200 characters or fewer").describe("Search keyword or use case description")
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
async ({ query }) => {
|
|
1246
|
+
let registry;
|
|
1247
|
+
try {
|
|
1248
|
+
registry = await fetchRegistry();
|
|
1249
|
+
} catch (err) {
|
|
1250
|
+
return registryError(err instanceof Error ? err.message : String(err));
|
|
1251
|
+
}
|
|
1252
|
+
const words = query.toLowerCase().split(/\s+/).filter((w) => w.length > 1);
|
|
1253
|
+
const effectiveWords = words.length > 0 ? words : [query.toLowerCase().trim()];
|
|
1254
|
+
const matches = registry.components.filter((c) => {
|
|
1255
|
+
const haystack = [
|
|
1256
|
+
c.name,
|
|
1257
|
+
c.description,
|
|
1258
|
+
c.whenToUse ?? "",
|
|
1259
|
+
c.whenNotToUse ?? "",
|
|
1260
|
+
c.type
|
|
1261
|
+
].join(" ").toLowerCase();
|
|
1262
|
+
return effectiveWords.every((w) => haystack.includes(w));
|
|
1263
|
+
});
|
|
1264
|
+
if (matches.length === 0) {
|
|
1265
|
+
return {
|
|
1266
|
+
content: [
|
|
1267
|
+
{
|
|
1268
|
+
type: "text",
|
|
1269
|
+
text: `No components found matching "${query}".`
|
|
1270
|
+
}
|
|
1271
|
+
]
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
const lines = [
|
|
1275
|
+
`Found ${matches.length} component${matches.length !== 1 ? "s" : ""} matching "${query}":
|
|
1276
|
+
`,
|
|
1277
|
+
...matches.map((c) => `- **${c.name}**: ${c.description}`),
|
|
1278
|
+
`
|
|
1279
|
+
Use get_component(<name>) for full schema and examples.`
|
|
1280
|
+
];
|
|
1281
|
+
return {
|
|
1282
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
server.registerTool(
|
|
1287
|
+
"get_output_standard",
|
|
1288
|
+
{
|
|
1289
|
+
description: "Get the MDX AI Output Standard \u2014 the system prompt block to inject so an LLM generates valid MDX that renders correctly with docsui components"
|
|
1290
|
+
},
|
|
1291
|
+
async () => ({
|
|
1292
|
+
content: [{ type: "text", text: OUTPUT_STANDARD }]
|
|
1293
|
+
})
|
|
1294
|
+
);
|
|
1295
|
+
server.registerTool(
|
|
1296
|
+
"list_categories",
|
|
1297
|
+
{
|
|
1298
|
+
description: "List docsui components grouped by category \u2014 use this to discover components before calling get_component"
|
|
1299
|
+
},
|
|
1300
|
+
async () => {
|
|
1301
|
+
let registry;
|
|
1302
|
+
try {
|
|
1303
|
+
registry = await fetchRegistry();
|
|
1304
|
+
} catch (err) {
|
|
1305
|
+
return registryError(err instanceof Error ? err.message : String(err));
|
|
1306
|
+
}
|
|
1307
|
+
const byName = new Map(registry.components.map((c) => [c.name, c]));
|
|
1308
|
+
const lines = [];
|
|
1309
|
+
for (const [category, names] of Object.entries(CATEGORIES)) {
|
|
1310
|
+
lines.push(`
|
|
1311
|
+
### ${category}`);
|
|
1312
|
+
for (const name of names) {
|
|
1313
|
+
const comp = byName.get(name);
|
|
1314
|
+
if (comp) lines.push(`- **${comp.name}**: ${comp.description}`);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
lines.push(
|
|
1318
|
+
"\nUse get_component(<name>) for full schema, or search_components(<query>) to find by use case."
|
|
1319
|
+
);
|
|
1320
|
+
return {
|
|
1321
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
);
|
|
1325
|
+
server.registerTool(
|
|
1326
|
+
"validate_mdx",
|
|
1327
|
+
{
|
|
1328
|
+
description: "Validate MDX content against the AI Output Standard \u2014 checks for dollar-sign math, raw HTML, H1 headings, heading depth, and unknown components",
|
|
1329
|
+
inputSchema: {
|
|
1330
|
+
content: z.string().min(1, "Content cannot be empty").max(5e4, "Content must be 50,000 characters or fewer").describe("The MDX content to validate")
|
|
1331
|
+
}
|
|
1332
|
+
},
|
|
1333
|
+
async ({ content }) => {
|
|
1334
|
+
const issues = validateMdxContent(content);
|
|
1335
|
+
if (issues.length === 0) {
|
|
1336
|
+
return {
|
|
1337
|
+
content: [
|
|
1338
|
+
{
|
|
1339
|
+
type: "text",
|
|
1340
|
+
text: "\u2705 No issues found \u2014 content follows the MDX AI Output Standard."
|
|
1341
|
+
}
|
|
1342
|
+
]
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
const lines = [
|
|
1346
|
+
`\u274C Found ${issues.length} issue${issues.length !== 1 ? "s" : ""}:
|
|
1347
|
+
`,
|
|
1348
|
+
...issues.map((i) => `- **Line ${i.line}** [${i.rule}]: ${i.text}`),
|
|
1349
|
+
`
|
|
1350
|
+
Use the review_mdx prompt to get a corrected version.`
|
|
1351
|
+
];
|
|
1352
|
+
return {
|
|
1353
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
);
|
|
1357
|
+
server.registerResource(
|
|
1358
|
+
"symbol-map",
|
|
1359
|
+
"registry://symbol-map",
|
|
1360
|
+
{
|
|
1361
|
+
title: "docsui Symbol Map",
|
|
1362
|
+
description: "Complete mapping of mathematical symbols / LaTeX names to docsui primitive components \u2014 use this to find the right component for any symbol",
|
|
1363
|
+
mimeType: "application/json"
|
|
1364
|
+
},
|
|
1365
|
+
async () => ({
|
|
1366
|
+
contents: [
|
|
1367
|
+
{
|
|
1368
|
+
uri: "registry://symbol-map",
|
|
1369
|
+
mimeType: "application/json",
|
|
1370
|
+
text: JSON.stringify(SYMBOL_MAP, null, 2)
|
|
1371
|
+
}
|
|
1372
|
+
]
|
|
1373
|
+
})
|
|
1374
|
+
);
|
|
1375
|
+
server.registerTool(
|
|
1376
|
+
"search_symbols",
|
|
1377
|
+
{
|
|
1378
|
+
description: "Search the symbol map by concept name, LaTeX command, Unicode symbol, or category \u2014 returns the docsui primitive component and MDX usage example",
|
|
1379
|
+
inputSchema: {
|
|
1380
|
+
query: z.string().min(1, "Query cannot be empty").max(200).describe(
|
|
1381
|
+
"What to search for \u2014 e.g. 'integral', '\\\\frac', '\u2211', 'greek', 'geometry'"
|
|
1382
|
+
)
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
async ({ query }) => {
|
|
1386
|
+
const results = searchSymbols(query);
|
|
1387
|
+
if (results.length === 0) {
|
|
1388
|
+
return {
|
|
1389
|
+
content: [
|
|
1390
|
+
{
|
|
1391
|
+
type: "text",
|
|
1392
|
+
text: `No symbols found for "${query}".
|
|
1393
|
+
|
|
1394
|
+
Try: 'integral', 'fraction', 'greek', 'geometry', 'arrow', 'logic', 'set', 'probability', 'physics', 'chemistry'`
|
|
1395
|
+
}
|
|
1396
|
+
]
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
const lines = [
|
|
1400
|
+
`Found ${results.length} symbol${results.length !== 1 ? "s" : ""} for "${query}":
|
|
1401
|
+
`,
|
|
1402
|
+
...results.map(
|
|
1403
|
+
(e) => `**${e.name}** [${e.category}]
|
|
1404
|
+
Symbols: ${e.symbols.join(", ")}
|
|
1405
|
+
LaTeX: ${e.latex.slice(0, 2).join(" or ")}
|
|
1406
|
+
\u2192 \`${e.usage}\`
|
|
1407
|
+
`
|
|
1408
|
+
)
|
|
1409
|
+
];
|
|
1410
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1411
|
+
}
|
|
1412
|
+
);
|
|
1413
|
+
server.registerTool(
|
|
1414
|
+
"list_symbol_categories",
|
|
1415
|
+
{
|
|
1416
|
+
description: "List all symbol categories in the symbol map \u2014 use with search_symbols to explore by category"
|
|
1417
|
+
},
|
|
1418
|
+
async () => {
|
|
1419
|
+
const lines = CATEGORIES_LIST.map((cat) => {
|
|
1420
|
+
const count = symbolsByCategory(cat).length;
|
|
1421
|
+
return `- **${cat}** (${count} symbols)`;
|
|
1422
|
+
});
|
|
1423
|
+
return {
|
|
1424
|
+
content: [
|
|
1425
|
+
{
|
|
1426
|
+
type: "text",
|
|
1427
|
+
text: `Symbol map categories:
|
|
1428
|
+
|
|
1429
|
+
${lines.join("\n")}
|
|
1430
|
+
|
|
1431
|
+
Use search_symbols with a category name to list all symbols in it.`
|
|
1432
|
+
}
|
|
1433
|
+
]
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
);
|
|
1437
|
+
server.registerTool(
|
|
1438
|
+
"get_symbol_cheatsheet",
|
|
1439
|
+
{
|
|
1440
|
+
description: "Get the complete symbol map as a formatted cheat sheet \u2014 every symbol with its component and usage, grouped by category",
|
|
1441
|
+
inputSchema: {
|
|
1442
|
+
category: z.string().optional().describe("Filter to a single category (e.g. 'calculus', 'greek')")
|
|
1443
|
+
}
|
|
1444
|
+
},
|
|
1445
|
+
async ({ category }) => {
|
|
1446
|
+
const entries = category ? symbolsByCategory(category) : SYMBOL_MAP;
|
|
1447
|
+
if (entries.length === 0) {
|
|
1448
|
+
return {
|
|
1449
|
+
content: [
|
|
1450
|
+
{ type: "text", text: `No entries for category "${category}".` }
|
|
1451
|
+
]
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
const lines = [];
|
|
1455
|
+
let lastCat = "";
|
|
1456
|
+
for (const e of entries) {
|
|
1457
|
+
if (e.category !== lastCat) {
|
|
1458
|
+
lines.push(`
|
|
1459
|
+
### ${e.category.toUpperCase().replace(/-/g, " ")}`);
|
|
1460
|
+
lastCat = e.category;
|
|
1461
|
+
}
|
|
1462
|
+
lines.push(` ${formatEntry(e)}`);
|
|
1463
|
+
}
|
|
1464
|
+
return {
|
|
1465
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
);
|
|
1469
|
+
server.registerTool(
|
|
1470
|
+
"convert_latex",
|
|
1471
|
+
{
|
|
1472
|
+
description: "Convert a LaTeX math expression to docsui primitive components deterministically. Use this whenever you need to write a complex math expression \u2014 pass the LaTeX and get back ready-to-paste MDX primitives.",
|
|
1473
|
+
inputSchema: {
|
|
1474
|
+
latex: z.string().min(1).max(2e3).describe(
|
|
1475
|
+
"LaTeX expression WITHOUT $ delimiters \u2014 e.g. \\\\int_{-\\\\infty}^{\\\\infty} f(x)\\\\,dx"
|
|
1476
|
+
),
|
|
1477
|
+
block: z.boolean().optional().describe(
|
|
1478
|
+
"Wrap in <Equation> for display/block mode (default: false = inline)"
|
|
1479
|
+
)
|
|
1480
|
+
}
|
|
1481
|
+
},
|
|
1482
|
+
async ({ latex, block = false }) => {
|
|
1483
|
+
const result = latexToPrimitives(latex, block);
|
|
1484
|
+
return {
|
|
1485
|
+
content: [
|
|
1486
|
+
{
|
|
1487
|
+
type: "text",
|
|
1488
|
+
text: `**Input LaTeX:** \`${latex}\`
|
|
1489
|
+
|
|
1490
|
+
**MDX Primitives:**
|
|
1491
|
+
\`\`\`mdx
|
|
1492
|
+
${result}
|
|
1493
|
+
\`\`\``
|
|
1494
|
+
}
|
|
1495
|
+
]
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
);
|
|
1499
|
+
server.registerTool(
|
|
1500
|
+
"convert_mdx_math",
|
|
1501
|
+
{
|
|
1502
|
+
description: "Convert all $...$ and $$...$$ LaTeX math in an MDX string to docsui primitives. Use this to fix AI-generated content that used dollar-sign math instead of components.",
|
|
1503
|
+
inputSchema: {
|
|
1504
|
+
content: z.string().min(1).max(5e4).describe("MDX content that may contain $...$ or $$...$$ math")
|
|
1505
|
+
}
|
|
1506
|
+
},
|
|
1507
|
+
async ({ content }) => {
|
|
1508
|
+
if (!hasLatex(content)) {
|
|
1509
|
+
return {
|
|
1510
|
+
content: [
|
|
1511
|
+
{
|
|
1512
|
+
type: "text",
|
|
1513
|
+
text: "\u2705 No LaTeX found \u2014 content already uses primitives."
|
|
1514
|
+
}
|
|
1515
|
+
]
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
const converted = convertMarkdownMath(content);
|
|
1519
|
+
return {
|
|
1520
|
+
content: [
|
|
1521
|
+
{
|
|
1522
|
+
type: "text",
|
|
1523
|
+
text: `**Converted MDX:**
|
|
1524
|
+
\`\`\`mdx
|
|
1525
|
+
${converted}
|
|
1526
|
+
\`\`\``
|
|
1527
|
+
}
|
|
1528
|
+
]
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
);
|
|
1532
|
+
server.registerTool(
|
|
1533
|
+
"parse_solution",
|
|
1534
|
+
{
|
|
1535
|
+
description: "Convert a plain-text step-by-step math solution (with \u21D2 steps, parenthetical reasons, fractions like 3/2) into <Solution><SolutionStep>...</SolutionStep></Solution> MDX components automatically.",
|
|
1536
|
+
inputSchema: {
|
|
1537
|
+
text: z.string().min(10).max(1e4).describe(
|
|
1538
|
+
"The plain-text solution \u2014 include the problem statement and all \u21D2 steps with parenthetical reasons"
|
|
1539
|
+
)
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
async ({ text }) => {
|
|
1543
|
+
const mdx = parseSolution(text);
|
|
1544
|
+
return {
|
|
1545
|
+
content: [
|
|
1546
|
+
{
|
|
1547
|
+
type: "text",
|
|
1548
|
+
text: `**Converted to MDX:**
|
|
1549
|
+
\`\`\`mdx
|
|
1550
|
+
${mdx}
|
|
1551
|
+
\`\`\``
|
|
1552
|
+
}
|
|
1553
|
+
]
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
);
|
|
1557
|
+
const transport = new StdioServerTransport();
|
|
1558
|
+
await server.connect(transport);
|
|
1559
|
+
}
|
|
1560
|
+
export {
|
|
1561
|
+
startMcpServer
|
|
1562
|
+
};
|