@appsoftwareltd/etherpk-mcp 0.9.5 → 0.10.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/dist/main.js +249 -63
- package/dist/main.js.map +1 -1
- package/dist/map-picture-page.js +121 -85
- package/package.json +5 -5
package/dist/main.js
CHANGED
|
@@ -717,7 +717,7 @@ function looksLikeAgentSetupCode(value) {
|
|
|
717
717
|
}
|
|
718
718
|
var package_default = {
|
|
719
719
|
name: "@appsoftwareltd/etherpk-mcp",
|
|
720
|
-
version: "0.
|
|
720
|
+
version: "0.10.0",
|
|
721
721
|
license: "Elastic-2.0",
|
|
722
722
|
description: "EtherPK Headless Client: an MCP server over a synced knowledge graph, run beside the agent on the user's own machine.",
|
|
723
723
|
type: "module",
|
|
@@ -4162,9 +4162,49 @@ function parseBlocks(markdown) {
|
|
|
4162
4162
|
return roots;
|
|
4163
4163
|
}
|
|
4164
4164
|
//#endregion
|
|
4165
|
+
//#region ../client/src/lib/document/extension-fence.ts
|
|
4166
|
+
/**
|
|
4167
|
+
* [[Extension Fence]]s (ADR 0138): a fence whose info word is `<extension id>.<name>` holds an
|
|
4168
|
+
* extension's data, such as a drawing's elements. Every reader of a document decides from the info
|
|
4169
|
+
* word alone, with no extension running, so the editor, the index, Semantic Search, the publisher
|
|
4170
|
+
* and the Headless Client always agree: the body is never read as writing, never searched, and never
|
|
4171
|
+
* published as code.
|
|
4172
|
+
*
|
|
4173
|
+
* Framework-free, so the index worker, the publisher and the Headless Client read it as the editor
|
|
4174
|
+
* does.
|
|
4175
|
+
*/
|
|
4176
|
+
/** An extension's id or a name under it: lowercase letters, digits and hyphens, from a letter. */
|
|
4177
|
+
var NAME = "[a-z][a-z0-9-]*";
|
|
4178
|
+
var EXTENSION_FENCE_INFO = new RegExp(`^${NAME}\\.${NAME}$`);
|
|
4179
|
+
/** Whether an info word names an Extension Fence: an extension's id, a dot and a name. */
|
|
4180
|
+
function isExtensionFenceInfo(info) {
|
|
4181
|
+
return EXTENSION_FENCE_INFO.test(info);
|
|
4182
|
+
}
|
|
4183
|
+
/** The extension an Extension Fence's info word names: the part before the dot. */
|
|
4184
|
+
function extensionOfFence(info) {
|
|
4185
|
+
return info.slice(0, info.indexOf("."));
|
|
4186
|
+
}
|
|
4187
|
+
/** Each complete, outermost Extension Fence in `lines`, by its opener's and closer's 0-based lines. */
|
|
4188
|
+
function extensionFences(lines) {
|
|
4189
|
+
const out = [];
|
|
4190
|
+
const { sorted, parent } = fencedBlockTree(fencedBlocks(lines));
|
|
4191
|
+
sorted.forEach((block, i) => {
|
|
4192
|
+
if (parent[i] !== -1) return;
|
|
4193
|
+
const info = fenceLineInfo(lines[block.start])?.info ?? "";
|
|
4194
|
+
if (isExtensionFenceInfo(info)) out.push({
|
|
4195
|
+
start: block.start,
|
|
4196
|
+
end: block.end,
|
|
4197
|
+
info
|
|
4198
|
+
});
|
|
4199
|
+
});
|
|
4200
|
+
return out;
|
|
4201
|
+
}
|
|
4202
|
+
//#endregion
|
|
4165
4203
|
//#region ../client/src/lib/document/map-text.ts
|
|
4166
4204
|
var NUMBER = String.raw`[+-]?(?:\d+(?:\.\d*)?|\.\d+)`;
|
|
4167
4205
|
var POINT = new RegExp(String.raw`^\s*(${NUMBER})\s*,\s*(${NUMBER})\s*$`);
|
|
4206
|
+
/** How many of the last `@`s on a line may end its name (`readMapLine`). */
|
|
4207
|
+
var MAX_NAME_ENDS = 16;
|
|
4168
4208
|
function readPoint(text) {
|
|
4169
4209
|
const match = POINT.exec(text);
|
|
4170
4210
|
if (!match) return null;
|
|
@@ -4214,7 +4254,8 @@ function readLocation(text, name) {
|
|
|
4214
4254
|
function readMapLine(text) {
|
|
4215
4255
|
const trimmed = text.trim();
|
|
4216
4256
|
if (trimmed === "") return null;
|
|
4217
|
-
|
|
4257
|
+
let tries = 0;
|
|
4258
|
+
for (let at = trimmed.lastIndexOf("@"); at >= 0 && tries < MAX_NAME_ENDS; at = at === 0 ? -1 : trimmed.lastIndexOf("@", at - 1), tries++) {
|
|
4218
4259
|
const line = readLocation(trimmed.slice(at + 1), trimmed.slice(0, at).trim());
|
|
4219
4260
|
if (line) return line;
|
|
4220
4261
|
}
|
|
@@ -4528,30 +4569,57 @@ function deriveDoc(text) {
|
|
|
4528
4569
|
const candidateTasks = blocks.filter((b) => b.kind === "task");
|
|
4529
4570
|
const fenced = candidateTasks.length === 0 ? () => false : fencedLines(text);
|
|
4530
4571
|
const taskBlocks = candidateTasks.filter((b) => !fenced(b.startLine));
|
|
4572
|
+
const tasks = taskBlocks.map((b) => {
|
|
4573
|
+
const tags = parseTaskTags(b.label);
|
|
4574
|
+
return {
|
|
4575
|
+
blockLocalId: b.localId,
|
|
4576
|
+
done: b.done ?? false,
|
|
4577
|
+
text: b.label,
|
|
4578
|
+
line: b.startLine,
|
|
4579
|
+
priority: tags.priority,
|
|
4580
|
+
waiting: tags.waiting,
|
|
4581
|
+
doing: tags.doing,
|
|
4582
|
+
cancelled: tags.cancelled,
|
|
4583
|
+
due: tags.due,
|
|
4584
|
+
completion: tags.completion,
|
|
4585
|
+
scheduled: tags.scheduled
|
|
4586
|
+
};
|
|
4587
|
+
});
|
|
4588
|
+
const maps = deriveMaps(text, blocks, links, lineToBlock);
|
|
4531
4589
|
return {
|
|
4532
4590
|
blocks,
|
|
4533
4591
|
links,
|
|
4534
|
-
tasks
|
|
4535
|
-
const tags = parseTaskTags(b.label);
|
|
4536
|
-
return {
|
|
4537
|
-
blockLocalId: b.localId,
|
|
4538
|
-
done: b.done ?? false,
|
|
4539
|
-
text: b.label,
|
|
4540
|
-
line: b.startLine,
|
|
4541
|
-
priority: tags.priority,
|
|
4542
|
-
waiting: tags.waiting,
|
|
4543
|
-
doing: tags.doing,
|
|
4544
|
-
cancelled: tags.cancelled,
|
|
4545
|
-
due: tags.due,
|
|
4546
|
-
completion: tags.completion,
|
|
4547
|
-
scheduled: tags.scheduled
|
|
4548
|
-
};
|
|
4549
|
-
}),
|
|
4592
|
+
tasks,
|
|
4550
4593
|
taskConcepts: deriveTaskConcepts(blocks, links, taskBlocks),
|
|
4551
|
-
...
|
|
4594
|
+
...maps,
|
|
4595
|
+
searchText: withoutExtensionFences(text, blocks, lineToBlock, maps.searchText)
|
|
4552
4596
|
};
|
|
4553
4597
|
}
|
|
4554
4598
|
/**
|
|
4599
|
+
* What search and Semantic Search read for each block holding an [[Extension Fence]] (ADR 0138):
|
|
4600
|
+
* the block's text with the fence's lines left out, so an extension's data (a drawing's elements)
|
|
4601
|
+
* never fills either index. Added to what `deriveMaps` gave, block by block.
|
|
4602
|
+
*/
|
|
4603
|
+
function withoutExtensionFences(text, blocks, lineToBlock, searchText) {
|
|
4604
|
+
if (!/```[a-z][a-z0-9-]*\.[a-z]/.test(text)) return searchText;
|
|
4605
|
+
const fences = extensionFences(text.split("\n"));
|
|
4606
|
+
if (fences.length === 0) return searchText;
|
|
4607
|
+
const hidden = /* @__PURE__ */ new Map();
|
|
4608
|
+
for (const fence of fences) {
|
|
4609
|
+
const id = lineToBlock.get(fence.start);
|
|
4610
|
+
if (id === void 0) continue;
|
|
4611
|
+
const lines = hidden.get(id) ?? /* @__PURE__ */ new Set();
|
|
4612
|
+
for (let line = fence.start; line <= fence.end; line++) lines.add(line);
|
|
4613
|
+
hidden.set(id, lines);
|
|
4614
|
+
}
|
|
4615
|
+
for (const [id, lines] of hidden) {
|
|
4616
|
+
const block = blocks[id];
|
|
4617
|
+
const read = (searchText.get(id) ?? block.text).split("\n").map((line, n) => lines.has(block.startLine + n) ? "" : line);
|
|
4618
|
+
searchText.set(id, read.join("\n"));
|
|
4619
|
+
}
|
|
4620
|
+
return searchText;
|
|
4621
|
+
}
|
|
4622
|
+
/**
|
|
4555
4623
|
* The [[Place]]s and [[Route]]s a document's [[Map Block]]s hold (ADR 0118), the [[Block
|
|
4556
4624
|
* Concept]]s of the blocks they sit in, and what text search reads for those blocks instead of
|
|
4557
4625
|
* their coordinates.
|
|
@@ -6056,7 +6124,8 @@ function backlinksFor(db, concept) {
|
|
|
6056
6124
|
sourceConcept: h.sourceConcept,
|
|
6057
6125
|
sourceKind: h.sourceKind,
|
|
6058
6126
|
refs: [],
|
|
6059
|
-
bodyNames: []
|
|
6127
|
+
bodyNames: [],
|
|
6128
|
+
sourceIsTarget: h.page_id === pageId
|
|
6060
6129
|
};
|
|
6061
6130
|
groups.set(h.sourceConcept, group);
|
|
6062
6131
|
}
|
|
@@ -12650,6 +12719,13 @@ function wikilinkRule(md, resolve) {
|
|
|
12650
12719
|
* grammars for code), so they are pre-rendered by the orchestrator and handed in as lookups;
|
|
12651
12720
|
* the renderer itself is synchronous and pure over its inputs.
|
|
12652
12721
|
*/
|
|
12722
|
+
/** A fence an extension published, as HTML the publisher writes: the extension never writes markup into a site. */
|
|
12723
|
+
function fenceOutputHtml(output) {
|
|
12724
|
+
if (output.kind === "picture") return `<figure class="diagram diagram-extension"><img src="${escapeHtml$3(output.src)}" alt="${escapeHtml$3(output.alt)}" width="${output.width}" height="${output.height}" loading="lazy"></figure>\n`;
|
|
12725
|
+
const size = (image) => image.width && image.height ? ` width="${Math.round(image.width)}" height="${Math.round(image.height)}"` : "";
|
|
12726
|
+
const image = output.image ? `<img src="${escapeHtml$3(output.image.src)}" alt="${escapeHtml$3(output.image.alt)}"${size(output.image)} loading="lazy">` : "";
|
|
12727
|
+
return `<figure class="diagram diagram-link"><a href="${escapeHtml$3(output.href)}">${image}</a><figcaption><a href="${escapeHtml$3(output.href)}">${escapeHtml$3(output.text)}</a></figcaption></figure>\n`;
|
|
12728
|
+
}
|
|
12653
12729
|
/** The names on a Map Block, one line for the search index and the excerpt; never its coordinates. */
|
|
12654
12730
|
function mapNames(source) {
|
|
12655
12731
|
return readMapBody(source.split("\n")).items.map((item) => item.name).filter((name) => name !== "").join(", ");
|
|
@@ -12697,7 +12773,7 @@ function plainText(tokens) {
|
|
|
12697
12773
|
if (info === "map") {
|
|
12698
12774
|
const names = mapNames(token.content);
|
|
12699
12775
|
if (names !== "") lines.push(names);
|
|
12700
|
-
} else if (info !== "mermaid" && info !== "math") lines.push(token.content.trim());
|
|
12776
|
+
} else if (info !== "mermaid" && info !== "math" && !isExtensionFenceInfo(info)) lines.push(token.content.trim());
|
|
12701
12777
|
}
|
|
12702
12778
|
return lines.join("\n");
|
|
12703
12779
|
}
|
|
@@ -12776,6 +12852,9 @@ function createDocumentRenderer(options) {
|
|
|
12776
12852
|
if (!picture) return "";
|
|
12777
12853
|
return `<figure class="map"><img src="${escapeHtml$3(picture.src)}" alt="${escapeHtml$3(picture.alt)}" width="${picture.width}" height="${picture.height}" loading="lazy"></figure>\n`;
|
|
12778
12854
|
}
|
|
12855
|
+
const output = options.fenceOutput?.(lang, code);
|
|
12856
|
+
if (output) return fenceOutputHtml(output);
|
|
12857
|
+
if (output === null || isExtensionFenceInfo(lang)) return "";
|
|
12779
12858
|
const highlighted = options.highlighted?.(lang, code);
|
|
12780
12859
|
return `<pre><code${lang === "" ? "" : ` class="language-${escapeHtml$3(lang)}"`}>${highlighted ?? escapeHtml$3(code)}</code></pre>\n`;
|
|
12781
12860
|
};
|
|
@@ -13219,14 +13298,89 @@ function createThemeRenderer(theme, includes) {
|
|
|
13219
13298
|
*/
|
|
13220
13299
|
/**
|
|
13221
13300
|
* Bumped when the way a map is drawn changes, so a site published again holds the new picture
|
|
13222
|
-
* under a new name rather than a cached one under the old.
|
|
13301
|
+
* under a new name rather than a cached one under the old. 2: a name that would run past the
|
|
13302
|
+
* picture's edge is shortened with an ellipsis.
|
|
13223
13303
|
*/
|
|
13224
|
-
var MAP_PICTURE_VERSION =
|
|
13304
|
+
var MAP_PICTURE_VERSION = 2;
|
|
13225
13305
|
/** A picture's file name: from what the map holds and how it is drawn, so an unchanged map publishes the same file. */
|
|
13226
13306
|
async function mapPictureName(source) {
|
|
13227
13307
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${MAP_PICTURE_VERSION}\n${source}`));
|
|
13228
13308
|
return [...new Uint8Array(digest)].slice(0, 8).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
13229
13309
|
}
|
|
13310
|
+
/** A fence's body as lines, as an extension's widget reads it: markdown-it's content ends with a line break. */
|
|
13311
|
+
function bodyLines(code) {
|
|
13312
|
+
const lines = code.split("\n");
|
|
13313
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
13314
|
+
return lines;
|
|
13315
|
+
}
|
|
13316
|
+
var PICTURE_EXTENSIONS = {
|
|
13317
|
+
"image/svg+xml": "svg",
|
|
13318
|
+
"image/png": "png",
|
|
13319
|
+
"image/webp": "webp"
|
|
13320
|
+
};
|
|
13321
|
+
/** An `https` address as written, or null for any other. */
|
|
13322
|
+
function httpsAddress(value) {
|
|
13323
|
+
if (typeof value !== "string") return null;
|
|
13324
|
+
try {
|
|
13325
|
+
return new URL(value).protocol === "https:" ? value : null;
|
|
13326
|
+
} catch {
|
|
13327
|
+
return null;
|
|
13328
|
+
}
|
|
13329
|
+
}
|
|
13330
|
+
/**
|
|
13331
|
+
* What an extension's `publish` answered for a fence, checked, its picture kept for the site under
|
|
13332
|
+
* a name taken from its bytes: a picture, a link (`https` only, as is any picture in it), null when
|
|
13333
|
+
* it is left out, or undefined when it could not be made, after which an ordinary fence is published
|
|
13334
|
+
* as code and an Extension Fence left out, with a warning either way.
|
|
13335
|
+
*/
|
|
13336
|
+
async function publishedFence(asked, info, concept, pictures, warnings) {
|
|
13337
|
+
try {
|
|
13338
|
+
const out = await asked;
|
|
13339
|
+
if (out === null) return null;
|
|
13340
|
+
if (out.kind === "picture") {
|
|
13341
|
+
const extension = PICTURE_EXTENSIONS[out.type];
|
|
13342
|
+
if (!(out.bytes instanceof Uint8Array) || !extension) throw new Error("its picture is not an SVG, PNG or WebP file");
|
|
13343
|
+
const digest = await crypto.subtle.digest("SHA-256", out.bytes);
|
|
13344
|
+
const path = `pictures/${[...new Uint8Array(digest)].slice(0, 8).map((byte) => byte.toString(16).padStart(2, "0")).join("")}.${extension}`;
|
|
13345
|
+
pictures.set(path, out.bytes);
|
|
13346
|
+
return {
|
|
13347
|
+
kind: "picture",
|
|
13348
|
+
src: path,
|
|
13349
|
+
alt: String(out.alt ?? ""),
|
|
13350
|
+
width: Math.round(out.width),
|
|
13351
|
+
height: Math.round(out.height)
|
|
13352
|
+
};
|
|
13353
|
+
}
|
|
13354
|
+
if (out.kind === "link") {
|
|
13355
|
+
const href = httpsAddress(out.href);
|
|
13356
|
+
if (!href) throw new Error("its link is not an https address");
|
|
13357
|
+
const src = httpsAddress(out.image?.src);
|
|
13358
|
+
const image = src && out.image ? {
|
|
13359
|
+
src,
|
|
13360
|
+
alt: String(out.image.alt ?? ""),
|
|
13361
|
+
width: out.image.width,
|
|
13362
|
+
height: out.image.height
|
|
13363
|
+
} : void 0;
|
|
13364
|
+
return {
|
|
13365
|
+
kind: "link",
|
|
13366
|
+
href,
|
|
13367
|
+
text: String(out.text ?? href),
|
|
13368
|
+
...image ? { image } : {}
|
|
13369
|
+
};
|
|
13370
|
+
}
|
|
13371
|
+
throw new Error("it answered with something that is neither a picture nor a link");
|
|
13372
|
+
} catch (error) {
|
|
13373
|
+
const why = error instanceof Error ? error.message : String(error);
|
|
13374
|
+
const extensionFence = isExtensionFenceInfo(info);
|
|
13375
|
+
warnings.push({
|
|
13376
|
+
level: "warning",
|
|
13377
|
+
code: "extension-fence-failed",
|
|
13378
|
+
message: `A \`${info}\` block in "${concept}" could not be published: ${why}. ${extensionFence ? "It is left out." : "It is published as its text."}`,
|
|
13379
|
+
concept
|
|
13380
|
+
});
|
|
13381
|
+
return extensionFence ? null : void 0;
|
|
13382
|
+
}
|
|
13383
|
+
}
|
|
13230
13384
|
function assetHrefOf(name) {
|
|
13231
13385
|
return `assets/${encodeURIComponent(name)}`;
|
|
13232
13386
|
}
|
|
@@ -13405,6 +13559,10 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13405
13559
|
}
|
|
13406
13560
|
const mermaidSvg = /* @__PURE__ */ new Map();
|
|
13407
13561
|
const mapPictures = /* @__PURE__ */ new Map();
|
|
13562
|
+
/** What each fence an extension publishes becomes, by its info word and body; null when it is left out. */
|
|
13563
|
+
const fenceOutputs = /* @__PURE__ */ new Map();
|
|
13564
|
+
/** The pictures extensions made of fences, by their path in the site. */
|
|
13565
|
+
const fencePictures = /* @__PURE__ */ new Map();
|
|
13408
13566
|
const highlighted = /* @__PURE__ */ new Map();
|
|
13409
13567
|
const highlight = env.highlightCode ?? highlightCode;
|
|
13410
13568
|
const probe = createDocumentRenderer({
|
|
@@ -13415,51 +13573,77 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13415
13573
|
doc: d,
|
|
13416
13574
|
body: bodyOf$1(d)
|
|
13417
13575
|
}));
|
|
13418
|
-
for (const { doc, body } of bodies) for (const fence of probe.fences(body))
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
|
|
13422
|
-
|
|
13576
|
+
for (const { doc, body } of bodies) for (const fence of probe.fences(body)) {
|
|
13577
|
+
const fenceKey = `${fence.lang}\n${fence.code}`;
|
|
13578
|
+
const extensionFence = isExtensionFenceInfo(fence.lang);
|
|
13579
|
+
const asked = fence.lang === "" || fenceOutputs.has(fenceKey) ? void 0 : env.publishFence?.({
|
|
13580
|
+
info: fence.lang,
|
|
13581
|
+
body: bodyLines(fence.code),
|
|
13582
|
+
document: doc.concept
|
|
13583
|
+
});
|
|
13584
|
+
if (asked) {
|
|
13585
|
+
const output = await publishedFence(asked, fence.lang, doc.concept, fencePictures, warnings);
|
|
13586
|
+
if (output !== void 0) {
|
|
13587
|
+
fenceOutputs.set(fenceKey, output);
|
|
13588
|
+
continue;
|
|
13589
|
+
}
|
|
13590
|
+
} else if (fenceOutputs.has(fenceKey)) continue;
|
|
13591
|
+
if (extensionFence) {
|
|
13592
|
+
fenceOutputs.set(fenceKey, null);
|
|
13423
13593
|
warnings.push({
|
|
13424
13594
|
level: "warning",
|
|
13425
|
-
code: "
|
|
13426
|
-
message: `
|
|
13427
|
-
concept: doc.concept
|
|
13428
|
-
});
|
|
13429
|
-
}
|
|
13430
|
-
} else if (fence.lang === "map") {
|
|
13431
|
-
if (mapPictures.has(fence.code) || errors.some((e) => e.code === "map-not-drawn" && e.concept === doc.concept)) continue;
|
|
13432
|
-
if (!env.renderMap) {
|
|
13433
|
-
errors.push({
|
|
13434
|
-
level: "error",
|
|
13435
|
-
code: "map-not-drawn",
|
|
13436
|
-
message: `"${doc.concept}" holds a map, which this publisher can't draw. Publish the site from EtherPK in a browser, where maps are drawn.`,
|
|
13595
|
+
code: "extension-fence-left-out",
|
|
13596
|
+
message: `"${doc.concept}" holds content from the ${extensionOfFence(fence.lang)} extension, which this publisher can't draw, so it is left out. Publish from EtherPK in a browser, with that extension on, to include it.`,
|
|
13437
13597
|
concept: doc.concept
|
|
13438
13598
|
});
|
|
13439
13599
|
continue;
|
|
13440
13600
|
}
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13444
|
-
|
|
13445
|
-
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13601
|
+
if (fence.lang === "mermaid") {
|
|
13602
|
+
if (!env.renderMermaid || mermaidSvg.has(fence.code)) continue;
|
|
13603
|
+
try {
|
|
13604
|
+
mermaidSvg.set(fence.code, await env.renderMermaid(fence.code));
|
|
13605
|
+
} catch (error) {
|
|
13606
|
+
warnings.push({
|
|
13607
|
+
level: "warning",
|
|
13608
|
+
code: "mermaid-render-failed",
|
|
13609
|
+
message: `A diagram in "${doc.concept}" could not be rendered: ${error instanceof Error ? error.message : String(error)}. It is left as its source for a script to draw.`,
|
|
13610
|
+
concept: doc.concept
|
|
13611
|
+
});
|
|
13612
|
+
}
|
|
13613
|
+
} else if (fence.lang === "map") {
|
|
13614
|
+
if (mapPictures.has(fence.code) || errors.some((e) => e.code === "map-not-drawn" && e.concept === doc.concept)) continue;
|
|
13615
|
+
if (!env.renderMap) {
|
|
13616
|
+
errors.push({
|
|
13617
|
+
level: "error",
|
|
13618
|
+
code: "map-not-drawn",
|
|
13619
|
+
message: `"${doc.concept}" holds a map, which this publisher can't draw. Publish the site from EtherPK in a browser, where maps are drawn.`,
|
|
13620
|
+
concept: doc.concept
|
|
13621
|
+
});
|
|
13622
|
+
continue;
|
|
13623
|
+
}
|
|
13624
|
+
try {
|
|
13625
|
+
const picture = await env.renderMap(fence.code);
|
|
13626
|
+
const path = `maps/${await mapPictureName(fence.code)}.${picture.type === "image/webp" ? "webp" : "png"}`;
|
|
13627
|
+
mapPictures.set(fence.code, {
|
|
13628
|
+
...picture,
|
|
13629
|
+
path,
|
|
13630
|
+
alt: mapAltText(fence.code.split("\n"))
|
|
13631
|
+
});
|
|
13632
|
+
} catch (error) {
|
|
13633
|
+
const why = error instanceof Error ? error.message : String(error);
|
|
13634
|
+
errors.push({
|
|
13635
|
+
level: "error",
|
|
13636
|
+
code: "map-not-drawn",
|
|
13637
|
+
message: `The map in "${doc.concept}" could not be drawn: ${why}. Nothing was published, so try again once it can be.`,
|
|
13638
|
+
concept: doc.concept
|
|
13639
|
+
});
|
|
13640
|
+
}
|
|
13641
|
+
} else if (fence.lang !== "" && fence.lang !== "math" && fence.lang !== "etherpk-cipher") {
|
|
13642
|
+
const key = `${fence.lang}\n${fence.code}`;
|
|
13643
|
+
if (highlighted.has(key)) continue;
|
|
13644
|
+
const html = await highlight(fence.lang, fence.code);
|
|
13645
|
+
if (html !== null) highlighted.set(key, html);
|
|
13457
13646
|
}
|
|
13458
|
-
} else if (fence.lang !== "" && fence.lang !== "math" && fence.lang !== "etherpk-cipher") {
|
|
13459
|
-
const key = `${fence.lang}\n${fence.code}`;
|
|
13460
|
-
if (highlighted.has(key)) continue;
|
|
13461
|
-
const html = await highlight(fence.lang, fence.code);
|
|
13462
|
-
if (html !== null) highlighted.set(key, html);
|
|
13463
13647
|
}
|
|
13464
13648
|
if (errors.length > 0) return {
|
|
13465
13649
|
bundle,
|
|
@@ -13491,7 +13675,8 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13491
13675
|
width: picture.width,
|
|
13492
13676
|
height: picture.height
|
|
13493
13677
|
};
|
|
13494
|
-
}
|
|
13678
|
+
},
|
|
13679
|
+
fenceOutput: (lang, code) => fenceOutputs.get(`${lang}\n${code}`)
|
|
13495
13680
|
});
|
|
13496
13681
|
const pages = [];
|
|
13497
13682
|
let done = 0;
|
|
@@ -13750,6 +13935,7 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13750
13935
|
report.assets.copied.push(name);
|
|
13751
13936
|
}
|
|
13752
13937
|
for (const picture of mapPictures.values()) bundle.set(picture.path, picture.bytes);
|
|
13938
|
+
for (const [path, bytes] of fencePictures) bundle.set(path, bytes);
|
|
13753
13939
|
for (const [path, text] of theme.files) if (path.startsWith("assets/")) bundle.set(`theme/${path.slice(7)}`, text);
|
|
13754
13940
|
if (customCss !== "") bundle.set("theme/custom.css", customCss.endsWith("\n") ? customCss : `${customCss}\n`);
|
|
13755
13941
|
if (hasMath) {
|
|
@@ -18496,7 +18682,7 @@ function createMcpServer(source, info) {
|
|
|
18496
18682
|
}, async (args) => run((graph) => rename$1(graph, args)));
|
|
18497
18683
|
server.registerTool("set_aliases", {
|
|
18498
18684
|
title: "Set aliases",
|
|
18499
|
-
description: "Replace a document's aliases: other names it answers to in [[wikilinks]],
|
|
18685
|
+
description: "Replace a document's aliases: other names it answers to in [[wikilinks]], Search and on a published site. The list you pass is the complete list, and it is shown in the document's frontmatter, in a block added if there is none. A name that is already another document's title or alias is refused with error \"name_taken\".",
|
|
18500
18686
|
inputSchema: {
|
|
18501
18687
|
concept,
|
|
18502
18688
|
aliases: z.array(z.string()).describe("Every alias the document should have - an empty list removes them all.")
|