zigsm 1.4.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Andrey Ryapov, "Zig and WebAssembly" contributors
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,180 @@
1
+ # ZSM
2
+
3
+ ZSM - fork of **Zig Docs MCP**
4
+
5
+ > [!NOTE]
6
+ > This fork keeps the original behavior and documentation flow nearly unchanged. The main addition vs upstream is addition of embeddings for better semantic retrieval in search tools.
7
+
8
+ Model Context Protocol (MCP) server that provides up-to-date documentation for the Zig programming language standard library and builtin functions.
9
+
10
+ It uses the same approach as Zig's official autodoc (ziglang.org) by reading STD lib source files directly through a WASM module. However instead of returning HTML, it outputs Markdown which significantly reduces token usage.
11
+
12
+ By default, the server uses your locally installed Zig compiler to serve documentation, ensuring you always get docs that match your actual Zig version. It can also fetch documentation from ziglang.org if needed.
13
+
14
+ > [!TIP]
15
+ > Add `use zigdocs` to your prompt if you want to explicitly instruct the LLM to use Zig docs tools. Otherwise, LLM will automatically decide when to utilize MCP tools based on the context of your questions.
16
+
17
+ <p align="center" width="100%">
18
+ <img src="https://raw.githubusercontent.com/zig-wasm/.github/refs/heads/main/static/readme_mcp_1.gif" width="49%" />
19
+ <img src="https://raw.githubusercontent.com/zig-wasm/.github/refs/heads/main/static/readme_mcp_2.gif" width="49%" />
20
+ </p>
21
+
22
+ ## Tools
23
+
24
+ - **`list_builtin_functions`** - Lists all available Zig builtin functions. Builtin functions are provided by the compiler and are prefixed with '@'. The comptime keyword on a parameter means that the parameter must be known at compile time. Use this to discover what functions are available, then use 'get_builtin_function' to get detailed documentation.
25
+ - **`get_builtin_function`** - Search for Zig builtin functions by name and get their documentation, signatures, and usage information. Returns all matching functions ranked by relevance.
26
+ - **`search_std_lib`** - Search the Zig standard library for declarations by name. Returns a list of matching items with their fully qualified names. Use this to discover available types, functions, and constants in the standard library.
27
+ - **`get_std_lib_item`** - Get detailed documentation for a specific standard library item by its fully qualified name (e.g., "std.ArrayList.init"). Returns comprehensive documentation including function signatures, parameters, errors, examples, and source code. Set `get_source_file: true` to retrieve the entire source file where the item is implemented.
28
+
29
+ When `VOYAGE_API_KEY` is set, search tools automatically use hybrid ranking (lexical + embeddings) and fall back to lexical-only results if embeddings are unavailable.
30
+
31
+ ## Commands
32
+
33
+ The CLI provides flexible options for version control and update management:
34
+
35
+ ```bash
36
+ # Start MCP server
37
+ zsm --doc-source local
38
+
39
+ # Use specific Zig version from ziglang.org instead of local Zig
40
+ zsm --doc-source remote --version 0.14.1
41
+
42
+ # Enable automatic daily updates
43
+ zsm --doc-source remote --update-policy daily
44
+
45
+ # Update documentation without starting MCP server (only for remote)
46
+ zsm update --version 0.15.1
47
+
48
+ # Start local web server to view documentation
49
+ zsm view --version 0.15.1
50
+ ```
51
+
52
+ **Version options `--version`**:
53
+ - `master` (default) - Latest development version from Zig's master branch
54
+ - `0.14.1`, `0.14.0`, etc. - Specific Zig release versions
55
+
56
+ **Update policies `--update-policy`**:
57
+ - `manual` (default) - No automatic updates, manual control only
58
+ - `daily` - Check for documentation updates once per day
59
+ - `startup` - Update documentation every time the server starts
60
+
61
+ **Documentation sources `--doc-source`**:
62
+ - `local` (default) - Use your locally installed Zig compiler's documentation server (`zig std`)
63
+ - `remote` - Download documentation from ziglang.org
64
+
65
+ ## Documentation Sources
66
+
67
+ ### Local Mode (Default)
68
+
69
+ The server automatically uses your local Zig installation to serve documentation via `zig std`. This ensures:
70
+ - Documentation always matches your installed Zig version
71
+ - No network requests needed for standard library docs
72
+ - Faster response times
73
+
74
+ ### Remote Mode
75
+
76
+ When using `--doc-source remote`, documentation is fetched from ziglang.org and cached in platform-specific directories:
77
+ - Linux: `~/.cache/zigsm/`
78
+ - macOS: `~/Library/Caches/zigsm/`
79
+ - Windows: `%LOCALAPPDATA%\zigsm\`
80
+
81
+ ## Embeddings (Voyage)
82
+
83
+ Optional environment variables:
84
+ - `VOYAGE_API_KEY` - Enables embeddings for doc search when set.
85
+ - `VOYAGE_MODEL` - Embedding model override (default: `voyage-3-lite`).
86
+
87
+ Embeddings are cached per Zig version, doc source, and model in the same cache directory used for docs.
88
+
89
+ ## Installation
90
+
91
+ The installation examples below use the local documentation source by default. In local mode, docs are served by your installed Zig via `zig std`, requiring no network and matching your actual Zig version. This is the recommended setup for most users. For downloading docs from ziglang.org instead, see Remote Documentation (Optional) below.
92
+
93
+ ### Claude Code
94
+ Using npx (Node.js)
95
+ ```bash
96
+ claude mcp add zsm -- npx -y zigsm@latest
97
+ ```
98
+
99
+ Using bunx (Bun)
100
+ ```bash
101
+ claude mcp add zsm -- bunx zigsm@latest
102
+ ```
103
+
104
+ ### Roo Code
105
+
106
+ 1. Click the **MCP** button in Roo Code
107
+ 2. Select **"Edit Global MCP"** or **"Edit Project MCP"**
108
+ 3. Add the configuration from the JSON template below
109
+
110
+ ### Augment Code
111
+
112
+ Navigate to **Settings → MCP Servers → Add Server** and use the JSON template below.
113
+
114
+ ### Claude Desktop
115
+
116
+ Add the JSON configuration below to your MCP settings file.
117
+
118
+ ### JSON Configuration Template
119
+
120
+ **Node.js:**
121
+ ```json
122
+ {
123
+ "mcpServers": {
124
+ "zsm": {
125
+ "command": "npx",
126
+ "args": ["-y", "zigsm@latest"]
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ **Bun:**
133
+ ```json
134
+ {
135
+ "mcpServers": {
136
+ "zsm": {
137
+ "command": "bunx",
138
+ "args": ["zigsm@latest"]
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ ### Remote Documentation (Optional)
145
+
146
+ If you prefer downloading documentation from ziglang.org instead of using your local Zig, enable remote mode explicitly and choose a version:
147
+
148
+ Using npx (Node.js)
149
+ ```bash
150
+ claude mcp add zsm -- npx -y zigsm@latest --doc-source remote --version master
151
+ ```
152
+
153
+ Using bunx (Bun)
154
+ ```bash
155
+ claude mcp add zsm -- bunx zigsm@latest --doc-source remote --version 0.14.1
156
+ ```
157
+
158
+ **Node.js (remote):**
159
+ ```json
160
+ {
161
+ "mcpServers": {
162
+ "zsm": {
163
+ "command": "npx",
164
+ "args": ["-y", "zigsm@latest", "--doc-source", "remote", "--version", "master"]
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ **Bun (remote):**
171
+ ```json
172
+ {
173
+ "mcpServers": {
174
+ "zsm": {
175
+ "command": "bunx",
176
+ "args": ["zigsm@latest", "--doc-source", "remote", "--version", "0.14.1"]
177
+ }
178
+ }
179
+ }
180
+ ```
package/dist/docs.js ADDED
@@ -0,0 +1,176 @@
1
+ import * as fs from "node:fs";
2
+ import * as http from "node:http";
3
+ import * as path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import envPaths from "env-paths";
6
+ import extractBuiltinFunctions from "./extract-builtin-functions.js";
7
+ import { getLocalStdSources, getZigVersion } from "./local-std-server.js";
8
+ export async function ensureDocs(zigVersion, updatePolicy = "manual", isMcpMode = true, docSource = "local") {
9
+ const paths = envPaths("zigsm", { suffix: "" });
10
+ const metadataPath = path.join(paths.cache, zigVersion, "metadata.json");
11
+ let shouldUpdate = false;
12
+ if (updatePolicy === "startup") {
13
+ shouldUpdate = true;
14
+ }
15
+ else if (updatePolicy === "daily") {
16
+ if (!fs.existsSync(metadataPath)) {
17
+ shouldUpdate = true;
18
+ }
19
+ else {
20
+ try {
21
+ const content = fs.readFileSync(metadataPath, "utf8");
22
+ const metadata = JSON.parse(content);
23
+ const dayInMs = 24 * 60 * 60 * 1000;
24
+ shouldUpdate = Date.now() - metadata.lastUpdate >= dayInMs;
25
+ }
26
+ catch {
27
+ shouldUpdate = true;
28
+ }
29
+ }
30
+ }
31
+ if (shouldUpdate) {
32
+ try {
33
+ if (!isMcpMode)
34
+ console.log(`Updating documentation for Zig version: ${zigVersion}`);
35
+ const builtinFunctions = await extractBuiltinFunctions(zigVersion, isMcpMode, true);
36
+ await downloadSourcesTar(zigVersion, isMcpMode, true, docSource);
37
+ const dir = path.dirname(metadataPath);
38
+ if (!fs.existsSync(dir)) {
39
+ fs.mkdirSync(dir, { recursive: true });
40
+ }
41
+ fs.writeFileSync(metadataPath, JSON.stringify({
42
+ lastUpdate: Date.now(),
43
+ version: zigVersion,
44
+ }, null, 2));
45
+ if (!isMcpMode)
46
+ console.log(`Successfully updated documentation for Zig version: ${zigVersion}`);
47
+ return builtinFunctions;
48
+ }
49
+ catch (error) {
50
+ if (error instanceof Error && error.message.includes("404")) {
51
+ console.error(`Error: Zig version '${zigVersion}' not found on ziglang.org. Please check the version number.`);
52
+ }
53
+ else {
54
+ console.error(`Error updating documentation for version ${zigVersion}:`, error);
55
+ }
56
+ throw error;
57
+ }
58
+ }
59
+ return await extractBuiltinFunctions(zigVersion, isMcpMode);
60
+ }
61
+ export async function downloadSourcesTar(zigVersion, isMcpMode = false, forceUpdate = false, docSource = "local") {
62
+ if (docSource === "local") {
63
+ try {
64
+ if (!isMcpMode)
65
+ console.log("Using local Zig std server for documentation");
66
+ const localVersion = getZigVersion();
67
+ if (!isMcpMode)
68
+ console.log(`Local Zig version: ${localVersion}`);
69
+ return await getLocalStdSources();
70
+ }
71
+ catch (error) {
72
+ if (!isMcpMode) {
73
+ console.log(`Failed to use local Zig std server: ${error}`);
74
+ console.log("Falling back to remote documentation");
75
+ }
76
+ }
77
+ }
78
+ const paths = envPaths("zigsm", { suffix: "" });
79
+ const versionCacheDir = path.join(paths.cache, zigVersion);
80
+ const sourcesPath = path.join(versionCacheDir, "sources.tar");
81
+ if (fs.existsSync(sourcesPath) && !forceUpdate) {
82
+ if (!isMcpMode)
83
+ console.log(`Using cached sources.tar from ${sourcesPath}`);
84
+ return new Uint8Array(fs.readFileSync(sourcesPath));
85
+ }
86
+ const url = `https://ziglang.org/documentation/${zigVersion}/std/sources.tar`;
87
+ if (!isMcpMode)
88
+ console.log(`Downloading sources.tar from: ${url}`);
89
+ const response = await fetch(url);
90
+ if (!response.ok) {
91
+ throw new Error(`Failed to download sources.tar from ${url}: ${response.status} ${response.statusText}`);
92
+ }
93
+ const buffer = await response.arrayBuffer();
94
+ const uint8Array = new Uint8Array(buffer);
95
+ if (!fs.existsSync(versionCacheDir)) {
96
+ fs.mkdirSync(versionCacheDir, { recursive: true });
97
+ }
98
+ fs.writeFileSync(sourcesPath, uint8Array);
99
+ if (!isMcpMode)
100
+ console.log(`Downloaded sources.tar to ${sourcesPath}`);
101
+ return uint8Array;
102
+ }
103
+ async function downloadSourcesTarPath(zigVersion) {
104
+ const paths = envPaths("zigsm", { suffix: "" });
105
+ const versionCacheDir = path.join(paths.cache, zigVersion);
106
+ const sourcesPath = path.join(versionCacheDir, "sources.tar");
107
+ if (fs.existsSync(sourcesPath)) {
108
+ console.log(`Using cached sources.tar from ${sourcesPath}`);
109
+ return sourcesPath;
110
+ }
111
+ await downloadSourcesTar(zigVersion, false);
112
+ return sourcesPath;
113
+ }
114
+ export async function startViewServer(zigVersion) {
115
+ try {
116
+ const sourcesPath = await downloadSourcesTarPath(zigVersion);
117
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
118
+ const wasmPath = path.join(currentDir, "main.wasm");
119
+ const indexPath = path.join(currentDir, "index.html");
120
+ const stdJsPath = path.join(currentDir, "std.js");
121
+ const port = 8080;
122
+ const server = http.createServer((req, res) => {
123
+ let filePath;
124
+ const url = req.url || "/";
125
+ if (url === "/" || url === "/index.html") {
126
+ filePath = indexPath;
127
+ }
128
+ else if (url === "/std.js") {
129
+ filePath = stdJsPath;
130
+ }
131
+ else if (url === "/main.wasm") {
132
+ filePath = wasmPath;
133
+ }
134
+ else if (url === "/sources.tar") {
135
+ filePath = sourcesPath;
136
+ }
137
+ else {
138
+ res.writeHead(404);
139
+ res.end("File not found");
140
+ return;
141
+ }
142
+ if (!fs.existsSync(filePath)) {
143
+ res.writeHead(404);
144
+ res.end("File not found");
145
+ return;
146
+ }
147
+ const ext = path.extname(filePath).toLowerCase();
148
+ const contentTypes = {
149
+ ".html": "text/html",
150
+ ".js": "text/javascript",
151
+ ".css": "text/css",
152
+ ".wasm": "application/wasm",
153
+ ".tar": "application/x-tar",
154
+ };
155
+ const contentType = contentTypes[ext] || "application/octet-stream";
156
+ res.writeHead(200, { "Content-Type": contentType });
157
+ fs.createReadStream(filePath).pipe(res);
158
+ });
159
+ server.listen(port, () => {
160
+ const url = `http://localhost:${port}`;
161
+ console.log(`Server started at ${url}`);
162
+ console.log(`Serving Zig ${zigVersion} documentation`);
163
+ console.log("Press Ctrl+C to stop the server");
164
+ });
165
+ process.on("SIGINT", () => {
166
+ console.log("\nShutting down server...");
167
+ server.close(() => {
168
+ process.exit(0);
169
+ });
170
+ });
171
+ }
172
+ catch (error) {
173
+ console.error("Error starting view server:", error);
174
+ throw error;
175
+ }
176
+ }
@@ -0,0 +1,155 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import * as cheerio from "cheerio";
4
+ import envPaths from "env-paths";
5
+ async function extractBuiltinFunctions(zigVersion, isMcpMode = true, forceUpdate = false) {
6
+ const paths = envPaths("zigsm", { suffix: "" });
7
+ const versionCacheDir = path.join(paths.cache, zigVersion);
8
+ const outputPath = path.join(versionCacheDir, "builtin-functions.json");
9
+ if (fs.existsSync(outputPath) && !forceUpdate) {
10
+ if (!isMcpMode)
11
+ console.log(`Using cached builtin functions from ${outputPath}`);
12
+ try {
13
+ const content = fs.readFileSync(outputPath, "utf8");
14
+ return JSON.parse(content);
15
+ }
16
+ catch (error) {
17
+ console.error(`Error reading cached file, re-extracting:`, error);
18
+ }
19
+ }
20
+ const url = `https://ziglang.org/documentation/${zigVersion}/`;
21
+ if (!isMcpMode)
22
+ console.log(`Downloading HTML from: ${url}`);
23
+ const response = await fetch(url);
24
+ if (!response.ok) {
25
+ throw new Error(`Failed to download HTML from ${url}: ${response.status} ${response.statusText}`);
26
+ }
27
+ const html = await response.text();
28
+ const $ = cheerio.load(html);
29
+ const builtins = [];
30
+ // Find "Builtin Functions" section
31
+ const builtinFunctionsSection = $('h2[id="Builtin-Functions"]');
32
+ if (builtinFunctionsSection.length === 0) {
33
+ throw new Error("Could not find Builtin Functions section in HTML");
34
+ }
35
+ let current = builtinFunctionsSection.next();
36
+ while (current.length && current.prop("tagName")?.toLowerCase() !== "h2") {
37
+ if (current.is("h3[id]")) {
38
+ const h3 = current;
39
+ const func = h3.find("a").first().text();
40
+ if (func.startsWith("@")) {
41
+ const signature = h3.next("pre").text().trim();
42
+ const descriptionParts = [];
43
+ const seeAlsoLinks = [];
44
+ let descCurrent = h3.next("pre").next();
45
+ while (descCurrent.length &&
46
+ !["h2", "h3"].includes(descCurrent.prop("tagName")?.toLowerCase() || "")) {
47
+ if (descCurrent.is("p")) {
48
+ const pHtml = descCurrent.html() || "";
49
+ const $p = cheerio.load(pHtml, {
50
+ xml: { xmlMode: false, decodeEntities: false },
51
+ });
52
+ $p("a").each((_, a) => {
53
+ const link = $p(a);
54
+ const href = link.attr("href") || "";
55
+ const text = link.text();
56
+ let markdownLink;
57
+ if (href.startsWith("#")) {
58
+ markdownLink = `[${text}](https://ziglang.org/documentation/${zigVersion}/${href})`;
59
+ }
60
+ else {
61
+ markdownLink = `[${text}](${href})`;
62
+ }
63
+ link.replaceWith(markdownLink);
64
+ });
65
+ $p("code").each((_, code) => {
66
+ const el = $p(code);
67
+ el.replaceWith(`\`${el.text()}\``);
68
+ });
69
+ const pText = $p.root().text();
70
+ descriptionParts.push(pText.replace(/\s+/g, " ").trim());
71
+ }
72
+ else if (descCurrent.is("ul")) {
73
+ // Convert each <li> to Markdown, handling <a> and <code> tags
74
+ descCurrent.children("li").each((_, li) => {
75
+ const liHtml = $(li).html() || "";
76
+ const $li = cheerio.load(liHtml, {
77
+ xml: { xmlMode: false, decodeEntities: false },
78
+ });
79
+ $li("a").each((_, a) => {
80
+ const link = $li(a);
81
+ const href = link.attr("href") || "";
82
+ const text = link.text();
83
+ let markdownLink;
84
+ if (href.startsWith("#")) {
85
+ markdownLink = `[${text}](https://ziglang.org/documentation/${zigVersion}/${href})`;
86
+ }
87
+ else {
88
+ markdownLink = `[${text}](${href})`;
89
+ }
90
+ link.replaceWith(markdownLink);
91
+ });
92
+ $li("code").each((_, code) => {
93
+ const el = $li(code);
94
+ el.replaceWith(`\`${el.text()}\``);
95
+ });
96
+ const liText = $li.root().text().replace(/\s+/g, " ").trim();
97
+ if (liText.length > 0) {
98
+ descriptionParts.push(`* ${liText}`);
99
+ }
100
+ });
101
+ }
102
+ else if (descCurrent.is("figure")) {
103
+ // Extract <figcaption> and <pre> content
104
+ const figcaption = descCurrent.find("figcaption").first().text().trim();
105
+ const pre = descCurrent.find("pre").first();
106
+ const code = pre.text();
107
+ let lang = "";
108
+ let label = "";
109
+ if (figcaption) {
110
+ label = `**${figcaption}**\n`;
111
+ if (figcaption.endsWith(".zig")) {
112
+ lang = "zig";
113
+ }
114
+ else if (figcaption.toLowerCase().includes("shell")) {
115
+ lang = "sh";
116
+ }
117
+ }
118
+ if (code) {
119
+ // Format as Markdown code block
120
+ const codeBlock = `${label}\n\`\`\`${lang}\n${code.trim()}\n\`\`\``;
121
+ descriptionParts.push(codeBlock.trim());
122
+ }
123
+ }
124
+ descCurrent = descCurrent.next();
125
+ }
126
+ // Join doc blocks with a single newline and collapse multiple newlines
127
+ let docs = descriptionParts.join("\n");
128
+ docs = docs.replace(/\n{2,}/g, "\n").replace(/\n+$/g, "");
129
+ if (docs.toLowerCase().endsWith("see also:")) {
130
+ docs = docs.slice(0, -"see also:".length).trim();
131
+ }
132
+ if (seeAlsoLinks.length > 0) {
133
+ if (docs.length > 0) {
134
+ docs += "\n";
135
+ }
136
+ docs += `See also:\n* ${seeAlsoLinks.join("\n* ")}`;
137
+ }
138
+ builtins.push({
139
+ func,
140
+ signature,
141
+ docs,
142
+ });
143
+ }
144
+ }
145
+ current = current.next();
146
+ }
147
+ if (!fs.existsSync(versionCacheDir)) {
148
+ fs.mkdirSync(versionCacheDir, { recursive: true });
149
+ }
150
+ fs.writeFileSync(outputPath, JSON.stringify(builtins, null, 2));
151
+ if (!isMcpMode)
152
+ console.log(`Extracted ${builtins.length} builtin functions to ${outputPath}`);
153
+ return builtins;
154
+ }
155
+ export default extractBuiltinFunctions;
@@ -0,0 +1,47 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Zig Documentation</title>
7
+ <style>
8
+ body {
9
+ font-family: monospace;
10
+ font-size: 14px;
11
+ margin: 20px;
12
+ }
13
+ #search {
14
+ width: 100%;
15
+ padding: 10px;
16
+ margin-bottom: 20px;
17
+ border: 1px solid #555;
18
+ font-family: monospace;
19
+ }
20
+ #content {
21
+ white-space: pre-wrap;
22
+ line-height: 1.4;
23
+ }
24
+ .hidden {
25
+ display: none;
26
+ }
27
+ #errors {
28
+ background: #400;
29
+ padding: 10px;
30
+ margin: 10px 0;
31
+ border: 1px solid #600;
32
+ }
33
+ </style>
34
+ </head>
35
+ <body>
36
+ <input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="Search documentation...">
37
+ <div id="content"></div>
38
+ <div id="errors" class="hidden">
39
+ <h1>Errors</h1>
40
+ <pre id="errorsText"></pre>
41
+ </div>
42
+ <script type="module">
43
+ import { startDocsViewer } from './std.js';
44
+ startDocsViewer();
45
+ </script>
46
+ </body>
47
+ </html>