@appsoftwareltd/etherpk-mcp 0.9.3 → 0.9.5
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 +8 -5
- package/dist/main.js +713 -96
- package/dist/main.js.map +1 -1
- package/dist/map-picture-page.js +292 -0
- package/package.json +9 -6
package/dist/main.js
CHANGED
|
@@ -14,7 +14,7 @@ import { spawn } from "node:child_process";
|
|
|
14
14
|
import { closeSync, createWriteStream, existsSync, openSync, statSync, watch } from "node:fs";
|
|
15
15
|
import { Readable } from "node:stream";
|
|
16
16
|
import { pipeline } from "node:stream/promises";
|
|
17
|
-
import { pathToFileURL } from "node:url";
|
|
17
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
18
18
|
import { Tokenizer } from "@huggingface/tokenizers";
|
|
19
19
|
import { deserialize, serialize } from "node:v8";
|
|
20
20
|
import { parser } from "@lezer/markdown";
|
|
@@ -30,6 +30,8 @@ import { languages } from "@codemirror/language-data";
|
|
|
30
30
|
import MarkdownIt from "markdown-it";
|
|
31
31
|
import katex from "katex";
|
|
32
32
|
import Mustache from "mustache";
|
|
33
|
+
import { createServer as createServer$1 } from "node:http";
|
|
34
|
+
import "maplibre-gl";
|
|
33
35
|
import { lookup } from "node:dns";
|
|
34
36
|
import { request } from "node:https";
|
|
35
37
|
import { UndirectedGraph } from "graphology";
|
|
@@ -627,6 +629,73 @@ function assetChunkCount(size) {
|
|
|
627
629
|
}
|
|
628
630
|
var DAY = 864e5;
|
|
629
631
|
365 * DAY, 30 * DAY, 7 * DAY;
|
|
632
|
+
/** Whether `text` is a short link a Sync Server opens: one of Google's share links. */
|
|
633
|
+
function isShortMapLink(text) {
|
|
634
|
+
let url;
|
|
635
|
+
try {
|
|
636
|
+
url = new URL(text.trim());
|
|
637
|
+
} catch {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "") return false;
|
|
641
|
+
const host = url.hostname.toLowerCase();
|
|
642
|
+
if (host === "maps.app.goo.gl") return /^\/[A-Za-z0-9_-]+\/?$/.test(url.pathname);
|
|
643
|
+
if (host === "goo.gl") return /^\/maps\/[A-Za-z0-9_-]+\/?$/.test(url.pathname);
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
z.strictObject({ url: z.string().trim().max(200).refine(isShortMapLink, "not a short map link") });
|
|
647
|
+
var latitude = z.number().min(-90).max(90);
|
|
648
|
+
var longitude = z.number().min(-180).max(180);
|
|
649
|
+
z.strictObject({
|
|
650
|
+
q: z.string().trim().min(1).max(200),
|
|
651
|
+
/** Where the map is looking, so the nearest places come first. */
|
|
652
|
+
near: z.strictObject({
|
|
653
|
+
lat: latitude,
|
|
654
|
+
lon: longitude
|
|
655
|
+
}).optional(),
|
|
656
|
+
/** The reader's language, two letters, for the names of places. */
|
|
657
|
+
lang: z.string().regex(/^[a-z]{2}$/).optional()
|
|
658
|
+
});
|
|
659
|
+
z.strictObject({
|
|
660
|
+
point: z.strictObject({
|
|
661
|
+
lat: latitude,
|
|
662
|
+
lon: longitude
|
|
663
|
+
}),
|
|
664
|
+
/** The reader's language, two letters, for the name of the place. */
|
|
665
|
+
lang: z.string().regex(/^[a-z]{2}$/).optional()
|
|
666
|
+
});
|
|
667
|
+
/** A place's address fields, as Photon names them. Anything else a service sends is dropped. */
|
|
668
|
+
var placeProperties = z.object({
|
|
669
|
+
name: z.string().optional(),
|
|
670
|
+
housenumber: z.string().optional(),
|
|
671
|
+
street: z.string().optional(),
|
|
672
|
+
postcode: z.string().optional(),
|
|
673
|
+
district: z.string().optional(),
|
|
674
|
+
city: z.string().optional(),
|
|
675
|
+
county: z.string().optional(),
|
|
676
|
+
state: z.string().optional(),
|
|
677
|
+
country: z.string().optional(),
|
|
678
|
+
countrycode: z.string().optional(),
|
|
679
|
+
/** What sort of place it is: a house, a street, a city, a postcode. */
|
|
680
|
+
type: z.string().optional()
|
|
681
|
+
});
|
|
682
|
+
var placeSearchFeatureSchema = z.object({
|
|
683
|
+
type: z.literal("Feature"),
|
|
684
|
+
geometry: z.object({
|
|
685
|
+
type: z.literal("Point"),
|
|
686
|
+
coordinates: z.tuple([longitude, latitude])
|
|
687
|
+
}),
|
|
688
|
+
properties: placeProperties
|
|
689
|
+
});
|
|
690
|
+
var placeSearchCreditSchema = z.object({
|
|
691
|
+
text: z.string().trim().min(1).max(100),
|
|
692
|
+
url: z.url({ protocol: /^https$/ })
|
|
693
|
+
});
|
|
694
|
+
z.object({
|
|
695
|
+
type: z.literal("FeatureCollection"),
|
|
696
|
+
features: z.array(placeSearchFeatureSchema).max(8),
|
|
697
|
+
credits: z.array(placeSearchCreditSchema).max(4).optional()
|
|
698
|
+
});
|
|
630
699
|
var AGENT_SETUP_CODE_PREFIX = "epk_setup_";
|
|
631
700
|
/** Every token and setup code carries 32 random bytes, which base64url writes as 43 characters. */
|
|
632
701
|
var SECRET_LENGTH = 43;
|
|
@@ -648,7 +717,7 @@ function looksLikeAgentSetupCode(value) {
|
|
|
648
717
|
}
|
|
649
718
|
var package_default = {
|
|
650
719
|
name: "@appsoftwareltd/etherpk-mcp",
|
|
651
|
-
version: "0.9.
|
|
720
|
+
version: "0.9.5",
|
|
652
721
|
license: "Elastic-2.0",
|
|
653
722
|
description: "EtherPK Headless Client: an MCP server over a synced knowledge graph, run beside the agent on the user's own machine.",
|
|
654
723
|
type: "module",
|
|
@@ -657,7 +726,7 @@ var package_default = {
|
|
|
657
726
|
files: ["bin", "dist"],
|
|
658
727
|
scripts: {
|
|
659
728
|
"client-tsconfig": "pnpm --dir ../client exec svelte-kit sync",
|
|
660
|
-
"build": "pnpm client-tsconfig && vite build",
|
|
729
|
+
"build": "pnpm client-tsconfig && vite build && vite build --config vite.map-page.config.ts",
|
|
661
730
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
662
731
|
"test": "pnpm client-tsconfig && vitest run",
|
|
663
732
|
"test:watch": "pnpm client-tsconfig && vitest",
|
|
@@ -669,7 +738,7 @@ var package_default = {
|
|
|
669
738
|
"@huggingface/tokenizers": "0.2.0",
|
|
670
739
|
"@lezer/highlight": "^1.2.3",
|
|
671
740
|
"@lezer/markdown": "^1.6.4",
|
|
672
|
-
"@modelcontextprotocol/sdk": "1.
|
|
741
|
+
"@modelcontextprotocol/sdk": "1.31.0",
|
|
673
742
|
"@noble/curves": "2.2.0",
|
|
674
743
|
"@noble/hashes": "2.2.0",
|
|
675
744
|
"@sqlite.org/sqlite-wasm": "3.53.0-build1",
|
|
@@ -677,8 +746,9 @@ var package_default = {
|
|
|
677
746
|
"graphology": "^0.26.0",
|
|
678
747
|
"graphology-communities-louvain": "^2.0.2",
|
|
679
748
|
"graphology-types": "^0.24.8",
|
|
680
|
-
"katex": "^0.
|
|
749
|
+
"katex": "^0.18.2",
|
|
681
750
|
"lib0": "0.2.117",
|
|
751
|
+
"maplibre-gl": "6.11.2",
|
|
682
752
|
"markdown-it": "15.0.2",
|
|
683
753
|
"mermaid": "^11.16.1",
|
|
684
754
|
"mustache": "4.2.0",
|
|
@@ -689,6 +759,8 @@ var package_default = {
|
|
|
689
759
|
"zod": "^4.3.6"
|
|
690
760
|
},
|
|
691
761
|
devDependencies: {
|
|
762
|
+
"@appsoftwareltd/etherpk-extension-graph-view": "workspace:*",
|
|
763
|
+
"@appsoftwareltd/etherpk-extension-maps": "workspace:*",
|
|
692
764
|
"@appsoftwareltd/etherpk-shared": "workspace:*",
|
|
693
765
|
"@appsoftwareltd/etherpk-themes": "workspace:*",
|
|
694
766
|
"@codemirror/view": "^6.43.0",
|
|
@@ -1392,7 +1464,8 @@ var KNOWN_FIELDS = [
|
|
|
1392
1464
|
"signingPublicKey",
|
|
1393
1465
|
"keyrings",
|
|
1394
1466
|
"protection",
|
|
1395
|
-
"pins"
|
|
1467
|
+
"pins",
|
|
1468
|
+
"settings"
|
|
1396
1469
|
];
|
|
1397
1470
|
async function openContent(envelope, vaultKey, aad) {
|
|
1398
1471
|
const { plaintext } = await openSymmetric({
|
|
@@ -1413,6 +1486,7 @@ async function openContent(envelope, vaultKey, aad) {
|
|
|
1413
1486
|
keyrings: deserializeKeyrings(utf8(content.keyrings)),
|
|
1414
1487
|
...content.protection ? { protection: content.protection } : {},
|
|
1415
1488
|
...content.pins ? { pins: content.pins } : {},
|
|
1489
|
+
...content.settings ? { settings: content.settings } : {},
|
|
1416
1490
|
...Object.keys(otherFields).length > 0 ? { otherFields } : {}
|
|
1417
1491
|
};
|
|
1418
1492
|
}
|
|
@@ -1608,6 +1682,34 @@ function createSyncApi(deps) {
|
|
|
1608
1682
|
return {
|
|
1609
1683
|
/** Resolve the authenticated service-local account before loading account-scoped data. */
|
|
1610
1684
|
me: () => call("/api/v1/sync/me"),
|
|
1685
|
+
/**
|
|
1686
|
+
* Search for a place by name, address or postcode (ADR 0119). The query travels in the
|
|
1687
|
+
* body, so no request log holds it. The answer is unchecked JSON in Photon's form, which
|
|
1688
|
+
* the caller reads with `readPlaceFeatures`; a refusal carries a `PLACE_SEARCH_REFUSAL` code.
|
|
1689
|
+
*/
|
|
1690
|
+
searchPlaces: (request, signal) => call("/api/v1/sync/maps/search", {
|
|
1691
|
+
method: "POST",
|
|
1692
|
+
body: JSON.stringify(request),
|
|
1693
|
+
signal
|
|
1694
|
+
}),
|
|
1695
|
+
/**
|
|
1696
|
+
* The place nearest a point, to offer a name for a place set down by hand (ADR 0119,
|
|
1697
|
+
* amendment of 2026-10-10). Answered and refused as `searchPlaces` is, with at most one place.
|
|
1698
|
+
*/
|
|
1699
|
+
reversePlace: (request, signal) => call("/api/v1/sync/maps/reverse", {
|
|
1700
|
+
method: "POST",
|
|
1701
|
+
body: JSON.stringify(request),
|
|
1702
|
+
signal
|
|
1703
|
+
}),
|
|
1704
|
+
/**
|
|
1705
|
+
* The full link a short map link leads to, which a browser page cannot follow itself (ADR
|
|
1706
|
+
* 0119, amendment of 2026-10-10). Any signed-in account may ask.
|
|
1707
|
+
*/
|
|
1708
|
+
openMapLink: (request, signal) => call("/api/v1/sync/maps/link", {
|
|
1709
|
+
method: "POST",
|
|
1710
|
+
body: JSON.stringify(request),
|
|
1711
|
+
signal
|
|
1712
|
+
}),
|
|
1611
1713
|
createGraph: () => call("/api/v1/sync/graphs", { method: "POST" }),
|
|
1612
1714
|
listGraphs: () => call("/api/v1/sync/graphs").then((r) => r.graphs),
|
|
1613
1715
|
/** Graphs plus the owned-storage rollup (one request - same endpoint as listGraphs). */
|
|
@@ -3649,6 +3751,33 @@ function fencedBlocks(lines, closerTolerance = 0) {
|
|
|
3649
3751
|
}
|
|
3650
3752
|
return blocks;
|
|
3651
3753
|
}
|
|
3754
|
+
/** {@link fencedBlockTree} by scan: a scan never changes, so neither does its tree. */
|
|
3755
|
+
var treeByBlocks = /* @__PURE__ */ new WeakMap();
|
|
3756
|
+
/**
|
|
3757
|
+
* The blocks of one scan as a tree. A code sample can hold a fenced block of its own, written deeper (a
|
|
3758
|
+
* markdown sample of a list item with code in it): the scan pairs the inner fences inside the outer
|
|
3759
|
+
* block, and lists blocks in the order they close, the inner pair before the block around it. Blocks
|
|
3760
|
+
* nest or are disjoint, never overlap partly. Read once per scan and shared, as the lookups run on
|
|
3761
|
+
* every caret move.
|
|
3762
|
+
*/
|
|
3763
|
+
function fencedBlockTree(blocks) {
|
|
3764
|
+
const cached = treeByBlocks.get(blocks);
|
|
3765
|
+
if (cached) return cached;
|
|
3766
|
+
const sorted = [...blocks].sort((a, b) => a.start - b.start);
|
|
3767
|
+
const parent = [];
|
|
3768
|
+
const open = [];
|
|
3769
|
+
sorted.forEach((block, i) => {
|
|
3770
|
+
while (open.length && sorted[open[open.length - 1]].end < block.start) open.pop();
|
|
3771
|
+
parent.push(open.length ? open[open.length - 1] : -1);
|
|
3772
|
+
open.push(i);
|
|
3773
|
+
});
|
|
3774
|
+
const tree = {
|
|
3775
|
+
sorted,
|
|
3776
|
+
parent
|
|
3777
|
+
};
|
|
3778
|
+
treeByBlocks.set(blocks, tree);
|
|
3779
|
+
return tree;
|
|
3780
|
+
}
|
|
3652
3781
|
/**
|
|
3653
3782
|
* A line of a fenced block as code: the indentation up to the fence's column is structure (the
|
|
3654
3783
|
* block's place in the outline) and goes; whatever lies past it is the code's own and stays.
|
|
@@ -4033,6 +4162,105 @@ function parseBlocks(markdown) {
|
|
|
4033
4162
|
return roots;
|
|
4034
4163
|
}
|
|
4035
4164
|
//#endregion
|
|
4165
|
+
//#region ../client/src/lib/document/map-text.ts
|
|
4166
|
+
var NUMBER = String.raw`[+-]?(?:\d+(?:\.\d*)?|\.\d+)`;
|
|
4167
|
+
var POINT = new RegExp(String.raw`^\s*(${NUMBER})\s*,\s*(${NUMBER})\s*$`);
|
|
4168
|
+
function readPoint(text) {
|
|
4169
|
+
const match = POINT.exec(text);
|
|
4170
|
+
if (!match) return null;
|
|
4171
|
+
const lat = Number(match[1]);
|
|
4172
|
+
const lon = Number(match[2]);
|
|
4173
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
|
|
4174
|
+
if (lat < -90 || lat > 90 || lon < -180 || lon > 180) return null;
|
|
4175
|
+
return {
|
|
4176
|
+
lat,
|
|
4177
|
+
lon
|
|
4178
|
+
};
|
|
4179
|
+
}
|
|
4180
|
+
/**
|
|
4181
|
+
* The part after the name: points joined by `>`, and for a route an optional `(recording)`.
|
|
4182
|
+
* The recording starts at the first `(`, which no point can hold, so a file name with brackets
|
|
4183
|
+
* of its own is kept whole.
|
|
4184
|
+
*/
|
|
4185
|
+
function readLocation(text, name) {
|
|
4186
|
+
let pointsText = text.trim();
|
|
4187
|
+
let track = null;
|
|
4188
|
+
const open = pointsText.indexOf("(");
|
|
4189
|
+
if (open >= 0) {
|
|
4190
|
+
if (!pointsText.endsWith(")")) return null;
|
|
4191
|
+
track = pointsText.slice(open + 1, -1).trim();
|
|
4192
|
+
if (track === "") return null;
|
|
4193
|
+
pointsText = pointsText.slice(0, open);
|
|
4194
|
+
}
|
|
4195
|
+
const points = [];
|
|
4196
|
+
for (const part of pointsText.split(">")) {
|
|
4197
|
+
const point = readPoint(part);
|
|
4198
|
+
if (!point) return null;
|
|
4199
|
+
points.push(point);
|
|
4200
|
+
}
|
|
4201
|
+
if (points.length === 1) return track === null ? {
|
|
4202
|
+
kind: "place",
|
|
4203
|
+
name,
|
|
4204
|
+
point: points[0]
|
|
4205
|
+
} : null;
|
|
4206
|
+
return {
|
|
4207
|
+
kind: "route",
|
|
4208
|
+
name,
|
|
4209
|
+
points,
|
|
4210
|
+
track
|
|
4211
|
+
};
|
|
4212
|
+
}
|
|
4213
|
+
/** What one line of a Map Block's body says, or null when it is not a place or a route. */
|
|
4214
|
+
function readMapLine(text) {
|
|
4215
|
+
const trimmed = text.trim();
|
|
4216
|
+
if (trimmed === "") return null;
|
|
4217
|
+
for (let at = trimmed.lastIndexOf("@"); at >= 0; at = at === 0 ? -1 : trimmed.lastIndexOf("@", at - 1)) {
|
|
4218
|
+
const line = readLocation(trimmed.slice(at + 1), trimmed.slice(0, at).trim());
|
|
4219
|
+
if (line) return line;
|
|
4220
|
+
}
|
|
4221
|
+
return readLocation(trimmed, "");
|
|
4222
|
+
}
|
|
4223
|
+
/** Every item in a Map Block's body, numbered by line. Blank lines are skipped, not reported. */
|
|
4224
|
+
function readMapBody(lines) {
|
|
4225
|
+
const body = {
|
|
4226
|
+
items: [],
|
|
4227
|
+
unread: []
|
|
4228
|
+
};
|
|
4229
|
+
lines.forEach((text, line) => {
|
|
4230
|
+
if (text.trim() === "") return;
|
|
4231
|
+
const read = readMapLine(text);
|
|
4232
|
+
if (read) body.items.push({
|
|
4233
|
+
...read,
|
|
4234
|
+
line
|
|
4235
|
+
});
|
|
4236
|
+
else body.unread.push({
|
|
4237
|
+
line,
|
|
4238
|
+
text
|
|
4239
|
+
});
|
|
4240
|
+
});
|
|
4241
|
+
return body;
|
|
4242
|
+
}
|
|
4243
|
+
/** How many names a picture's alt text lists before it counts the rest. */
|
|
4244
|
+
var NAMED_IN_ALT = 10;
|
|
4245
|
+
/**
|
|
4246
|
+
* The words for a picture of a map (a published Map Block, ADR 0118): what is on it, by name,
|
|
4247
|
+
* never where. Places and routes without a name are counted, and past the first ten names the
|
|
4248
|
+
* rest are counted too, so the text stays a sentence a screen reader can read out.
|
|
4249
|
+
*/
|
|
4250
|
+
function mapAltText(body) {
|
|
4251
|
+
const { items } = readMapBody(body);
|
|
4252
|
+
if (items.length === 0) return "An empty map";
|
|
4253
|
+
const named = items.filter((item) => item.name !== "").map((item) => item.name);
|
|
4254
|
+
const parts = named.slice(0, NAMED_IN_ALT);
|
|
4255
|
+
const more = named.length - parts.length;
|
|
4256
|
+
if (more > 0) parts.push(`${more} more`);
|
|
4257
|
+
const unnamedPlaces = items.filter((item) => item.name === "" && item.kind === "place").length;
|
|
4258
|
+
const unnamedRoutes = items.filter((item) => item.name === "" && item.kind === "route").length;
|
|
4259
|
+
if (unnamedPlaces > 0) parts.push(`${unnamedPlaces} unnamed ${unnamedPlaces === 1 ? "place" : "places"}`);
|
|
4260
|
+
if (unnamedRoutes > 0) parts.push(`${unnamedRoutes} unnamed ${unnamedRoutes === 1 ? "route" : "routes"}`);
|
|
4261
|
+
return `Map of ${parts.length === 1 ? parts[0] : `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}`}`;
|
|
4262
|
+
}
|
|
4263
|
+
//#endregion
|
|
4036
4264
|
//#region ../client/src/lib/document/protection/fence-info.ts
|
|
4037
4265
|
/**
|
|
4038
4266
|
* The one name for the protected fence, and the one way to recognise one in raw text.
|
|
@@ -4296,7 +4524,7 @@ function blocksAndLinks(text) {
|
|
|
4296
4524
|
}
|
|
4297
4525
|
/** Derive the index rows for a single document's text. Pure. */
|
|
4298
4526
|
function deriveDoc(text) {
|
|
4299
|
-
const { blocks, links } = blocksAndLinks(text);
|
|
4527
|
+
const { blocks, links, lineToBlock } = blocksAndLinks(text);
|
|
4300
4528
|
const candidateTasks = blocks.filter((b) => b.kind === "task");
|
|
4301
4529
|
const fenced = candidateTasks.length === 0 ? () => false : fencedLines(text);
|
|
4302
4530
|
const taskBlocks = candidateTasks.filter((b) => !fenced(b.startLine));
|
|
@@ -4319,7 +4547,67 @@ function deriveDoc(text) {
|
|
|
4319
4547
|
scheduled: tags.scheduled
|
|
4320
4548
|
};
|
|
4321
4549
|
}),
|
|
4322
|
-
taskConcepts: deriveTaskConcepts(blocks, links, taskBlocks)
|
|
4550
|
+
taskConcepts: deriveTaskConcepts(blocks, links, taskBlocks),
|
|
4551
|
+
...deriveMaps(text, blocks, links, lineToBlock)
|
|
4552
|
+
};
|
|
4553
|
+
}
|
|
4554
|
+
/**
|
|
4555
|
+
* The [[Place]]s and [[Route]]s a document's [[Map Block]]s hold (ADR 0118), the [[Block
|
|
4556
|
+
* Concept]]s of the blocks they sit in, and what text search reads for those blocks instead of
|
|
4557
|
+
* their coordinates.
|
|
4558
|
+
*
|
|
4559
|
+
* A Map Block is a complete, outermost fence whose info word is `map`: one shown as an example
|
|
4560
|
+
* inside another fence is text. A document holding any protected content gives nothing, as the
|
|
4561
|
+
* Map View leaves Protected Documents out: the index is plaintext at rest, and the conservative
|
|
4562
|
+
* reading of a page with a cipher fence beside other text is the only safe one.
|
|
4563
|
+
*/
|
|
4564
|
+
function deriveMaps(text, blocks, links, lineToBlock) {
|
|
4565
|
+
const searchText = /* @__PURE__ */ new Map();
|
|
4566
|
+
if (!text.includes("```map") || containsCipherFence(text)) return {
|
|
4567
|
+
mapItems: [],
|
|
4568
|
+
mapConcepts: [],
|
|
4569
|
+
searchText
|
|
4570
|
+
};
|
|
4571
|
+
const lines = text.split("\n");
|
|
4572
|
+
const { sorted, parent } = fencedBlockTree(fencedBlocks(lines));
|
|
4573
|
+
const mapItems = [];
|
|
4574
|
+
const mapBlocks = /* @__PURE__ */ new Set();
|
|
4575
|
+
const searchLines = /* @__PURE__ */ new Map();
|
|
4576
|
+
sorted.forEach((fence, i) => {
|
|
4577
|
+
if (parent[i] !== -1 || fenceLineInfo(lines[fence.start])?.info !== "map") return;
|
|
4578
|
+
const blockLocalId = lineToBlock.get(fence.start);
|
|
4579
|
+
if (blockLocalId === void 0) return;
|
|
4580
|
+
mapBlocks.add(blockLocalId);
|
|
4581
|
+
const body = lines.slice(fence.start + 1, fence.end).map((line) => codeLineText(line, fence.fenceColumn));
|
|
4582
|
+
body.forEach((_, n) => searchLines.set(fence.start + 1 + n, ""));
|
|
4583
|
+
for (const item of readMapBody(body).items) {
|
|
4584
|
+
const line = fence.start + 1 + item.line;
|
|
4585
|
+
searchLines.set(line, item.name);
|
|
4586
|
+
mapItems.push({
|
|
4587
|
+
blockLocalId,
|
|
4588
|
+
fenceLine: fence.start,
|
|
4589
|
+
line,
|
|
4590
|
+
kind: item.kind,
|
|
4591
|
+
name: item.name,
|
|
4592
|
+
text: body[item.line]
|
|
4593
|
+
});
|
|
4594
|
+
}
|
|
4595
|
+
});
|
|
4596
|
+
for (const id of mapBlocks) {
|
|
4597
|
+
const block = blocks[id];
|
|
4598
|
+
const read = block.text.split("\n").map((line, n) => searchLines.get(block.startLine + n) ?? line);
|
|
4599
|
+
searchText.set(id, read.join("\n"));
|
|
4600
|
+
}
|
|
4601
|
+
const byBlock = linksByBlock(links);
|
|
4602
|
+
const mapConcepts = [];
|
|
4603
|
+
if (byBlock.size > 0) for (const id of mapBlocks) for (const { concept } of conceptChain(blocks, byBlock, id)) mapConcepts.push({
|
|
4604
|
+
blockLocalId: id,
|
|
4605
|
+
concept
|
|
4606
|
+
});
|
|
4607
|
+
return {
|
|
4608
|
+
mapItems,
|
|
4609
|
+
mapConcepts,
|
|
4610
|
+
searchText
|
|
4323
4611
|
};
|
|
4324
4612
|
}
|
|
4325
4613
|
/**
|
|
@@ -4841,6 +5129,15 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
4841
5129
|
-- the canonical name in would go stale the moment a page is renamed.
|
|
4842
5130
|
CREATE TABLE IF NOT EXISTS task_concepts (
|
|
4843
5131
|
page_id INTEGER NOT NULL, block_local_id INTEGER NOT NULL, concept_key TEXT NOT NULL);
|
|
5132
|
+
-- [[Place]]s and [[Route]]s (ADR 0118): one row per line of a [[Map Block]] that reads, under the
|
|
5133
|
+
-- block its fence belongs to. text is the line as written; the query reads its points from it.
|
|
5134
|
+
CREATE TABLE IF NOT EXISTS map_items (
|
|
5135
|
+
page_id INTEGER NOT NULL, block_local_id INTEGER NOT NULL, fence_line INTEGER NOT NULL,
|
|
5136
|
+
line INTEGER NOT NULL, kind TEXT NOT NULL, name TEXT NOT NULL, text TEXT NOT NULL);
|
|
5137
|
+
-- The [[Block Concept]]s of each block holding a Map Block, as task_concepts holds a task's, so a
|
|
5138
|
+
-- [[Map View]] and a [[Kanban Board]] for one concept agree on what lies under it.
|
|
5139
|
+
CREATE TABLE IF NOT EXISTS map_concepts (
|
|
5140
|
+
page_id INTEGER NOT NULL, block_local_id INTEGER NOT NULL, concept_key TEXT NOT NULL);
|
|
4844
5141
|
-- [[Passage]]s for [[Semantic Search]] (ADR 0076): the pieces of a document that get an
|
|
4845
5142
|
-- [[Embedding]] each, keyed by the hash of their text (semantic/passages.ts). The vectors
|
|
4846
5143
|
-- themselves live in the ATTACHed embeddings schema (semantic/embedding-db.ts), OUTSIDE this
|
|
@@ -4875,6 +5172,7 @@ CREATE INDEX IF NOT EXISTS tasks_page ON tasks(page_id);
|
|
|
4875
5172
|
-- The Name Filter's whole selectivity: without it, filtering to a concept scans every task
|
|
4876
5173
|
-- row in the graph. Mirrors links_concept_key, and like it stays put during a bulk rebuild.
|
|
4877
5174
|
CREATE INDEX IF NOT EXISTS task_concepts_key ON task_concepts(concept_key);
|
|
5175
|
+
CREATE INDEX IF NOT EXISTS map_concepts_key ON map_concepts(concept_key);
|
|
4878
5176
|
-- Search's text index. One row per block, so a document's rank can be "how many blocks
|
|
4879
5177
|
-- mention this" - the ordering the results actually display. See the block_fts notes above
|
|
4880
5178
|
-- createSchema for why unicode61 rather than trigram, and why the rowid encodes the block.
|
|
@@ -4907,7 +5205,7 @@ function createSchema(db) {
|
|
|
4907
5205
|
db.exec(SCHEMA$1);
|
|
4908
5206
|
db.run("INSERT OR IGNORE INTO index_metadata (key, value) VALUES ('active_generation', 1)");
|
|
4909
5207
|
db.run("INSERT OR IGNORE INTO index_metadata (key, value) VALUES ('revision', 0)");
|
|
4910
|
-
db.exec(`PRAGMA user_version =
|
|
5208
|
+
db.exec(`PRAGMA user_version = 16`);
|
|
4911
5209
|
}
|
|
4912
5210
|
function activeIndexGeneration(db) {
|
|
4913
5211
|
return db.all("SELECT value FROM index_metadata WHERE key = 'active_generation'")[0]?.value ?? 1;
|
|
@@ -4928,7 +5226,7 @@ function advanceIndexRevision(db) {
|
|
|
4928
5226
|
*/
|
|
4929
5227
|
function isUsableIndex(db) {
|
|
4930
5228
|
try {
|
|
4931
|
-
return db.all("PRAGMA user_version")[0]?.user_version ===
|
|
5229
|
+
return db.all("PRAGMA user_version")[0]?.user_version === 16;
|
|
4932
5230
|
} catch {
|
|
4933
5231
|
return false;
|
|
4934
5232
|
}
|
|
@@ -5043,7 +5341,7 @@ function insertDerived(db, pageId, doc) {
|
|
|
5043
5341
|
t.matchEnd,
|
|
5044
5342
|
null
|
|
5045
5343
|
]);
|
|
5046
|
-
const { blocks, links, tasks, taskConcepts } = deriveDoc(doc.text);
|
|
5344
|
+
const { blocks, links, tasks, taskConcepts, mapItems, mapConcepts, searchText } = deriveDoc(doc.text);
|
|
5047
5345
|
for (const b of blocks) {
|
|
5048
5346
|
db.run("INSERT INTO blocks (page_id, local_id, parent_local_id, ord, kind, depth, done, start_line, end_line, label, text) VALUES (?,?,?,?,?,?,?,?,?,?,?)", [
|
|
5049
5347
|
pageId,
|
|
@@ -5059,8 +5357,9 @@ function insertDerived(db, pageId, doc) {
|
|
|
5059
5357
|
b.text
|
|
5060
5358
|
]);
|
|
5061
5359
|
if (isTextSearchable(b) && b.localId < 1048576) {
|
|
5062
|
-
|
|
5063
|
-
|
|
5360
|
+
const text = searchText.get(b.localId) ?? b.text;
|
|
5361
|
+
db.run("INSERT INTO block_fts (rowid, text) VALUES (?,?)", [pageId * BLOCK_FTS_STRIDE + b.localId, text]);
|
|
5362
|
+
searchable.push(text);
|
|
5064
5363
|
}
|
|
5065
5364
|
}
|
|
5066
5365
|
for (const l of links) db.run("INSERT INTO links (page_id, concept, concept_key, line, line_text, match_start, match_end, block_local_id) VALUES (?,?,?,?,?,?,?,?)", [
|
|
@@ -5090,7 +5389,12 @@ function insertDerived(db, pageId, doc) {
|
|
|
5090
5389
|
t.scheduled
|
|
5091
5390
|
]);
|
|
5092
5391
|
insertTaskConcepts(db, pageId, doc, tasks, taskConcepts);
|
|
5093
|
-
|
|
5392
|
+
insertMapRows(db, pageId, doc, mapItems, mapConcepts);
|
|
5393
|
+
const passageBlocks = searchText.size === 0 ? blocks : blocks.map((b) => ({
|
|
5394
|
+
...b,
|
|
5395
|
+
text: searchText.get(b.localId) ?? b.text
|
|
5396
|
+
}));
|
|
5397
|
+
for (const passage of derivePassages(doc.concept, passageBlocks)) db.run("INSERT INTO passages (page_id, ord, start_line, end_line, first_block_local_id, content_hash, text) VALUES (?,?,?,?,?,?,?)", [
|
|
5094
5398
|
pageId,
|
|
5095
5399
|
passage.ord,
|
|
5096
5400
|
passage.startLine,
|
|
@@ -5164,6 +5468,37 @@ function insertTaskConcepts(db, pageId, doc, tasks, taskConcepts) {
|
|
|
5164
5468
|
for (const tc of taskConcepts) write(tc.blockLocalId, conceptKey$1(tc.concept));
|
|
5165
5469
|
}
|
|
5166
5470
|
/**
|
|
5471
|
+
* Write one document's [[Map Block]] rows (ADR 0118): its items, and the [[Block Concept]]s of
|
|
5472
|
+
* each block holding a Map Block. The document's own concept and the scopes in its name sit at
|
|
5473
|
+
* the root of every block's chain, added here as {@link insertTaskConcepts} adds them for tasks.
|
|
5474
|
+
*/
|
|
5475
|
+
function insertMapRows(db, pageId, doc, mapItems, mapConcepts) {
|
|
5476
|
+
if (mapItems.length === 0) return;
|
|
5477
|
+
for (const item of mapItems) db.run("INSERT INTO map_items (page_id, block_local_id, fence_line, line, kind, name, text) VALUES (?,?,?,?,?,?,?)", [
|
|
5478
|
+
pageId,
|
|
5479
|
+
item.blockLocalId,
|
|
5480
|
+
item.fenceLine,
|
|
5481
|
+
item.line,
|
|
5482
|
+
item.kind,
|
|
5483
|
+
item.name,
|
|
5484
|
+
item.text
|
|
5485
|
+
]);
|
|
5486
|
+
const written = /* @__PURE__ */ new Set();
|
|
5487
|
+
const write = (blockLocalId, key) => {
|
|
5488
|
+
const seen = `${blockLocalId} ${key}`;
|
|
5489
|
+
if (written.has(seen)) return;
|
|
5490
|
+
written.add(seen);
|
|
5491
|
+
db.run("INSERT INTO map_concepts (page_id, block_local_id, concept_key) VALUES (?,?,?)", [
|
|
5492
|
+
pageId,
|
|
5493
|
+
blockLocalId,
|
|
5494
|
+
key
|
|
5495
|
+
]);
|
|
5496
|
+
};
|
|
5497
|
+
const rootKeys = documentTaskConcepts(doc.concept).map(conceptKey$1);
|
|
5498
|
+
for (const blockLocalId of new Set(mapItems.map((item) => item.blockLocalId))) for (const key of rootKeys) write(blockLocalId, key);
|
|
5499
|
+
for (const row of mapConcepts) write(row.blockLocalId, conceptKey$1(row.concept));
|
|
5500
|
+
}
|
|
5501
|
+
/**
|
|
5167
5502
|
* Run `work` as ONE transaction. Without this every INSERT is its own implicit transaction,
|
|
5168
5503
|
* which is most of what made a full rebuild cost seconds rather than milliseconds.
|
|
5169
5504
|
*/
|
|
@@ -5235,6 +5570,8 @@ function deleteGeneration(db, generation) {
|
|
|
5235
5570
|
"links",
|
|
5236
5571
|
"tasks",
|
|
5237
5572
|
"task_concepts",
|
|
5573
|
+
"map_items",
|
|
5574
|
+
"map_concepts",
|
|
5238
5575
|
"passages",
|
|
5239
5576
|
"properties"
|
|
5240
5577
|
]) db.run(`DELETE FROM ${table} WHERE page_id IN (SELECT id FROM pages WHERE generation = ?)`, [generation]);
|
|
@@ -5339,6 +5676,8 @@ function deleteDerivedRows(db, pageId) {
|
|
|
5339
5676
|
db.run("DELETE FROM links WHERE page_id = ?", [pageId]);
|
|
5340
5677
|
db.run("DELETE FROM tasks WHERE page_id = ?", [pageId]);
|
|
5341
5678
|
db.run("DELETE FROM task_concepts WHERE page_id = ?", [pageId]);
|
|
5679
|
+
db.run("DELETE FROM map_items WHERE page_id = ?", [pageId]);
|
|
5680
|
+
db.run("DELETE FROM map_concepts WHERE page_id = ?", [pageId]);
|
|
5342
5681
|
db.run("DELETE FROM passages WHERE page_id = ?", [pageId]);
|
|
5343
5682
|
db.run("DELETE FROM properties WHERE page_id = ?", [pageId]);
|
|
5344
5683
|
}
|
|
@@ -6529,7 +6868,7 @@ function cacheRoot(env) {
|
|
|
6529
6868
|
* recovered from a joined path (see there).
|
|
6530
6869
|
*/
|
|
6531
6870
|
var CACHE_FILE_NAME = `local-cache.v${CACHE_DB_VERSION}.bin`;
|
|
6532
|
-
var INDEX_FILE_NAME = `index.
|
|
6871
|
+
var INDEX_FILE_NAME = `index.v16.sqlite`;
|
|
6533
6872
|
var VECTORS_FILE_NAME = `vectors.v1.sqlite`;
|
|
6534
6873
|
function cacheFile(dir) {
|
|
6535
6874
|
return join(dir, CACHE_FILE_NAME);
|
|
@@ -10768,16 +11107,17 @@ async function hostRequest(endpoint, request, options = {}) {
|
|
|
10768
11107
|
}
|
|
10769
11108
|
/**
|
|
10770
11109
|
* The settings a publish reads, which the command line decides even when the graph's host runs
|
|
10771
|
-
* the publish: where the publish folders are remembered,
|
|
10772
|
-
*
|
|
10773
|
-
* own browser.
|
|
11110
|
+
* the publish: where the publish folders are remembered, which browser draws diagrams and maps,
|
|
11111
|
+
* and which style a map's picture is drawn over. The host was started from another environment
|
|
11112
|
+
* (an agent's), and a scheduled publish may name its own browser or its own map host.
|
|
10774
11113
|
*/
|
|
10775
11114
|
var PUBLISH_SETTINGS = [
|
|
10776
11115
|
"ETHERPK_MCP_PUBLISH_CONFIG",
|
|
10777
11116
|
"XDG_CONFIG_HOME",
|
|
10778
11117
|
"ETHERPK_CHROMIUM",
|
|
10779
11118
|
"PLAYWRIGHT_BROWSERS_PATH",
|
|
10780
|
-
"PLAYWRIGHT_CHROMIUM_EXECUTABLE"
|
|
11119
|
+
"PLAYWRIGHT_CHROMIUM_EXECUTABLE",
|
|
11120
|
+
"ETHERPK_MAP_STYLE_URL"
|
|
10781
11121
|
];
|
|
10782
11122
|
/** The publish settings in `env`, for a `publish` request. */
|
|
10783
11123
|
function publishSettings(env) {
|
|
@@ -11179,6 +11519,7 @@ async function writeSite(folder, bundle, options) {
|
|
|
11179
11519
|
//#endregion
|
|
11180
11520
|
//#region ../../packages/themes/src/index.ts
|
|
11181
11521
|
var files = /* #__PURE__ */ Object.assign({
|
|
11522
|
+
"../etherpk-al-folio/assets/favicon.svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 191.82 166.13\">\n <path fill=\"#0a0a0a\" fill-rule=\"evenodd\" d=\"M67.09,132.92,47.93,166.13H9.59L0,149.52,86.33,0H105.5L19.17,149.52H38.34l19.17-33.2h76.8l9.59,16.6Zm48-83.12,9.65,16.71H143.9L115.08,16.6q-24,41.57-48,83.12H143.9q14.37,24.9,28.76,49.8h-96l-9.59,16.61H182.24l9.58-16.61-38.34-66.4H96C95.85,82.9,113.39,52.73,115.08,49.8Z\"/>\n</svg>\n",
|
|
11182
11523
|
"../etherpk-al-folio/assets/search.js": "/*\n * Search for a Published Site: reads search.json (built over the publication's own documents\n * and nothing else) and scores documents by title and text matches. No dependencies, no\n * network beyond the one index file, and nothing runs until the search box is used. The same\n * script runs the sidebar toggle on small screens, so a page carries one script.\n */\n(function () {\n 'use strict';\n\n // -- Navigation toggle (small screens) -------------------------------------------------\n var toggle = document.querySelector('.nav-toggle');\n var backdrop = document.querySelector('.nav-backdrop');\n var nav = document.getElementById('site-nav');\n function setNavOpen(open) {\n var was = document.body.classList.contains('nav-open');\n document.body.classList.toggle('nav-open', open);\n if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n if (backdrop) backdrop.hidden = !open;\n // Focus follows the drawer: into its first link on open, back to the button on close,\n // so a keyboard or screen-reader user is never left on something now hidden.\n if (open && !was) {\n var first = nav.querySelector('a');\n if (first) first.focus();\n } else if (!open && was && toggle) {\n toggle.focus();\n }\n }\n if (toggle && nav) {\n toggle.addEventListener('click', function () { setNavOpen(!document.body.classList.contains('nav-open')); });\n if (backdrop) backdrop.addEventListener('click', function () { setNavOpen(false); });\n document.addEventListener('keydown', function (e) {\n if (e.key === 'Escape' && document.body.classList.contains('nav-open')) setNavOpen(false);\n });\n }\n\n // -- Search --------------------------------------------------------------------------\n var form = document.querySelector('.site-search');\n if (!form) return;\n var input = form.querySelector('input[type=\"search\"]');\n var results = form.querySelector('.site-search-results');\n var indexUrl = form.getAttribute('data-search-index') || 'search.json';\n var scriptUrl = form.getAttribute('data-search-script') || 'search-index.js';\n var index = null;\n var loading = null;\n /* True once neither route produced the index: the results then say so rather than \"nothing matches\". */\n var unavailable = false;\n\n /* The index as a script assigning window.etherpkSearchIndex: what a site opened from disk can\n load, since file:// refuses fetch but allows <script src>. */\n function loadScript() {\n return new Promise(function (resolve, reject) {\n if (window.etherpkSearchIndex) { resolve(window.etherpkSearchIndex); return; }\n var s = document.createElement('script');\n s.src = scriptUrl;\n s.onload = function () {\n if (window.etherpkSearchIndex) resolve(window.etherpkSearchIndex);\n else reject(new Error('no index'));\n };\n s.onerror = function () { reject(new Error('no script')); };\n document.head.appendChild(s);\n });\n }\n\n function load() {\n if (index) return Promise.resolve(index);\n if (!loading) {\n loading = fetch(indexUrl).then(function (r) {\n if (!r.ok) throw new Error(String(r.status));\n return r.json();\n }).catch(loadScript).then(function (data) {\n index = (data && data.documents) || [];\n return index;\n }).catch(function () { unavailable = true; index = []; return index; });\n }\n return loading;\n }\n\n function tokens(q) {\n return q.toLowerCase().split(/\\s+/).filter(function (t) { return t.length > 0; });\n }\n\n function score(doc, terms) {\n var title = (doc.title || '').toLowerCase();\n var text = (doc.text || '').toLowerCase();\n var s = 0;\n for (var i = 0; i < terms.length; i++) {\n var t = terms[i];\n if (title === t) s += 20;\n else if (title.indexOf(t) === 0) s += 10;\n else if (title.indexOf(t) !== -1) s += 6;\n var at = text.indexOf(t);\n if (at !== -1) s += 2;\n else if (title.indexOf(t) === -1) return 0; // every term must match somewhere\n }\n return s;\n }\n\n function snippet(doc, terms) {\n var text = doc.text || '';\n var lower = text.toLowerCase();\n var at = -1;\n for (var i = 0; i < terms.length && at === -1; i++) at = lower.indexOf(terms[i]);\n if (at === -1) return doc.excerpt || '';\n var start = Math.max(0, at - 60);\n var end = Math.min(text.length, at + 100);\n return (start > 0 ? '…' : '') + text.slice(start, end).replace(/\\s+/g, ' ') + (end < text.length ? '…' : '');\n }\n\n function escapeHtml(s) {\n return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n function render(list, q) {\n if (!q) { results.hidden = true; results.innerHTML = ''; return; }\n if (unavailable) {\n results.innerHTML = '<p class=\"site-search-empty\">The search index could not be loaded.</p>';\n results.hidden = false;\n return;\n }\n if (list.length === 0) {\n results.innerHTML = '<p class=\"site-search-empty\">Nothing matches \"' + escapeHtml(q) + '\".</p>';\n results.hidden = false;\n return;\n }\n results.innerHTML = list.map(function (hit) {\n return '<a class=\"site-search-hit\" role=\"option\" href=\"' + escapeHtml(hit.doc.url) + '\">' +\n '<span class=\"site-search-title\">' + escapeHtml(hit.doc.title) + '</span>' +\n '<span class=\"site-search-snippet\">' + escapeHtml(hit.snippet) + '</span></a>';\n }).join('');\n results.hidden = false;\n }\n\n var timer = null;\n function run() {\n var q = input.value.trim();\n var terms = tokens(q);\n if (terms.length === 0) { render([], ''); return; }\n load().then(function (docs) {\n var hits = [];\n for (var i = 0; i < docs.length; i++) {\n var s = score(docs[i], terms);\n if (s > 0) hits.push({ doc: docs[i], score: s, snippet: snippet(docs[i], terms) });\n }\n hits.sort(function (a, b) { return b.score - a.score || a.doc.title.localeCompare(b.doc.title); });\n render(hits.slice(0, 12), q);\n });\n }\n\n input.addEventListener('input', function () {\n clearTimeout(timer);\n timer = setTimeout(run, 120);\n });\n input.addEventListener('focus', function () { load(); });\n form.addEventListener('submit', function (e) {\n e.preventDefault();\n var first = results.querySelector('a');\n if (first) window.location.href = first.getAttribute('href');\n });\n document.addEventListener('keydown', function (e) {\n if (e.key === '/' && document.activeElement !== input && !/input|textarea/i.test(document.activeElement.tagName)) {\n e.preventDefault();\n input.focus();\n }\n if (e.key === 'Escape' && !results.hidden) { render([], ''); }\n });\n document.addEventListener('click', function (e) {\n if (!form.contains(e.target)) render([], '');\n });\n})();\n",
|
|
11183
11524
|
"../etherpk-al-folio/assets/theme.css": "/* EtherPK's al-folio: after Jekyll's al-folio (alshedivat and contributors, MIT), written afresh\n for EtherPK's view - the academic personal site: a slim navbar with the name on the left and\n lower-case menu on the right, one theme colour for links and rules, an about page as the front\n page with a \"news\" list, plain post lists, and a footer that stays out of the way. System\n fonts, so the site loads nothing from anywhere. Classes the publisher emits and this file\n styles: .wikilink, .wikilink-missing, .task, .task-tag, .toc, .backlinks, .site-nav-list,\n .diagram, .math, .tok-* (code). */\n\n:root {\n --global-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n --global-font-mono: ui-monospace, \"Roboto Mono\", Menlo, Consolas, monospace;\n --global-bg-color: #ffffff;\n --global-code-bg-color: #fafafa;\n --global-text-color: #000000;\n --global-text-color-light: #828282;\n --global-theme-color: #b509ac;\n --global-hover-color: #b509ac;\n --global-hover-text-color: #ffffff;\n --global-footer-bg-color: #f2f3f3;\n --global-footer-text-color: #828282;\n --global-footer-link-color: #000000;\n --global-distill-app-color: #828282;\n --global-divider-color: rgba(0, 0, 0, 0.1);\n --global-card-bg-color: #ffffff;\n --global-highlight-color: #fff3a3;\n --global-missing-color: #b45309;\n --max-content-width: 800px;\n}\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --global-bg-color: #1c1c1d;\n --global-code-bg-color: #2c2c2d;\n --global-text-color: #e8e8e8;\n --global-text-color-light: #e8e8e8;\n --global-theme-color: #2698ba;\n --global-hover-color: #2698ba;\n --global-hover-text-color: #ffffff;\n --global-footer-bg-color: #2c2c2d;\n --global-footer-text-color: #e8e8e8;\n --global-footer-link-color: #ffffff;\n --global-divider-color: #424246;\n --global-card-bg-color: #212529;\n --global-highlight-color: #6b5a00;\n --global-missing-color: #f59e0b;\n }\n}\n:root[data-theme=\"dark\"] {\n --global-bg-color: #1c1c1d;\n --global-code-bg-color: #2c2c2d;\n --global-text-color: #e8e8e8;\n --global-text-color-light: #e8e8e8;\n --global-theme-color: #2698ba;\n --global-hover-color: #2698ba;\n --global-hover-text-color: #ffffff;\n --global-footer-bg-color: #2c2c2d;\n --global-footer-text-color: #e8e8e8;\n --global-footer-link-color: #ffffff;\n --global-divider-color: #424246;\n --global-card-bg-color: #212529;\n --global-highlight-color: #6b5a00;\n --global-missing-color: #f59e0b;\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { font-size: 16px; }\nbody {\n margin: 0;\n display: flex;\n flex-direction: column;\n min-height: 100vh;\n font-family: var(--global-font-family);\n line-height: 1.5;\n background: var(--global-bg-color);\n color: var(--global-text-color);\n -webkit-font-smoothing: antialiased;\n}\na { color: var(--global-theme-color); text-decoration: none; }\na:hover { color: var(--global-hover-color); text-decoration: underline; }\nimg { max-width: 100%; height: auto; }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--global-bg-color); padding: 8px 16px; z-index: 100; }\n.skip-link:focus { left: 16px; top: 16px; }\n.container { width: 100%; max-width: 1000px; margin: 0 auto; padding: 0 15px; }\n\n/* -- Navbar ----------------------------------------------------------------------------- */\n.navbar {\n position: sticky;\n top: 0;\n z-index: 20;\n background: var(--global-bg-color);\n border-bottom: 1px solid var(--global-divider-color);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n}\n.navbar-inner { display: flex; align-items: center; justify-content: space-between; gap: 20px; max-width: 1000px; margin: 0 auto; padding: 10px 15px; }\n.navbar-brand { display: inline-flex; align-items: center; gap: 8px; font-size: 1.25rem; font-weight: 400; color: var(--global-text-color); }\n.navbar-brand:hover { color: var(--global-hover-color); text-decoration: none; }\n.site-logo { flex-shrink: 0; }\n.navbar-nav { display: flex; align-items: center; gap: 20px; }\n.site-nav-list { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; gap: 4px; }\n.site-nav-list li { position: relative; }\n.site-nav-list li > a, .nav-group-label { display: block; padding: 8px 12px; color: var(--global-text-color); font-size: 1rem; text-transform: lowercase; }\n.site-nav-list li > a:hover { color: var(--global-hover-color); text-decoration: none; }\n.site-nav-list .nav-current > a { font-weight: 700; }\n.site-nav-list .nav-current > a::after { content: \"\"; display: block; width: 100%; height: 2px; margin-top: 2px; background: var(--global-theme-color); }\n.nav-group-label { color: var(--global-text-color-light); cursor: default; }\n.site-nav-list .nav-open > a, .site-nav-list .nav-open > .nav-group-label { color: var(--global-theme-color); }\n/* A nested outline level drops down under its parent, on hover or keyboard focus. */\n.site-nav-list .site-nav-list {\n display: none;\n position: absolute;\n top: 100%;\n left: 0;\n flex-direction: column;\n align-items: stretch;\n min-width: 12rem;\n padding: 6px 0;\n background: var(--global-bg-color);\n border: 1px solid var(--global-divider-color);\n border-radius: 4px;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);\n z-index: 25;\n}\n.site-nav-list li:hover > .site-nav-list, .site-nav-list li:focus-within > .site-nav-list { display: flex; }\n.site-nav-list .site-nav-list a { font-size: 0.95rem; white-space: nowrap; }\n.site-nav-list .site-nav-list .nav-current > a::after { display: none; }\n.theme-toggle { display: inline-flex; align-items: center; padding: 6px; border: 0; background: none; color: var(--global-text-color); cursor: pointer; }\n.theme-toggle:hover { color: var(--global-hover-color); }\n.theme-toggle .sun { display: none; }\n:root[data-theme=\"dark\"] .theme-toggle .sun { display: block; }\n:root[data-theme=\"dark\"] .theme-toggle .moon { display: none; }\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) .theme-toggle .sun { display: block; }\n :root:not([data-theme=\"light\"]) .theme-toggle .moon { display: none; }\n}\n.nav-toggle, .nav-backdrop { display: none; }\n.site-search { position: relative; }\n.site-search input {\n width: 11rem;\n padding: 6px 10px;\n border: 1px solid var(--global-divider-color);\n border-radius: 4px;\n background: var(--global-bg-color);\n color: var(--global-text-color);\n font: inherit;\n font-size: 0.95rem;\n}\n.site-search input:focus { outline: 2px solid var(--global-theme-color); outline-offset: 1px; }\n.site-search-results {\n position: absolute;\n right: 0;\n top: calc(100% + 6px);\n width: min(26rem, 90vw);\n max-height: 24rem;\n overflow: auto;\n background: var(--global-bg-color);\n border: 1px solid var(--global-divider-color);\n border-radius: 4px;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 8px 12px; color: var(--global-text-color); border-bottom: 1px solid var(--global-divider-color); }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--global-code-bg-color); text-decoration: none; }\n.site-search-title { display: block; font-weight: 700; }\n.site-search-snippet { display: block; color: var(--global-text-color-light); font-size: 0.9rem; }\n.site-search-empty { margin: 0; padding: 8px 12px; color: var(--global-text-color-light); }\n\n/* -- Content ---------------------------------------------------------------------------- */\n.content { flex: 1; padding: 40px 0 60px; }\n.post-header { margin-bottom: 30px; }\n.post-title { margin: 0 0 6px; font-size: 2.5rem; font-weight: 300; line-height: 1.2; }\n.post-title .wikilink { color: inherit; }\n.post-meta { margin: 0; color: var(--global-text-color-light); font-size: 0.9rem; }\n.post-content { font-size: 1rem; }\n.post-content h1, .post-content h2, .post-content h3, .post-content h4 { margin: 2rem 0 0.75rem; font-weight: 500; line-height: 1.2; }\n.post-content h2 { font-size: 1.75rem; }\n.post-content h3 { font-size: 1.4rem; }\n.post-content h4 { font-size: 1.15rem; }\n.post-content p { margin: 0 0 1rem; }\n.post-content ul, .post-content ol { padding-left: 1.5rem; margin: 0 0 1rem; }\n.post-content li { margin: 0.2rem 0; }\n.post-content img { margin: 1rem 0; border-radius: 4px; }\n.post-content .diagram { margin: 1rem 0; text-align: center; }\n.post-content .diagram svg { max-width: 100%; height: auto; }\n.post-content blockquote { margin: 1rem 0; padding: 0.5rem 1rem; border-left: 5px solid var(--global-theme-color); color: var(--global-text-color-light); font-size: 1.1rem; }\n.post-content hr { border: 0; border-top: 1px solid var(--global-divider-color); margin: 2rem 0; }\n.post-content table { width: 100%; border-collapse: collapse; margin: 1rem 0; }\n.post-content th, .post-content td { padding: 0.5rem 0.75rem; border-top: 1px solid var(--global-divider-color); text-align: left; }\n.post-content thead th { border-bottom: 2px solid var(--global-divider-color); }\n.post-content code { padding: 2px 5px; background: var(--global-code-bg-color); border-radius: 3px; font-family: var(--global-font-mono); font-size: 0.9em; color: var(--global-theme-color); }\n.post-content pre { margin: 1rem 0; padding: 1rem; background: var(--global-code-bg-color); border: 1px solid var(--global-divider-color); border-radius: 4px; overflow-x: auto; font-family: var(--global-font-mono); font-size: 0.9rem; line-height: 1.5; }\n.post-content pre code { padding: 0; background: none; color: inherit; font-size: inherit; }\n.math-block { overflow-x: auto; margin: 1rem 0; }\nmark { background: var(--global-highlight-color); color: inherit; padding: 0 0.15em; border-radius: 2px; }\n\n/* Wikilinks and tasks. */\na.wikilink { color: var(--global-theme-color); }\na.wikilink-missing { color: var(--global-missing-color); text-decoration: underline dotted; }\nli.task { list-style: none; margin-left: -1.5rem; }\nli.task input[type=\"checkbox\"] { accent-color: var(--global-theme-color); margin-right: 6px; vertical-align: -0.1em; }\nli.task.done { color: var(--global-text-color-light); }\n.task-tag { display: inline-block; padding: 0 6px; margin-right: 4px; border: 1px solid var(--global-theme-color); border-radius: 3px; color: var(--global-theme-color); font-size: 0.8rem; font-weight: 500; }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* The table of contents, quietly boxed. */\n.toc { margin: 0 0 2rem; }\n.toc details { border: 1px solid var(--global-divider-color); border-radius: 4px; padding: 0.5rem 1rem; }\n.toc summary { cursor: pointer; font-weight: 500; }\n.toc ul { list-style: none; margin: 0.5rem 0 0; padding: 0; }\n.toc li { margin: 0.2rem 0; }\n.toc li.toc-level-3 { padding-left: 1rem; }\n.toc li.toc-level-4 { padding-left: 2rem; }\n\n/* The about page: news like al-folio's, a table with dates in the first column. */\n.about .post-title { font-size: 2.5rem; }\n.news { margin-top: 2.5rem; }\n.news h2, .posts h2 { font-size: 1.5rem; font-weight: 400; text-transform: lowercase; margin: 0 0 0.5rem; }\n.news h2 a { color: var(--global-text-color); }\n.news h2 a:hover { color: var(--global-hover-color); text-decoration: none; }\n.news-table { width: 100%; border-collapse: collapse; }\n.news-table th, .news-table td { padding: 0.35rem 0.5rem; border-top: 1px solid var(--global-divider-color); vertical-align: top; text-align: left; }\n.news-table th { width: 8rem; font-weight: 700; color: var(--global-text-color-light); white-space: nowrap; }\n.news-excerpt { color: var(--global-text-color-light); }\n.posts { margin-top: 2.5rem; }\n\n/* Post lists, the blog index. */\n.post-list { list-style: none; margin: 0; padding: 0; }\n.post-list li { padding: 1rem 0; border-bottom: 1px solid var(--global-divider-color); }\n.post-list li:last-child { border-bottom: 0; }\n.post-list h3 { margin: 0 0 0.25rem; font-size: 1.4rem; font-weight: 400; }\n/* The contents page of a docs site: the outline as a tree, then any page it leaves out. */\n.page-index-list { list-style: none; margin: 0; padding: 0; }\n.page-index-list .page-index-list { padding-left: 1.5rem; margin-top: 0.3rem; }\n.page-index-list li { margin: 0.3rem 0; }\n.page-index-group-label { color: var(--global-text-color-light); text-transform: lowercase; }\n.page-index-other { margin: 1.5rem 0 0.25rem; font-size: 1.1rem; font-weight: 400; color: var(--global-text-color-light); text-transform: lowercase; }\n/* The posts archive: a year, then one line per post. */\n.archive-year { margin: 1.5rem 0 0.25rem; font-size: 1.1rem; font-weight: 400; color: var(--global-text-color-light); }\n.archive-list li { padding: 0.35rem 0; border-bottom: 0; }\n.archive-list time { color: var(--global-text-color-light); margin-right: 0.75rem; font-variant-numeric: tabular-nums; }\n.all-posts { margin: 1rem 0 0; }\n.post-list h3 a { color: var(--global-text-color); }\n.post-list h3 a:hover { color: var(--global-hover-color); text-decoration: none; }\n.post-list p { margin: 0 0 0.25rem; }\n.post-list-empty { color: var(--global-text-color-light); }\n.post-nav { display: flex; justify-content: space-between; gap: 1rem; margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--global-divider-color); }\n.post-nav .newer { margin-left: auto; }\n\n/* Linked references. */\n.backlinks { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--global-divider-color); }\n.backlinks h2 { margin: 0 0 0.75rem; font-size: 1.25rem; font-weight: 500; }\n.backlink-list { list-style: none; margin: 0; padding: 0; }\n.backlink-source { margin: 0 0 0.75rem; }\n.backlink-source > a { font-weight: 500; }\n.backlink-source ul { list-style: none; margin: 0.25rem 0 0; padding: 0 0 0 1rem; border-left: 2px solid var(--global-divider-color); }\n.backlink-context { margin: 0.15rem 0; color: var(--global-text-color-light); font-size: 0.95rem; }\n\n/* -- Footer ----------------------------------------------------------------------------- */\n.footer { background: var(--global-footer-bg-color); color: var(--global-footer-text-color); font-size: 0.85rem; text-align: center; padding: 1rem 0; }\n.footer a { color: var(--global-footer-link-color); }\n.footer a:hover { color: var(--global-hover-color); }\n\n/* Code tokens (lezer classHighlighter names). */\n.tok-keyword { color: #0a7; }\n.tok-string, .tok-string2 { color: #690; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: #999; font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: #905; }\n.tok-function, .tok-definition.tok-variableName { color: #c60; }\n.tok-typeName, .tok-className, .tok-namespace { color: #b58900; }\n.tok-operator, .tok-punctuation { color: #555; }\n.tok-propertyName, .tok-attributeName { color: #268bd2; }\n.tok-tagName { color: #22863a; }\n.tok-invalid { color: #d00; }\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) .tok-keyword { color: #4ec9b0; }\n :root:not([data-theme=\"light\"]) .tok-string, :root:not([data-theme=\"light\"]) .tok-string2 { color: #b5cea8; }\n :root:not([data-theme=\"light\"]) .tok-number, :root:not([data-theme=\"light\"]) .tok-atom { color: #d19a66; }\n :root:not([data-theme=\"light\"]) .tok-function { color: #dcdcaa; }\n :root:not([data-theme=\"light\"]) .tok-typeName, :root:not([data-theme=\"light\"]) .tok-className { color: #e5c07b; }\n :root:not([data-theme=\"light\"]) .tok-operator, :root:not([data-theme=\"light\"]) .tok-punctuation { color: #abb2bf; }\n :root:not([data-theme=\"light\"]) .tok-propertyName { color: #61afef; }\n :root:not([data-theme=\"light\"]) .tok-tagName { color: #98c379; }\n}\n:root[data-theme=\"dark\"] .tok-keyword { color: #4ec9b0; }\n:root[data-theme=\"dark\"] .tok-string, :root[data-theme=\"dark\"] .tok-string2 { color: #b5cea8; }\n:root[data-theme=\"dark\"] .tok-number, :root[data-theme=\"dark\"] .tok-atom { color: #d19a66; }\n:root[data-theme=\"dark\"] .tok-function { color: #dcdcaa; }\n:root[data-theme=\"dark\"] .tok-typeName, :root[data-theme=\"dark\"] .tok-className { color: #e5c07b; }\n:root[data-theme=\"dark\"] .tok-operator, :root[data-theme=\"dark\"] .tok-punctuation { color: #abb2bf; }\n:root[data-theme=\"dark\"] .tok-propertyName { color: #61afef; }\n:root[data-theme=\"dark\"] .tok-tagName { color: #98c379; }\n\n/* -- Small screens: the navbar collapses behind al-folio's three bars ------------------- */\n@media (max-width: 768px) {\n .nav-toggle {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 5px;\n width: 44px;\n height: 44px;\n padding: 10px;\n border: 1px solid var(--global-divider-color);\n border-radius: 4px;\n background: none;\n cursor: pointer;\n }\n .icon-bar { display: block; height: 2px; background: var(--global-text-color); border-radius: 1px; }\n .navbar-nav {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(20rem, 85vw);\n flex-direction: column;\n align-items: stretch;\n gap: 12px;\n padding: 30px 20px;\n background: var(--global-bg-color);\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);\n transform: translateX(100%);\n visibility: hidden;\n transition: transform 160ms ease-out, visibility 0s linear 160ms;\n z-index: 40;\n overflow-y: auto;\n }\n body.nav-open .navbar-nav { transform: translateX(0); visibility: visible; transition: transform 160ms ease-out; }\n body.nav-open .nav-backdrop { display: block; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.35); z-index: 35; }\n .site-nav-list { flex-direction: column; align-items: stretch; }\n /* Inside the drawer every level is simply listed. */\n .site-nav-list .site-nav-list { display: flex; position: static; padding-left: 12px; border: 0; box-shadow: none; background: none; }\n .site-search input { width: 100%; }\n .site-search-results { left: 0; right: 0; width: auto; }\n .post-title, .about .post-title { font-size: 2rem; }\n}\n@media (prefers-reduced-motion: reduce) {\n .navbar-nav, body.nav-open .navbar-nav { transition: none; }\n}\n",
|
|
11184
11525
|
"../etherpk-al-folio/assets/theme.js": "/*\n * The light / dark toggle: a `.theme-toggle` button flips the page between the two and the\n * choice is remembered in this browser; with no choice made the page follows the system. The\n * stylesheet keys on `data-theme` on <html>, and shell-top applies the remembered choice before\n * the first paint so a dark reader never sees a light flash.\n */\n(function () {\n 'use strict';\n var KEY = 'etherpk-theme';\n var root = document.documentElement;\n function isDark() {\n var chosen = root.getAttribute('data-theme');\n if (chosen === 'dark' || chosen === 'light') return chosen === 'dark';\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n function apply(theme) {\n if (theme === 'dark' || theme === 'light') root.setAttribute('data-theme', theme);\n else root.removeAttribute('data-theme');\n var buttons = document.querySelectorAll('.theme-toggle');\n for (var i = 0; i < buttons.length; i++) buttons[i].setAttribute('aria-pressed', isDark() ? 'true' : 'false');\n }\n try { apply(localStorage.getItem(KEY)); } catch (_e) { apply(null); }\n var toggles = document.querySelectorAll('.theme-toggle');\n for (var i = 0; i < toggles.length; i++) {\n toggles[i].addEventListener('click', function () {\n var next = isDark() ? 'light' : 'dark';\n apply(next);\n try { localStorage.setItem(KEY, next); } catch (_e) { /* private mode: the choice lasts the page */ }\n });\n }\n})();\n",
|
|
@@ -11190,6 +11531,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11190
11531
|
"../etherpk-al-folio/partials/after-content.html": "",
|
|
11191
11532
|
"../etherpk-al-folio/partials/backlinks.html": " {{#page.hasBacklinks}}\n <section class=\"backlinks\" aria-labelledby=\"backlinks-heading\">\n <h2 id=\"backlinks-heading\">Linked references</h2>\n <ul class=\"backlink-list\">\n {{#page.backlinks}}\n <li class=\"backlink-source\">\n <a href=\"{{url}}\">{{title}}</a>\n <ul>\n {{#refs}}<li class=\"backlink-context\">{{{contextHtml}}}</li>{{/refs}}\n </ul>\n </li>\n {{/page.backlinks}}\n </ul>\n </section>\n {{/page.hasBacklinks}}\n",
|
|
11192
11533
|
"../etherpk-al-folio/partials/before-content.html": "",
|
|
11534
|
+
"../etherpk-al-folio/partials/favicon.html": "<!-- The EtherPK icon in the browser tab: assets/favicon.svg, the same file the EtherPK apps use. Fill the \"favicon\" include with your own <link rel=\"icon\"> tag to replace it. -->\n<link rel=\"icon\" href=\"theme/favicon.svg\" type=\"image/svg+xml\">\n",
|
|
11193
11535
|
"../etherpk-al-folio/partials/footer.html": " <footer class=\"footer\">\n <div class=\"container\">\n © {{site.year}} {{site.title}}. Published with <a href=\"https://etherpk.com\" rel=\"noopener\">EtherPK</a>.{{#site.hasFeed}} <a href=\"feed.xml\">Feed</a>.{{/site.hasFeed}}\n </div>\n </footer>\n",
|
|
11194
11536
|
"../etherpk-al-folio/partials/head.html": "",
|
|
11195
11537
|
"../etherpk-al-folio/partials/header.html": " <header class=\"navbar\">\n <div class=\"navbar-inner\">\n <a class=\"navbar-brand site-title\" href=\"index.html\">{{> logo}}<span class=\"site-title-text\">{{site.title}}</span></a>\n <button class=\"nav-toggle navbar-toggler\" type=\"button\" aria-controls=\"site-nav\" aria-expanded=\"false\" aria-label=\"Menu\"><span class=\"icon-bar top-bar\"></span><span class=\"icon-bar middle-bar\"></span><span class=\"icon-bar bottom-bar\"></span></button>\n <div class=\"nav-backdrop\" hidden></div>\n <nav id=\"site-nav\" class=\"navbar-nav site-nav\" aria-label=\"Site\">\n <ul class=\"site-nav-list\">\n{{#nav}}{{> nav-item}}{{/nav}}\n </ul>\n <form class=\"site-search\" role=\"search\" data-search-index=\"{{site.search.url}}\" data-search-script=\"{{site.search.scriptUrl}}\">\n <label class=\"visually-hidden\" for=\"site-search-input\">Search</label>\n <input id=\"site-search-input\" type=\"search\" name=\"q\" placeholder=\"Search\" autocomplete=\"off\">\n <div class=\"site-search-results\" role=\"listbox\" hidden></div>\n </form>\n <button class=\"theme-toggle\" type=\"button\" aria-label=\"Toggle light and dark\" aria-pressed=\"false\">\n <svg class=\"moon\" viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg>\n <svg class=\"sun\" viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/></svg>\n </button>\n </nav>\n </div>\n </header>\n",
|
|
@@ -11197,12 +11539,13 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11197
11539
|
"../etherpk-al-folio/partials/nav-item.html": " <li class=\"{{#current}}nav-current {{/current}}{{#open}}nav-open {{/open}}{{^href}}nav-group{{/href}}\">\n {{#href}}<a href=\"{{href}}\"{{#current}} aria-current=\"page\"{{/current}}{{#external}} rel=\"noopener\"{{/external}}>{{label}}</a>{{/href}}\n {{^href}}<span class=\"nav-group-label\" tabindex=\"0\">{{{labelHtml}}}</span>{{/href}}\n {{#hasChildren}}<ul class=\"site-nav-list\">{{#children}}{{> nav-item}}{{/children}}</ul>{{/hasChildren}}\n </li>\n",
|
|
11198
11540
|
"../etherpk-al-folio/partials/scripts.html": "",
|
|
11199
11541
|
"../etherpk-al-folio/partials/shell-bottom.html": " {{> after-content}}\n {{> backlinks}}\n </main>\n </div>\n {{> footer}}\n {{> theme-scripts}}\n {{> scripts}}\n</body>\n</html>\n",
|
|
11200
|
-
"../etherpk-al-folio/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} | {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n <script>try{var t=localStorage.getItem('etherpk-theme');if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}<\/script>\n {{> head}}\n</head>\n<body class=\"layout-al-folio{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <div class=\"container\">\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11542
|
+
"../etherpk-al-folio/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} | {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n <script>try{var t=localStorage.getItem('etherpk-theme');if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}<\/script>\n {{> favicon}}\n {{> head}}\n</head>\n<body class=\"layout-al-folio{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <div class=\"container\">\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11201
11543
|
"../etherpk-al-folio/partials/theme-scripts.html": " <script src=\"theme/search.js\" defer><\/script>\n <script src=\"theme/theme.js\" defer><\/script>\n",
|
|
11202
11544
|
"../etherpk-al-folio/partials/toc.html": " {{#page.hasToc}}\n <nav class=\"toc\" aria-label=\"Table of contents\">\n <details>\n <summary>Contents</summary>\n <ul>\n {{#page.toc}}<li class=\"toc-level-{{level}}\"><a href=\"#{{id}}\">{{text}}</a></li>{{/page.toc}}\n </ul>\n </details>\n </nav>\n {{/page.hasToc}}\n",
|
|
11203
|
-
"../etherpk-al-folio/theme.json": "{\n \"name\": \"etherpk-al-folio\",\n \"title\": \"EtherPK al-folio\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Jekyll's al-folio: an academic personal site with a slim top navbar, an about page as the front page, a news list from the journal, a plain post list and a light and dark toggle.\",\n \"kinds\": [\"docs\", \"blog\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The mark before the site title in the navbar: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The navbar. Replaces the theme's brand, menu, search box and theme toggle.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the theme toggle): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/search.js\", \"assets/theme.js\"\n ]\n}\n",
|
|
11545
|
+
"../etherpk-al-folio/theme.json": "{\n \"name\": \"etherpk-al-folio\",\n \"title\": \"EtherPK al-folio\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Jekyll's al-folio: an academic personal site with a slim top navbar, an about page as the front page, a news list from the journal, a plain post list and a light and dark toggle.\",\n \"kinds\": [\"docs\", \"blog\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The mark before the site title in the navbar: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"favicon\", \"description\": \"The icon in the browser tab: a <link rel=\\\"icon\\\"> tag. Raw HTML in the page body. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The navbar. Replaces the theme's brand, menu, search box and theme toggle.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the theme toggle): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/favicon.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/favicon.svg\", \"assets/search.js\", \"assets/theme.js\"\n ]\n}\n",
|
|
11546
|
+
"../etherpk-blog/assets/favicon.svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 191.82 166.13\">\n <path fill=\"#0a0a0a\" fill-rule=\"evenodd\" d=\"M67.09,132.92,47.93,166.13H9.59L0,149.52,86.33,0H105.5L19.17,149.52H38.34l19.17-33.2h76.8l9.59,16.6Zm48-83.12,9.65,16.71H143.9L115.08,16.6q-24,41.57-48,83.12H143.9q14.37,24.9,28.76,49.8h-96l-9.59,16.61H182.24l9.58-16.61-38.34-66.4H96C95.85,82.9,113.39,52.73,115.08,49.8Z\"/>\n</svg>\n",
|
|
11204
11547
|
"../etherpk-blog/assets/search.js": "/*\n * Search for a Published Site: reads search.json (built over the publication's own documents\n * and nothing else) and scores documents by title and text matches. No dependencies, no\n * network beyond the one index file, and nothing runs until the search box is used. The same\n * script runs the sidebar toggle on small screens, so a page carries one script.\n */\n(function () {\n 'use strict';\n\n // -- Navigation toggle (small screens) -------------------------------------------------\n var toggle = document.querySelector('.nav-toggle');\n var backdrop = document.querySelector('.nav-backdrop');\n var nav = document.getElementById('site-nav');\n function setNavOpen(open) {\n var was = document.body.classList.contains('nav-open');\n document.body.classList.toggle('nav-open', open);\n if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n if (backdrop) backdrop.hidden = !open;\n // Focus follows the drawer: into its first link on open, back to the button on close,\n // so a keyboard or screen-reader user is never left on something now hidden.\n if (open && !was) {\n var first = nav.querySelector('a');\n if (first) first.focus();\n } else if (!open && was && toggle) {\n toggle.focus();\n }\n }\n if (toggle && nav) {\n toggle.addEventListener('click', function () { setNavOpen(!document.body.classList.contains('nav-open')); });\n if (backdrop) backdrop.addEventListener('click', function () { setNavOpen(false); });\n document.addEventListener('keydown', function (e) {\n if (e.key === 'Escape' && document.body.classList.contains('nav-open')) setNavOpen(false);\n });\n }\n\n // -- Search --------------------------------------------------------------------------\n var form = document.querySelector('.site-search');\n if (!form) return;\n var input = form.querySelector('input[type=\"search\"]');\n var results = form.querySelector('.site-search-results');\n var indexUrl = form.getAttribute('data-search-index') || 'search.json';\n var scriptUrl = form.getAttribute('data-search-script') || 'search-index.js';\n var index = null;\n var loading = null;\n /* True once neither route produced the index: the results then say so rather than \"nothing matches\". */\n var unavailable = false;\n\n /* The index as a script assigning window.etherpkSearchIndex: what a site opened from disk can\n load, since file:// refuses fetch but allows <script src>. */\n function loadScript() {\n return new Promise(function (resolve, reject) {\n if (window.etherpkSearchIndex) { resolve(window.etherpkSearchIndex); return; }\n var s = document.createElement('script');\n s.src = scriptUrl;\n s.onload = function () {\n if (window.etherpkSearchIndex) resolve(window.etherpkSearchIndex);\n else reject(new Error('no index'));\n };\n s.onerror = function () { reject(new Error('no script')); };\n document.head.appendChild(s);\n });\n }\n\n function load() {\n if (index) return Promise.resolve(index);\n if (!loading) {\n loading = fetch(indexUrl).then(function (r) {\n if (!r.ok) throw new Error(String(r.status));\n return r.json();\n }).catch(loadScript).then(function (data) {\n index = (data && data.documents) || [];\n return index;\n }).catch(function () { unavailable = true; index = []; return index; });\n }\n return loading;\n }\n\n function tokens(q) {\n return q.toLowerCase().split(/\\s+/).filter(function (t) { return t.length > 0; });\n }\n\n function score(doc, terms) {\n var title = (doc.title || '').toLowerCase();\n var text = (doc.text || '').toLowerCase();\n var s = 0;\n for (var i = 0; i < terms.length; i++) {\n var t = terms[i];\n if (title === t) s += 20;\n else if (title.indexOf(t) === 0) s += 10;\n else if (title.indexOf(t) !== -1) s += 6;\n var at = text.indexOf(t);\n if (at !== -1) s += 2;\n else if (title.indexOf(t) === -1) return 0; // every term must match somewhere\n }\n return s;\n }\n\n function snippet(doc, terms) {\n var text = doc.text || '';\n var lower = text.toLowerCase();\n var at = -1;\n for (var i = 0; i < terms.length && at === -1; i++) at = lower.indexOf(terms[i]);\n if (at === -1) return doc.excerpt || '';\n var start = Math.max(0, at - 60);\n var end = Math.min(text.length, at + 100);\n return (start > 0 ? '…' : '') + text.slice(start, end).replace(/\\s+/g, ' ') + (end < text.length ? '…' : '');\n }\n\n function escapeHtml(s) {\n return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n function render(list, q) {\n if (!q) { results.hidden = true; results.innerHTML = ''; return; }\n if (unavailable) {\n results.innerHTML = '<p class=\"site-search-empty\">The search index could not be loaded.</p>';\n results.hidden = false;\n return;\n }\n if (list.length === 0) {\n results.innerHTML = '<p class=\"site-search-empty\">Nothing matches \"' + escapeHtml(q) + '\".</p>';\n results.hidden = false;\n return;\n }\n results.innerHTML = list.map(function (hit) {\n return '<a class=\"site-search-hit\" role=\"option\" href=\"' + escapeHtml(hit.doc.url) + '\">' +\n '<span class=\"site-search-title\">' + escapeHtml(hit.doc.title) + '</span>' +\n '<span class=\"site-search-snippet\">' + escapeHtml(hit.snippet) + '</span></a>';\n }).join('');\n results.hidden = false;\n }\n\n var timer = null;\n function run() {\n var q = input.value.trim();\n var terms = tokens(q);\n if (terms.length === 0) { render([], ''); return; }\n load().then(function (docs) {\n var hits = [];\n for (var i = 0; i < docs.length; i++) {\n var s = score(docs[i], terms);\n if (s > 0) hits.push({ doc: docs[i], score: s, snippet: snippet(docs[i], terms) });\n }\n hits.sort(function (a, b) { return b.score - a.score || a.doc.title.localeCompare(b.doc.title); });\n render(hits.slice(0, 12), q);\n });\n }\n\n input.addEventListener('input', function () {\n clearTimeout(timer);\n timer = setTimeout(run, 120);\n });\n input.addEventListener('focus', function () { load(); });\n form.addEventListener('submit', function (e) {\n e.preventDefault();\n var first = results.querySelector('a');\n if (first) window.location.href = first.getAttribute('href');\n });\n document.addEventListener('keydown', function (e) {\n if (e.key === '/' && document.activeElement !== input && !/input|textarea/i.test(document.activeElement.tagName)) {\n e.preventDefault();\n input.focus();\n }\n if (e.key === 'Escape' && !results.hidden) { render([], ''); }\n });\n document.addEventListener('click', function (e) {\n if (!form.contains(e.target)) render([], '');\n });\n})();\n",
|
|
11205
|
-
"../etherpk-blog/assets/theme.css": "/* EtherPK blog theme. Tokens follow the app's own (agent blue, the gk surface scale); the font\n is the system stack so the site loads nothing from anywhere. Dark mode follows the reader's\n preference. Classes the publisher emits and this file styles: .wikilink, .wikilink-missing,\n .task, .task-tag, .toc, .backlinks, .site-nav-list, .diagram, .math, .tok-* (code). */\n\n:root {\n --font-sans: \"Inter\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n --agent-500: #007acc;\n --agent-600: #005f9e;\n --agent-100: #c2e3ff;\n --surface-0: #ffffff;\n --surface-1: #f9fafb;\n --surface-2: #e5e7eb;\n --text-strong: #030712;\n --text-default: #374151;\n --text-muted: #6b7280;\n --border-soft: rgba(3, 7, 18, 0.08);\n --border-strong: rgba(3, 7, 18, 0.16);\n --mark: #fff3a3;\n --missing: #b45309;\n --nav-width: 17rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --agent-500: #5ab3ff;\n --agent-600: #94ccff;\n --agent-100: #002e4f;\n --surface-0: #18181b;\n --surface-1: #1c1c20;\n --surface-2: #2a2a30;\n --text-strong: #fafafa;\n --text-default: #d4d4d8;\n --text-muted: #a1a1aa;\n --border-soft: rgba(255, 255, 255, 0.08);\n --border-strong: rgba(255, 255, 255, 0.16);\n --mark: #6b5a00;\n --missing: #f59e0b;\n }\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { font-family: var(--font-sans); -webkit-font-smoothing: antialiased; }\nbody {\n margin: 0;\n color: var(--text-default);\n background: var(--surface-0);\n line-height: 1.6;\n font-size: 16px;\n}\na { color: var(--agent-500); }\na:hover { color: var(--agent-600); }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--surface-1); padding: 0.5rem 1rem; z-index: 100; }\n.skip-link:focus { left: 1rem; top: 1rem; }\n\n/* -- Header ----------------------------------------------------------------------------- */\n.site-header {\n display: flex;\n align-items: center;\n gap: 1rem;\n padding: 0.75rem 1.5rem;\n border-bottom: 1px solid var(--border-soft);\n background: var(--surface-1);\n position: sticky;\n top: 0;\n z-index: 20;\n}\n.site-title { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; font-weight: 600; color: var(--text-strong); text-decoration: none; font-size: 1.05rem; }\n.site-title:hover { color: var(--agent-500); }\n.site-logo { flex-shrink: 0; }\n.site-title-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.site-search { position: relative; margin-left: auto; flex: 0 1 22rem; }\n.site-search input {\n width: 100%;\n padding: 0.45rem 0.75rem;\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n background: var(--surface-0);\n color: var(--text-default);\n font: inherit;\n}\n.site-search input:focus { outline: 2px solid var(--agent-500); outline-offset: 1px; }\n.site-search-results {\n position: absolute;\n top: calc(100% + 0.35rem);\n left: 0;\n right: 0;\n max-height: 24rem;\n overflow: auto;\n background: var(--surface-0);\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 0.55rem 0.75rem; text-decoration: none; color: var(--text-default); border-bottom: 1px solid var(--border-soft); }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--surface-1); }\n.site-search-title { display: block; font-weight: 600; color: var(--text-strong); }\n.site-search-snippet { display: block; font-size: 0.9rem; color: var(--text-muted); }\n.site-search-empty { margin: 0; padding: 0.6rem 0.75rem; color: var(--text-muted); }\n\n/* -- Shell: top navigation, centred column --------------------------------------------- */\n.site-header { display: block; padding: 1rem 1.5rem 0; position: static; }\n.site-header-row { display: flex; align-items: center; gap: 1rem; max-width: 46rem; margin: 0 auto; }\n.site-title { font-size: 1.35rem; }\n.site-nav { max-width: 46rem; margin: 0.75rem auto 0; }\n.site-nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; }\n.site-nav-list .site-nav-list { display: block; padding-left: 0.75rem; }\n.site-nav a, .site-nav .nav-group-label { color: var(--text-default); text-decoration: none; font-size: 0.95rem; }\n.site-nav a:hover { color: var(--text-strong); }\n.site-nav .nav-current > a { color: var(--agent-500); font-weight: 600; }\n.site-nav .nav-group-label { font-weight: 600; color: var(--text-strong); }\n.site-nav a .wikilink { color: inherit; text-decoration: none; }\n.nav-toggle, .nav-backdrop { display: none; }\n\n.content { padding: 2rem 1.5rem 4rem; max-width: 46rem; margin: 0 auto; }\n.post-header h1 { font-size: 2rem; line-height: 1.2; margin: 0 0 0.5rem; color: var(--text-strong); }\n.post-header h1 .wikilink { color: inherit; text-decoration: none; }\n.post-header h1 .wikilink:hover { text-decoration: underline; }\n.post-date { color: var(--text-muted); font-size: 0.95rem; }\n.post-body h2 { margin-top: 2rem; font-size: 1.4rem; color: var(--text-strong); }\n.post-body h3 { margin-top: 1.5rem; font-size: 1.15rem; color: var(--text-strong); }\n.post-body ul { padding-left: 1.4rem; }\n.post-body li { margin: 0.2rem 0; }\n.post-body img { max-width: 100%; height: auto; border-radius: 0.35rem; }\n.post-body table { border-collapse: collapse; margin: 1rem 0; width: 100%; }\n.post-body th, .post-body td { border: 1px solid var(--border-strong); padding: 0.4rem 0.6rem; text-align: left; }\n.post-body th { background: var(--surface-1); }\n.post-body blockquote { border-left: 3px solid var(--border-strong); margin: 1rem 0; padding: 0.25rem 1rem; color: var(--text-muted); }\n.post-body code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-1); padding: 0.1em 0.35em; border-radius: 0.25rem; }\n.post-body pre { background: var(--surface-1); border: 1px solid var(--border-soft); border-radius: 0.5rem; padding: 0.9rem 1rem; overflow-x: auto; }\n.post-body pre code { background: none; padding: 0; font-size: 0.88rem; }\nmark { background: var(--mark); color: inherit; padding: 0 0.15em; border-radius: 0.2em; }\n\n/* Wikilinks, as the editor colours them. A missing target is visibly missing. */\na.wikilink { text-decoration: none; border-bottom: 1px solid var(--agent-100); }\na.wikilink:hover { border-bottom-color: var(--agent-500); }\na.wikilink-missing { color: var(--missing); text-decoration: none; border-bottom: 1px dashed var(--missing); }\n\n/* Tasks and their badges. */\nli.task { list-style: none; margin-left: -1.4rem; padding-left: 0; }\nli.task input[type=\"checkbox\"] { margin-right: 0.35rem; vertical-align: -0.1em; }\nli.task.done { color: var(--text-muted); }\n.task-tag { display: inline-block; font-size: 0.8rem; font-weight: 600; padding: 0.05em 0.5em; border-radius: 999px; background: var(--surface-2); color: var(--text-strong); margin-right: 0.25rem; vertical-align: 0.1em; }\n.task-tag.priority-1 { background: #fee2e2; color: #991b1b; }\n.task-tag.priority-2 { background: #ffedd5; color: #9a3412; }\n.task-tag.priority-3 { background: #fef9c3; color: #854d0e; }\n.task-tag.waiting, .task-tag.doing { background: var(--agent-100); color: var(--agent-600); }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* Table of contents, diagrams, maths. */\n.toc { margin: 0 0 1.5rem; font-size: 0.95rem; }\n.toc summary { cursor: pointer; font-weight: 600; color: var(--text-strong); }\n.toc ul { list-style: none; padding-left: 0; margin: 0.4rem 0 0; }\n.toc li.toc-level-3 { padding-left: 1rem; }\n.toc li.toc-level-4 { padding-left: 2rem; }\n.diagram { margin: 1rem 0; text-align: center; }\n.diagram svg { max-width: 100%; height: auto; }\n.math-block { overflow-x: auto; margin: 1rem 0; }\n\n/* Linked references, beneath the content. */\n.backlinks { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border-soft); }\n.backlinks h2 { font-size: 1.1rem; color: var(--text-strong); margin: 0 0 0.75rem; }\n.backlink-list { list-style: none; padding: 0; margin: 0; }\n.backlink-source { margin: 0 0 0.9rem; }\n.backlink-source > a { font-weight: 600; }\n.backlink-source ul { list-style: none; padding-left: 1rem; margin: 0.25rem 0 0; border-left: 2px solid var(--border-soft); }\n.backlink-context { color: var(--text-muted); font-size: 0.95rem; margin: 0.15rem 0; }\n\n/* Archive and index lists. */\n.journal-archive, .page-index-list { list-style: none; padding: 0; }\n.journal-archive li { margin: 0 0 1rem; }\n.journal-archive time { color: var(--text-muted); margin-right: 0.5rem; font-variant-numeric: tabular-nums; }\n.archive-excerpt { margin: 0.2rem 0 0; color: var(--text-muted); }\n.archive-year { margin: 1.5rem 0 0.5rem; font-size: 1.1rem; }\n.all-posts { margin: 1.5rem 0 0; }\n\n.site-footer { border-top: 1px solid var(--border-soft); padding: 1rem 1.5rem; color: var(--text-muted); font-size: 0.9rem; text-align: center; }\n\n/* Code tokens (lezer classHighlighter names). */\n.tok-keyword { color: #0a7; }\n.tok-string, .tok-string2 { color: #690; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: #999; font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: #905; }\n.tok-function, .tok-definition.tok-variableName { color: #c60; }\n.tok-typeName, .tok-className, .tok-namespace { color: #b58900; }\n.tok-operator, .tok-punctuation { color: #555; }\n.tok-propertyName, .tok-attributeName { color: #268bd2; }\n.tok-tagName { color: #22863a; }\n.tok-invalid { color: #d00; }\n@media (prefers-color-scheme: dark) {\n .tok-keyword { color: #4ec9b0; }\n .tok-string, .tok-string2 { color: #b5cea8; }\n .tok-comment, .tok-lineComment, .tok-blockComment { color: #7f848e; }\n .tok-number, .tok-bool, .tok-null, .tok-atom { color: #d19a66; }\n .tok-function, .tok-definition.tok-variableName { color: #dcdcaa; }\n .tok-typeName, .tok-className, .tok-namespace { color: #e5c07b; }\n .tok-operator, .tok-punctuation { color: #abb2bf; }\n .tok-propertyName, .tok-attributeName { color: #61afef; }\n .tok-tagName { color: #98c379; }\n}\n\n/* -- Posts ------------------------------------------------------------------------------ */\n.post-summary { margin: 0 0 2rem; }\n.post-summary h2 { margin: 0 0 0.2rem; font-size: 1.35rem; }\n.post-summary h2 a { color: var(--text-strong); text-decoration: none; }\n.post-summary h2 a:hover { color: var(--agent-500); }\n.post-summary time { color: var(--text-muted); font-size: 0.9rem; font-variant-numeric: tabular-nums; }\n.post-summary p { margin: 0.4rem 0 0; color: var(--text-default); }\n.home-intro { margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid var(--border-soft); }\n.post-list-empty { color: var(--text-muted); }\n@media (max-width: 40rem) {\n .site-header-row { flex-wrap: wrap; }\n .site-search { flex-basis: 100%; margin-left: 0; }\n .content { padding: 1.25rem 1rem 3rem; }\n}\n",
|
|
11548
|
+
"../etherpk-blog/assets/theme.css": "/* EtherPK blog theme. Tokens follow the app's own (agent blue, the gk surface scale); the font\n is the system stack so the site loads nothing from anywhere. Dark mode follows the reader's\n preference. Classes the publisher emits and this file styles: .wikilink, .wikilink-missing,\n .task, .task-tag, .toc, .backlinks, .site-nav-list, .diagram, .math, .tok-* (code). */\n\n:root {\n --font-sans: \"Inter\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n --agent-500: #007acc;\n --agent-600: #005f9e;\n --agent-100: #c2e3ff;\n --surface-0: #ffffff;\n --surface-1: #f9fafb;\n --surface-2: #e5e7eb;\n --text-strong: #030712;\n --text-default: #374151;\n --text-muted: #6b7280;\n --border-soft: rgba(3, 7, 18, 0.08);\n --border-strong: rgba(3, 7, 18, 0.16);\n --mark: #fff3a3;\n --missing: #b45309;\n --nav-width: 17rem;\n /* The width of the text column. The header, the navigation and the post share it, so the\n site title lines up with the text beneath it. Pictures are capped to it as well. */\n --content-width: 52rem;\n --gutter: 1.5rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --agent-500: #5ab3ff;\n --agent-600: #94ccff;\n --agent-100: #002e4f;\n --surface-0: #18181b;\n --surface-1: #1c1c20;\n --surface-2: #2a2a30;\n --text-strong: #fafafa;\n --text-default: #d4d4d8;\n --text-muted: #a1a1aa;\n --border-soft: rgba(255, 255, 255, 0.08);\n --border-strong: rgba(255, 255, 255, 0.16);\n --mark: #6b5a00;\n --missing: #f59e0b;\n }\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { font-family: var(--font-sans); -webkit-font-smoothing: antialiased; }\nbody {\n margin: 0;\n color: var(--text-default);\n background: var(--surface-0);\n line-height: 1.6;\n font-size: 16px;\n}\na { color: var(--agent-500); }\na:hover { color: var(--agent-600); }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--surface-1); padding: 0.5rem 1rem; z-index: 100; }\n.skip-link:focus { left: 1rem; top: 1rem; }\n\n/* -- Header ----------------------------------------------------------------------------- */\n.site-header {\n display: flex;\n align-items: center;\n gap: 1rem;\n padding: 0.75rem 1.5rem;\n border-bottom: 1px solid var(--border-soft);\n background: var(--surface-1);\n position: sticky;\n top: 0;\n z-index: 20;\n}\n.site-title { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; font-weight: 600; color: var(--text-strong); text-decoration: none; font-size: 1.05rem; }\n.site-title:hover { color: var(--agent-500); }\n.site-logo { flex-shrink: 0; }\n.site-title-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.site-search { position: relative; margin-left: auto; flex: 0 1 22rem; }\n.site-search input {\n width: 100%;\n padding: 0.45rem 0.75rem;\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n background: var(--surface-0);\n color: var(--text-default);\n font: inherit;\n}\n.site-search input:focus { outline: 2px solid var(--agent-500); outline-offset: 1px; }\n.site-search-results {\n position: absolute;\n top: calc(100% + 0.35rem);\n left: 0;\n right: 0;\n max-height: 24rem;\n overflow: auto;\n background: var(--surface-0);\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 0.55rem 0.75rem; text-decoration: none; color: var(--text-default); border-bottom: 1px solid var(--border-soft); }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--surface-1); }\n.site-search-title { display: block; font-weight: 600; color: var(--text-strong); }\n.site-search-snippet { display: block; font-size: 0.9rem; color: var(--text-muted); }\n.site-search-empty { margin: 0; padding: 0.6rem 0.75rem; color: var(--text-muted); }\n\n/* -- Shell: top navigation, centred column --------------------------------------------- */\n.site-header { display: block; padding: 1rem var(--gutter) 0; position: static; }\n.site-header-row { display: flex; align-items: center; gap: 1rem; max-width: var(--content-width); margin: 0 auto; }\n.site-title { font-size: 1.35rem; }\n.site-nav { max-width: var(--content-width); margin: 0.75rem auto 0; }\n.site-nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; }\n.site-nav-list .site-nav-list { display: block; padding-left: 0.75rem; }\n.site-nav a, .site-nav .nav-group-label { color: var(--text-default); text-decoration: none; font-size: 0.95rem; }\n.site-nav a:hover { color: var(--text-strong); }\n.site-nav .nav-current > a { color: var(--agent-500); font-weight: 600; }\n.site-nav .nav-group-label { font-weight: 600; color: var(--text-strong); }\n.site-nav a .wikilink { color: inherit; text-decoration: none; }\n.nav-toggle, .nav-backdrop { display: none; }\n\n/* The padding sits outside the text column (border-box would take it from inside). */\n.content { padding: 2rem var(--gutter) 4rem; max-width: calc(var(--content-width) + 2 * var(--gutter)); margin: 0 auto; }\n.post-header h1 { font-size: 2rem; line-height: 1.2; margin: 0 0 0.5rem; color: var(--text-strong); }\n.post-header h1 .wikilink { color: inherit; text-decoration: none; }\n.post-header h1 .wikilink:hover { text-decoration: underline; }\n.post-date { color: var(--text-muted); font-size: 0.95rem; }\n.post-body h2 { margin-top: 2rem; font-size: 1.4rem; color: var(--text-strong); }\n.post-body h3 { margin-top: 1.5rem; font-size: 1.15rem; color: var(--text-strong); }\n.post-body ul { padding-left: 1.4rem; }\n.post-body li { margin: 0.2rem 0; }\n.post-body img { max-width: 100%; height: auto; border-radius: 0.35rem; }\n.post-body table { border-collapse: collapse; margin: 1rem 0; width: 100%; }\n.post-body th, .post-body td { border: 1px solid var(--border-strong); padding: 0.4rem 0.6rem; text-align: left; }\n.post-body th { background: var(--surface-1); }\n.post-body blockquote { border-left: 3px solid var(--border-strong); margin: 1rem 0; padding: 0.25rem 1rem; color: var(--text-muted); }\n.post-body code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-1); padding: 0.1em 0.35em; border-radius: 0.25rem; }\n.post-body pre { background: var(--surface-1); border: 1px solid var(--border-soft); border-radius: 0.5rem; padding: 0.9rem 1rem; overflow-x: auto; }\n.post-body pre code { background: none; padding: 0; font-size: 0.88rem; }\nmark { background: var(--mark); color: inherit; padding: 0 0.15em; border-radius: 0.2em; }\n\n/* Wikilinks, as the editor colours them. A missing target is visibly missing. */\na.wikilink { text-decoration: none; border-bottom: 1px solid var(--agent-100); }\na.wikilink:hover { border-bottom-color: var(--agent-500); }\na.wikilink-missing { color: var(--missing); text-decoration: none; border-bottom: 1px dashed var(--missing); }\n\n/* Tasks and their badges. */\nli.task { list-style: none; margin-left: -1.4rem; padding-left: 0; }\nli.task input[type=\"checkbox\"] { margin-right: 0.35rem; vertical-align: -0.1em; }\nli.task.done { color: var(--text-muted); }\n.task-tag { display: inline-block; font-size: 0.8rem; font-weight: 600; padding: 0.05em 0.5em; border-radius: 999px; background: var(--surface-2); color: var(--text-strong); margin-right: 0.25rem; vertical-align: 0.1em; }\n.task-tag.priority-1 { background: #fee2e2; color: #991b1b; }\n.task-tag.priority-2 { background: #ffedd5; color: #9a3412; }\n.task-tag.priority-3 { background: #fef9c3; color: #854d0e; }\n.task-tag.waiting, .task-tag.doing { background: var(--agent-100); color: var(--agent-600); }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* Table of contents, diagrams, maths. */\n.toc { margin: 0 0 1.5rem; font-size: 0.95rem; }\n.toc summary { cursor: pointer; font-weight: 600; color: var(--text-strong); }\n.toc ul { list-style: none; padding-left: 0; margin: 0.4rem 0 0; }\n.toc li.toc-level-3 { padding-left: 1rem; }\n.toc li.toc-level-4 { padding-left: 2rem; }\n.diagram { margin: 1rem 0; text-align: center; }\n.diagram svg { max-width: 100%; height: auto; }\n.math-block { overflow-x: auto; margin: 1rem 0; }\n\n/* Linked references, beneath the content. */\n.backlinks { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border-soft); }\n.backlinks h2 { font-size: 1.1rem; color: var(--text-strong); margin: 0 0 0.75rem; }\n.backlink-list { list-style: none; padding: 0; margin: 0; }\n.backlink-source { margin: 0 0 0.9rem; }\n.backlink-source > a { font-weight: 600; }\n.backlink-source ul { list-style: none; padding-left: 1rem; margin: 0.25rem 0 0; border-left: 2px solid var(--border-soft); }\n.backlink-context { color: var(--text-muted); font-size: 0.95rem; margin: 0.15rem 0; }\n\n/* Archive and index lists. */\n.journal-archive, .page-index-list { list-style: none; padding: 0; }\n.journal-archive li { margin: 0 0 1rem; }\n.journal-archive time { color: var(--text-muted); margin-right: 0.5rem; font-variant-numeric: tabular-nums; }\n.archive-excerpt { margin: 0.2rem 0 0; color: var(--text-muted); }\n.archive-year { margin: 1.5rem 0 0.5rem; font-size: 1.1rem; }\n.all-posts { margin: 1.5rem 0 0; }\n\n.site-footer { border-top: 1px solid var(--border-soft); padding: 1rem 1.5rem; color: var(--text-muted); font-size: 0.9rem; text-align: center; }\n\n/* Code tokens (lezer classHighlighter names). */\n.tok-keyword { color: #0a7; }\n.tok-string, .tok-string2 { color: #690; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: #999; font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: #905; }\n.tok-function, .tok-definition.tok-variableName { color: #c60; }\n.tok-typeName, .tok-className, .tok-namespace { color: #b58900; }\n.tok-operator, .tok-punctuation { color: #555; }\n.tok-propertyName, .tok-attributeName { color: #268bd2; }\n.tok-tagName { color: #22863a; }\n.tok-invalid { color: #d00; }\n@media (prefers-color-scheme: dark) {\n .tok-keyword { color: #4ec9b0; }\n .tok-string, .tok-string2 { color: #b5cea8; }\n .tok-comment, .tok-lineComment, .tok-blockComment { color: #7f848e; }\n .tok-number, .tok-bool, .tok-null, .tok-atom { color: #d19a66; }\n .tok-function, .tok-definition.tok-variableName { color: #dcdcaa; }\n .tok-typeName, .tok-className, .tok-namespace { color: #e5c07b; }\n .tok-operator, .tok-punctuation { color: #abb2bf; }\n .tok-propertyName, .tok-attributeName { color: #61afef; }\n .tok-tagName { color: #98c379; }\n}\n\n/* -- Posts ------------------------------------------------------------------------------ */\n.post-summary { margin: 0 0 2rem; }\n.post-summary h2 { margin: 0 0 0.2rem; font-size: 1.35rem; }\n.post-summary h2 a { color: var(--text-strong); text-decoration: none; }\n.post-summary h2 a:hover { color: var(--agent-500); }\n.post-summary time { color: var(--text-muted); font-size: 0.9rem; font-variant-numeric: tabular-nums; }\n.post-summary p { margin: 0.4rem 0 0; color: var(--text-default); }\n.home-intro { margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid var(--border-soft); }\n.post-list-empty { color: var(--text-muted); }\n@media (max-width: 40rem) {\n :root { --gutter: 1rem; }\n .site-header-row { flex-wrap: wrap; }\n .site-search { flex-basis: 100%; margin-left: 0; }\n .content { padding-top: 1.25rem; padding-bottom: 3rem; }\n}\n",
|
|
11206
11549
|
"../etherpk-blog/layouts/404.html": "{{> shell-top}}\n <article class=\"post not-published\">\n <header class=\"post-header\">\n <h1>Not published</h1>\n </header>\n <div class=\"post-body\">\n <p>The page you followed a link to is not part of this site.</p>\n <p><a href=\"index.html\">Back to {{site.title}}</a></p>\n </div>\n </article>\n{{> shell-bottom}}\n",
|
|
11207
11550
|
"../etherpk-blog/layouts/archive.html": "{{> shell-top}}\n <article class=\"post archive\">\n <header class=\"post-header\">\n <h1>{{#page.isPostsArchive}}Posts{{/page.isPostsArchive}}{{#page.isJournalArchive}}Journal{{/page.isJournalArchive}}</h1>\n </header>\n {{#page.isPostsArchive}}\n {{#site.postsByYear}}\n <h2 class=\"archive-year\" id=\"y{{year}}\">{{year}}</h2>\n <ul class=\"journal-archive\">\n {{#posts}}\n <li>\n <time datetime=\"{{date}}\">{{date}}</time>\n <a href=\"{{url}}\">{{title}}</a>\n </li>\n {{/posts}}\n </ul>\n {{/site.postsByYear}}\n {{/page.isPostsArchive}}\n {{#page.isJournalArchive}}\n <ul class=\"journal-archive\">\n {{#site.journals}}\n <li>\n <time datetime=\"{{date}}\">{{date}}</time>\n <a href=\"{{url}}\">{{title}}</a>\n {{#excerpt}}<p class=\"archive-excerpt\">{{excerpt}}</p>{{/excerpt}}\n </li>\n {{/site.journals}}\n </ul>\n {{/page.isJournalArchive}}\n </article>\n{{> shell-bottom}}\n",
|
|
11208
11551
|
"../etherpk-blog/layouts/home.html": "{{> shell-top}}\n {{^page.isGeneratedIndex}}\n <section class=\"home-intro\">\n <div class=\"post-body\">\n{{{page.content}}}\n </div>\n </section>\n {{/page.isGeneratedIndex}}\n <section class=\"post-list\" aria-label=\"Posts\">\n {{#site.hasPosts}}\n {{#site.recentPosts}}\n <article class=\"post-summary\">\n <h2><a href=\"{{url}}\">{{title}}</a></h2>\n <time datetime=\"{{date}}\">{{date}}</time>\n {{#excerpt}}<p>{{excerpt}}</p>{{/excerpt}}\n </article>\n {{/site.recentPosts}}\n <p class=\"all-posts\"><a href=\"{{site.postsUrl}}\">All {{site.postCount}} posts →</a></p>\n {{/site.hasPosts}}\n {{^site.hasPosts}}\n <p class=\"post-list-empty\">No dated documents are published yet. A journal entry is dated by its day, a page by a <code>date:</code> line in its frontmatter.</p>\n {{/site.hasPosts}}\n </section>\n{{> shell-bottom}}\n",
|
|
@@ -11211,6 +11554,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11211
11554
|
"../etherpk-blog/partials/after-content.html": "",
|
|
11212
11555
|
"../etherpk-blog/partials/backlinks.html": " {{#page.hasBacklinks}}\n <section class=\"backlinks\" aria-labelledby=\"backlinks-heading\">\n <h2 id=\"backlinks-heading\">Linked references</h2>\n <ul class=\"backlink-list\">\n {{#page.backlinks}}\n <li class=\"backlink-source\">\n <a href=\"{{url}}\">{{title}}</a>\n <ul>\n {{#refs}}<li class=\"backlink-context\">{{{contextHtml}}}</li>{{/refs}}\n </ul>\n </li>\n {{/page.backlinks}}\n </ul>\n </section>\n {{/page.hasBacklinks}}\n",
|
|
11213
11556
|
"../etherpk-blog/partials/before-content.html": "",
|
|
11557
|
+
"../etherpk-blog/partials/favicon.html": "<!-- The EtherPK icon in the browser tab: assets/favicon.svg, the same file the EtherPK apps use. Fill the \"favicon\" include with your own <link rel=\"icon\"> tag to replace it. -->\n<link rel=\"icon\" href=\"theme/favicon.svg\" type=\"image/svg+xml\">\n",
|
|
11214
11558
|
"../etherpk-blog/partials/footer.html": " <footer class=\"site-footer\">\n <p>{{site.title}}{{#site.hasFeed}} · <a href=\"feed.xml\">Feed</a>{{/site.hasFeed}} · published with <a href=\"https://etherpk.com\" rel=\"noopener\">EtherPK</a></p>\n </footer>\n",
|
|
11215
11559
|
"../etherpk-blog/partials/head.html": "",
|
|
11216
11560
|
"../etherpk-blog/partials/header.html": " <header class=\"site-header\">\n <div class=\"site-header-row\">\n <a class=\"site-title\" href=\"index.html\">{{> logo}}<span class=\"site-title-text\">{{site.title}}</span></a>\n <form class=\"site-search\" role=\"search\" data-search-index=\"{{site.search.url}}\" data-search-script=\"{{site.search.scriptUrl}}\">\n <label class=\"visually-hidden\" for=\"site-search-input\">Search</label>\n <input id=\"site-search-input\" type=\"search\" name=\"q\" placeholder=\"Search\" autocomplete=\"off\">\n <div class=\"site-search-results\" role=\"listbox\" hidden></div>\n </form>\n </div>\n <nav class=\"site-nav\" aria-label=\"Site\">\n <ul class=\"site-nav-list\">\n{{#nav}}{{> nav-item}}{{/nav}}\n </ul>\n </nav>\n </header>\n",
|
|
@@ -11218,11 +11562,12 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11218
11562
|
"../etherpk-blog/partials/nav-item.html": " <li class=\"{{#current}}nav-current {{/current}}{{#open}}nav-open {{/open}}{{^href}}nav-group{{/href}}\">\n {{#href}}<a href=\"{{href}}\"{{#current}} aria-current=\"page\"{{/current}}{{#external}} rel=\"noopener\"{{/external}}>{{label}}</a>{{/href}}\n {{^href}}<span class=\"nav-group-label\">{{{labelHtml}}}</span>{{/href}}\n {{#hasChildren}}<ul class=\"site-nav-list\">{{#children}}{{> nav-item}}{{/children}}</ul>{{/hasChildren}}\n </li>\n",
|
|
11219
11563
|
"../etherpk-blog/partials/scripts.html": "",
|
|
11220
11564
|
"../etherpk-blog/partials/shell-bottom.html": " {{> after-content}}\n {{> backlinks}}\n </main>\n {{> footer}}\n {{> theme-scripts}}\n {{> scripts}}\n</body>\n</html>\n",
|
|
11221
|
-
"../etherpk-blog/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} · {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n {{> head}}\n</head>\n<body class=\"layout-blog{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11565
|
+
"../etherpk-blog/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} · {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n {{> favicon}}\n {{> head}}\n</head>\n<body class=\"layout-blog{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11222
11566
|
"../etherpk-blog/partials/theme-scripts.html": " <script src=\"theme/search.js\" defer><\/script>\n",
|
|
11223
|
-
"../etherpk-blog/theme.json": "{\n \"name\": \"etherpk-blog\",\n \"title\": \"EtherPK Blog\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"A blog: dated documents as posts newest first, a top navigation from the publication's outline, the feed, and search.\",\n \"kinds\": [\"blog\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The logo before the site title in the header: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's title, navigation and search box.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the sidebar drawer): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/backlinks.html\", \"partials/before-content.html\", \"partials/after-content.html\",\n \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/search.js\"\n ]\n}\n",
|
|
11567
|
+
"../etherpk-blog/theme.json": "{\n \"name\": \"etherpk-blog\",\n \"title\": \"EtherPK Blog\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"A blog: dated documents as posts newest first, a top navigation from the publication's outline, the feed, and search.\",\n \"kinds\": [\"blog\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The logo before the site title in the header: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"favicon\", \"description\": \"The icon in the browser tab: a <link rel=\\\"icon\\\"> tag. Raw HTML in the page body. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's title, navigation and search box.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the sidebar drawer): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/favicon.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/backlinks.html\", \"partials/before-content.html\", \"partials/after-content.html\",\n \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/favicon.svg\", \"assets/search.js\"\n ]\n}\n",
|
|
11568
|
+
"../etherpk-docs/assets/favicon.svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 191.82 166.13\">\n <path fill=\"#0a0a0a\" fill-rule=\"evenodd\" d=\"M67.09,132.92,47.93,166.13H9.59L0,149.52,86.33,0H105.5L19.17,149.52H38.34l19.17-33.2h76.8l9.59,16.6Zm48-83.12,9.65,16.71H143.9L115.08,16.6q-24,41.57-48,83.12H143.9q14.37,24.9,28.76,49.8h-96l-9.59,16.61H182.24l9.58-16.61-38.34-66.4H96C95.85,82.9,113.39,52.73,115.08,49.8Z\"/>\n</svg>\n",
|
|
11224
11569
|
"../etherpk-docs/assets/search.js": "/*\n * Search for a Published Site: reads search.json (built over the publication's own documents\n * and nothing else) and scores documents by title and text matches. No dependencies, no\n * network beyond the one index file, and nothing runs until the search box is used. The same\n * script runs the sidebar toggle on small screens, so a page carries one script.\n */\n(function () {\n 'use strict';\n\n // -- Navigation toggle (small screens) -------------------------------------------------\n var toggle = document.querySelector('.nav-toggle');\n var backdrop = document.querySelector('.nav-backdrop');\n var nav = document.getElementById('site-nav');\n function setNavOpen(open) {\n var was = document.body.classList.contains('nav-open');\n document.body.classList.toggle('nav-open', open);\n if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n if (backdrop) backdrop.hidden = !open;\n // Focus follows the drawer: into its first link on open, back to the button on close,\n // so a keyboard or screen-reader user is never left on something now hidden.\n if (open && !was) {\n var first = nav.querySelector('a');\n if (first) first.focus();\n } else if (!open && was && toggle) {\n toggle.focus();\n }\n }\n if (toggle && nav) {\n toggle.addEventListener('click', function () { setNavOpen(!document.body.classList.contains('nav-open')); });\n if (backdrop) backdrop.addEventListener('click', function () { setNavOpen(false); });\n document.addEventListener('keydown', function (e) {\n if (e.key === 'Escape' && document.body.classList.contains('nav-open')) setNavOpen(false);\n });\n }\n\n // -- Search --------------------------------------------------------------------------\n var form = document.querySelector('.site-search');\n if (!form) return;\n var input = form.querySelector('input[type=\"search\"]');\n var results = form.querySelector('.site-search-results');\n var indexUrl = form.getAttribute('data-search-index') || 'search.json';\n var scriptUrl = form.getAttribute('data-search-script') || 'search-index.js';\n var index = null;\n var loading = null;\n /* True once neither route produced the index: the results then say so rather than \"nothing matches\". */\n var unavailable = false;\n\n /* The index as a script assigning window.etherpkSearchIndex: what a site opened from disk can\n load, since file:// refuses fetch but allows <script src>. */\n function loadScript() {\n return new Promise(function (resolve, reject) {\n if (window.etherpkSearchIndex) { resolve(window.etherpkSearchIndex); return; }\n var s = document.createElement('script');\n s.src = scriptUrl;\n s.onload = function () {\n if (window.etherpkSearchIndex) resolve(window.etherpkSearchIndex);\n else reject(new Error('no index'));\n };\n s.onerror = function () { reject(new Error('no script')); };\n document.head.appendChild(s);\n });\n }\n\n function load() {\n if (index) return Promise.resolve(index);\n if (!loading) {\n loading = fetch(indexUrl).then(function (r) {\n if (!r.ok) throw new Error(String(r.status));\n return r.json();\n }).catch(loadScript).then(function (data) {\n index = (data && data.documents) || [];\n return index;\n }).catch(function () { unavailable = true; index = []; return index; });\n }\n return loading;\n }\n\n function tokens(q) {\n return q.toLowerCase().split(/\\s+/).filter(function (t) { return t.length > 0; });\n }\n\n function score(doc, terms) {\n var title = (doc.title || '').toLowerCase();\n var text = (doc.text || '').toLowerCase();\n var s = 0;\n for (var i = 0; i < terms.length; i++) {\n var t = terms[i];\n if (title === t) s += 20;\n else if (title.indexOf(t) === 0) s += 10;\n else if (title.indexOf(t) !== -1) s += 6;\n var at = text.indexOf(t);\n if (at !== -1) s += 2;\n else if (title.indexOf(t) === -1) return 0; // every term must match somewhere\n }\n return s;\n }\n\n function snippet(doc, terms) {\n var text = doc.text || '';\n var lower = text.toLowerCase();\n var at = -1;\n for (var i = 0; i < terms.length && at === -1; i++) at = lower.indexOf(terms[i]);\n if (at === -1) return doc.excerpt || '';\n var start = Math.max(0, at - 60);\n var end = Math.min(text.length, at + 100);\n return (start > 0 ? '…' : '') + text.slice(start, end).replace(/\\s+/g, ' ') + (end < text.length ? '…' : '');\n }\n\n function escapeHtml(s) {\n return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n function render(list, q) {\n if (!q) { results.hidden = true; results.innerHTML = ''; return; }\n if (unavailable) {\n results.innerHTML = '<p class=\"site-search-empty\">The search index could not be loaded.</p>';\n results.hidden = false;\n return;\n }\n if (list.length === 0) {\n results.innerHTML = '<p class=\"site-search-empty\">Nothing matches \"' + escapeHtml(q) + '\".</p>';\n results.hidden = false;\n return;\n }\n results.innerHTML = list.map(function (hit) {\n return '<a class=\"site-search-hit\" role=\"option\" href=\"' + escapeHtml(hit.doc.url) + '\">' +\n '<span class=\"site-search-title\">' + escapeHtml(hit.doc.title) + '</span>' +\n '<span class=\"site-search-snippet\">' + escapeHtml(hit.snippet) + '</span></a>';\n }).join('');\n results.hidden = false;\n }\n\n var timer = null;\n function run() {\n var q = input.value.trim();\n var terms = tokens(q);\n if (terms.length === 0) { render([], ''); return; }\n load().then(function (docs) {\n var hits = [];\n for (var i = 0; i < docs.length; i++) {\n var s = score(docs[i], terms);\n if (s > 0) hits.push({ doc: docs[i], score: s, snippet: snippet(docs[i], terms) });\n }\n hits.sort(function (a, b) { return b.score - a.score || a.doc.title.localeCompare(b.doc.title); });\n render(hits.slice(0, 12), q);\n });\n }\n\n input.addEventListener('input', function () {\n clearTimeout(timer);\n timer = setTimeout(run, 120);\n });\n input.addEventListener('focus', function () { load(); });\n form.addEventListener('submit', function (e) {\n e.preventDefault();\n var first = results.querySelector('a');\n if (first) window.location.href = first.getAttribute('href');\n });\n document.addEventListener('keydown', function (e) {\n if (e.key === '/' && document.activeElement !== input && !/input|textarea/i.test(document.activeElement.tagName)) {\n e.preventDefault();\n input.focus();\n }\n if (e.key === 'Escape' && !results.hidden) { render([], ''); }\n });\n document.addEventListener('click', function (e) {\n if (!form.contains(e.target)) render([], '');\n });\n})();\n",
|
|
11225
|
-
"../etherpk-docs/assets/theme.css": "/* EtherPK docs theme. Tokens follow the app's own (agent blue, the gk surface scale); the font\n is the system stack so the site loads nothing from anywhere. Dark mode follows the reader's\n preference. Classes the publisher emits and this file styles: .wikilink, .wikilink-missing,\n .task, .task-tag, .toc, .backlinks, .site-nav-list, .diagram, .math, .tok-* (code). */\n\n:root {\n --font-sans: \"Inter\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n --agent-500: #007acc;\n --agent-600: #005f9e;\n --agent-100: #c2e3ff;\n --surface-0: #ffffff;\n --surface-1: #f9fafb;\n --surface-2: #e5e7eb;\n --text-strong: #030712;\n --text-default: #374151;\n --text-muted: #6b7280;\n --border-soft: rgba(3, 7, 18, 0.08);\n --border-strong: rgba(3, 7, 18, 0.16);\n --mark: #fff3a3;\n --missing: #b45309;\n --nav-width: 17rem;\n /* The header is exactly this tall, so the sticky sidebar and the drawer toggle can be\n measured against it rather than guessing at the search box's height. */\n --header-height: 3.5rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --agent-500: #5ab3ff;\n --agent-600: #94ccff;\n --agent-100: #002e4f;\n --surface-0: #18181b;\n --surface-1: #1c1c20;\n --surface-2: #2a2a30;\n --text-strong: #fafafa;\n --text-default: #d4d4d8;\n --text-muted: #a1a1aa;\n --border-soft: rgba(255, 255, 255, 0.08);\n --border-strong: rgba(255, 255, 255, 0.16);\n --mark: #6b5a00;\n --missing: #f59e0b;\n }\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { font-family: var(--font-sans); -webkit-font-smoothing: antialiased; }\nbody {\n margin: 0;\n color: var(--text-default);\n background: var(--surface-0);\n line-height: 1.6;\n font-size: 16px;\n}\na { color: var(--agent-500); }\na:hover { color: var(--agent-600); }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--surface-1); padding: 0.5rem 1rem; z-index: 100; }\n.skip-link:focus { left: 1rem; top: 1rem; }\n\n/* -- Header ----------------------------------------------------------------------------- */\n.site-header {\n display: flex;\n align-items: center;\n gap: 1rem;\n height: var(--header-height);\n padding: 0 1.5rem;\n border-bottom: 1px solid var(--border-soft);\n background: var(--surface-1);\n position: sticky;\n top: 0;\n z-index: 20;\n}\n.site-title { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; font-weight: 600; color: var(--text-strong); text-decoration: none; font-size: 1.05rem; }\n.site-title:hover { color: var(--agent-500); }\n.site-logo { flex-shrink: 0; }\n.site-title-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.site-search { position: relative; margin-left: auto; flex: 0 1 22rem; }\n.site-search input {\n width: 100%;\n padding: 0.4rem 0.75rem;\n line-height: 1.25;\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n background: var(--surface-0);\n color: var(--text-default);\n font: inherit;\n}\n.site-search input:focus { outline: 2px solid var(--agent-500); outline-offset: 1px; }\n.site-search-results {\n position: absolute;\n top: calc(100% + 0.35rem);\n left: 0;\n right: 0;\n max-height: 24rem;\n overflow: auto;\n background: var(--surface-0);\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 0.55rem 0.75rem; text-decoration: none; color: var(--text-default); border-bottom: 1px solid var(--border-soft); }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--surface-1); }\n.site-search-title { display: block; font-weight: 600; color: var(--text-strong); }\n.site-search-snippet { display: block; font-size: 0.9rem; color: var(--text-muted); }\n.site-search-empty { margin: 0; padding: 0.6rem 0.75rem; color: var(--text-muted); }\n\n/* -- Shell: sidebar + content ------------------------------------------------------------ */\n.shell { display: grid; grid-template-columns: var(--nav-width) minmax(0, 1fr); min-height: calc(100vh - var(--header-height)); }\n.site-nav {\n border-right: 1px solid var(--border-soft);\n background: var(--surface-1);\n padding: 1.25rem 1rem;\n overflow-y: auto;\n position: sticky;\n top: var(--header-height);\n height: calc(100vh - var(--header-height));\n}\n.site-nav-list { list-style: none; margin: 0; padding: 0; }\n.site-nav-list .site-nav-list { padding-left: 0.85rem; border-left: 1px solid var(--border-soft); margin-left: 0.5rem; }\n.site-nav li { margin: 0.1rem 0; }\n.site-nav a, .site-nav .nav-group-label {\n display: block;\n padding: 0.3rem 0.6rem;\n border-radius: 0.4rem;\n color: var(--text-default);\n text-decoration: none;\n font-size: 0.95rem;\n line-height: 1.4;\n}\n.site-nav a:hover { background: var(--surface-2); color: var(--text-strong); }\n.site-nav .nav-current > a { background: var(--agent-100); color: var(--text-strong); font-weight: 600; }\n.site-nav .nav-group-label { font-weight: 600; color: var(--text-strong); margin-top: 0.75rem; }\n.site-nav a .wikilink { color: inherit; text-decoration: none; }\n.nav-toggle { display: none; }\n.nav-backdrop { display: none; }\n\n.content { padding: 2rem 3rem 4rem; max-width: 52rem; width: 100%; }\n.page-header h1 { font-size: 2rem; line-height: 1.2; margin: 0 0 0.5rem; color: var(--text-strong); }\n.page-header h1 .wikilink { color: inherit; text-decoration: none; }\n.page-header h1 .wikilink:hover { text-decoration: underline; }\n.page-date { color: var(--text-muted); font-size: 0.95rem; }\n.page-body h2 { margin-top: 2rem; font-size: 1.4rem; color: var(--text-strong); }\n.page-body h3 { margin-top: 1.5rem; font-size: 1.15rem; color: var(--text-strong); }\n.page-body ul { padding-left: 1.4rem; }\n.page-body li { margin: 0.2rem 0; }\n.page-body img { max-width: 100%; height: auto; border-radius: 0.35rem; }\n.page-body table { border-collapse: collapse; margin: 1rem 0; width: 100%; }\n.page-body th, .page-body td { border: 1px solid var(--border-strong); padding: 0.4rem 0.6rem; text-align: left; }\n.page-body th { background: var(--surface-1); }\n.page-body blockquote { border-left: 3px solid var(--border-strong); margin: 1rem 0; padding: 0.25rem 1rem; color: var(--text-muted); }\n.page-body code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-1); padding: 0.1em 0.35em; border-radius: 0.25rem; }\n.page-body pre { background: var(--surface-1); border: 1px solid var(--border-soft); border-radius: 0.5rem; padding: 0.9rem 1rem; overflow-x: auto; }\n.page-body pre code { background: none; padding: 0; font-size: 0.88rem; }\nmark { background: var(--mark); color: inherit; padding: 0 0.15em; border-radius: 0.2em; }\n\n/* Wikilinks, as the editor colours them. A missing target is visibly missing. */\na.wikilink { text-decoration: none; border-bottom: 1px solid var(--agent-100); }\na.wikilink:hover { border-bottom-color: var(--agent-500); }\na.wikilink-missing { color: var(--missing); text-decoration: none; border-bottom: 1px dashed var(--missing); }\n\n/* Tasks and their badges. */\nli.task { list-style: none; margin-left: -1.4rem; padding-left: 0; }\nli.task input[type=\"checkbox\"] { margin-right: 0.35rem; vertical-align: -0.1em; }\nli.task.done { color: var(--text-muted); }\n.task-tag { display: inline-block; font-size: 0.8rem; font-weight: 600; padding: 0.05em 0.5em; border-radius: 999px; background: var(--surface-2); color: var(--text-strong); margin-right: 0.25rem; vertical-align: 0.1em; }\n.task-tag.priority-1 { background: #fee2e2; color: #991b1b; }\n.task-tag.priority-2 { background: #ffedd5; color: #9a3412; }\n.task-tag.priority-3 { background: #fef9c3; color: #854d0e; }\n.task-tag.waiting, .task-tag.doing { background: var(--agent-100); color: var(--agent-600); }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* Table of contents, diagrams, maths. */\n.toc { margin: 0 0 1.5rem; font-size: 0.95rem; }\n.toc summary { cursor: pointer; font-weight: 600; color: var(--text-strong); }\n.toc ul { list-style: none; padding-left: 0; margin: 0.4rem 0 0; }\n.toc li.toc-level-3 { padding-left: 1rem; }\n.toc li.toc-level-4 { padding-left: 2rem; }\n.diagram { margin: 1rem 0; text-align: center; }\n.diagram svg { max-width: 100%; height: auto; }\n.math-block { overflow-x: auto; margin: 1rem 0; }\n\n/* Linked references, beneath the content. */\n.backlinks { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border-soft); }\n.backlinks h2 { font-size: 1.1rem; color: var(--text-strong); margin: 0 0 0.75rem; }\n.backlink-list { list-style: none; padding: 0; margin: 0; }\n.backlink-source { margin: 0 0 0.9rem; }\n.backlink-source > a { font-weight: 600; }\n.backlink-source ul { list-style: none; padding-left: 1rem; margin: 0.25rem 0 0; border-left: 2px solid var(--border-soft); }\n.backlink-context { color: var(--text-muted); font-size: 0.95rem; margin: 0.15rem 0; }\n\n/* Archive and index lists. */\n.journal-archive, .page-index-list { list-style: none; padding: 0; }\n/* The contents page: the outline as a tree, then any page it leaves out. */\n.page-index-list .page-index-list { padding-left: 1.5rem; margin-top: 0.3rem; }\n.page-index-list li { margin: 0.4rem 0; }\n.page-index-group-label { color: var(--text-muted); font-weight: 600; }\n.page-index-other { margin: 1.5rem 0 0.5rem; font-size: 1.1rem; }\n.journal-archive li { margin: 0 0 1rem; }\n.journal-archive time { color: var(--text-muted); margin-right: 0.5rem; font-variant-numeric: tabular-nums; }\n.archive-excerpt { margin: 0.2rem 0 0; color: var(--text-muted); }\n.archive-year { margin: 1.5rem 0 0.5rem; font-size: 1.1rem; }\n\n.site-footer { border-top: 1px solid var(--border-soft); padding: 1rem 1.5rem; color: var(--text-muted); font-size: 0.9rem; text-align: center; }\n\n/* Code tokens (lezer classHighlighter names). */\n.tok-keyword { color: #0a7; }\n.tok-string, .tok-string2 { color: #690; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: #999; font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: #905; }\n.tok-function, .tok-definition.tok-variableName { color: #c60; }\n.tok-typeName, .tok-className, .tok-namespace { color: #b58900; }\n.tok-operator, .tok-punctuation { color: #555; }\n.tok-propertyName, .tok-attributeName { color: #268bd2; }\n.tok-tagName { color: #22863a; }\n.tok-invalid { color: #d00; }\n@media (prefers-color-scheme: dark) {\n .tok-keyword { color: #4ec9b0; }\n .tok-string, .tok-string2 { color: #b5cea8; }\n .tok-comment, .tok-lineComment, .tok-blockComment { color: #7f848e; }\n .tok-number, .tok-bool, .tok-null, .tok-atom { color: #d19a66; }\n .tok-function, .tok-definition.tok-variableName { color: #dcdcaa; }\n .tok-typeName, .tok-className, .tok-namespace { color: #e5c07b; }\n .tok-operator, .tok-punctuation { color: #abb2bf; }\n .tok-propertyName, .tok-attributeName { color: #61afef; }\n .tok-tagName { color: #98c379; }\n}\n\n/* -- Small screens: the sidebar becomes a drawer ---------------------------------------- */\n@media (max-width: 56rem) {\n /* Content flows on its own; the drawer and its toggle are fixed, so the shell reserves no\n row for either (a grid row here stretched the toggle to the full page height). */\n .shell { display: block; min-height: 0; }\n .site-header { padding-right: 4.25rem; }\n .nav-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: fixed;\n top: 0.5rem;\n right: 1rem;\n width: 2.5rem;\n height: 2.5rem;\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n background: var(--surface-1);\n color: var(--text-strong);\n cursor: pointer;\n z-index: 45;\n }\n .nav-toggle:hover { background: var(--surface-2); }\n .nav-toggle:focus-visible { outline: 2px solid var(--agent-500); outline-offset: 1px; }\n .site-nav {\n position: fixed;\n top: 0;\n left: 0;\n bottom: 0;\n height: auto;\n width: min(20rem, 85vw);\n border-right: 1px solid var(--border-strong);\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);\n transform: translateX(-100%);\n /* Hidden while closed, so its links are not tab stops behind the page; the visibility\n change waits for the slide out. */\n visibility: hidden;\n transition: transform 160ms ease-out, visibility 0s linear 160ms;\n z-index: 40;\n }\n body.nav-open .site-nav { transform: translateX(0); visibility: visible; transition: transform 160ms ease-out; }\n body.nav-open .nav-backdrop { display: block; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.35); z-index: 35; }\n .content { padding: 1rem 1.25rem 3rem; }\n .site-search { flex-basis: 12rem; }\n}\n@media (prefers-reduced-motion: reduce) {\n .site-nav { transition: none; }\n body.nav-open .site-nav { transition: none; }\n}\n",
|
|
11570
|
+
"../etherpk-docs/assets/theme.css": "/* EtherPK docs theme. Tokens follow the app's own (agent blue, the gk surface scale); the font\n is the system stack so the site loads nothing from anywhere. Dark mode follows the reader's\n preference. Classes the publisher emits and this file styles: .wikilink, .wikilink-missing,\n .task, .task-tag, .toc, .backlinks, .site-nav-list, .diagram, .math, .tok-* (code). */\n\n:root {\n --font-sans: \"Inter\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n --agent-500: #007acc;\n --agent-600: #005f9e;\n --agent-100: #c2e3ff;\n --surface-0: #ffffff;\n --surface-1: #f9fafb;\n --surface-2: #e5e7eb;\n --text-strong: #030712;\n --text-default: #374151;\n --text-muted: #6b7280;\n --border-soft: rgba(3, 7, 18, 0.08);\n --border-strong: rgba(3, 7, 18, 0.16);\n --mark: #fff3a3;\n --missing: #b45309;\n --nav-width: 17rem;\n /* The header is exactly this tall, so the sticky sidebar and the drawer toggle can be\n measured against it rather than guessing at the search box's height. */\n --header-height: 3.5rem;\n /* The width of the text column beside the sidebar. Pictures are capped to it as well. */\n --content-width: 52rem;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --agent-500: #5ab3ff;\n --agent-600: #94ccff;\n --agent-100: #002e4f;\n --surface-0: #18181b;\n --surface-1: #1c1c20;\n --surface-2: #2a2a30;\n --text-strong: #fafafa;\n --text-default: #d4d4d8;\n --text-muted: #a1a1aa;\n --border-soft: rgba(255, 255, 255, 0.08);\n --border-strong: rgba(255, 255, 255, 0.16);\n --mark: #6b5a00;\n --missing: #f59e0b;\n }\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { font-family: var(--font-sans); -webkit-font-smoothing: antialiased; }\nbody {\n margin: 0;\n color: var(--text-default);\n background: var(--surface-0);\n line-height: 1.6;\n font-size: 16px;\n}\na { color: var(--agent-500); }\na:hover { color: var(--agent-600); }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--surface-1); padding: 0.5rem 1rem; z-index: 100; }\n.skip-link:focus { left: 1rem; top: 1rem; }\n\n/* -- Header ----------------------------------------------------------------------------- */\n.site-header {\n display: flex;\n align-items: center;\n gap: 1rem;\n height: var(--header-height);\n padding: 0 1.5rem;\n border-bottom: 1px solid var(--border-soft);\n background: var(--surface-1);\n position: sticky;\n top: 0;\n z-index: 20;\n}\n.site-title { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; font-weight: 600; color: var(--text-strong); text-decoration: none; font-size: 1.05rem; }\n.site-title:hover { color: var(--agent-500); }\n.site-logo { flex-shrink: 0; }\n.site-title-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.site-search { position: relative; margin-left: auto; flex: 0 1 22rem; }\n.site-search input {\n width: 100%;\n padding: 0.4rem 0.75rem;\n line-height: 1.25;\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n background: var(--surface-0);\n color: var(--text-default);\n font: inherit;\n}\n.site-search input:focus { outline: 2px solid var(--agent-500); outline-offset: 1px; }\n.site-search-results {\n position: absolute;\n top: calc(100% + 0.35rem);\n left: 0;\n right: 0;\n max-height: 24rem;\n overflow: auto;\n background: var(--surface-0);\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 0.55rem 0.75rem; text-decoration: none; color: var(--text-default); border-bottom: 1px solid var(--border-soft); }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--surface-1); }\n.site-search-title { display: block; font-weight: 600; color: var(--text-strong); }\n.site-search-snippet { display: block; font-size: 0.9rem; color: var(--text-muted); }\n.site-search-empty { margin: 0; padding: 0.6rem 0.75rem; color: var(--text-muted); }\n\n/* -- Shell: sidebar + content ------------------------------------------------------------ */\n.shell { display: grid; grid-template-columns: var(--nav-width) minmax(0, 1fr); min-height: calc(100vh - var(--header-height)); }\n.site-nav {\n border-right: 1px solid var(--border-soft);\n background: var(--surface-1);\n padding: 1.25rem 1rem;\n overflow-y: auto;\n position: sticky;\n top: var(--header-height);\n height: calc(100vh - var(--header-height));\n}\n.site-nav-list { list-style: none; margin: 0; padding: 0; }\n.site-nav-list .site-nav-list { padding-left: 0.85rem; border-left: 1px solid var(--border-soft); margin-left: 0.5rem; }\n.site-nav li { margin: 0.1rem 0; }\n.site-nav a, .site-nav .nav-group-label {\n display: block;\n padding: 0.3rem 0.6rem;\n border-radius: 0.4rem;\n color: var(--text-default);\n text-decoration: none;\n font-size: 0.95rem;\n line-height: 1.4;\n}\n.site-nav a:hover { background: var(--surface-2); color: var(--text-strong); }\n.site-nav .nav-current > a { background: var(--agent-100); color: var(--text-strong); font-weight: 600; }\n.site-nav .nav-group-label { font-weight: 600; color: var(--text-strong); margin-top: 0.75rem; }\n.site-nav a .wikilink { color: inherit; text-decoration: none; }\n.nav-toggle { display: none; }\n.nav-backdrop { display: none; }\n\n/* The padding sits outside the text column (border-box would take it from inside). */\n.content { padding: 2rem 3rem 4rem; max-width: calc(var(--content-width) + 6rem); width: 100%; }\n.page-header h1 { font-size: 2rem; line-height: 1.2; margin: 0 0 0.5rem; color: var(--text-strong); }\n.page-header h1 .wikilink { color: inherit; text-decoration: none; }\n.page-header h1 .wikilink:hover { text-decoration: underline; }\n.page-date { color: var(--text-muted); font-size: 0.95rem; }\n.page-body h2 { margin-top: 2rem; font-size: 1.4rem; color: var(--text-strong); }\n.page-body h3 { margin-top: 1.5rem; font-size: 1.15rem; color: var(--text-strong); }\n.page-body ul { padding-left: 1.4rem; }\n.page-body li { margin: 0.2rem 0; }\n.page-body img { max-width: 100%; height: auto; border-radius: 0.35rem; }\n.page-body table { border-collapse: collapse; margin: 1rem 0; width: 100%; }\n.page-body th, .page-body td { border: 1px solid var(--border-strong); padding: 0.4rem 0.6rem; text-align: left; }\n.page-body th { background: var(--surface-1); }\n.page-body blockquote { border-left: 3px solid var(--border-strong); margin: 1rem 0; padding: 0.25rem 1rem; color: var(--text-muted); }\n.page-body code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-1); padding: 0.1em 0.35em; border-radius: 0.25rem; }\n.page-body pre { background: var(--surface-1); border: 1px solid var(--border-soft); border-radius: 0.5rem; padding: 0.9rem 1rem; overflow-x: auto; }\n.page-body pre code { background: none; padding: 0; font-size: 0.88rem; }\nmark { background: var(--mark); color: inherit; padding: 0 0.15em; border-radius: 0.2em; }\n\n/* Wikilinks, as the editor colours them. A missing target is visibly missing. */\na.wikilink { text-decoration: none; border-bottom: 1px solid var(--agent-100); }\na.wikilink:hover { border-bottom-color: var(--agent-500); }\na.wikilink-missing { color: var(--missing); text-decoration: none; border-bottom: 1px dashed var(--missing); }\n\n/* Tasks and their badges. */\nli.task { list-style: none; margin-left: -1.4rem; padding-left: 0; }\nli.task input[type=\"checkbox\"] { margin-right: 0.35rem; vertical-align: -0.1em; }\nli.task.done { color: var(--text-muted); }\n.task-tag { display: inline-block; font-size: 0.8rem; font-weight: 600; padding: 0.05em 0.5em; border-radius: 999px; background: var(--surface-2); color: var(--text-strong); margin-right: 0.25rem; vertical-align: 0.1em; }\n.task-tag.priority-1 { background: #fee2e2; color: #991b1b; }\n.task-tag.priority-2 { background: #ffedd5; color: #9a3412; }\n.task-tag.priority-3 { background: #fef9c3; color: #854d0e; }\n.task-tag.waiting, .task-tag.doing { background: var(--agent-100); color: var(--agent-600); }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* Table of contents, diagrams, maths. */\n.toc { margin: 0 0 1.5rem; font-size: 0.95rem; }\n.toc summary { cursor: pointer; font-weight: 600; color: var(--text-strong); }\n.toc ul { list-style: none; padding-left: 0; margin: 0.4rem 0 0; }\n.toc li.toc-level-3 { padding-left: 1rem; }\n.toc li.toc-level-4 { padding-left: 2rem; }\n.diagram { margin: 1rem 0; text-align: center; }\n.diagram svg { max-width: 100%; height: auto; }\n.math-block { overflow-x: auto; margin: 1rem 0; }\n\n/* Linked references, beneath the content. */\n.backlinks { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border-soft); }\n.backlinks h2 { font-size: 1.1rem; color: var(--text-strong); margin: 0 0 0.75rem; }\n.backlink-list { list-style: none; padding: 0; margin: 0; }\n.backlink-source { margin: 0 0 0.9rem; }\n.backlink-source > a { font-weight: 600; }\n.backlink-source ul { list-style: none; padding-left: 1rem; margin: 0.25rem 0 0; border-left: 2px solid var(--border-soft); }\n.backlink-context { color: var(--text-muted); font-size: 0.95rem; margin: 0.15rem 0; }\n\n/* Archive and index lists. */\n.journal-archive, .page-index-list { list-style: none; padding: 0; }\n/* The contents page: the outline as a tree, then any page it leaves out. */\n.page-index-list .page-index-list { padding-left: 1.5rem; margin-top: 0.3rem; }\n.page-index-list li { margin: 0.4rem 0; }\n.page-index-group-label { color: var(--text-muted); font-weight: 600; }\n.page-index-other { margin: 1.5rem 0 0.5rem; font-size: 1.1rem; }\n.journal-archive li { margin: 0 0 1rem; }\n.journal-archive time { color: var(--text-muted); margin-right: 0.5rem; font-variant-numeric: tabular-nums; }\n.archive-excerpt { margin: 0.2rem 0 0; color: var(--text-muted); }\n.archive-year { margin: 1.5rem 0 0.5rem; font-size: 1.1rem; }\n\n.site-footer { border-top: 1px solid var(--border-soft); padding: 1rem 1.5rem; color: var(--text-muted); font-size: 0.9rem; text-align: center; }\n\n/* Code tokens (lezer classHighlighter names). */\n.tok-keyword { color: #0a7; }\n.tok-string, .tok-string2 { color: #690; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: #999; font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: #905; }\n.tok-function, .tok-definition.tok-variableName { color: #c60; }\n.tok-typeName, .tok-className, .tok-namespace { color: #b58900; }\n.tok-operator, .tok-punctuation { color: #555; }\n.tok-propertyName, .tok-attributeName { color: #268bd2; }\n.tok-tagName { color: #22863a; }\n.tok-invalid { color: #d00; }\n@media (prefers-color-scheme: dark) {\n .tok-keyword { color: #4ec9b0; }\n .tok-string, .tok-string2 { color: #b5cea8; }\n .tok-comment, .tok-lineComment, .tok-blockComment { color: #7f848e; }\n .tok-number, .tok-bool, .tok-null, .tok-atom { color: #d19a66; }\n .tok-function, .tok-definition.tok-variableName { color: #dcdcaa; }\n .tok-typeName, .tok-className, .tok-namespace { color: #e5c07b; }\n .tok-operator, .tok-punctuation { color: #abb2bf; }\n .tok-propertyName, .tok-attributeName { color: #61afef; }\n .tok-tagName { color: #98c379; }\n}\n\n/* -- Small screens: the sidebar becomes a drawer ---------------------------------------- */\n@media (max-width: 56rem) {\n /* Content flows on its own; the drawer and its toggle are fixed, so the shell reserves no\n row for either (a grid row here stretched the toggle to the full page height). */\n .shell { display: block; min-height: 0; }\n .site-header { padding-right: 4.25rem; }\n .nav-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: fixed;\n top: 0.5rem;\n right: 1rem;\n width: 2.5rem;\n height: 2.5rem;\n border: 1px solid var(--border-strong);\n border-radius: 0.5rem;\n background: var(--surface-1);\n color: var(--text-strong);\n cursor: pointer;\n z-index: 45;\n }\n .nav-toggle:hover { background: var(--surface-2); }\n .nav-toggle:focus-visible { outline: 2px solid var(--agent-500); outline-offset: 1px; }\n .site-nav {\n position: fixed;\n top: 0;\n left: 0;\n bottom: 0;\n height: auto;\n width: min(20rem, 85vw);\n border-right: 1px solid var(--border-strong);\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);\n transform: translateX(-100%);\n /* Hidden while closed, so its links are not tab stops behind the page; the visibility\n change waits for the slide out. */\n visibility: hidden;\n transition: transform 160ms ease-out, visibility 0s linear 160ms;\n z-index: 40;\n }\n body.nav-open .site-nav { transform: translateX(0); visibility: visible; transition: transform 160ms ease-out; }\n body.nav-open .nav-backdrop { display: block; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.35); z-index: 35; }\n .content { padding: 1rem 1.25rem 3rem; }\n .site-search { flex-basis: 12rem; }\n}\n@media (prefers-reduced-motion: reduce) {\n .site-nav { transition: none; }\n body.nav-open .site-nav { transition: none; }\n}\n",
|
|
11226
11571
|
"../etherpk-docs/layouts/404.html": "{{> shell-top}}\n <article class=\"page not-published\">\n <header class=\"page-header\">\n <h1>Not published</h1>\n </header>\n <div class=\"page-body\">\n <p>The page you followed a link to is not part of this site.</p>\n <p><a href=\"index.html\">Back to {{site.title}}</a></p>\n </div>\n </article>\n{{> shell-bottom}}\n",
|
|
11227
11572
|
"../etherpk-docs/layouts/archive.html": "{{> shell-top}}\n <article class=\"page archive\">\n <header class=\"page-header\">\n <h1>{{#page.isPostsArchive}}Posts{{/page.isPostsArchive}}{{#page.isJournalArchive}}Journal{{/page.isJournalArchive}}</h1>\n </header>\n {{#page.isPostsArchive}}\n {{#site.postsByYear}}\n <h2 class=\"archive-year\" id=\"y{{year}}\">{{year}}</h2>\n <ul class=\"journal-archive\">\n {{#posts}}\n <li>\n <time datetime=\"{{date}}\">{{date}}</time>\n <a href=\"{{url}}\">{{title}}</a>\n </li>\n {{/posts}}\n </ul>\n {{/site.postsByYear}}\n {{/page.isPostsArchive}}\n {{#page.isJournalArchive}}\n <ul class=\"journal-archive\">\n {{#site.journals}}\n <li>\n <time datetime=\"{{date}}\">{{date}}</time>\n <a href=\"{{url}}\">{{title}}</a>\n {{#excerpt}}<p class=\"archive-excerpt\">{{excerpt}}</p>{{/excerpt}}\n </li>\n {{/site.journals}}\n </ul>\n {{/page.isJournalArchive}}\n </article>\n{{> shell-bottom}}\n",
|
|
11228
11573
|
"../etherpk-docs/layouts/home.html": "{{> shell-top}}\n <article class=\"page home\">\n {{^page.isGeneratedIndex}}\n <header class=\"page-header\">\n <h1>{{{page.titleHtml}}}</h1>\n </header>\n{{> toc}}\n {{/page.isGeneratedIndex}}\n {{#page.isGeneratedIndex}}\n <header class=\"page-header\">\n <h1>{{site.title}}</h1>\n </header>\n {{/page.isGeneratedIndex}}\n <div class=\"page-body\">\n{{{page.content}}}\n </div>\n </article>\n{{> shell-bottom}}\n",
|
|
@@ -11231,6 +11576,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11231
11576
|
"../etherpk-docs/partials/after-content.html": "",
|
|
11232
11577
|
"../etherpk-docs/partials/backlinks.html": " {{#page.hasBacklinks}}\n <section class=\"backlinks\" aria-labelledby=\"backlinks-heading\">\n <h2 id=\"backlinks-heading\">Linked references</h2>\n <ul class=\"backlink-list\">\n {{#page.backlinks}}\n <li class=\"backlink-source\">\n <a href=\"{{url}}\">{{title}}</a>\n <ul>\n {{#refs}}<li class=\"backlink-context\">{{{contextHtml}}}</li>{{/refs}}\n </ul>\n </li>\n {{/page.backlinks}}\n </ul>\n </section>\n {{/page.hasBacklinks}}\n",
|
|
11233
11578
|
"../etherpk-docs/partials/before-content.html": "",
|
|
11579
|
+
"../etherpk-docs/partials/favicon.html": "<!-- The EtherPK icon in the browser tab: assets/favicon.svg, the same file the EtherPK apps use. Fill the \"favicon\" include with your own <link rel=\"icon\"> tag to replace it. -->\n<link rel=\"icon\" href=\"theme/favicon.svg\" type=\"image/svg+xml\">\n",
|
|
11234
11580
|
"../etherpk-docs/partials/footer.html": " <footer class=\"site-footer\">\n <p>{{site.title}} · published with <a href=\"https://etherpk.com\" rel=\"noopener\">EtherPK</a></p>\n </footer>\n",
|
|
11235
11581
|
"../etherpk-docs/partials/head.html": "",
|
|
11236
11582
|
"../etherpk-docs/partials/header.html": " <header class=\"site-header\">\n <a class=\"site-title\" href=\"index.html\">{{> logo}}<span class=\"site-title-text\">{{site.title}}</span></a>\n <form class=\"site-search\" role=\"search\" data-search-index=\"{{site.search.url}}\" data-search-script=\"{{site.search.scriptUrl}}\">\n <label class=\"visually-hidden\" for=\"site-search-input\">Search</label>\n <input id=\"site-search-input\" type=\"search\" name=\"q\" placeholder=\"Search\" autocomplete=\"off\" aria-describedby=\"site-search-hint\">\n <span id=\"site-search-hint\" class=\"visually-hidden\">Type to search the published pages - results appear below the box.</span>\n <div class=\"site-search-results\" role=\"listbox\" hidden></div>\n </form>\n </header>\n",
|
|
@@ -11239,10 +11585,11 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11239
11585
|
"../etherpk-docs/partials/nav.html": " <nav id=\"site-nav\" class=\"site-nav\" aria-label=\"Site\">\n <ul class=\"site-nav-list\">\n{{#nav}}{{> nav-item}}{{/nav}}\n </ul>\n </nav>\n",
|
|
11240
11586
|
"../etherpk-docs/partials/scripts.html": "",
|
|
11241
11587
|
"../etherpk-docs/partials/shell-bottom.html": " {{> after-content}}\n {{> backlinks}}\n </main>\n </div>\n {{> footer}}\n {{> theme-scripts}}\n {{> scripts}}\n</body>\n</html>\n",
|
|
11242
|
-
"../etherpk-docs/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} · {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n {{> head}}\n</head>\n<body class=\"layout-docs{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <!-- The drawer toggle lives outside the header include, so a replaced header keeps the navigation reachable on a phone. The stylesheet pins it top right on small screens and hides it otherwise. Icon: Lucide \"menu\" (https://lucide.dev/icons/menu), ISC licence. -->\n <button class=\"nav-toggle\" type=\"button\" aria-controls=\"site-nav\" aria-expanded=\"false\" aria-label=\"Menu\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M4 6h16\"/><path d=\"M4 12h16\"/><path d=\"M4 18h16\"/></svg></button>\n <div class=\"nav-backdrop\" hidden></div>\n <div class=\"shell\">\n {{> nav}}\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11588
|
+
"../etherpk-docs/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} · {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n {{> favicon}}\n {{> head}}\n</head>\n<body class=\"layout-docs{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <!-- The drawer toggle lives outside the header include, so a replaced header keeps the navigation reachable on a phone. The stylesheet pins it top right on small screens and hides it otherwise. Icon: Lucide \"menu\" (https://lucide.dev/icons/menu), ISC licence. -->\n <button class=\"nav-toggle\" type=\"button\" aria-controls=\"site-nav\" aria-expanded=\"false\" aria-label=\"Menu\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M4 6h16\"/><path d=\"M4 12h16\"/><path d=\"M4 18h16\"/></svg></button>\n <div class=\"nav-backdrop\" hidden></div>\n <div class=\"shell\">\n {{> nav}}\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11243
11589
|
"../etherpk-docs/partials/theme-scripts.html": " <script src=\"theme/search.js\" defer><\/script>\n",
|
|
11244
11590
|
"../etherpk-docs/partials/toc.html": " {{#page.hasToc}}\n <nav class=\"toc\" aria-label=\"On this page\">\n <details open>\n <summary>On this page</summary>\n <ul>\n {{#page.toc}}<li class=\"toc-level-{{level}}\"><a href=\"#{{id}}\">{{text}}</a></li>{{/page.toc}}\n </ul>\n </details>\n </nav>\n {{/page.hasToc}}\n",
|
|
11245
|
-
"../etherpk-docs/theme.json": "{\n \"name\": \"etherpk-docs\",\n \"title\": \"EtherPK Docs\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"Documentation: a sidebar of the publication's navigation, a table of contents, linked references beneath each page, and search.\",\n \"kinds\": [\"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The logo before the site title in the header: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's title bar and search box.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the sidebar drawer): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/header.html\",\n \"partials/nav.html\", \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/search.js\"\n ]\n}\n",
|
|
11591
|
+
"../etherpk-docs/theme.json": "{\n \"name\": \"etherpk-docs\",\n \"title\": \"EtherPK Docs\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"Documentation: a sidebar of the publication's navigation, a table of contents, linked references beneath each page, and search.\",\n \"kinds\": [\"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The logo before the site title in the header: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"favicon\", \"description\": \"The icon in the browser tab: a <link rel=\\\"icon\\\"> tag. Raw HTML in the page body. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's title bar and search box.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the sidebar drawer): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/favicon.html\", \"partials/header.html\",\n \"partials/nav.html\", \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/favicon.svg\", \"assets/search.js\"\n ]\n}\n",
|
|
11592
|
+
"../etherpk-papermod/assets/favicon.svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 191.82 166.13\">\n <path fill=\"#0a0a0a\" fill-rule=\"evenodd\" d=\"M67.09,132.92,47.93,166.13H9.59L0,149.52,86.33,0H105.5L19.17,149.52H38.34l19.17-33.2h76.8l9.59,16.6Zm48-83.12,9.65,16.71H143.9L115.08,16.6q-24,41.57-48,83.12H143.9q14.37,24.9,28.76,49.8h-96l-9.59,16.61H182.24l9.58-16.61-38.34-66.4H96C95.85,82.9,113.39,52.73,115.08,49.8Z\"/>\n</svg>\n",
|
|
11246
11593
|
"../etherpk-papermod/assets/search.js": "/*\n * Search for a Published Site: reads search.json (built over the publication's own documents\n * and nothing else) and scores documents by title and text matches. No dependencies, no\n * network beyond the one index file, and nothing runs until the search box is used. The same\n * script runs the sidebar toggle on small screens, so a page carries one script.\n */\n(function () {\n 'use strict';\n\n // -- Navigation toggle (small screens) -------------------------------------------------\n var toggle = document.querySelector('.nav-toggle');\n var backdrop = document.querySelector('.nav-backdrop');\n var nav = document.getElementById('site-nav');\n function setNavOpen(open) {\n var was = document.body.classList.contains('nav-open');\n document.body.classList.toggle('nav-open', open);\n if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n if (backdrop) backdrop.hidden = !open;\n // Focus follows the drawer: into its first link on open, back to the button on close,\n // so a keyboard or screen-reader user is never left on something now hidden.\n if (open && !was) {\n var first = nav.querySelector('a');\n if (first) first.focus();\n } else if (!open && was && toggle) {\n toggle.focus();\n }\n }\n if (toggle && nav) {\n toggle.addEventListener('click', function () { setNavOpen(!document.body.classList.contains('nav-open')); });\n if (backdrop) backdrop.addEventListener('click', function () { setNavOpen(false); });\n document.addEventListener('keydown', function (e) {\n if (e.key === 'Escape' && document.body.classList.contains('nav-open')) setNavOpen(false);\n });\n }\n\n // -- Search --------------------------------------------------------------------------\n var form = document.querySelector('.site-search');\n if (!form) return;\n var input = form.querySelector('input[type=\"search\"]');\n var results = form.querySelector('.site-search-results');\n var indexUrl = form.getAttribute('data-search-index') || 'search.json';\n var scriptUrl = form.getAttribute('data-search-script') || 'search-index.js';\n var index = null;\n var loading = null;\n /* True once neither route produced the index: the results then say so rather than \"nothing matches\". */\n var unavailable = false;\n\n /* The index as a script assigning window.etherpkSearchIndex: what a site opened from disk can\n load, since file:// refuses fetch but allows <script src>. */\n function loadScript() {\n return new Promise(function (resolve, reject) {\n if (window.etherpkSearchIndex) { resolve(window.etherpkSearchIndex); return; }\n var s = document.createElement('script');\n s.src = scriptUrl;\n s.onload = function () {\n if (window.etherpkSearchIndex) resolve(window.etherpkSearchIndex);\n else reject(new Error('no index'));\n };\n s.onerror = function () { reject(new Error('no script')); };\n document.head.appendChild(s);\n });\n }\n\n function load() {\n if (index) return Promise.resolve(index);\n if (!loading) {\n loading = fetch(indexUrl).then(function (r) {\n if (!r.ok) throw new Error(String(r.status));\n return r.json();\n }).catch(loadScript).then(function (data) {\n index = (data && data.documents) || [];\n return index;\n }).catch(function () { unavailable = true; index = []; return index; });\n }\n return loading;\n }\n\n function tokens(q) {\n return q.toLowerCase().split(/\\s+/).filter(function (t) { return t.length > 0; });\n }\n\n function score(doc, terms) {\n var title = (doc.title || '').toLowerCase();\n var text = (doc.text || '').toLowerCase();\n var s = 0;\n for (var i = 0; i < terms.length; i++) {\n var t = terms[i];\n if (title === t) s += 20;\n else if (title.indexOf(t) === 0) s += 10;\n else if (title.indexOf(t) !== -1) s += 6;\n var at = text.indexOf(t);\n if (at !== -1) s += 2;\n else if (title.indexOf(t) === -1) return 0; // every term must match somewhere\n }\n return s;\n }\n\n function snippet(doc, terms) {\n var text = doc.text || '';\n var lower = text.toLowerCase();\n var at = -1;\n for (var i = 0; i < terms.length && at === -1; i++) at = lower.indexOf(terms[i]);\n if (at === -1) return doc.excerpt || '';\n var start = Math.max(0, at - 60);\n var end = Math.min(text.length, at + 100);\n return (start > 0 ? '…' : '') + text.slice(start, end).replace(/\\s+/g, ' ') + (end < text.length ? '…' : '');\n }\n\n function escapeHtml(s) {\n return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n function render(list, q) {\n if (!q) { results.hidden = true; results.innerHTML = ''; return; }\n if (unavailable) {\n results.innerHTML = '<p class=\"site-search-empty\">The search index could not be loaded.</p>';\n results.hidden = false;\n return;\n }\n if (list.length === 0) {\n results.innerHTML = '<p class=\"site-search-empty\">Nothing matches \"' + escapeHtml(q) + '\".</p>';\n results.hidden = false;\n return;\n }\n results.innerHTML = list.map(function (hit) {\n return '<a class=\"site-search-hit\" role=\"option\" href=\"' + escapeHtml(hit.doc.url) + '\">' +\n '<span class=\"site-search-title\">' + escapeHtml(hit.doc.title) + '</span>' +\n '<span class=\"site-search-snippet\">' + escapeHtml(hit.snippet) + '</span></a>';\n }).join('');\n results.hidden = false;\n }\n\n var timer = null;\n function run() {\n var q = input.value.trim();\n var terms = tokens(q);\n if (terms.length === 0) { render([], ''); return; }\n load().then(function (docs) {\n var hits = [];\n for (var i = 0; i < docs.length; i++) {\n var s = score(docs[i], terms);\n if (s > 0) hits.push({ doc: docs[i], score: s, snippet: snippet(docs[i], terms) });\n }\n hits.sort(function (a, b) { return b.score - a.score || a.doc.title.localeCompare(b.doc.title); });\n render(hits.slice(0, 12), q);\n });\n }\n\n input.addEventListener('input', function () {\n clearTimeout(timer);\n timer = setTimeout(run, 120);\n });\n input.addEventListener('focus', function () { load(); });\n form.addEventListener('submit', function (e) {\n e.preventDefault();\n var first = results.querySelector('a');\n if (first) window.location.href = first.getAttribute('href');\n });\n document.addEventListener('keydown', function (e) {\n if (e.key === '/' && document.activeElement !== input && !/input|textarea/i.test(document.activeElement.tagName)) {\n e.preventDefault();\n input.focus();\n }\n if (e.key === 'Escape' && !results.hidden) { render([], ''); }\n });\n document.addEventListener('click', function (e) {\n if (!form.contains(e.target)) render([], '');\n });\n})();\n",
|
|
11247
11594
|
"../etherpk-papermod/assets/theme.css": "/* EtherPK's PaperMod: after Hugo's PaperMod (adityatelange, MIT), written afresh for EtherPK's\n view - the same quiet single column, entry cards, top menu, sun / moon toggle and post\n pagination. System fonts, so the site loads nothing from anywhere. Classes the publisher\n emits and this file styles: .wikilink, .wikilink-missing, .task, .task-tag, .toc, .backlinks,\n .site-nav-list, .diagram, .math, .tok-* (code). */\n\n:root {\n --gap: 24px;\n --content-gap: 20px;\n --nav-width: 1024px;\n --main-width: 720px;\n --header-height: 60px;\n --footer-height: 60px;\n --radius: 8px;\n --font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif;\n --font-mono: ui-monospace, \"Cascadia Code\", \"Fira Code\", Menlo, Consolas, monospace;\n --theme: rgb(255, 255, 255);\n --entry: rgb(255, 255, 255);\n --primary: rgb(30, 30, 30);\n --secondary: rgb(108, 108, 108);\n --tertiary: rgb(214, 214, 214);\n --content: rgb(31, 31, 31);\n --code-block-bg: rgb(28, 29, 33);\n --code-bg: rgb(245, 245, 245);\n --border: rgb(238, 238, 238);\n --mark: #fff3a3;\n --missing: #b45309;\n}\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --theme: rgb(29, 30, 32);\n --entry: rgb(46, 46, 51);\n --primary: rgb(218, 218, 219);\n --secondary: rgb(155, 156, 157);\n --tertiary: rgb(65, 66, 68);\n --content: rgb(196, 196, 197);\n --code-block-bg: rgb(46, 46, 51);\n --code-bg: rgb(55, 56, 62);\n --border: rgb(51, 51, 51);\n --mark: #6b5a00;\n --missing: #f59e0b;\n }\n}\n:root[data-theme=\"dark\"] {\n --theme: rgb(29, 30, 32);\n --entry: rgb(46, 46, 51);\n --primary: rgb(218, 218, 219);\n --secondary: rgb(155, 156, 157);\n --tertiary: rgb(65, 66, 68);\n --content: rgb(196, 196, 197);\n --code-block-bg: rgb(46, 46, 51);\n --code-bg: rgb(55, 56, 62);\n --border: rgb(51, 51, 51);\n --mark: #6b5a00;\n --missing: #f59e0b;\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { -webkit-tap-highlight-color: transparent; scroll-behavior: smooth; }\n@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }\nbody {\n margin: 0;\n font-family: var(--font);\n font-size: 18px;\n line-height: 1.6;\n word-break: break-word;\n background: var(--theme);\n color: var(--content);\n -webkit-font-smoothing: antialiased;\n}\na { color: inherit; text-decoration: none; }\nimg { max-width: 100%; height: auto; display: block; border-radius: 4px; }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--entry); color: var(--primary); padding: 8px 16px; z-index: 100; }\n.skip-link:focus { left: 16px; top: 16px; }\n\n/* -- Header ----------------------------------------------------------------------------- */\n.nav {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n align-items: center;\n max-width: calc(var(--nav-width) + var(--gap) * 2);\n margin-inline: auto;\n height: var(--header-height);\n line-height: var(--header-height);\n padding: 0 var(--gap);\n}\n.logo { display: flex; align-items: center; gap: 10px; }\n.site-title { display: inline-flex; align-items: center; gap: 8px; font-size: 24px; font-weight: 700; color: var(--primary); }\n.site-logo { flex-shrink: 0; }\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n padding: 0;\n border: 0;\n background: none;\n color: var(--primary);\n cursor: pointer;\n}\n.theme-toggle .sun { display: none; }\n:root[data-theme=\"dark\"] .theme-toggle .sun { display: block; }\n:root[data-theme=\"dark\"] .theme-toggle .moon { display: none; }\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) .theme-toggle .sun { display: block; }\n :root:not([data-theme=\"light\"]) .theme-toggle .moon { display: none; }\n}\n#menu { list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: 0 20px; margin: 0; padding: 0; }\n#menu li { position: relative; }\n#menu li > a, #menu .nav-group-label { display: block; font-size: 16px; color: var(--primary); white-space: nowrap; }\n#menu li > a:hover { opacity: 0.8; }\n#menu .nav-current > a { border-bottom: 2px solid currentColor; line-height: calc(var(--header-height) - 2px); }\n#menu .nav-group-label { color: var(--secondary); cursor: default; }\n#menu .nav-open > a, #menu .nav-open > .nav-group-label { color: var(--primary); }\n/* A nested outline level is a drop-down under its parent, opened by hover or keyboard focus\n (the group label is focusable for that; a parent that is a page is a link as well). */\n#menu .site-nav-list {\n display: none;\n position: absolute;\n top: 100%;\n left: 0;\n flex-direction: column;\n min-width: 12rem;\n margin: 0;\n padding: 6px 0;\n list-style: none;\n background: var(--entry);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);\n line-height: 1.6;\n z-index: 25;\n}\n#menu li:hover > .site-nav-list, #menu li:focus-within > .site-nav-list { display: flex; }\n#menu .site-nav-list li > a, #menu .site-nav-list .nav-group-label { padding: 6px 14px; font-size: 15px; line-height: 1.6; border: 0; }\n#menu .site-nav-list .nav-current > a { font-weight: 600; }\n.menu-search { margin-left: auto; }\n.site-search { position: relative; line-height: normal; }\n.site-search input {\n width: 12rem;\n padding: 6px 12px;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--entry);\n color: var(--primary);\n font: inherit;\n font-size: 15px;\n}\n.site-search input:focus { outline: 2px solid var(--secondary); outline-offset: 1px; }\n.site-search-results {\n position: absolute;\n right: 0;\n top: calc(100% + 6px);\n width: min(26rem, 90vw);\n max-height: 24rem;\n overflow: auto;\n background: var(--entry);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 10px 14px; color: var(--primary); border-bottom: 1px solid var(--border); font-size: 15px; line-height: 1.4; }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--code-bg); }\n.site-search-title { display: block; font-weight: 600; }\n.site-search-snippet { display: block; color: var(--secondary); font-size: 14px; }\n.site-search-empty { margin: 0; padding: 10px 14px; color: var(--secondary); font-size: 15px; }\n\n/* -- Main column ------------------------------------------------------------------------ */\n.main {\n position: relative;\n min-height: calc(100vh - var(--header-height) - var(--footer-height));\n max-width: calc(var(--main-width) + var(--gap) * 2);\n margin: auto;\n padding: var(--gap);\n}\n.page-header h1 { font-size: 40px; margin: 0 0 var(--gap); color: var(--primary); }\n\n/* Entries: the card list PaperMod is known for. The whole card is the link. */\n.post-entry {\n position: relative;\n margin-bottom: var(--gap);\n padding: var(--gap);\n background: var(--entry);\n border-radius: var(--radius);\n transition: transform 0.1s;\n border: 1px solid var(--border);\n}\n.post-entry:active { transform: scale(0.96); }\n.entry-header h2 { margin: 0; font-size: 24px; line-height: 1.3; color: var(--primary); }\n.entry-content { margin: 8px 0; color: var(--secondary); font-size: 14px; line-height: 1.6; }\n.entry-content p { margin: 0; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }\n.entry-footer { color: var(--secondary); font-size: 13px; }\n.entry-link { position: absolute; inset: 0; }\n.post-list-empty { color: var(--secondary); }\n\n/* A single post. */\n.post-single .post-header { margin-bottom: var(--content-gap); }\n.post-title { margin: 0 0 8px; font-size: 40px; line-height: 1.2; color: var(--primary); }\n.post-title .wikilink { color: inherit; border: 0; }\n.post-meta { color: var(--secondary); font-size: 14px; display: flex; flex-wrap: wrap; gap: 6px; }\n.home-info { margin-bottom: var(--gap); }\n.post-content { color: var(--content); }\n.post-content h1, .post-content h2, .post-content h3, .post-content h4 { margin: 24px 0 16px; color: var(--primary); }\n.post-content h2 { font-size: 32px; }\n.post-content h3 { font-size: 24px; }\n.post-content h4 { font-size: 16px; }\n.post-content a:not(.wikilink):not(.wikilink-missing) { box-shadow: 0 1px 0 var(--primary); }\n.post-content a:not(.wikilink):not(.wikilink-missing):hover { box-shadow: 0 2px 0 var(--primary); }\n.post-content p { margin: 16px 0; }\n.post-content ul, .post-content ol { padding-left: 20px; }\n.post-content li { margin-top: 5px; }\n.post-content img { margin: 1rem 0; }\n.post-content figure, .post-content .diagram { margin: 1rem auto; text-align: center; }\n.post-content .diagram svg { max-width: 100%; height: auto; }\n.post-content blockquote { margin: 20px 0; padding: 0 14px; border-left: 3px solid var(--primary); color: var(--secondary); }\n.post-content hr { margin: 30px 0; height: 2px; background: var(--tertiary); border: 0; }\n.post-content table { margin: 20px 0; width: 100%; border-collapse: collapse; font-size: 16px; }\n.post-content th, .post-content td { padding: 6px 12px; border: 1px solid var(--border); text-align: left; }\n.post-content th { background: var(--code-bg); color: var(--primary); }\n.post-content code { padding: 2px 6px; background: var(--code-bg); border-radius: 2px; font-family: var(--font-mono); font-size: 0.78em; line-height: 1.5; }\n.post-content pre {\n position: relative;\n margin: 20px 0;\n padding: 12px 14px;\n background: var(--code-block-bg);\n color: rgb(213, 213, 214);\n border-radius: var(--radius);\n overflow-x: auto;\n font-family: var(--font-mono);\n font-size: 0.78em;\n line-height: 1.6;\n}\n.post-content pre code { padding: 0; background: none; color: inherit; font-size: inherit; }\n.math-block { overflow-x: auto; margin: 1rem 0; }\nmark { background: var(--mark); color: inherit; padding: 0 0.15em; border-radius: 2px; }\n\n/* Wikilinks and tasks, as the editor shows them. */\na.wikilink { color: var(--primary); box-shadow: 0 1px 0 var(--tertiary); }\na.wikilink:hover { box-shadow: 0 1px 0 var(--primary); }\na.wikilink-missing { color: var(--missing); box-shadow: 0 1px 0 var(--missing); }\nli.task { list-style: none; margin-left: -20px; }\nli.task input[type=\"checkbox\"] { margin-right: 6px; vertical-align: -0.1em; }\nli.task.done { color: var(--secondary); }\n.task-tag { display: inline-block; font-size: 0.75em; font-weight: 600; padding: 0 8px; border-radius: 999px; background: var(--code-bg); color: var(--primary); margin-right: 4px; vertical-align: 0.1em; }\n.task-tag.priority-1 { background: #fee2e2; color: #991b1b; }\n.task-tag.priority-2 { background: #ffedd5; color: #9a3412; }\n.task-tag.priority-3 { background: #fef9c3; color: #854d0e; }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* Table of contents, the PaperMod box. */\n.toc { margin: 0 0 40px; padding: 0 2px; border: 1px solid var(--border); background: var(--code-bg); border-radius: var(--radius); }\n.toc details summary { cursor: pointer; margin-inline-start: 20px; padding: 12px 0; font-weight: 500; color: var(--primary); }\n.toc .inner { margin: 0 0 12px 20px; padding: 0 15px; font-size: 16px; }\n.toc ul { list-style: none; margin: 0; padding: 0; }\n.toc li { margin: 4px 0; }\n.toc li.toc-level-3 { padding-left: 1rem; }\n.toc li.toc-level-4 { padding-left: 2rem; }\n.toc a { color: var(--content); }\n.toc a:hover { color: var(--primary); }\n\n/* Previous / next, PaperMod's paginav. */\n.post-footer { margin-top: 56px; }\n.paginav { display: flex; gap: 8px; margin: 10px 0; border: 1px solid var(--border); background: var(--code-bg); border-radius: var(--radius); line-height: 30px; }\n.paginav a { flex: 1; padding: 14px 16px; color: var(--primary); }\n.paginav .title { font-size: 13px; letter-spacing: 1px; text-transform: uppercase; color: var(--secondary); }\n.paginav .next { text-align: right; margin-left: auto; }\n\n/* Linked references, beneath the content. */\n.backlinks { max-width: var(--main-width); margin: 40px auto 0; padding-top: 20px; border-top: 1px solid var(--border); }\n.backlinks h2 { font-size: 20px; color: var(--primary); margin: 0 0 12px; }\n.backlink-list { list-style: none; padding: 0; margin: 0; }\n.backlink-source { margin: 0 0 14px; }\n.backlink-source > a { font-weight: 600; color: var(--primary); }\n.backlink-source ul { list-style: none; padding-left: 14px; margin: 4px 0 0; border-left: 2px solid var(--border); }\n.backlink-context { color: var(--secondary); font-size: 15px; margin: 2px 0; }\n\n/* Archive. */\n.archive-entry { position: relative; margin-bottom: 16px; padding: 24px; background: var(--entry); border: 1px solid var(--border); border-radius: var(--radius); }\n.archive-entry-title { margin: 0 0 6px; font-size: 20px; }\n.archive-entry-title a { color: var(--primary); }\n.archive-entry-content { color: var(--secondary); font-size: 14px; }\n/* The contents page of a docs site: the outline as a tree, then any page it leaves out. */\n.page-contents { padding: 24px; background: var(--entry); border: 1px solid var(--border); border-radius: var(--radius); }\n.page-index-list { list-style: none; margin: 0; padding: 0; }\n.page-index-list .page-index-list { padding-left: 20px; margin-top: 6px; }\n.page-index-list li { margin: 6px 0; }\n.page-index-list a { color: var(--primary); }\n.page-index-group-label { color: var(--secondary); font-weight: 600; }\n.page-index-other { margin: 24px 0 8px; font-size: 18px; }\n.archive-year-header { margin: 24px 0 12px; font-size: 32px; color: var(--secondary); }\n.all-posts { margin: 8px 0 0; }\n.all-posts a { color: var(--primary); }\n.not-found { text-align: center; margin: 80px 0; }\n.not-found h1 { font-size: 160px; font-weight: 700; line-height: 1; color: var(--primary); }\n\n/* Footer and the top link. */\n.footer {\n max-width: calc(var(--main-width) + var(--gap) * 2);\n margin: auto;\n padding: calc((var(--footer-height) - 36px) / 2) var(--gap);\n color: var(--secondary);\n font-size: 12px;\n text-align: center;\n line-height: 36px;\n}\n.footer span { margin-inline-start: 1px; }\n.footer a { color: inherit; border-bottom: 1px solid var(--secondary); }\n.top-link {\n position: fixed;\n bottom: 60px;\n right: 30px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: var(--tertiary);\n color: var(--secondary);\n font-size: 22px;\n line-height: 1;\n transition: opacity 0.2s;\n}\n.top-link:hover { color: var(--primary); }\n\n/* Code tokens (lezer classHighlighter names) on the dark code block. */\n.tok-keyword { color: #4ec9b0; }\n.tok-string, .tok-string2 { color: #b5cea8; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: #7f848e; font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: #d19a66; }\n.tok-function, .tok-definition.tok-variableName { color: #dcdcaa; }\n.tok-typeName, .tok-className, .tok-namespace { color: #e5c07b; }\n.tok-operator, .tok-punctuation { color: #abb2bf; }\n.tok-propertyName, .tok-attributeName { color: #61afef; }\n.tok-tagName { color: #98c379; }\n.tok-invalid { color: #f87171; }\n\n/* -- Small screens ---------------------------------------------------------------------- */\n@media (max-width: 768px) {\n :root { --gap: 14px; }\n .nav { padding: 0 var(--gap); }\n #menu { width: 100%; height: 44px; line-height: 44px; }\n .menu-search { margin-left: auto; }\n .site-search input { width: 9rem; }\n .post-title, .page-header h1 { font-size: 30px; }\n .post-content h2 { font-size: 26px; }\n .not-found h1 { font-size: 100px; }\n .top-link { right: 16px; bottom: 30px; }\n}\n@media (max-width: 500px) {\n .nav { height: auto; padding-bottom: 8px; }\n .menu-search { flex-basis: 100%; }\n .site-search input { width: 100%; }\n}\n.nav-toggle, .nav-backdrop { display: none; }\n",
|
|
11248
11595
|
"../etherpk-papermod/assets/theme.js": "/*\n * The light / dark toggle: a `.theme-toggle` button flips the page between the two and the\n * choice is remembered in this browser; with no choice made the page follows the system. The\n * stylesheet keys on `data-theme` on <html>, and shell-top applies the remembered choice before\n * the first paint so a dark reader never sees a light flash.\n */\n(function () {\n 'use strict';\n var KEY = 'etherpk-theme';\n var root = document.documentElement;\n function isDark() {\n var chosen = root.getAttribute('data-theme');\n if (chosen === 'dark' || chosen === 'light') return chosen === 'dark';\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n function apply(theme) {\n if (theme === 'dark' || theme === 'light') root.setAttribute('data-theme', theme);\n else root.removeAttribute('data-theme');\n var buttons = document.querySelectorAll('.theme-toggle');\n for (var i = 0; i < buttons.length; i++) buttons[i].setAttribute('aria-pressed', isDark() ? 'true' : 'false');\n }\n try { apply(localStorage.getItem(KEY)); } catch (_e) { apply(null); }\n var toggles = document.querySelectorAll('.theme-toggle');\n for (var i = 0; i < toggles.length; i++) {\n toggles[i].addEventListener('click', function () {\n var next = isDark() ? 'light' : 'dark';\n apply(next);\n try { localStorage.setItem(KEY, next); } catch (_e) { /* private mode: the choice lasts the page */ }\n });\n }\n})();\n",
|
|
@@ -11254,6 +11601,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11254
11601
|
"../etherpk-papermod/partials/after-content.html": "",
|
|
11255
11602
|
"../etherpk-papermod/partials/backlinks.html": " {{#page.hasBacklinks}}\n <section class=\"backlinks\" aria-labelledby=\"backlinks-heading\">\n <h2 id=\"backlinks-heading\">Linked references</h2>\n <ul class=\"backlink-list\">\n {{#page.backlinks}}\n <li class=\"backlink-source\">\n <a href=\"{{url}}\">{{title}}</a>\n <ul>\n {{#refs}}<li class=\"backlink-context\">{{{contextHtml}}}</li>{{/refs}}\n </ul>\n </li>\n {{/page.backlinks}}\n </ul>\n </section>\n {{/page.hasBacklinks}}\n",
|
|
11256
11603
|
"../etherpk-papermod/partials/before-content.html": "",
|
|
11604
|
+
"../etherpk-papermod/partials/favicon.html": "<!-- The EtherPK icon in the browser tab: assets/favicon.svg, the same file the EtherPK apps use. Fill the \"favicon\" include with your own <link rel=\"icon\"> tag to replace it. -->\n<link rel=\"icon\" href=\"theme/favicon.svg\" type=\"image/svg+xml\">\n",
|
|
11257
11605
|
"../etherpk-papermod/partials/footer.html": " <footer class=\"footer\">\n <span>© {{site.year}} {{site.title}}</span>\n <span>·</span>\n <span>Published with <a href=\"https://etherpk.com\" rel=\"noopener\">EtherPK</a>{{#site.hasFeed}} · <a href=\"feed.xml\">Feed</a>{{/site.hasFeed}}</span>\n </footer>\n",
|
|
11258
11606
|
"../etherpk-papermod/partials/head.html": "",
|
|
11259
11607
|
"../etherpk-papermod/partials/header.html": " <header class=\"header\">\n <nav class=\"nav\" aria-label=\"Site\">\n <div class=\"logo\">\n <a class=\"site-title\" href=\"index.html\">{{> logo}}<span class=\"site-title-text\">{{site.title}}</span></a>\n <button class=\"theme-toggle\" type=\"button\" aria-label=\"Toggle light and dark\" aria-pressed=\"false\">\n <svg class=\"moon\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg>\n <svg class=\"sun\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/></svg>\n </button>\n </div>\n <ul id=\"menu\" class=\"site-nav-list\">\n{{#nav}}{{> nav-item}}{{/nav}}\n <li class=\"menu-search\">\n <form class=\"site-search\" role=\"search\" data-search-index=\"{{site.search.url}}\" data-search-script=\"{{site.search.scriptUrl}}\">\n <label class=\"visually-hidden\" for=\"site-search-input\">Search</label>\n <input id=\"site-search-input\" type=\"search\" name=\"q\" placeholder=\"Search\" autocomplete=\"off\">\n <div class=\"site-search-results\" role=\"listbox\" hidden></div>\n </form>\n </li>\n </ul>\n </nav>\n </header>\n",
|
|
@@ -11263,10 +11611,11 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11263
11611
|
"../etherpk-papermod/partials/post-meta.html": " <div class=\"post-meta\">{{#page.hasDate}}<time datetime=\"{{page.date}}\">{{page.date}}</time> · {{/page.hasDate}}<span>{{page.readingMinutes}} min</span></div>\n",
|
|
11264
11612
|
"../etherpk-papermod/partials/scripts.html": "",
|
|
11265
11613
|
"../etherpk-papermod/partials/shell-bottom.html": " {{> after-content}}\n {{> backlinks}}\n </main>\n {{> footer}}\n <a href=\"#top\" class=\"top-link\" aria-label=\"Go to top\" title=\"Go to top\">↑</a>\n {{> theme-scripts}}\n {{> scripts}}\n</body>\n</html>\n",
|
|
11266
|
-
"../etherpk-papermod/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} | {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n <!-- The remembered light / dark choice, applied before the first paint (theme.js owns the toggle). -->\n <script>try{var t=localStorage.getItem('etherpk-theme');if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}<\/script>\n {{> head}}\n</head>\n<body class=\"layout-papermod{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\" id=\"top\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <main id=\"content\" class=\"main\">\n {{> before-content}}\n",
|
|
11614
|
+
"../etherpk-papermod/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} | {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n <!-- The remembered light / dark choice, applied before the first paint (theme.js owns the toggle). -->\n <script>try{var t=localStorage.getItem('etherpk-theme');if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}<\/script>\n {{> favicon}}\n {{> head}}\n</head>\n<body class=\"layout-papermod{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\" id=\"top\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n {{> header}}\n <main id=\"content\" class=\"main\">\n {{> before-content}}\n",
|
|
11267
11615
|
"../etherpk-papermod/partials/theme-scripts.html": " <script src=\"theme/search.js\" defer><\/script>\n <script src=\"theme/theme.js\" defer><\/script>\n",
|
|
11268
11616
|
"../etherpk-papermod/partials/toc.html": " {{#page.hasToc}}\n <div class=\"toc\">\n <details>\n <summary accesskey=\"c\">Table of contents</summary>\n <div class=\"inner\"><ul>\n {{#page.toc}}<li class=\"toc-level-{{level}}\"><a href=\"#{{id}}\">{{text}}</a></li>{{/page.toc}}\n </ul></div>\n </details>\n </div>\n {{/page.hasToc}}\n",
|
|
11269
|
-
"../etherpk-papermod/theme.json": "{\n \"name\": \"etherpk-papermod\",\n \"title\": \"EtherPK PaperMod\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Hugo's PaperMod: a clean single column, post cards with reading time, a top menu, a light and dark toggle, previous and next between posts.\",\n \"kinds\": [\"blog\", \"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The logo before the site title in the header: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's title, menu, search box and theme toggle.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the theme toggle): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\", \"partials/post-meta.html\", \"partials/post-entry.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/search.js\", \"assets/theme.js\"\n ]\n}\n",
|
|
11617
|
+
"../etherpk-papermod/theme.json": "{\n \"name\": \"etherpk-papermod\",\n \"title\": \"EtherPK PaperMod\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Hugo's PaperMod: a clean single column, post cards with reading time, a top menu, a light and dark toggle, previous and next between posts.\",\n \"kinds\": [\"blog\", \"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The logo before the site title in the header: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"favicon\", \"description\": \"The icon in the browser tab: a <link rel=\\\"icon\\\"> tag. Raw HTML in the page body. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's title, menu, search box and theme toggle.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the theme toggle): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/favicon.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\", \"partials/post-meta.html\", \"partials/post-entry.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/favicon.svg\", \"assets/search.js\", \"assets/theme.js\"\n ]\n}\n",
|
|
11618
|
+
"../etherpk-stack/assets/favicon.svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 191.82 166.13\">\n <path fill=\"#0a0a0a\" fill-rule=\"evenodd\" d=\"M67.09,132.92,47.93,166.13H9.59L0,149.52,86.33,0H105.5L19.17,149.52H38.34l19.17-33.2h76.8l9.59,16.6Zm48-83.12,9.65,16.71H143.9L115.08,16.6q-24,41.57-48,83.12H143.9q14.37,24.9,28.76,49.8h-96l-9.59,16.61H182.24l9.58-16.61-38.34-66.4H96C95.85,82.9,113.39,52.73,115.08,49.8Z\"/>\n</svg>\n",
|
|
11270
11619
|
"../etherpk-stack/assets/search.js": "/*\n * Search for a Published Site: reads search.json (built over the publication's own documents\n * and nothing else) and scores documents by title and text matches. No dependencies, no\n * network beyond the one index file, and nothing runs until the search box is used. The same\n * script runs the sidebar toggle on small screens, so a page carries one script.\n */\n(function () {\n 'use strict';\n\n // -- Navigation toggle (small screens) -------------------------------------------------\n var toggle = document.querySelector('.nav-toggle');\n var backdrop = document.querySelector('.nav-backdrop');\n var nav = document.getElementById('site-nav');\n function setNavOpen(open) {\n var was = document.body.classList.contains('nav-open');\n document.body.classList.toggle('nav-open', open);\n if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n if (backdrop) backdrop.hidden = !open;\n // Focus follows the drawer: into its first link on open, back to the button on close,\n // so a keyboard or screen-reader user is never left on something now hidden.\n if (open && !was) {\n var first = nav.querySelector('a');\n if (first) first.focus();\n } else if (!open && was && toggle) {\n toggle.focus();\n }\n }\n if (toggle && nav) {\n toggle.addEventListener('click', function () { setNavOpen(!document.body.classList.contains('nav-open')); });\n if (backdrop) backdrop.addEventListener('click', function () { setNavOpen(false); });\n document.addEventListener('keydown', function (e) {\n if (e.key === 'Escape' && document.body.classList.contains('nav-open')) setNavOpen(false);\n });\n }\n\n // -- Search --------------------------------------------------------------------------\n var form = document.querySelector('.site-search');\n if (!form) return;\n var input = form.querySelector('input[type=\"search\"]');\n var results = form.querySelector('.site-search-results');\n var indexUrl = form.getAttribute('data-search-index') || 'search.json';\n var scriptUrl = form.getAttribute('data-search-script') || 'search-index.js';\n var index = null;\n var loading = null;\n /* True once neither route produced the index: the results then say so rather than \"nothing matches\". */\n var unavailable = false;\n\n /* The index as a script assigning window.etherpkSearchIndex: what a site opened from disk can\n load, since file:// refuses fetch but allows <script src>. */\n function loadScript() {\n return new Promise(function (resolve, reject) {\n if (window.etherpkSearchIndex) { resolve(window.etherpkSearchIndex); return; }\n var s = document.createElement('script');\n s.src = scriptUrl;\n s.onload = function () {\n if (window.etherpkSearchIndex) resolve(window.etherpkSearchIndex);\n else reject(new Error('no index'));\n };\n s.onerror = function () { reject(new Error('no script')); };\n document.head.appendChild(s);\n });\n }\n\n function load() {\n if (index) return Promise.resolve(index);\n if (!loading) {\n loading = fetch(indexUrl).then(function (r) {\n if (!r.ok) throw new Error(String(r.status));\n return r.json();\n }).catch(loadScript).then(function (data) {\n index = (data && data.documents) || [];\n return index;\n }).catch(function () { unavailable = true; index = []; return index; });\n }\n return loading;\n }\n\n function tokens(q) {\n return q.toLowerCase().split(/\\s+/).filter(function (t) { return t.length > 0; });\n }\n\n function score(doc, terms) {\n var title = (doc.title || '').toLowerCase();\n var text = (doc.text || '').toLowerCase();\n var s = 0;\n for (var i = 0; i < terms.length; i++) {\n var t = terms[i];\n if (title === t) s += 20;\n else if (title.indexOf(t) === 0) s += 10;\n else if (title.indexOf(t) !== -1) s += 6;\n var at = text.indexOf(t);\n if (at !== -1) s += 2;\n else if (title.indexOf(t) === -1) return 0; // every term must match somewhere\n }\n return s;\n }\n\n function snippet(doc, terms) {\n var text = doc.text || '';\n var lower = text.toLowerCase();\n var at = -1;\n for (var i = 0; i < terms.length && at === -1; i++) at = lower.indexOf(terms[i]);\n if (at === -1) return doc.excerpt || '';\n var start = Math.max(0, at - 60);\n var end = Math.min(text.length, at + 100);\n return (start > 0 ? '…' : '') + text.slice(start, end).replace(/\\s+/g, ' ') + (end < text.length ? '…' : '');\n }\n\n function escapeHtml(s) {\n return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n function render(list, q) {\n if (!q) { results.hidden = true; results.innerHTML = ''; return; }\n if (unavailable) {\n results.innerHTML = '<p class=\"site-search-empty\">The search index could not be loaded.</p>';\n results.hidden = false;\n return;\n }\n if (list.length === 0) {\n results.innerHTML = '<p class=\"site-search-empty\">Nothing matches \"' + escapeHtml(q) + '\".</p>';\n results.hidden = false;\n return;\n }\n results.innerHTML = list.map(function (hit) {\n return '<a class=\"site-search-hit\" role=\"option\" href=\"' + escapeHtml(hit.doc.url) + '\">' +\n '<span class=\"site-search-title\">' + escapeHtml(hit.doc.title) + '</span>' +\n '<span class=\"site-search-snippet\">' + escapeHtml(hit.snippet) + '</span></a>';\n }).join('');\n results.hidden = false;\n }\n\n var timer = null;\n function run() {\n var q = input.value.trim();\n var terms = tokens(q);\n if (terms.length === 0) { render([], ''); return; }\n load().then(function (docs) {\n var hits = [];\n for (var i = 0; i < docs.length; i++) {\n var s = score(docs[i], terms);\n if (s > 0) hits.push({ doc: docs[i], score: s, snippet: snippet(docs[i], terms) });\n }\n hits.sort(function (a, b) { return b.score - a.score || a.doc.title.localeCompare(b.doc.title); });\n render(hits.slice(0, 12), q);\n });\n }\n\n input.addEventListener('input', function () {\n clearTimeout(timer);\n timer = setTimeout(run, 120);\n });\n input.addEventListener('focus', function () { load(); });\n form.addEventListener('submit', function (e) {\n e.preventDefault();\n var first = results.querySelector('a');\n if (first) window.location.href = first.getAttribute('href');\n });\n document.addEventListener('keydown', function (e) {\n if (e.key === '/' && document.activeElement !== input && !/input|textarea/i.test(document.activeElement.tagName)) {\n e.preventDefault();\n input.focus();\n }\n if (e.key === 'Escape' && !results.hidden) { render([], ''); }\n });\n document.addEventListener('click', function (e) {\n if (!form.contains(e.target)) render([], '');\n });\n})();\n",
|
|
11271
11620
|
"../etherpk-stack/assets/theme.css": "/* EtherPK's Stack: after Hugo's Stack (Jimmy Cai), written afresh for EtherPK's view - the\n three-column card layout: identity and menu on the left, article cards on a soft grey ground\n in the middle, widgets on the right, rounded corners and a faint shadow on every card, and\n the sidebar folding into a header with a hamburger on a phone. System fonts, so the site\n loads nothing from anywhere. Classes the publisher emits and this file styles: .wikilink,\n .wikilink-missing, .task, .task-tag, .toc, .backlinks, .site-nav-list, .diagram, .math,\n .tok-* (code). */\n\n:root {\n --font-sans: \"Lato\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n --font-mono: ui-monospace, Menlo, Consolas, \"Liberation Mono\", monospace;\n --body-background: #f5f5fa;\n --card-background: #fff;\n --card-background-selected: #eaeaea;\n --card-text-color-main: #000;\n --card-text-color-secondary: #747474;\n --card-text-color-tertiary: #767676;\n --card-separator-color: rgba(218, 218, 218, 0.5);\n --card-border-radius: 10px;\n --card-padding: 25px;\n --accent-color: #34495e;\n --accent-color-darker: #2c3e50;\n --accent-color-text: #fff;\n --body-text-color: #bababa;\n --tag-border-radius: 4px;\n --section-separation: 40px;\n --left-sidebar-width: 300px;\n --right-sidebar-width: 300px;\n --shadow-l1: 0 4px 8px rgba(0, 0, 0, 0.04), 0 0 2px rgba(0, 0, 0, 0.06), 0 0 1px rgba(0, 0, 0, 0.04);\n --shadow-l2: 0 10px 20px rgba(0, 0, 0, 0.04), 0 2px 6px rgba(0, 0, 0, 0.04), 0 0 1px rgba(0, 0, 0, 0.04);\n --code-bg: #f8f8f8;\n --mark: #fff3a3;\n --missing: #b45309;\n}\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --body-background: #303030;\n --card-background: #424242;\n --card-background-selected: rgba(255, 255, 255, 0.16);\n --card-text-color-main: rgba(255, 255, 255, 0.9);\n --card-text-color-secondary: rgba(255, 255, 255, 0.7);\n --card-text-color-tertiary: rgba(255, 255, 255, 0.5);\n --card-separator-color: rgba(255, 255, 255, 0.12);\n --accent-color: #bdbdbd;\n --accent-color-darker: #e0e0e0;\n --accent-color-text: #000;\n --code-bg: #2e2e2e;\n --mark: #6b5a00;\n --missing: #f59e0b;\n }\n}\n:root[data-theme=\"dark\"] {\n --body-background: #303030;\n --card-background: #424242;\n --card-background-selected: rgba(255, 255, 255, 0.16);\n --card-text-color-main: rgba(255, 255, 255, 0.9);\n --card-text-color-secondary: rgba(255, 255, 255, 0.7);\n --card-text-color-tertiary: rgba(255, 255, 255, 0.5);\n --card-separator-color: rgba(255, 255, 255, 0.12);\n --accent-color: #bdbdbd;\n --accent-color-darker: #e0e0e0;\n --accent-color-text: #000;\n --code-bg: #2e2e2e;\n --mark: #6b5a00;\n --missing: #f59e0b;\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { font-size: 62.5%; }\nbody {\n margin: 0;\n font-family: var(--font-sans);\n font-size: 1.6rem;\n line-height: 1.6;\n background: var(--body-background);\n color: var(--card-text-color-main);\n -webkit-font-smoothing: antialiased;\n}\na { color: var(--accent-color); text-decoration: none; }\na:hover { color: var(--accent-color-darker); }\nimg { max-width: 100%; height: auto; display: block; }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--card-background); padding: 8px 16px; z-index: 100; }\n.skip-link:focus { left: 16px; top: 16px; }\n\n/* -- The three columns ------------------------------------------------------------------ */\n.container {\n display: grid;\n grid-template-columns: var(--left-sidebar-width) minmax(0, 1fr) var(--right-sidebar-width);\n gap: var(--section-separation);\n max-width: 1280px;\n margin: 0 auto;\n padding: 0 15px;\n}\n.sidebar { position: sticky; top: 0; align-self: flex-start; max-height: 100vh; overflow-y: auto; padding: var(--section-separation) 0; }\n.main { padding: var(--section-separation) 0; }\n\n/* -- Left sidebar: identity and menu ---------------------------------------------------- */\n.site-info { display: flex; flex-direction: column; gap: 10px; margin-bottom: var(--section-separation); }\n.site-title { display: flex; flex-direction: column; gap: 15px; color: var(--card-text-color-main); }\n.site-avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100px;\n height: 100px;\n border-radius: 100%;\n background: var(--card-background);\n box-shadow: var(--shadow-l1);\n color: var(--accent-color);\n}\n.site-avatar .site-logo { width: 48px; height: 42px; }\n.site-name { font-size: 1.6rem; font-weight: 700; }\n.site-description { margin: 0; font-size: 1.4rem; color: var(--card-text-color-secondary); }\n.menu { display: flex; flex-direction: column; gap: 10px; }\n.site-nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }\n.site-nav-list li > a, .nav-group-label, .menu-item {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 10px 20px;\n border-radius: 6px;\n color: var(--card-text-color-main);\n font-size: 1.4rem;\n text-decoration: none;\n}\n.site-nav-list li > a:hover, .menu-item:hover { background: var(--card-background-selected); color: var(--card-text-color-main); }\n.site-nav-list .nav-current > a { background: var(--accent-color); color: var(--accent-color-text); }\n.nav-group-label { font-weight: 700; color: var(--card-text-color-secondary); font-size: 1.3rem; padding-bottom: 0; }\n.site-nav-list .site-nav-list { padding-left: 16px; margin-top: 6px; }\n.menu-item { border: 0; background: none; font: inherit; font-size: 1.4rem; cursor: pointer; width: 100%; text-align: left; }\n.nav-toggle, .nav-backdrop { display: none; }\n\n/* -- Cards ------------------------------------------------------------------------------ */\n.article-list { display: flex; flex-direction: column; gap: var(--section-separation); }\n.article-card, .article-page {\n display: block;\n background: var(--card-background);\n border-radius: var(--card-border-radius);\n box-shadow: var(--shadow-l1);\n overflow: hidden;\n transition: box-shadow 0.3s ease;\n}\n.article-card:hover { box-shadow: var(--shadow-l2); }\n.article-details { padding: var(--card-padding); }\n.article-category { display: inline-block; margin-bottom: 8px; font-size: 1.2rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--card-text-color-tertiary); }\n.article-card .article-title { margin: 0 0 10px; font-size: 2.2rem; line-height: 1.3; font-weight: 700; }\n.article-card .article-title a { color: var(--card-text-color-main); }\n.article-subtitle { margin: 0 0 15px; font-size: 1.6rem; color: var(--card-text-color-secondary); }\n.article-time { font-size: 1.4rem; color: var(--card-text-color-tertiary); }\n.article-list-empty { color: var(--card-text-color-secondary); }\n.all-posts { margin: 0; }\n.all-posts a { color: var(--accent-color); font-weight: 600; text-decoration: none; }\n/* The contents page of a docs site: the outline as a tree in one card, then any page it leaves out. */\n.page-index-list { list-style: none; margin: 0; padding: 0; }\n.page-index-list .page-index-list { padding-left: 2rem; margin-top: 0.35rem; }\n.page-index-list li { margin: 0.35rem 0; }\n.page-index-list a { color: var(--card-text-color-main); text-decoration: none; }\n.page-index-list a:hover { color: var(--accent-color); }\n.page-index-group-label { color: var(--card-text-color-secondary); font-weight: 700; text-transform: uppercase; font-size: 1.3rem; letter-spacing: 0.05em; }\n.page-index-other { margin: 2rem 0 0.5rem; font-size: 1.8rem; }\n/* The posts archive: a card per year, one line per post inside it. */\n.archive-year { padding: var(--card-padding); background: var(--card-background); border-radius: var(--card-border-radius); box-shadow: var(--shadow-l1); margin-bottom: var(--section-separation); }\n.archive-year-title { margin: 0 0 0.75rem; font-size: 1.6rem; color: var(--card-text-color-main); }\n.archive-list { list-style: none; margin: 0; padding: 0; }\n.archive-list li { padding: 0.35rem 0; }\n.archive-list time { color: var(--card-text-color-tertiary); margin-right: 0.75rem; font-variant-numeric: tabular-nums; }\n.archive-list a { color: var(--card-text-color-main); text-decoration: none; }\n.archive-list a:hover { color: var(--accent-color); }\n.section-header h1 { margin: 0 0 20px; font-size: 2.6rem; }\n\n/* A single article. */\n.article-header { padding: var(--card-padding) var(--card-padding) 0; }\n.article-page .article-title { margin: 0 0 10px; font-size: 2.6rem; line-height: 1.25; font-weight: 700; }\n.article-page .article-title .wikilink { color: inherit; }\n.article-header .article-details { padding: 0; margin-bottom: 20px; font-size: 1.4rem; color: var(--card-text-color-tertiary); }\n.article-content { padding: 0 var(--card-padding) var(--card-padding); font-size: 1.6rem; color: var(--card-text-color-main); }\n.article-content h1, .article-content h2, .article-content h3, .article-content h4 { margin: 2em 0 0.8em; line-height: 1.3; }\n.article-content h2 { font-size: 2.2rem; }\n.article-content h3 { font-size: 1.8rem; }\n.article-content p { margin: 1em 0; }\n.article-content ul, .article-content ol { padding-left: 24px; }\n.article-content li { margin: 4px 0; }\n.article-content img { margin: 1.5em auto; border-radius: var(--card-border-radius); }\n.article-content .diagram { margin: 1.5em 0; text-align: center; }\n.article-content .diagram svg { max-width: 100%; height: auto; }\n.article-content blockquote { margin: 1.5em 0; padding: 15px 22px; border-left: 4px solid var(--accent-color); background: var(--card-background-selected); border-radius: 0 var(--tag-border-radius) var(--tag-border-radius) 0; }\n.article-content blockquote p { margin: 0; }\n.article-content table { width: 100%; border-collapse: collapse; margin: 1.5em 0; }\n.article-content th, .article-content td { padding: 8px 12px; border-bottom: 1px solid var(--card-separator-color); text-align: left; }\n.article-content th { color: var(--card-text-color-secondary); font-size: 1.4rem; text-transform: uppercase; letter-spacing: 0.03em; }\n.article-content code { padding: 2px 6px; background: var(--code-bg); border-radius: var(--tag-border-radius); font-family: var(--font-mono); font-size: 0.9em; }\n.article-content pre { margin: 1.5em 0; padding: 20px; background: var(--code-bg); border-radius: var(--card-border-radius); overflow-x: auto; font-family: var(--font-mono); font-size: 1.4rem; line-height: 1.5; }\n.article-content pre code { padding: 0; background: none; font-size: inherit; }\n.article-content hr { border: 0; border-top: 1px solid var(--card-separator-color); margin: 2em 0; }\n.math-block { overflow-x: auto; margin: 1.5em 0; }\nmark { background: var(--mark); color: inherit; padding: 0 0.15em; border-radius: 2px; }\n\n/* Wikilinks and tasks. */\na.wikilink { color: var(--accent-color); border-bottom: 1px solid var(--card-separator-color); }\na.wikilink:hover { border-bottom-color: var(--accent-color); }\na.wikilink-missing { color: var(--missing); border-bottom: 1px dashed var(--missing); }\nli.task { list-style: none; margin-left: -24px; }\nli.task input[type=\"checkbox\"] { accent-color: var(--accent-color); margin-right: 6px; vertical-align: -0.1em; }\nli.task.done { color: var(--card-text-color-tertiary); }\n.task-tag { display: inline-block; padding: 1px 8px; margin-right: 4px; border-radius: var(--tag-border-radius); background: var(--card-background-selected); font-size: 1.2rem; font-weight: 700; }\n.task-tag.priority-1 { background: #fee2e2; color: #991b1b; }\n.task-tag.priority-2 { background: #ffedd5; color: #9a3412; }\n.task-tag.priority-3 { background: #fef9c3; color: #854d0e; }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* Previous / next as small cards. */\n.article-footer { padding: 0 var(--card-padding) var(--card-padding); }\n.article-pagination { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }\n.pagination-card { display: flex; flex-direction: column; gap: 4px; padding: 15px 20px; background: var(--body-background); box-shadow: none; color: var(--card-text-color-main); }\n.pagination-card.next { text-align: right; grid-column: 2; }\n.pagination-label { font-size: 1.2rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--card-text-color-tertiary); }\n.pagination-title { font-weight: 700; }\n\n/* Linked references, as a card. */\n.backlinks { margin-top: var(--section-separation); padding: var(--card-padding); background: var(--card-background); border-radius: var(--card-border-radius); box-shadow: var(--shadow-l1); }\n.backlinks h2 { margin: 0 0 15px; font-size: 1.8rem; }\n.backlink-list { list-style: none; margin: 0; padding: 0; }\n.backlink-source { margin: 0 0 15px; }\n.backlink-source > a { font-weight: 700; color: var(--card-text-color-main); }\n.backlink-source ul { list-style: none; margin: 4px 0 0; padding: 0 0 0 15px; border-left: 2px solid var(--card-separator-color); }\n.backlink-context { margin: 2px 0; font-size: 1.4rem; color: var(--card-text-color-secondary); }\n\n/* -- Right sidebar: widgets ------------------------------------------------------------- */\n.right-sidebar { display: flex; flex-direction: column; gap: var(--section-separation); }\n.widget { display: flex; flex-direction: column; gap: 15px; }\n.widget-title { margin: 0; font-size: 1.4rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--card-text-color-secondary); }\n.widget-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }\n.widget-list li a {\n display: block;\n padding: 10px 15px;\n background: var(--card-background);\n border-radius: var(--tag-border-radius);\n box-shadow: var(--shadow-l1);\n color: var(--card-text-color-main);\n font-size: 1.4rem;\n}\n.widget-list li a:hover { box-shadow: var(--shadow-l2); }\n.widget-list li a { display: flex; justify-content: space-between; gap: 10px; }\n.widget-count { color: var(--card-text-color-tertiary); }\n.widget-more { margin: 0; font-size: 1.4rem; }\n.toc ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }\n.toc li.toc-level-3 { padding-left: 1.5rem; }\n.toc li.toc-level-4 { padding-left: 3rem; }\n.toc a { color: var(--card-text-color-secondary); font-size: 1.4rem; }\n.toc a:hover { color: var(--accent-color); }\n.site-search { position: relative; }\n.site-search input {\n width: 100%;\n padding: 10px 15px;\n border: 0;\n border-radius: var(--card-border-radius);\n background: var(--card-background);\n box-shadow: var(--shadow-l1);\n color: var(--card-text-color-main);\n font: inherit;\n font-size: 1.4rem;\n}\n.site-search input:focus { outline: 2px solid var(--accent-color); outline-offset: 1px; }\n.site-search-results {\n position: absolute;\n left: 0;\n right: 0;\n top: calc(100% + 8px);\n max-height: 24rem;\n overflow: auto;\n background: var(--card-background);\n border-radius: var(--card-border-radius);\n box-shadow: var(--shadow-l2);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 10px 15px; color: var(--card-text-color-main); border-bottom: 1px solid var(--card-separator-color); font-size: 1.4rem; }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--card-background-selected); }\n.site-search-title { display: block; font-weight: 700; }\n.site-search-snippet { display: block; color: var(--card-text-color-secondary); font-size: 1.3rem; }\n.site-search-empty { margin: 0; padding: 10px 15px; color: var(--card-text-color-secondary); font-size: 1.4rem; }\n\n/* -- Footer ----------------------------------------------------------------------------- */\n.site-footer { margin-top: var(--section-separation); font-size: 1.4rem; color: var(--card-text-color-tertiary); }\n.site-footer p { margin: 4px 0; }\n\n/* Code tokens (lezer classHighlighter names). */\n.tok-keyword { color: #0a7; }\n.tok-string, .tok-string2 { color: #690; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: #999; font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: #905; }\n.tok-function, .tok-definition.tok-variableName { color: #c60; }\n.tok-typeName, .tok-className, .tok-namespace { color: #b58900; }\n.tok-operator, .tok-punctuation { color: #555; }\n.tok-propertyName, .tok-attributeName { color: #268bd2; }\n.tok-tagName { color: #22863a; }\n.tok-invalid { color: #d00; }\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) .tok-keyword { color: #4ec9b0; }\n :root:not([data-theme=\"light\"]) .tok-string, :root:not([data-theme=\"light\"]) .tok-string2 { color: #b5cea8; }\n :root:not([data-theme=\"light\"]) .tok-number, :root:not([data-theme=\"light\"]) .tok-atom { color: #d19a66; }\n :root:not([data-theme=\"light\"]) .tok-function { color: #dcdcaa; }\n :root:not([data-theme=\"light\"]) .tok-typeName, :root:not([data-theme=\"light\"]) .tok-className { color: #e5c07b; }\n :root:not([data-theme=\"light\"]) .tok-operator, :root:not([data-theme=\"light\"]) .tok-punctuation { color: #abb2bf; }\n :root:not([data-theme=\"light\"]) .tok-propertyName { color: #61afef; }\n :root:not([data-theme=\"light\"]) .tok-tagName { color: #98c379; }\n}\n:root[data-theme=\"dark\"] .tok-keyword { color: #4ec9b0; }\n:root[data-theme=\"dark\"] .tok-string, :root[data-theme=\"dark\"] .tok-string2 { color: #b5cea8; }\n:root[data-theme=\"dark\"] .tok-number, :root[data-theme=\"dark\"] .tok-atom { color: #d19a66; }\n:root[data-theme=\"dark\"] .tok-function { color: #dcdcaa; }\n:root[data-theme=\"dark\"] .tok-typeName, :root[data-theme=\"dark\"] .tok-className { color: #e5c07b; }\n:root[data-theme=\"dark\"] .tok-operator, :root[data-theme=\"dark\"] .tok-punctuation { color: #abb2bf; }\n:root[data-theme=\"dark\"] .tok-propertyName { color: #61afef; }\n:root[data-theme=\"dark\"] .tok-tagName { color: #98c379; }\n\n/* -- Two columns, then one -------------------------------------------------------------- */\n@media (max-width: 1024px) {\n .container { grid-template-columns: var(--left-sidebar-width) minmax(0, 1fr); }\n .right-sidebar { display: none; }\n}\n@media (max-width: 768px) {\n .container { display: block; padding: 0 15px; }\n .left-sidebar {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 15px;\n max-height: none;\n padding: 20px 0;\n overflow: visible;\n }\n .site-info { margin: 0; }\n .site-title { flex-direction: row; align-items: center; gap: 12px; }\n .site-avatar { width: 48px; height: 48px; }\n .site-avatar .site-logo { width: 24px; height: 21px; }\n .site-description { display: none; }\n .nav-toggle {\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 5px;\n width: 44px;\n height: 44px;\n padding: 10px;\n border: 0;\n border-radius: var(--tag-border-radius);\n background: var(--card-background);\n box-shadow: var(--shadow-l1);\n cursor: pointer;\n }\n .nav-toggle span { display: block; height: 2px; background: var(--card-text-color-main); border-radius: 2px; }\n .menu {\n position: fixed;\n top: 0;\n left: 0;\n bottom: 0;\n width: min(30rem, 85vw); /* rem is 10px here */\n padding: 30px 20px;\n background: var(--card-background);\n box-shadow: var(--shadow-l2);\n transform: translateX(-100%);\n visibility: hidden;\n transition: transform 160ms ease-out, visibility 0s linear 160ms;\n z-index: 40;\n overflow-y: auto;\n }\n body.nav-open .menu { transform: translateX(0); visibility: visible; transition: transform 160ms ease-out; }\n body.nav-open .nav-backdrop { display: block; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.35); z-index: 35; }\n .main { padding: 10px 0 var(--section-separation); }\n .article-card .article-title { font-size: 1.9rem; }\n .article-page .article-title { font-size: 2.2rem; }\n .article-pagination { grid-template-columns: 1fr; }\n .pagination-card.next { grid-column: auto; }\n}\n@media (prefers-reduced-motion: reduce) {\n .menu, body.nav-open .menu, .article-card { transition: none; }\n}\n",
|
|
11272
11621
|
"../etherpk-stack/assets/theme.js": "/*\n * The light / dark toggle: a `.theme-toggle` button flips the page between the two and the\n * choice is remembered in this browser; with no choice made the page follows the system. The\n * stylesheet keys on `data-theme` on <html>, and shell-top applies the remembered choice before\n * the first paint so a dark reader never sees a light flash.\n */\n(function () {\n 'use strict';\n var KEY = 'etherpk-theme';\n var root = document.documentElement;\n function isDark() {\n var chosen = root.getAttribute('data-theme');\n if (chosen === 'dark' || chosen === 'light') return chosen === 'dark';\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n function apply(theme) {\n if (theme === 'dark' || theme === 'light') root.setAttribute('data-theme', theme);\n else root.removeAttribute('data-theme');\n var buttons = document.querySelectorAll('.theme-toggle');\n for (var i = 0; i < buttons.length; i++) buttons[i].setAttribute('aria-pressed', isDark() ? 'true' : 'false');\n }\n try { apply(localStorage.getItem(KEY)); } catch (_e) { apply(null); }\n var toggles = document.querySelectorAll('.theme-toggle');\n for (var i = 0; i < toggles.length; i++) {\n toggles[i].addEventListener('click', function () {\n var next = isDark() ? 'light' : 'dark';\n apply(next);\n try { localStorage.setItem(KEY, next); } catch (_e) { /* private mode: the choice lasts the page */ }\n });\n }\n})();\n",
|
|
@@ -11279,6 +11628,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11279
11628
|
"../etherpk-stack/partials/article-card.html": " <article class=\"article-card\">\n <div class=\"article-details\">\n <span class=\"article-category\">{{kind}}</span>\n <h2 class=\"article-title\"><a href=\"{{url}}\">{{title}}</a></h2>\n {{#excerpt}}<p class=\"article-subtitle\">{{excerpt}}</p>{{/excerpt}}\n <footer class=\"article-time\">{{#date}}<time datetime=\"{{date}}\">{{date}}</time>{{/date}}</footer>\n </div>\n </article>\n",
|
|
11280
11629
|
"../etherpk-stack/partials/backlinks.html": " {{#page.hasBacklinks}}\n <section class=\"backlinks\" aria-labelledby=\"backlinks-heading\">\n <h2 id=\"backlinks-heading\">Linked references</h2>\n <ul class=\"backlink-list\">\n {{#page.backlinks}}\n <li class=\"backlink-source\">\n <a href=\"{{url}}\">{{title}}</a>\n <ul>\n {{#refs}}<li class=\"backlink-context\">{{{contextHtml}}}</li>{{/refs}}\n </ul>\n </li>\n {{/page.backlinks}}\n </ul>\n </section>\n {{/page.hasBacklinks}}\n",
|
|
11281
11630
|
"../etherpk-stack/partials/before-content.html": "",
|
|
11631
|
+
"../etherpk-stack/partials/favicon.html": "<!-- The EtherPK icon in the browser tab: assets/favicon.svg, the same file the EtherPK apps use. Fill the \"favicon\" include with your own <link rel=\"icon\"> tag to replace it. -->\n<link rel=\"icon\" href=\"theme/favicon.svg\" type=\"image/svg+xml\">\n",
|
|
11282
11632
|
"../etherpk-stack/partials/footer.html": " <footer class=\"site-footer\">\n <p>© {{site.year}} {{site.title}}{{#site.hasFeed}} · <a href=\"feed.xml\">Feed</a>{{/site.hasFeed}}</p>\n <p>Published with <a href=\"https://etherpk.com\" rel=\"noopener\">EtherPK</a></p>\n </footer>\n",
|
|
11283
11633
|
"../etherpk-stack/partials/head.html": "",
|
|
11284
11634
|
"../etherpk-stack/partials/header.html": " <header class=\"site-info\">\n <a class=\"site-title\" href=\"index.html\">\n <span class=\"site-avatar\">{{> logo}}</span>\n <span class=\"site-name\">{{site.title}}</span>\n </a>\n {{#page.isHome}}{{#page.excerpt}}<p class=\"site-description\">{{page.excerpt}}</p>{{/page.excerpt}}{{/page.isHome}}\n </header>\n",
|
|
@@ -11286,11 +11636,12 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11286
11636
|
"../etherpk-stack/partials/nav-item.html": " <li class=\"{{#current}}nav-current {{/current}}{{#open}}nav-open {{/open}}{{^href}}nav-group{{/href}}\">\n {{#href}}<a href=\"{{href}}\"{{#current}} aria-current=\"page\"{{/current}}{{#external}} rel=\"noopener\"{{/external}}>{{label}}</a>{{/href}}\n {{^href}}<span class=\"nav-group-label\">{{{labelHtml}}}</span>{{/href}}\n {{#hasChildren}}<ul class=\"site-nav-list\">{{#children}}{{> nav-item}}{{/children}}</ul>{{/hasChildren}}\n </li>\n",
|
|
11287
11637
|
"../etherpk-stack/partials/scripts.html": "",
|
|
11288
11638
|
"../etherpk-stack/partials/shell-bottom.html": " {{> after-content}}\n {{> backlinks}}\n {{> footer}}\n </main>\n <aside class=\"sidebar right-sidebar\">\n {{> widgets}}\n </aside>\n </div>\n {{> theme-scripts}}\n {{> scripts}}\n</body>\n</html>\n",
|
|
11289
|
-
"../etherpk-stack/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} - {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n <script>try{var t=localStorage.getItem('etherpk-theme');if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}<\/script>\n {{> head}}\n</head>\n<body class=\"layout-stack{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n <div class=\"container\">\n <aside class=\"sidebar left-sidebar\">\n {{> header}}\n <button class=\"nav-toggle menu-icon\" type=\"button\" aria-controls=\"site-nav\" aria-expanded=\"false\" aria-label=\"Menu\"><span></span><span></span><span></span></button>\n <div class=\"nav-backdrop\" hidden></div>\n <nav id=\"site-nav\" class=\"menu site-nav\" aria-label=\"Site\">\n <ul class=\"site-nav-list\">\n{{#nav}}{{> nav-item}}{{/nav}}\n </ul>\n <button class=\"theme-toggle menu-item\" type=\"button\" aria-pressed=\"false\"><svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg><span>Dark mode</span></button>\n </nav>\n </aside>\n <main id=\"content\" class=\"main\">\n {{> before-content}}\n",
|
|
11639
|
+
"../etherpk-stack/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} - {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n <script>try{var t=localStorage.getItem('etherpk-theme');if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}<\/script>\n {{> favicon}}\n {{> head}}\n</head>\n<body class=\"layout-stack{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n <div class=\"container\">\n <aside class=\"sidebar left-sidebar\">\n {{> header}}\n <button class=\"nav-toggle menu-icon\" type=\"button\" aria-controls=\"site-nav\" aria-expanded=\"false\" aria-label=\"Menu\"><span></span><span></span><span></span></button>\n <div class=\"nav-backdrop\" hidden></div>\n <nav id=\"site-nav\" class=\"menu site-nav\" aria-label=\"Site\">\n <ul class=\"site-nav-list\">\n{{#nav}}{{> nav-item}}{{/nav}}\n </ul>\n <button class=\"theme-toggle menu-item\" type=\"button\" aria-pressed=\"false\"><svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg><span>Dark mode</span></button>\n </nav>\n </aside>\n <main id=\"content\" class=\"main\">\n {{> before-content}}\n",
|
|
11290
11640
|
"../etherpk-stack/partials/theme-scripts.html": " <script src=\"theme/search.js\" defer><\/script>\n <script src=\"theme/theme.js\" defer><\/script>\n",
|
|
11291
11641
|
"../etherpk-stack/partials/toc.html": " <nav class=\"toc\" aria-label=\"Table of contents\">\n <ul>\n {{#page.toc}}<li class=\"toc-level-{{level}}\"><a href=\"#{{id}}\">{{text}}</a></li>{{/page.toc}}\n </ul>\n </nav>\n",
|
|
11292
11642
|
"../etherpk-stack/partials/widgets.html": " <section class=\"widget search-widget\" aria-label=\"Search\">\n <form class=\"site-search\" role=\"search\" data-search-index=\"{{site.search.url}}\" data-search-script=\"{{site.search.scriptUrl}}\">\n <label class=\"visually-hidden\" for=\"site-search-input\">Search</label>\n <input id=\"site-search-input\" type=\"search\" name=\"q\" placeholder=\"Search\" autocomplete=\"off\">\n <div class=\"site-search-results\" role=\"listbox\" hidden></div>\n </form>\n </section>\n {{#page.hasToc}}\n <section class=\"widget toc-widget\">\n <h2 class=\"widget-title\">Table of contents</h2>\n{{> toc}}\n </section>\n {{/page.hasToc}}\n {{#site.hasPostsArchive}}\n <!-- Years with counts, as Stack's archives widget does: a list of every post would grow\n with the blog; the archive page carries them. -->\n <section class=\"widget archives\">\n <h2 class=\"widget-title\">Archives</h2>\n <ul class=\"widget-list\">\n {{#site.postsByYear}}<li><a href=\"{{site.postsUrl}}#y{{year}}\"><span>{{year}}</span><span class=\"widget-count\">{{count}}</span></a></li>{{/site.postsByYear}}\n </ul>\n <p class=\"widget-more\"><a href=\"{{site.postsUrl}}\">All posts</a></p>\n </section>\n {{/site.hasPostsArchive}}\n {{#site.hasJournals}}\n <section class=\"widget journal\">\n <h2 class=\"widget-title\">Journal</h2>\n <p class=\"widget-more\"><a href=\"journal.html\">Every entry, by day</a></p>\n </section>\n {{/site.hasJournals}}\n <!-- A blog's posts are in the archive above; only its other pages belong here. -->\n {{#site.isBlog}}{{#site.hasUndatedPages}}\n <section class=\"widget pages\">\n <h2 class=\"widget-title\">Pages</h2>\n <ul class=\"widget-list\">\n {{#site.undatedPages}}<li><a href=\"{{url}}\"><span>{{title}}</span></a></li>{{/site.undatedPages}}\n </ul>\n </section>\n {{/site.hasUndatedPages}}{{/site.isBlog}}\n {{^site.isBlog}}\n <section class=\"widget pages\">\n <h2 class=\"widget-title\">Pages</h2>\n <ul class=\"widget-list\">\n {{#site.pages}}<li><a href=\"{{url}}\"><span>{{title}}</span></a></li>{{/site.pages}}\n </ul>\n </section>\n {{/site.isBlog}}\n",
|
|
11293
|
-
"../etherpk-stack/theme.json": "{\n \"name\": \"etherpk-stack\",\n \"title\": \"EtherPK Stack\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Hugo's Stack: a card-based blog with a left sidebar of site identity and menu, article cards in the middle, and widgets (search, contents, archive, pages) on the right.\",\n \"kinds\": [\"blog\", \"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The avatar above the site title in the sidebar: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The left sidebar's identity block (avatar, title, description). Replaces the theme's - the menu stays.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The footer under the article column.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the theme toggle): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\", \"partials/article-card.html\", \"partials/widgets.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/search.js\", \"assets/theme.js\"\n ]\n}\n",
|
|
11643
|
+
"../etherpk-stack/theme.json": "{\n \"name\": \"etherpk-stack\",\n \"title\": \"EtherPK Stack\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Hugo's Stack: a card-based blog with a left sidebar of site identity and menu, article cards in the middle, and widgets (search, contents, archive, pages) on the right.\",\n \"kinds\": [\"blog\", \"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The avatar above the site title in the sidebar: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"favicon\", \"description\": \"The icon in the browser tab: a <link rel=\\\"icon\\\"> tag. Raw HTML in the page body. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The left sidebar's identity block (avatar, title, description). Replaces the theme's - the menu stays.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The footer under the article column.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search, the theme toggle): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/favicon.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\", \"partials/article-card.html\", \"partials/widgets.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/favicon.svg\", \"assets/search.js\", \"assets/theme.js\"\n ]\n}\n",
|
|
11644
|
+
"../etherpk-terminal/assets/favicon.svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 191.82 166.13\">\n <path fill=\"#0a0a0a\" fill-rule=\"evenodd\" d=\"M67.09,132.92,47.93,166.13H9.59L0,149.52,86.33,0H105.5L19.17,149.52H38.34l19.17-33.2h76.8l9.59,16.6Zm48-83.12,9.65,16.71H143.9L115.08,16.6q-24,41.57-48,83.12H143.9q14.37,24.9,28.76,49.8h-96l-9.59,16.61H182.24l9.58-16.61-38.34-66.4H96C95.85,82.9,113.39,52.73,115.08,49.8Z\"/>\n</svg>\n",
|
|
11294
11645
|
"../etherpk-terminal/assets/search.js": "/*\n * Search for a Published Site: reads search.json (built over the publication's own documents\n * and nothing else) and scores documents by title and text matches. No dependencies, no\n * network beyond the one index file, and nothing runs until the search box is used. The same\n * script runs the sidebar toggle on small screens, so a page carries one script.\n */\n(function () {\n 'use strict';\n\n // -- Navigation toggle (small screens) -------------------------------------------------\n var toggle = document.querySelector('.nav-toggle');\n var backdrop = document.querySelector('.nav-backdrop');\n var nav = document.getElementById('site-nav');\n function setNavOpen(open) {\n var was = document.body.classList.contains('nav-open');\n document.body.classList.toggle('nav-open', open);\n if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n if (backdrop) backdrop.hidden = !open;\n // Focus follows the drawer: into its first link on open, back to the button on close,\n // so a keyboard or screen-reader user is never left on something now hidden.\n if (open && !was) {\n var first = nav.querySelector('a');\n if (first) first.focus();\n } else if (!open && was && toggle) {\n toggle.focus();\n }\n }\n if (toggle && nav) {\n toggle.addEventListener('click', function () { setNavOpen(!document.body.classList.contains('nav-open')); });\n if (backdrop) backdrop.addEventListener('click', function () { setNavOpen(false); });\n document.addEventListener('keydown', function (e) {\n if (e.key === 'Escape' && document.body.classList.contains('nav-open')) setNavOpen(false);\n });\n }\n\n // -- Search --------------------------------------------------------------------------\n var form = document.querySelector('.site-search');\n if (!form) return;\n var input = form.querySelector('input[type=\"search\"]');\n var results = form.querySelector('.site-search-results');\n var indexUrl = form.getAttribute('data-search-index') || 'search.json';\n var scriptUrl = form.getAttribute('data-search-script') || 'search-index.js';\n var index = null;\n var loading = null;\n /* True once neither route produced the index: the results then say so rather than \"nothing matches\". */\n var unavailable = false;\n\n /* The index as a script assigning window.etherpkSearchIndex: what a site opened from disk can\n load, since file:// refuses fetch but allows <script src>. */\n function loadScript() {\n return new Promise(function (resolve, reject) {\n if (window.etherpkSearchIndex) { resolve(window.etherpkSearchIndex); return; }\n var s = document.createElement('script');\n s.src = scriptUrl;\n s.onload = function () {\n if (window.etherpkSearchIndex) resolve(window.etherpkSearchIndex);\n else reject(new Error('no index'));\n };\n s.onerror = function () { reject(new Error('no script')); };\n document.head.appendChild(s);\n });\n }\n\n function load() {\n if (index) return Promise.resolve(index);\n if (!loading) {\n loading = fetch(indexUrl).then(function (r) {\n if (!r.ok) throw new Error(String(r.status));\n return r.json();\n }).catch(loadScript).then(function (data) {\n index = (data && data.documents) || [];\n return index;\n }).catch(function () { unavailable = true; index = []; return index; });\n }\n return loading;\n }\n\n function tokens(q) {\n return q.toLowerCase().split(/\\s+/).filter(function (t) { return t.length > 0; });\n }\n\n function score(doc, terms) {\n var title = (doc.title || '').toLowerCase();\n var text = (doc.text || '').toLowerCase();\n var s = 0;\n for (var i = 0; i < terms.length; i++) {\n var t = terms[i];\n if (title === t) s += 20;\n else if (title.indexOf(t) === 0) s += 10;\n else if (title.indexOf(t) !== -1) s += 6;\n var at = text.indexOf(t);\n if (at !== -1) s += 2;\n else if (title.indexOf(t) === -1) return 0; // every term must match somewhere\n }\n return s;\n }\n\n function snippet(doc, terms) {\n var text = doc.text || '';\n var lower = text.toLowerCase();\n var at = -1;\n for (var i = 0; i < terms.length && at === -1; i++) at = lower.indexOf(terms[i]);\n if (at === -1) return doc.excerpt || '';\n var start = Math.max(0, at - 60);\n var end = Math.min(text.length, at + 100);\n return (start > 0 ? '…' : '') + text.slice(start, end).replace(/\\s+/g, ' ') + (end < text.length ? '…' : '');\n }\n\n function escapeHtml(s) {\n return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n function render(list, q) {\n if (!q) { results.hidden = true; results.innerHTML = ''; return; }\n if (unavailable) {\n results.innerHTML = '<p class=\"site-search-empty\">The search index could not be loaded.</p>';\n results.hidden = false;\n return;\n }\n if (list.length === 0) {\n results.innerHTML = '<p class=\"site-search-empty\">Nothing matches \"' + escapeHtml(q) + '\".</p>';\n results.hidden = false;\n return;\n }\n results.innerHTML = list.map(function (hit) {\n return '<a class=\"site-search-hit\" role=\"option\" href=\"' + escapeHtml(hit.doc.url) + '\">' +\n '<span class=\"site-search-title\">' + escapeHtml(hit.doc.title) + '</span>' +\n '<span class=\"site-search-snippet\">' + escapeHtml(hit.snippet) + '</span></a>';\n }).join('');\n results.hidden = false;\n }\n\n var timer = null;\n function run() {\n var q = input.value.trim();\n var terms = tokens(q);\n if (terms.length === 0) { render([], ''); return; }\n load().then(function (docs) {\n var hits = [];\n for (var i = 0; i < docs.length; i++) {\n var s = score(docs[i], terms);\n if (s > 0) hits.push({ doc: docs[i], score: s, snippet: snippet(docs[i], terms) });\n }\n hits.sort(function (a, b) { return b.score - a.score || a.doc.title.localeCompare(b.doc.title); });\n render(hits.slice(0, 12), q);\n });\n }\n\n input.addEventListener('input', function () {\n clearTimeout(timer);\n timer = setTimeout(run, 120);\n });\n input.addEventListener('focus', function () { load(); });\n form.addEventListener('submit', function (e) {\n e.preventDefault();\n var first = results.querySelector('a');\n if (first) window.location.href = first.getAttribute('href');\n });\n document.addEventListener('keydown', function (e) {\n if (e.key === '/' && document.activeElement !== input && !/input|textarea/i.test(document.activeElement.tagName)) {\n e.preventDefault();\n input.focus();\n }\n if (e.key === 'Escape' && !results.hidden) { render([], ''); }\n });\n document.addEventListener('click', function (e) {\n if (!form.contains(e.target)) render([], '');\n });\n})();\n",
|
|
11295
11646
|
"../etherpk-terminal/assets/theme.css": "/* EtherPK's Terminal: after Hugo's Terminal (panr, MIT), written afresh for EtherPK's view - one\n monospace face, a dark ground, no colour at all - the accent is a brighter shade of the text,\n so everything is drawn in tones of one ink - the block logo with its blinking cursor,\n underlined titles, framed boxes and bordered buttons. Uses Fira Code\n or the system monospace, so the site loads nothing from anywhere. Classes the publisher emits\n and this file styles: .wikilink, .wikilink-missing, .task, .task-tag, .toc, .backlinks,\n .site-nav-list, .diagram, .math, .tok-* (code). */\n\n:root {\n --accent: #f2f2f2;\n --background: #1d1e28;\n --color: #cfcfd6;\n --color-muted: #8f8f9a;\n --border-color: rgba(255, 255, 255, 0.1);\n --code-bg: rgba(255, 255, 255, 0.06);\n --font: \"Fira Code\", ui-monospace, Monaco, Consolas, \"Ubuntu Mono\", monospace;\n --container-width: 1080px;\n --mark: rgba(255, 255, 255, 0.22);\n --missing: var(--color-muted);\n}\n/* Terminal is dark by nature; a light system preference is respected, the ink inverted. */\n@media (prefers-color-scheme: light) {\n :root:not([data-theme=\"dark\"]) {\n --accent: #111111;\n --background: #f7f4ef;\n --color: #2b2b2b;\n --color-muted: #6a6a6a;\n --mark: rgba(0, 0, 0, 0.14);\n --border-color: rgba(0, 0, 0, 0.12);\n --code-bg: rgba(0, 0, 0, 0.05);\n }\n}\n\n*, *::before, *::after { box-sizing: border-box; }\nhtml { color-scheme: dark light; }\nbody {\n margin: 0;\n font-family: var(--font);\n font-size: 0.9375rem;\n line-height: 1.6;\n background: var(--background);\n color: var(--color);\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n font-feature-settings: \"liga\", \"tnum\", \"zero\", \"ss01\", \"locl\";\n}\na { color: var(--accent); }\na:hover { text-decoration: none; }\nimg { max-width: 100%; height: auto; display: block; }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.skip-link { position: absolute; left: -999px; top: 0; background: var(--accent); color: var(--background); padding: 8px 16px; z-index: 100; }\n.skip-link:focus { left: 16px; top: 16px; }\n\n.container { max-width: var(--container-width); margin: 0 auto; padding: 40px; }\n\n/* -- Header: the prompt-style logo ------------------------------------------------------ */\n.header { margin-bottom: 40px; }\n.header__inner { display: flex; align-items: center; justify-content: space-between; gap: 20px; }\n/* The block logo: the mark, the title and a blinking cursor on one line, the mark's box sized\n to the text's cap height so the two read as one glyph run. */\n.logo {\n display: inline-flex;\n align-items: center;\n gap: 12px;\n padding: 8px 16px;\n background: var(--accent);\n color: var(--background);\n font-size: 1.375rem;\n line-height: 1;\n font-weight: bold;\n text-decoration: none;\n}\n.logo:hover { text-decoration: none; }\n.logo__mark { display: inline-flex; align-items: center; line-height: 0; }\n.logo__mark .site-logo { display: block; width: 1.3em; height: 1.125em; }\n.logo__cursor {\n display: inline-block;\n width: 0.5em;\n height: 1em;\n background: var(--background);\n animation: cursor 1s infinite;\n}\n@keyframes cursor { 0% { opacity: 0; } 50% { opacity: 1; } 100% { opacity: 0; } }\n@media (prefers-reduced-motion: reduce) { .logo__cursor { animation: none; } }\n.menu { margin-top: 20px; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 30px; }\n.menu__inner { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px 30px; }\n.menu__inner li a, .menu__inner .nav-group-label { color: var(--color); text-decoration: none; }\n.menu__inner li a:hover { color: var(--accent); }\n.menu__inner .nav-current > a { color: var(--accent); }\n.menu__inner .nav-current > a::after { content: \" *\"; }\n.menu__inner .nav-group-label { color: var(--color-muted); }\n.menu__inner .nav-group-label::after { content: \":\"; }\n/* A nested outline level follows its parent on the same line, the way a path reads. */\n.menu__inner li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 12px; }\n.menu__inner .site-nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0 12px; }\n.menu__inner .site-nav-list li::before { content: \"/\"; color: var(--color-muted); margin-right: 12px; }\n.nav-toggle { display: none; }\n.nav-backdrop { display: none; }\n.site-search { position: relative; margin-left: auto; flex: 0 1 16rem; }\n.site-search input {\n width: 100%;\n padding: 5px 10px;\n border: 1px solid var(--accent);\n background: transparent;\n color: var(--color);\n font: inherit;\n}\n.site-search input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }\n.site-search input::placeholder { color: var(--color-muted); }\n.site-search-results {\n position: absolute;\n right: 0;\n top: calc(100% + 6px);\n width: min(26rem, 90vw);\n max-height: 24rem;\n overflow: auto;\n background: var(--background);\n border: 1px solid var(--accent);\n z-index: 30;\n}\n.site-search-hit { display: block; padding: 8px 12px; color: var(--color); text-decoration: none; border-bottom: 1px solid var(--border-color); }\n.site-search-hit:hover, .site-search-hit:focus { background: var(--code-bg); }\n.site-search-title { display: block; color: var(--accent); }\n.site-search-snippet { display: block; color: var(--color-muted); font-size: 0.9rem; }\n.site-search-empty { margin: 0; padding: 8px 12px; color: var(--color-muted); }\n\n/* -- Posts ------------------------------------------------------------------------------ */\n.posts { width: 100%; }\n.post { width: 100%; }\n.post.on-list { margin-bottom: 40px; padding-bottom: 40px; border-bottom: 1px dotted var(--accent); }\n.post.on-list:last-child { border-bottom: 0; }\n.post-title {\n position: relative;\n margin: 0 0 15px;\n font-size: 1.6rem;\n line-height: 1.3;\n color: var(--accent);\n text-decoration: underline;\n text-decoration-color: var(--accent);\n text-decoration-thickness: 2px;\n text-underline-offset: 6px;\n}\n.post-title a { color: inherit; text-decoration: none; }\n.post-title .wikilink { color: inherit; text-decoration: none; }\n.index-title { text-decoration: none; }\n.index-title::before { content: \"# \"; }\n.post-meta { margin: 0 0 20px; font-size: 0.9rem; color: var(--color-muted); }\n.post-content { margin-top: 20px; }\n.post-content h1, .post-content h2, .post-content h3, .post-content h4 { font-weight: bold; line-height: 1.3; margin: 40px 0 20px; }\n.post-content h2 { font-size: 1.3rem; }\n.post-content h3 { font-size: 1.2rem; }\n.post-content h2::before { content: \"## \"; color: var(--accent); }\n.post-content h3::before { content: \"### \"; color: var(--accent); }\n.post-content p { margin: 0 0 20px; }\n.post-content ul, .post-content ol { margin: 0 0 20px; padding-left: 24px; }\n.post-content li { margin: 4px 0; }\n.post-content ul li::marker { color: var(--accent); }\n.post-content img { margin: 20px 0; border: 1px solid var(--border-color); }\n.post-content .diagram { margin: 20px 0; text-align: center; }\n.post-content .diagram svg { max-width: 100%; height: auto; }\n.post-content blockquote {\n position: relative;\n margin: 40px 0;\n padding: 25px;\n border-top: 1px solid var(--accent);\n border-bottom: 1px solid var(--accent);\n font-style: italic;\n}\n.post-content blockquote::before { content: \"”\"; position: absolute; top: -40px; left: 10px; font-size: 3.875rem; font-style: normal; color: var(--accent); }\n.post-content blockquote p:last-child { margin-bottom: 0; }\n.post-content hr { border: 0; border-top: 1px dashed var(--accent); margin: 40px 0; }\n.post-content table { width: 100%; border-collapse: collapse; margin: 20px 0; }\n.post-content th, .post-content td { padding: 8px 12px; border: 1px dashed var(--accent); text-align: left; }\n.post-content th { color: var(--accent); }\n.post-content code { padding: 1px 6px; background: var(--code-bg); font-family: inherit; font-size: 0.95em; }\n.post-content pre {\n margin: 20px 0;\n padding: 20px;\n background: var(--code-bg);\n border-left: 3px solid var(--accent);\n overflow-x: auto;\n font-family: inherit;\n font-size: 0.95rem;\n line-height: 1.5;\n}\n.post-content pre code { padding: 0; background: none; font-size: inherit; }\n.math-block { overflow-x: auto; margin: 20px 0; }\nmark { background: var(--mark); color: inherit; padding: 0 0.15em; }\n\n/* Wikilinks and tasks. */\na.wikilink { color: var(--accent); }\na.wikilink-missing { color: var(--missing); text-decoration-style: dotted; }\nli.task { list-style: none; margin-left: -24px; }\nli.task input[type=\"checkbox\"] { accent-color: var(--accent); margin-right: 6px; vertical-align: -0.1em; }\nli.task.done { color: var(--color-muted); text-decoration: line-through; }\n.task-tag { display: inline-block; padding: 0 6px; margin-right: 4px; border: 1px solid var(--accent); color: var(--accent); font-size: 0.8rem; }\n.task-tag.cancelled { text-decoration: line-through; }\n\n/* Buttons, frames, the table of contents and pagination. */\n.button {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 8px 18px;\n border: 1px solid var(--accent);\n color: var(--accent);\n font: inherit;\n text-decoration: none;\n background: transparent;\n cursor: pointer;\n}\n.button:hover { background: var(--accent); color: var(--background); }\n.button.inline { display: inline-flex; }\n.read-more { margin-top: 10px; }\n.all-posts { margin: 30px 0 0; }\n/* The contents page of a docs site: the outline as a tree, then any page it leaves out. */\n.page-index-list { list-style: none; margin: 0; padding: 0; }\n.page-index-list .page-index-list { padding-left: 24px; }\n.page-index-list li { margin: 6px 0; }\n.page-index-list li::before { content: \"- \"; color: var(--accent); }\n.page-index-list a { color: var(--color); }\n.page-index-list a:hover { color: var(--accent); }\n.page-index-group-label { color: var(--color-muted); }\n.page-index-group-label::after { content: \":\"; }\n.page-index-other { margin: 30px 0 10px; font-size: 1.1rem; }\n.page-index-other::before { content: \"## \"; color: var(--accent); }\n/* The archives: a year as a prompt-style heading, then one line per post. */\n.archive-year { margin: 30px 0 10px; font-size: 1.1rem; }\n.archive-year::before { content: \"## \"; color: var(--accent); }\n.archive-list { list-style: none; margin: 0; padding: 0; }\n.archive-list li { margin: 6px 0; }\n.archive-list time { color: var(--color-muted); margin-right: 12px; font-variant-numeric: tabular-nums; }\n.archive-list a { color: var(--color); }\n.archive-list a:hover { color: var(--accent); }\n.archive-excerpt { color: var(--color-muted); }\n.framed { border: 1px solid var(--accent); padding: 20px; }\n.toc { margin: 20px 0 40px; }\n.toc__title { margin: 0 0 10px; color: var(--accent); }\n.toc ul { list-style: none; margin: 0; padding: 0; }\n.toc li { margin: 4px 0; }\n.toc li::before { content: \"- \"; color: var(--accent); }\n.toc li.toc-level-3 { padding-left: 1.5rem; }\n.toc li.toc-level-4 { padding-left: 3rem; }\n.toc a { color: var(--color); text-decoration: none; }\n.toc a:hover { color: var(--accent); }\n.pagination { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px; margin-top: 50px; }\n.pagination__next { margin-left: auto; }\n.button__text { max-width: 20rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n/* Linked references, framed like everything else worth a box. */\n.backlinks { margin-top: 40px; border: 1px solid var(--accent); padding: 20px; }\n.backlinks h2 { margin: 0 0 12px; font-size: 1rem; color: var(--accent); }\n.backlinks h2::before { content: \":: \"; }\n.backlink-list { list-style: none; margin: 0; padding: 0; }\n.backlink-source { margin: 0 0 12px; }\n.backlink-source > a { color: var(--color); }\n.backlink-source ul { list-style: none; margin: 4px 0 0; padding: 0 0 0 16px; border-left: 1px dashed var(--accent); }\n.backlink-context { color: var(--color-muted); font-size: 0.9rem; margin: 2px 0; }\n\n/* -- Footer ----------------------------------------------------------------------------- */\n.footer { margin-top: 60px; padding-top: 20px; border-top: 1px dotted var(--accent); font-size: 0.9rem; color: var(--color-muted); }\n.footer__inner { display: flex; flex-wrap: wrap; gap: 10px; }\n.copyright { display: flex; flex-wrap: wrap; gap: 0 8px; }\n.footer__separator { color: var(--accent); }\n\n/* Code tokens (lezer classHighlighter names). */\n/* Code tokens (lezer classHighlighter names), in tones of the one ink: weight and italics carry\n the distinction the usual colours would. */\n.tok-keyword { color: var(--accent); font-weight: bold; }\n.tok-string, .tok-string2 { color: var(--color); font-style: italic; }\n.tok-comment, .tok-lineComment, .tok-blockComment { color: var(--color-muted); font-style: italic; }\n.tok-number, .tok-bool, .tok-null, .tok-atom { color: var(--accent); }\n.tok-function, .tok-definition.tok-variableName { color: var(--accent); }\n.tok-typeName, .tok-className, .tok-namespace { color: var(--accent); font-weight: bold; }\n.tok-operator, .tok-punctuation { color: var(--color-muted); }\n.tok-propertyName, .tok-attributeName { color: var(--color); }\n.tok-tagName { color: var(--accent); }\n.tok-invalid { color: var(--missing); text-decoration: underline wavy; }\n\n/* -- Small screens: the menu behind a Terminal-style button ----------------------------- */\n@media (max-width: 684px) {\n .container { padding: 20px; }\n .nav-toggle {\n display: inline-block;\n padding: 5px 10px;\n border: 1px solid var(--accent);\n background: transparent;\n color: var(--accent);\n font: inherit;\n cursor: pointer;\n }\n .nav-toggle::before { content: \"$ \"; }\n .menu { display: none; flex-direction: column; align-items: stretch; gap: 12px; margin-top: 20px; padding: 20px; border: 1px solid var(--accent); }\n body.nav-open .menu { display: flex; }\n .menu__inner { flex-direction: column; gap: 8px; }\n .menu__inner li { flex-direction: column; align-items: flex-start; }\n .menu__inner .site-nav-list { flex-direction: column; padding-left: 12px; }\n .menu__inner .site-nav-list li::before { margin-right: 6px; }\n .site-search { margin-left: 0; flex-basis: auto; }\n .post-title { font-size: 1.3rem; }\n .pagination__prev, .pagination__next { flex-basis: 100%; }\n .pagination__next { justify-content: flex-end; }\n}\n",
|
|
11296
11647
|
"../etherpk-terminal/layouts/404.html": "{{> shell-top}}\n <article class=\"post\">\n <h1 class=\"post-title\">404 :: not published</h1>\n <div class=\"post-content\">\n <p>$ the page you followed a link to is not part of this site.</p>\n <p><a class=\"button inline\" href=\"index.html\">← {{site.title}}</a></p>\n </div>\n </article>\n{{> shell-bottom}}\n",
|
|
@@ -11301,6 +11652,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11301
11652
|
"../etherpk-terminal/partials/after-content.html": "",
|
|
11302
11653
|
"../etherpk-terminal/partials/backlinks.html": " {{#page.hasBacklinks}}\n <section class=\"backlinks\" aria-labelledby=\"backlinks-heading\">\n <h2 id=\"backlinks-heading\">Linked references</h2>\n <ul class=\"backlink-list\">\n {{#page.backlinks}}\n <li class=\"backlink-source\">\n <a href=\"{{url}}\">{{title}}</a>\n <ul>\n {{#refs}}<li class=\"backlink-context\">{{{contextHtml}}}</li>{{/refs}}\n </ul>\n </li>\n {{/page.backlinks}}\n </ul>\n </section>\n {{/page.hasBacklinks}}\n",
|
|
11303
11654
|
"../etherpk-terminal/partials/before-content.html": "",
|
|
11655
|
+
"../etherpk-terminal/partials/favicon.html": "<!-- The EtherPK icon in the browser tab: assets/favicon.svg, the same file the EtherPK apps use. Fill the \"favicon\" include with your own <link rel=\"icon\"> tag to replace it. -->\n<link rel=\"icon\" href=\"theme/favicon.svg\" type=\"image/svg+xml\">\n",
|
|
11304
11656
|
"../etherpk-terminal/partials/footer.html": " <footer class=\"footer\">\n <div class=\"footer__inner\">\n <div class=\"copyright\">\n <span>© {{site.year}} {{site.title}}</span>\n <span class=\"footer__separator\" aria-hidden=\"true\">::</span>\n <span>Published with <a href=\"https://etherpk.com\" rel=\"noopener\">EtherPK</a></span>\n {{#site.hasFeed}}<span class=\"footer__separator\" aria-hidden=\"true\">::</span><a href=\"feed.xml\">Feed</a>{{/site.hasFeed}}\n </div>\n </div>\n </footer>\n",
|
|
11305
11657
|
"../etherpk-terminal/partials/head.html": "",
|
|
11306
11658
|
"../etherpk-terminal/partials/header.html": " <header class=\"header\">\n <div class=\"header__inner\">\n <a class=\"site-title logo\" href=\"index.html\">\n <span class=\"logo__mark\">{{> logo}}</span>\n <span class=\"logo__text\">{{site.title}}</span>\n <span class=\"logo__cursor\" aria-hidden=\"true\"></span>\n </a>\n <button class=\"nav-toggle menu-trigger\" type=\"button\" aria-controls=\"site-nav\" aria-expanded=\"false\" aria-label=\"Menu\">Menu</button>\n </div>\n <div class=\"nav-backdrop\" hidden></div>\n <nav id=\"site-nav\" class=\"menu site-nav\" aria-label=\"Site\">\n <ul class=\"menu__inner site-nav-list\">\n{{#nav}}{{> nav-item}}{{/nav}}\n </ul>\n <form class=\"site-search\" role=\"search\" data-search-index=\"{{site.search.url}}\" data-search-script=\"{{site.search.scriptUrl}}\">\n <label class=\"visually-hidden\" for=\"site-search-input\">Search</label>\n <input id=\"site-search-input\" type=\"search\" name=\"q\" placeholder=\"$ search\" autocomplete=\"off\">\n <div class=\"site-search-results\" role=\"listbox\" hidden></div>\n </form>\n </nav>\n </header>\n",
|
|
@@ -11310,10 +11662,10 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
11310
11662
|
"../etherpk-terminal/partials/post-meta.html": " <div class=\"post-meta\">{{#page.hasDate}}<time class=\"post-date\" datetime=\"{{page.date}}\">{{page.date}}</time> :: {{/page.hasDate}}<span class=\"post-reading\">{{page.readingMinutes}} min read</span></div>\n",
|
|
11311
11663
|
"../etherpk-terminal/partials/scripts.html": "",
|
|
11312
11664
|
"../etherpk-terminal/partials/shell-bottom.html": " {{> after-content}}\n {{> backlinks}}\n </main>\n {{> footer}}\n </div>\n {{> theme-scripts}}\n {{> scripts}}\n</body>\n</html>\n",
|
|
11313
|
-
"../etherpk-terminal/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} :: {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n {{> head}}\n</head>\n<body class=\"layout-terminal{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n <div class=\"container\">\n {{> header}}\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11665
|
+
"../etherpk-terminal/partials/shell-top.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"generator\" content=\"EtherPK\">\n <title>{{page.title}}{{^page.isHome}} :: {{site.title}}{{/page.isHome}}</title>\n {{#page.excerpt}}<meta name=\"description\" content=\"{{page.excerpt}}\">{{/page.excerpt}}\n <link rel=\"stylesheet\" href=\"theme/theme.css\">\n {{#site.hasMath}}<link rel=\"stylesheet\" href=\"theme/katex/katex.min.css\">{{/site.hasMath}}\n {{#site.hasCustomCss}}<link rel=\"stylesheet\" href=\"theme/custom.css\">{{/site.hasCustomCss}}\n {{#site.hasFeed}}<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{{site.title}}\" href=\"feed.xml\">{{/site.hasFeed}}\n {{> favicon}}\n {{> head}}\n</head>\n<body class=\"layout-terminal{{#page.isHome}} is-home{{/page.isHome}}{{#page.isJournal}} is-journal{{/page.isJournal}}\">\n <a class=\"skip-link\" href=\"#content\">Skip to content</a>\n <div class=\"container\">\n {{> header}}\n <main id=\"content\" class=\"content\">\n {{> before-content}}\n",
|
|
11314
11666
|
"../etherpk-terminal/partials/theme-scripts.html": " <script src=\"theme/search.js\" defer><\/script>\n",
|
|
11315
11667
|
"../etherpk-terminal/partials/toc.html": " {{#page.hasToc}}\n <nav class=\"toc framed\" aria-label=\"Table of contents\">\n <p class=\"toc__title\">:: Table of contents</p>\n <ul>\n {{#page.toc}}<li class=\"toc-level-{{level}}\"><a href=\"#{{id}}\">{{text}}</a></li>{{/page.toc}}\n </ul>\n </nav>\n {{/page.hasToc}}\n",
|
|
11316
|
-
"../etherpk-terminal/theme.json": "{\n \"name\": \"etherpk-terminal\",\n \"title\": \"EtherPK Terminal\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Hugo's Terminal: monospace, dark, one ink in shades, a block logo with a blinking cursor, underlined titles and framed boxes.\",\n \"kinds\": [\"blog\", \"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The mark before the site title in the header's prompt box: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's prompt-style logo, menu and search box.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\", \"partials/post-meta.html\", \"partials/post-entry.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/search.js\"\n ]\n}\n"
|
|
11668
|
+
"../etherpk-terminal/theme.json": "{\n \"name\": \"etherpk-terminal\",\n \"title\": \"EtherPK Terminal\",\n \"version\": \"1.0.0\",\n \"contract\": 1,\n \"description\": \"In the mould of Hugo's Terminal: monospace, dark, one ink in shades, a block logo with a blinking cursor, underlined titles and framed boxes.\",\n \"kinds\": [\"blog\", \"docs\"],\n \"includes\": [\n { \"name\": \"head\", \"description\": \"Extra markup inside <head>: analytics, fonts, meta tags. Raw HTML in the page body.\" },\n { \"name\": \"logo\", \"description\": \"The mark before the site title in the header's prompt box: an image or inline SVG. Replaces the EtherPK icon.\" },\n { \"name\": \"favicon\", \"description\": \"The icon in the browser tab: a <link rel=\\\"icon\\\"> tag. Raw HTML in the page body. Replaces the EtherPK icon.\" },\n { \"name\": \"header\", \"description\": \"The site header. Replaces the theme's prompt-style logo, menu and search box.\" },\n { \"name\": \"before-content\", \"description\": \"Rendered above every page's content: a banner, a notice.\" },\n { \"name\": \"after-content\", \"description\": \"Rendered below every page's content, above the linked references: an author bio, a comments widget.\" },\n { \"name\": \"footer\", \"description\": \"The site footer.\" },\n { \"name\": \"scripts\", \"description\": \"Scripts before </body>, added after the theme's own (search): analytics, a widget. Raw HTML in the page body.\" },\n { \"name\": \"styles\", \"description\": \"CSS appended after the theme's stylesheet: the page's first css fence.\", \"kind\": \"css\" }\n ],\n \"files\": [\n \"layouts/page.html\", \"layouts/home.html\", \"layouts/journal.html\", \"layouts/archive.html\", \"layouts/404.html\",\n \"partials/shell-top.html\", \"partials/shell-bottom.html\", \"partials/head.html\", \"partials/logo.html\", \"partials/favicon.html\", \"partials/header.html\",\n \"partials/nav-item.html\", \"partials/toc.html\", \"partials/backlinks.html\", \"partials/post-meta.html\", \"partials/post-entry.html\",\n \"partials/before-content.html\", \"partials/after-content.html\", \"partials/footer.html\", \"partials/theme-scripts.html\", \"partials/scripts.html\",\n \"assets/theme.css\", \"assets/favicon.svg\", \"assets/search.js\"\n ]\n}\n"
|
|
11317
11669
|
});
|
|
11318
11670
|
var themes = /* @__PURE__ */ new Map();
|
|
11319
11671
|
for (const [path, text] of Object.entries(files)) {
|
|
@@ -11978,6 +12330,14 @@ function isSingleFileName(name) {
|
|
|
11978
12330
|
*/
|
|
11979
12331
|
/** Matches a display-size hint at the end of alt text: `|300` or `|300x200` (with optional trailing space). */
|
|
11980
12332
|
var DISPLAY_SIZE_HINT_REGEX = /\|(\d{1,5})(?:[xX](\d{1,5}))?\s*$/;
|
|
12333
|
+
/**
|
|
12334
|
+
* The CSS `max-width` for a hinted width: the hint, but never wider than the container. Both the
|
|
12335
|
+
* editor and the publisher set it inline, and an inline value outranks a stylesheet's
|
|
12336
|
+
* `img { max-width: 100% }`, so a bare `${px}px` would push a wide image out of a narrower column.
|
|
12337
|
+
*/
|
|
12338
|
+
function displayMaxWidth(px) {
|
|
12339
|
+
return `min(${px}px, 100%)`;
|
|
12340
|
+
}
|
|
11981
12341
|
/** Parse the alt text into its clean label and any display-size hint. A width < 1 (or no hint) yields no dimensions. */
|
|
11982
12342
|
function parseImageDisplaySizeHint(alt) {
|
|
11983
12343
|
const match = DISPLAY_SIZE_HINT_REGEX.exec(alt);
|
|
@@ -12290,6 +12650,10 @@ function wikilinkRule(md, resolve) {
|
|
|
12290
12650
|
* grammars for code), so they are pre-rendered by the orchestrator and handed in as lookups;
|
|
12291
12651
|
* the renderer itself is synchronous and pure over its inputs.
|
|
12292
12652
|
*/
|
|
12653
|
+
/** The names on a Map Block, one line for the search index and the excerpt; never its coordinates. */
|
|
12654
|
+
function mapNames(source) {
|
|
12655
|
+
return readMapBody(source.split("\n")).items.map((item) => item.name).filter((name) => name !== "").join(", ");
|
|
12656
|
+
}
|
|
12293
12657
|
/** A document-relative asset reference: any number of `../`, `assets/`, the name. */
|
|
12294
12658
|
var ASSET_REF$1 = /^(?:\.\.\/)*assets\/(.+)$/;
|
|
12295
12659
|
/**
|
|
@@ -12329,7 +12693,11 @@ function plainText(tokens) {
|
|
|
12329
12693
|
else if (child.type === "softbreak" || child.type === "hardbreak") line += " ";
|
|
12330
12694
|
if (line.trim() !== "") lines.push(line.trim());
|
|
12331
12695
|
} else if (token.type === "fence" || token.type === "code_block") {
|
|
12332
|
-
|
|
12696
|
+
const info = token.info.trim().split(/\s+/)[0] ?? "";
|
|
12697
|
+
if (info === "map") {
|
|
12698
|
+
const names = mapNames(token.content);
|
|
12699
|
+
if (names !== "") lines.push(names);
|
|
12700
|
+
} else if (info !== "mermaid" && info !== "math") lines.push(token.content.trim());
|
|
12333
12701
|
}
|
|
12334
12702
|
return lines.join("\n");
|
|
12335
12703
|
}
|
|
@@ -12368,7 +12736,7 @@ function createDocumentRenderer(options) {
|
|
|
12368
12736
|
const parsed = parseImageDisplaySizeHint(self.renderInlineAsText(token.children ?? [], opts, env));
|
|
12369
12737
|
token.attrSet("alt", parsed.cleanAlt);
|
|
12370
12738
|
if (parsed.maxWidth !== void 0) {
|
|
12371
|
-
const style = [`max-width:${parsed.maxWidth}
|
|
12739
|
+
const style = [`max-width:${displayMaxWidth(parsed.maxWidth)}`];
|
|
12372
12740
|
if (parsed.maxHeight !== void 0) style.push(`max-height:${parsed.maxHeight}px`);
|
|
12373
12741
|
token.attrSet("style", style.join(";"));
|
|
12374
12742
|
}
|
|
@@ -12403,6 +12771,11 @@ function createDocumentRenderer(options) {
|
|
|
12403
12771
|
return `<div class="math math-block">${renderMath(code.replace(/\n$/, ""), true)}</div>\n`;
|
|
12404
12772
|
}
|
|
12405
12773
|
if (lang === "etherpk-cipher") return "";
|
|
12774
|
+
if (lang === "map") {
|
|
12775
|
+
const picture = options.mapPicture?.(code);
|
|
12776
|
+
if (!picture) return "";
|
|
12777
|
+
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
|
+
}
|
|
12406
12779
|
const highlighted = options.highlighted?.(lang, code);
|
|
12407
12780
|
return `<pre><code${lang === "" ? "" : ` class="language-${escapeHtml$3(lang)}"`}>${highlighted ?? escapeHtml$3(code)}</code></pre>\n`;
|
|
12408
12781
|
};
|
|
@@ -12844,6 +13217,16 @@ function createThemeRenderer(theme, includes) {
|
|
|
12844
13217
|
* a pre-render pass for the asynchronous bits (diagrams, code); then rendering, includes, the
|
|
12845
13218
|
* theme, the derived files. Everything derived is computed from the included documents alone.
|
|
12846
13219
|
*/
|
|
13220
|
+
/**
|
|
13221
|
+
* 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.
|
|
13223
|
+
*/
|
|
13224
|
+
var MAP_PICTURE_VERSION = 1;
|
|
13225
|
+
/** A picture's file name: from what the map holds and how it is drawn, so an unchanged map publishes the same file. */
|
|
13226
|
+
async function mapPictureName(source) {
|
|
13227
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${MAP_PICTURE_VERSION}\n${source}`));
|
|
13228
|
+
return [...new Uint8Array(digest)].slice(0, 8).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
13229
|
+
}
|
|
12847
13230
|
function assetHrefOf(name) {
|
|
12848
13231
|
return `assets/${encodeURIComponent(name)}`;
|
|
12849
13232
|
}
|
|
@@ -13021,6 +13404,7 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13021
13404
|
includeDocs.set(slot, doc);
|
|
13022
13405
|
}
|
|
13023
13406
|
const mermaidSvg = /* @__PURE__ */ new Map();
|
|
13407
|
+
const mapPictures = /* @__PURE__ */ new Map();
|
|
13024
13408
|
const highlighted = /* @__PURE__ */ new Map();
|
|
13025
13409
|
const highlight = env.highlightCode ?? highlightCode;
|
|
13026
13410
|
const probe = createDocumentRenderer({
|
|
@@ -13043,12 +13427,44 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13043
13427
|
concept: doc.concept
|
|
13044
13428
|
});
|
|
13045
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.`,
|
|
13437
|
+
concept: doc.concept
|
|
13438
|
+
});
|
|
13439
|
+
continue;
|
|
13440
|
+
}
|
|
13441
|
+
try {
|
|
13442
|
+
const picture = await env.renderMap(fence.code);
|
|
13443
|
+
const path = `maps/${await mapPictureName(fence.code)}.${picture.type === "image/webp" ? "webp" : "png"}`;
|
|
13444
|
+
mapPictures.set(fence.code, {
|
|
13445
|
+
...picture,
|
|
13446
|
+
path,
|
|
13447
|
+
alt: mapAltText(fence.code.split("\n"))
|
|
13448
|
+
});
|
|
13449
|
+
} catch (error) {
|
|
13450
|
+
const why = error instanceof Error ? error.message : String(error);
|
|
13451
|
+
errors.push({
|
|
13452
|
+
level: "error",
|
|
13453
|
+
code: "map-not-drawn",
|
|
13454
|
+
message: `The map in "${doc.concept}" could not be drawn: ${why}. Nothing was published, so try again once it can be.`,
|
|
13455
|
+
concept: doc.concept
|
|
13456
|
+
});
|
|
13457
|
+
}
|
|
13046
13458
|
} else if (fence.lang !== "" && fence.lang !== "math" && fence.lang !== "etherpk-cipher") {
|
|
13047
13459
|
const key = `${fence.lang}\n${fence.code}`;
|
|
13048
13460
|
if (highlighted.has(key)) continue;
|
|
13049
13461
|
const html = await highlight(fence.lang, fence.code);
|
|
13050
13462
|
if (html !== null) highlighted.set(key, html);
|
|
13051
13463
|
}
|
|
13464
|
+
if (errors.length > 0) return {
|
|
13465
|
+
bundle,
|
|
13466
|
+
report
|
|
13467
|
+
};
|
|
13052
13468
|
let diagramScope = {
|
|
13053
13469
|
prefix: "mermaid",
|
|
13054
13470
|
n: 0
|
|
@@ -13066,7 +13482,16 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13066
13482
|
const svg = mermaidSvg.get(src);
|
|
13067
13483
|
return svg === void 0 ? void 0 : withDiagramId(svg, `${diagramScope.prefix}_${++diagramScope.n}`);
|
|
13068
13484
|
},
|
|
13069
|
-
highlighted: (lang, code) => highlighted.get(`${lang}\n${code}`)
|
|
13485
|
+
highlighted: (lang, code) => highlighted.get(`${lang}\n${code}`),
|
|
13486
|
+
mapPicture: (src) => {
|
|
13487
|
+
const picture = mapPictures.get(src);
|
|
13488
|
+
return picture && {
|
|
13489
|
+
src: picture.path,
|
|
13490
|
+
alt: picture.alt,
|
|
13491
|
+
width: picture.width,
|
|
13492
|
+
height: picture.height
|
|
13493
|
+
};
|
|
13494
|
+
}
|
|
13070
13495
|
});
|
|
13071
13496
|
const pages = [];
|
|
13072
13497
|
let done = 0;
|
|
@@ -13324,6 +13749,7 @@ async function publishPublication(source, publication, env, options = {}) {
|
|
|
13324
13749
|
bundle.set(`assets/${name}`, asset.bytes);
|
|
13325
13750
|
report.assets.copied.push(name);
|
|
13326
13751
|
}
|
|
13752
|
+
for (const picture of mapPictures.values()) bundle.set(picture.path, picture.bytes);
|
|
13327
13753
|
for (const [path, text] of theme.files) if (path.startsWith("assets/")) bundle.set(`theme/${path.slice(7)}`, text);
|
|
13328
13754
|
if (customCss !== "") bundle.set("theme/custom.css", customCss.endsWith("\n") ? customCss : `${customCss}\n`);
|
|
13329
13755
|
if (hasMath) {
|
|
@@ -13675,7 +14101,7 @@ async function runPublish(publication, deps) {
|
|
|
13675
14101
|
* Client bundles, so both hosts draw the same SVG, and the post-processing is the Client's
|
|
13676
14102
|
* (`browser-environment.ts`): the minted id dropped (it collides across pages), `role="img"`.
|
|
13677
14103
|
*/
|
|
13678
|
-
var require$
|
|
14104
|
+
var require$2 = createRequire(import.meta.url);
|
|
13679
14105
|
/** Where Playwright keeps the browsers this package installs: under our cache, not the user's global one. */
|
|
13680
14106
|
function browsersDir(env) {
|
|
13681
14107
|
return join(cacheRoot(env), "browsers");
|
|
@@ -13718,7 +14144,7 @@ async function setupDiagrams(env, say) {
|
|
|
13718
14144
|
say(before.source === "env" ? `Using the Chromium named by ETHERPK_CHROMIUM: ${before.executable}` : `Chromium is already installed: ${before.executable}`);
|
|
13719
14145
|
return before;
|
|
13720
14146
|
}
|
|
13721
|
-
const cli = require$
|
|
14147
|
+
const cli = require$2.resolve("playwright-core/cli.js");
|
|
13722
14148
|
const dir = env.PLAYWRIGHT_BROWSERS_PATH?.trim() || browsersDir(env);
|
|
13723
14149
|
say(`Installing Chromium into ${dir} (about 170 MB, once per computer - a diagram is rendered by a browser, and this is the one etherpk-mcp drives)…`);
|
|
13724
14150
|
await new Promise((resolve, reject) => {
|
|
@@ -13751,7 +14177,7 @@ async function setupDiagrams(env, say) {
|
|
|
13751
14177
|
return after;
|
|
13752
14178
|
}
|
|
13753
14179
|
/** The page every render runs in: Mermaid loaded once, configured as the Client configures it. */
|
|
13754
|
-
var PAGE = "<!doctype html><html><head><meta charset=\"utf-8\"></head><body></body></html>";
|
|
14180
|
+
var PAGE$1 = "<!doctype html><html><head><meta charset=\"utf-8\"></head><body></body></html>";
|
|
13755
14181
|
/** A headless browser from whichever Chromium is available, or null. The caller closes it. */
|
|
13756
14182
|
async function launchChromium(env) {
|
|
13757
14183
|
const status = await chromiumStatus(env);
|
|
@@ -13773,8 +14199,8 @@ async function openDiagramRenderer(env) {
|
|
|
13773
14199
|
const page = () => {
|
|
13774
14200
|
if (!pagePromise) pagePromise = (async () => {
|
|
13775
14201
|
const p = await (await browser.newContext({ javaScriptEnabled: true })).newPage();
|
|
13776
|
-
await p.setContent(PAGE);
|
|
13777
|
-
await p.addScriptTag({ path: require$
|
|
14202
|
+
await p.setContent(PAGE$1);
|
|
14203
|
+
await p.addScriptTag({ path: require$2.resolve("mermaid/dist/mermaid.min.js") });
|
|
13778
14204
|
await p.evaluate(() => {
|
|
13779
14205
|
window.mermaid.initialize({
|
|
13780
14206
|
startOnLoad: false,
|
|
@@ -13811,6 +14237,131 @@ async function openDiagramRenderer(env) {
|
|
|
13811
14237
|
};
|
|
13812
14238
|
}
|
|
13813
14239
|
//#endregion
|
|
14240
|
+
//#region ../../extensions/maps/src/map-host.ts
|
|
14241
|
+
var HOST_STYLE = "https://maps.etherpk.com/styles/light.json";
|
|
14242
|
+
var DEFAULT_MAP_STYLES = {
|
|
14243
|
+
light: HOST_STYLE,
|
|
14244
|
+
dark: HOST_STYLE
|
|
14245
|
+
};
|
|
14246
|
+
//#endregion
|
|
14247
|
+
//#region src/map-pictures.ts
|
|
14248
|
+
/**
|
|
14249
|
+
* Map pictures for a publish from the Headless Client (ADR 0118): a [[Map Block]] is published as
|
|
14250
|
+
* a picture of the map, never as its lines, and the picture is drawn in a real Chromium, the one
|
|
14251
|
+
* Mermaid diagrams use (diagrams.ts), because MapLibre needs WebGL and a page to draw in.
|
|
14252
|
+
*
|
|
14253
|
+
* The page runs the maps extension's own drawing code (`map-picture-page.js`, built from
|
|
14254
|
+
* `src/map-picture-page.ts` beside the Node bundle), so a map published from here looks as it
|
|
14255
|
+
* does published from the browser. It is served, with the MapLibre this package installs, from a
|
|
14256
|
+
* server on the loopback address that answers only those files, so MapLibre's worker loads as it
|
|
14257
|
+
* does in the Client. The basemap comes from EtherPK's map host, or the style
|
|
14258
|
+
* `ETHERPK_MAP_STYLE_URL` names; set empty, maps are drawn on a plain background.
|
|
14259
|
+
*/
|
|
14260
|
+
var require$1 = createRequire(import.meta.url);
|
|
14261
|
+
/** The light basemap style a publish draws with, or null for a plain background. */
|
|
14262
|
+
function publishMapStyle(env) {
|
|
14263
|
+
const named = env.ETHERPK_MAP_STYLE_URL;
|
|
14264
|
+
if (named === void 0) return DEFAULT_MAP_STYLES.light;
|
|
14265
|
+
return named.trim() === "" ? null : named.trim();
|
|
14266
|
+
}
|
|
14267
|
+
/** Whether a publish of these bodies draws a map: a fenced block whose info string is `map`. */
|
|
14268
|
+
function needsMaps(bodies) {
|
|
14269
|
+
for (const body of bodies) if (/^\s*(?:-\s+)?```map\s*$/m.test(body)) return true;
|
|
14270
|
+
return false;
|
|
14271
|
+
}
|
|
14272
|
+
/** The page: MapLibre by the import map, then the drawing code, which puts its function on `window`. */
|
|
14273
|
+
var PAGE = `<!doctype html>
|
|
14274
|
+
<html><head><meta charset="utf-8">
|
|
14275
|
+
<script type="importmap">{"imports":{"maplibre-gl":"/maplibre-gl.mjs"}}<\/script>
|
|
14276
|
+
<script type="module" src="/map-picture-page.js"><\/script>
|
|
14277
|
+
</head><body></body></html>`;
|
|
14278
|
+
/** The files the page loads, by path, and where each is on disk. */
|
|
14279
|
+
function pageFiles() {
|
|
14280
|
+
const maplibre = dirname(require$1.resolve("maplibre-gl/dist/maplibre-gl.mjs"));
|
|
14281
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
14282
|
+
const bundle = here.endsWith("dist") ? join(here, "map-picture-page.js") : join(here, "..", "dist", "map-picture-page.js");
|
|
14283
|
+
return /* @__PURE__ */ new Map([
|
|
14284
|
+
["/maplibre-gl.mjs", join(maplibre, "maplibre-gl.mjs")],
|
|
14285
|
+
["/maplibre-gl-shared.mjs", join(maplibre, "maplibre-gl-shared.mjs")],
|
|
14286
|
+
["/maplibre-gl-worker.mjs", join(maplibre, "maplibre-gl-worker.mjs")],
|
|
14287
|
+
["/map-picture-page.js", bundle]
|
|
14288
|
+
]);
|
|
14289
|
+
}
|
|
14290
|
+
/** A server on the loopback address that answers the page and its files, and nothing else. */
|
|
14291
|
+
async function servePage() {
|
|
14292
|
+
const files = pageFiles();
|
|
14293
|
+
const server = createServer$1((request, response) => {
|
|
14294
|
+
const path = new URL(request.url ?? "/", "http://localhost").pathname;
|
|
14295
|
+
if (path === "/map.html") {
|
|
14296
|
+
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(PAGE);
|
|
14297
|
+
return;
|
|
14298
|
+
}
|
|
14299
|
+
const file = files.get(path);
|
|
14300
|
+
if (!file) {
|
|
14301
|
+
response.writeHead(404).end();
|
|
14302
|
+
return;
|
|
14303
|
+
}
|
|
14304
|
+
readFile(file).then((bytes) => response.writeHead(200, { "Content-Type": "text/javascript; charset=utf-8" }).end(bytes), () => response.writeHead(404).end());
|
|
14305
|
+
});
|
|
14306
|
+
await new Promise((resolve, reject) => {
|
|
14307
|
+
server.once("error", reject);
|
|
14308
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
14309
|
+
});
|
|
14310
|
+
const address = server.address();
|
|
14311
|
+
if (!address || typeof address === "string") throw new Error("The map page server has no port.");
|
|
14312
|
+
return {
|
|
14313
|
+
server,
|
|
14314
|
+
origin: `http://127.0.0.1:${address.port}`
|
|
14315
|
+
};
|
|
14316
|
+
}
|
|
14317
|
+
/**
|
|
14318
|
+
* A renderer over one headless browser, opened for a publish and closed after it. Null when no
|
|
14319
|
+
* browser is available; the caller decides what that means (a publish with maps refuses).
|
|
14320
|
+
*/
|
|
14321
|
+
async function openMapRenderer(env) {
|
|
14322
|
+
const browser = await launchChromium(env);
|
|
14323
|
+
if (!browser) return null;
|
|
14324
|
+
const style = publishMapStyle(env);
|
|
14325
|
+
let served = null;
|
|
14326
|
+
let pagePromise;
|
|
14327
|
+
const page = () => {
|
|
14328
|
+
pagePromise ??= (async () => {
|
|
14329
|
+
served = await servePage();
|
|
14330
|
+
const p = await (await browser.newContext()).newPage();
|
|
14331
|
+
await p.goto(`${served.origin}/map.html`);
|
|
14332
|
+
await p.waitForFunction(() => typeof window.etherpkDrawMapPicture === "function", void 0, { timeout: 15e3 });
|
|
14333
|
+
return p;
|
|
14334
|
+
})();
|
|
14335
|
+
return pagePromise;
|
|
14336
|
+
};
|
|
14337
|
+
return {
|
|
14338
|
+
async render(source) {
|
|
14339
|
+
const drawn = await (await page()).evaluate(async ([text, basemap]) => {
|
|
14340
|
+
const picture = await window.etherpkDrawMapPicture(text, { style: basemap });
|
|
14341
|
+
let binary = "";
|
|
14342
|
+
for (const byte of picture.bytes) binary += String.fromCharCode(byte);
|
|
14343
|
+
return {
|
|
14344
|
+
type: picture.type,
|
|
14345
|
+
width: picture.width,
|
|
14346
|
+
height: picture.height,
|
|
14347
|
+
data: btoa(binary)
|
|
14348
|
+
};
|
|
14349
|
+
}, [source, style]);
|
|
14350
|
+
return {
|
|
14351
|
+
type: drawn.type,
|
|
14352
|
+
width: drawn.width,
|
|
14353
|
+
height: drawn.height,
|
|
14354
|
+
bytes: new Uint8Array(Buffer.from(drawn.data, "base64"))
|
|
14355
|
+
};
|
|
14356
|
+
},
|
|
14357
|
+
async dispose() {
|
|
14358
|
+
await browser.close().catch(() => {});
|
|
14359
|
+
const open = served;
|
|
14360
|
+
await new Promise((resolve) => open ? open.server.close(() => resolve()) : resolve());
|
|
14361
|
+
}
|
|
14362
|
+
};
|
|
14363
|
+
}
|
|
14364
|
+
//#endregion
|
|
13814
14365
|
//#region src/public-fetch.ts
|
|
13815
14366
|
/**
|
|
13816
14367
|
* Fetching text from the public internet, and nowhere else: the only network reads a tool makes
|
|
@@ -13959,8 +14510,8 @@ async function fetchPublicText(href, options = {}) {
|
|
|
13959
14510
|
/**
|
|
13960
14511
|
* The publisher's environment in Node (ADR 0082, ADR 0084): themes resolved the way the Client
|
|
13961
14512
|
* resolves them (bundled, the graph's own, a url), KaTeX's stylesheet and fonts read from the
|
|
13962
|
-
* package this depends on, Mermaid through a real browser when one is available,
|
|
13963
|
-
* folder over `fs` under the same rules as the browser's directory handle. Browser-free
|
|
14513
|
+
* package this depends on, Mermaid and map pictures through a real browser when one is available,
|
|
14514
|
+
* and a site folder over `fs` under the same rules as the browser's directory handle. Browser-free
|
|
13964
14515
|
* counterpart of `host/browser-environment.ts`; the core is shared.
|
|
13965
14516
|
*/
|
|
13966
14517
|
var require = createRequire(import.meta.url);
|
|
@@ -13992,6 +14543,7 @@ function createNodePublishEnvironment(deps) {
|
|
|
13992
14543
|
graphTheme: deps.graphTheme
|
|
13993
14544
|
}),
|
|
13994
14545
|
...renderer ? { renderMermaid: (source) => renderer.render(source) } : {},
|
|
14546
|
+
...deps.maps ? { renderMap: (source) => deps.maps.render(source) } : {},
|
|
13995
14547
|
katexAssets
|
|
13996
14548
|
};
|
|
13997
14549
|
}
|
|
@@ -15243,35 +15795,19 @@ async function writeDownload(directory, name, bytes) {
|
|
|
15243
15795
|
}
|
|
15244
15796
|
throw new Error(`${directory} already holds a thousand files named like ${safe}.`);
|
|
15245
15797
|
}
|
|
15246
|
-
/**
|
|
15247
|
-
* Namespace a View kind to the extension that contributes it, so two extensions
|
|
15248
|
-
* can each contribute a kind without colliding. First-party (core) kinds are
|
|
15249
|
-
* unprefixed (`document`, `backlinks`, …); extension kinds are
|
|
15250
|
-
* `"<extensionId>.<kind>"`, mirroring the `publisher.command` convention.
|
|
15251
|
-
*/
|
|
15252
|
-
function namespacedViewKind(extensionId, kind) {
|
|
15253
|
-
return `${extensionId}.${kind}`;
|
|
15254
|
-
}
|
|
15255
15798
|
//#endregion
|
|
15256
|
-
//#region ../client/src/lib/
|
|
15799
|
+
//#region ../client/src/lib/navigation/concept-path.ts
|
|
15257
15800
|
/**
|
|
15258
|
-
*
|
|
15259
|
-
*
|
|
15260
|
-
*
|
|
15261
|
-
*
|
|
15262
|
-
* Two kinds rather than one kind with two targets, because the Layout finds a resident by kind
|
|
15263
|
-
* (`findView`, residents.ts) and places a View by its kind's natural region: the resident must
|
|
15264
|
-
* never be confused with the whole graph, and each has its own home.
|
|
15801
|
+
* A concept in a URL path (ADR 0023): each `/`-separated segment encoded on its own, so a
|
|
15802
|
+
* concept containing `/` spans several path segments and round-trips through a SvelteKit rest
|
|
15803
|
+
* param, which re-joins segments with `/` and decodes each. Its own module so the extension
|
|
15804
|
+
* address book and the document URLs share it without importing each other.
|
|
15265
15805
|
*/
|
|
15266
|
-
var GRAPH_VIEW_EXTENSION_ID = "graph-view";
|
|
15267
|
-
namespacedViewKind(GRAPH_VIEW_EXTENSION_ID, "local");
|
|
15268
|
-
namespacedViewKind(GRAPH_VIEW_EXTENSION_ID, "whole");
|
|
15269
|
-
//#endregion
|
|
15270
|
-
//#region ../client/src/lib/navigation/document-url.ts
|
|
15271
|
-
/** Encode a concept for the `[...concept]` rest param: per-segment, `/` kept. */
|
|
15272
15806
|
function encodeConceptPath(concept) {
|
|
15273
15807
|
return concept.split("/").map(encodeURIComponent).join("/");
|
|
15274
15808
|
}
|
|
15809
|
+
//#endregion
|
|
15810
|
+
//#region ../client/src/lib/navigation/document-url.ts
|
|
15275
15811
|
/** The Document URL for a concept within a graph. */
|
|
15276
15812
|
function documentUrl(graphId, concept) {
|
|
15277
15813
|
return `${graphUrl(graphId)}/d/${encodeConceptPath(concept)}`;
|
|
@@ -15424,7 +15960,7 @@ async function taskLinesWith(lines, fingerprint) {
|
|
|
15424
15960
|
return out;
|
|
15425
15961
|
}
|
|
15426
15962
|
//#endregion
|
|
15427
|
-
//#region
|
|
15963
|
+
//#region ../../extensions/graph-view/src/model/graph-model.ts
|
|
15428
15964
|
/**
|
|
15429
15965
|
* The [[Graph View]]'s model: the index's {@link LinkGraph} turned into a graphology graph the
|
|
15430
15966
|
* drawing and the insight lists both read, and the questions asked of it (what is near a
|
|
@@ -15543,7 +16079,7 @@ function shortestPath(graph, from, to) {
|
|
|
15543
16079
|
return null;
|
|
15544
16080
|
}
|
|
15545
16081
|
//#endregion
|
|
15546
|
-
//#region
|
|
16082
|
+
//#region ../../extensions/graph-view/src/model/insights.ts
|
|
15547
16083
|
/**
|
|
15548
16084
|
* The lists the whole-graph [[Graph View]] shows beside the picture, and the Headless Client's
|
|
15549
16085
|
* `graph_insights` tool returns: [[Hub]]s, [[Pageless Concept]]s by how many documents mention
|
|
@@ -17011,20 +17547,31 @@ async function publish(graph, args, host) {
|
|
|
17011
17547
|
}
|
|
17012
17548
|
const selected = selectDocuments(source.documents, publication, summary.publications);
|
|
17013
17549
|
const includeTexts = Object.values(publication.includes).map((concept) => source.documents.find((d) => d.concept.toLowerCase() === concept.toLowerCase())?.text ?? "");
|
|
17550
|
+
const bodies = [...selected.included.map((d) => d.text), ...includeTexts];
|
|
17014
17551
|
let renderer = null;
|
|
17015
|
-
if (needsMermaid(
|
|
17552
|
+
if (needsMermaid(bodies)) {
|
|
17016
17553
|
renderer = await openDiagramRenderer(env);
|
|
17017
17554
|
if (!renderer) {
|
|
17018
17555
|
const status = await chromiumStatus(env, cmd);
|
|
17019
17556
|
throw new ToolError("chromium_unavailable", `"${publication.name}" has Mermaid diagrams, which a publish draws with a browser, and none is set up on this computer. Ask the user to run: ${status.setupCommand} (or set ETHERPK_CHROMIUM to a Chromium on this machine). Nothing was published.`);
|
|
17020
17557
|
}
|
|
17021
17558
|
}
|
|
17559
|
+
let maps = null;
|
|
17560
|
+
if (needsMaps(bodies)) {
|
|
17561
|
+
maps = await openMapRenderer(env);
|
|
17562
|
+
if (!maps) {
|
|
17563
|
+
await renderer?.dispose();
|
|
17564
|
+
const status = await chromiumStatus(env, cmd);
|
|
17565
|
+
throw new ToolError("chromium_unavailable", `"${publication.name}" has maps, which a publish draws as pictures with a browser, and none is set up on this computer. Ask the user to run: ${status.setupCommand} (or set ETHERPK_CHROMIUM to a Chromium on this machine). Nothing was published.`);
|
|
17566
|
+
}
|
|
17567
|
+
}
|
|
17022
17568
|
try {
|
|
17023
17569
|
const run = await runPublish(publication, {
|
|
17024
17570
|
source,
|
|
17025
17571
|
environment: createNodePublishEnvironment({
|
|
17026
17572
|
graphTheme: (id) => graph.publishing.graphTheme(id),
|
|
17027
|
-
renderer
|
|
17573
|
+
renderer,
|
|
17574
|
+
maps
|
|
17028
17575
|
}),
|
|
17029
17576
|
unsettled
|
|
17030
17577
|
});
|
|
@@ -17064,6 +17611,7 @@ async function publish(graph, args, host) {
|
|
|
17064
17611
|
};
|
|
17065
17612
|
} finally {
|
|
17066
17613
|
await renderer?.dispose();
|
|
17614
|
+
await maps?.dispose();
|
|
17067
17615
|
}
|
|
17068
17616
|
}
|
|
17069
17617
|
/**
|
|
@@ -18023,7 +18571,7 @@ function createMcpServer(source, info) {
|
|
|
18023
18571
|
}, async (args) => run((graph) => updatePublication(graph, args, host)));
|
|
18024
18572
|
server.registerTool("publish", {
|
|
18025
18573
|
title: "Publish",
|
|
18026
|
-
description: "Render a publication to its publish folder on this machine and return the report: what was included and why documents were left out, missing links, assets, warnings. The folder is the one the user set with \"etherpk-mcp publish --publication <id> --out <dir>\" (error \"no_publish_folder\" until then - the tool never chooses a folder). Pages with Mermaid diagrams need the browser from \"diagrams setup\" (error \"chromium_unavailable\"). The report is this result, trimmed to counts and first entries - nothing of it is written into the folder, so the site never names the documents it leaves out. Publishing writes files - it does not deploy them.",
|
|
18574
|
+
description: "Render a publication to its publish folder on this machine and return the report: what was included and why documents were left out, missing links, assets, warnings. The folder is the one the user set with \"etherpk-mcp publish --publication <id> --out <dir>\" (error \"no_publish_folder\" until then - the tool never chooses a folder). Pages with Mermaid diagrams or maps need the browser from \"diagrams setup\" (error \"chromium_unavailable\"). The report is this result, trimmed to counts and first entries - nothing of it is written into the folder, so the site never names the documents it leaves out. Publishing writes files - it does not deploy them.",
|
|
18027
18575
|
inputSchema: { id: z.string().min(1) }
|
|
18028
18576
|
}, async (args) => run((graph) => publish(graph, args, host)));
|
|
18029
18577
|
server.registerTool("list_themes", {
|
|
@@ -18619,19 +19167,6 @@ function sameNames(a, b) {
|
|
|
18619
19167
|
}
|
|
18620
19168
|
//#endregion
|
|
18621
19169
|
//#region ../client/src/lib/document/index-link-graph.ts
|
|
18622
|
-
/**
|
|
18623
|
-
* The [[Graph View]]'s query over the [[Derived Index]]: every [[Concept]] and every pair of
|
|
18624
|
-
* concepts a [[Wikilink]] joins, in one answer.
|
|
18625
|
-
*
|
|
18626
|
-
* Everything comes from rows the index already holds: the `pages` and `aliases` tables, and the
|
|
18627
|
-
* `links` table grouped by source document. No document is opened or parsed, which is what lets
|
|
18628
|
-
* a whole graph be drawn from one round trip.
|
|
18629
|
-
*
|
|
18630
|
-
* A [[Protected Document]] contributes no body links (the parser suppresses links inside the
|
|
18631
|
-
* cipher fence, and the index never sees plaintext), so it appears here as a name with lines in
|
|
18632
|
-
* from plaintext documents and none out. Its title scopes still draw a line, because a page's
|
|
18633
|
-
* name is visible wherever the page is.
|
|
18634
|
-
*/
|
|
18635
19170
|
function linkGraph(db) {
|
|
18636
19171
|
const generation = activeIndexGeneration(db);
|
|
18637
19172
|
const pages = db.all("SELECT concept, concept_key AS key, kind, protected FROM pages WHERE generation = ? ORDER BY id", [generation]);
|
|
@@ -18648,6 +19183,7 @@ function linkGraph(db) {
|
|
|
18648
19183
|
name: page.concept,
|
|
18649
19184
|
kind: page.kind
|
|
18650
19185
|
};
|
|
19186
|
+
if (page.kind === "journal" && isJournalConcept(page.concept)) concept.day = page.concept;
|
|
18651
19187
|
concepts.set(page.key, page.protected ? {
|
|
18652
19188
|
...concept,
|
|
18653
19189
|
protected: true
|
|
@@ -18706,6 +19242,57 @@ function linkGraph(db) {
|
|
|
18706
19242
|
};
|
|
18707
19243
|
}
|
|
18708
19244
|
//#endregion
|
|
19245
|
+
//#region ../client/src/lib/document/index-map-items.ts
|
|
19246
|
+
/**
|
|
19247
|
+
* The most items one answer carries. Far beyond a real graph's maps (hundreds of Map Blocks of
|
|
19248
|
+
* tens of places each), and small enough that a pathological graph cannot stall the worker or
|
|
19249
|
+
* the drawing.
|
|
19250
|
+
*/
|
|
19251
|
+
var MAP_ITEMS_CAP = 2e4;
|
|
19252
|
+
/**
|
|
19253
|
+
* Whether the document with this concept key holds any place or route in the active generation.
|
|
19254
|
+
* Asked before and after a document is indexed again, so an update can say whether it touched a
|
|
19255
|
+
* map and an open Map View reads again only when one did.
|
|
19256
|
+
*/
|
|
19257
|
+
function pageHasMapItems(db, key) {
|
|
19258
|
+
return db.all(`SELECT 1 AS found FROM map_items m JOIN pages p ON p.id = m.page_id
|
|
19259
|
+
WHERE p.generation = ? AND p.concept_key = ? LIMIT 1`, [activeIndexGeneration(db), key]).length > 0;
|
|
19260
|
+
}
|
|
19261
|
+
/** Every place and route answering to `concept`, or in the whole graph when it is null. */
|
|
19262
|
+
function mapItems(db, concept, cap = MAP_ITEMS_CAP) {
|
|
19263
|
+
const where = ["p.generation = ?", "p.protected = 0"];
|
|
19264
|
+
const params = [activeIndexGeneration(db)];
|
|
19265
|
+
if (concept !== null) {
|
|
19266
|
+
const names = conceptFamilyKeys(db, conceptKey$1(concept));
|
|
19267
|
+
where.push(`EXISTS (SELECT 1 FROM map_concepts mc
|
|
19268
|
+
WHERE mc.page_id = m.page_id AND mc.block_local_id = m.block_local_id
|
|
19269
|
+
AND mc.concept_key IN (${names.map(() => "?").join(",")}))`);
|
|
19270
|
+
params.push(...names);
|
|
19271
|
+
}
|
|
19272
|
+
const rows = db.all(`SELECT p.concept, p.kind, m.line, m.fence_line, m.text
|
|
19273
|
+
FROM map_items m JOIN pages p ON p.id = m.page_id
|
|
19274
|
+
WHERE ${where.join(" AND ")}
|
|
19275
|
+
ORDER BY p.concept_key, p.id, m.line
|
|
19276
|
+
LIMIT ?`, [...params, cap + 1]);
|
|
19277
|
+
const truncated = rows.length > cap;
|
|
19278
|
+
const items = [];
|
|
19279
|
+
for (const row of truncated ? rows.slice(0, cap) : rows) {
|
|
19280
|
+
const item = readMapLine(row.text);
|
|
19281
|
+
if (item) items.push({
|
|
19282
|
+
concept: row.concept,
|
|
19283
|
+
kind: row.kind,
|
|
19284
|
+
line: row.line,
|
|
19285
|
+
fenceLine: row.fence_line,
|
|
19286
|
+
text: row.text,
|
|
19287
|
+
item
|
|
19288
|
+
});
|
|
19289
|
+
}
|
|
19290
|
+
return {
|
|
19291
|
+
items,
|
|
19292
|
+
truncated
|
|
19293
|
+
};
|
|
19294
|
+
}
|
|
19295
|
+
//#endregion
|
|
18709
19296
|
//#region ../client/src/lib/document/index-worker/core.ts
|
|
18710
19297
|
/**
|
|
18711
19298
|
* The index server, free of any transport (ADR 0041 §2). It owns the SQLite database and
|
|
@@ -18805,7 +19392,7 @@ function createIndexCore(host) {
|
|
|
18805
19392
|
return db.all("SELECT COUNT(*) AS n FROM pages WHERE generation = ?", [activeIndexGeneration(db)])[0]?.n ?? 0;
|
|
18806
19393
|
}
|
|
18807
19394
|
const candidateExists = (candidate) => candidate !== void 0 && candidate.kind !== "pageless";
|
|
18808
|
-
function deltaFor(revision, before, after, backlinkTargetsChanged) {
|
|
19395
|
+
function deltaFor(revision, before, after, backlinkTargetsChanged, mapsChanged) {
|
|
18809
19396
|
const existingAdded = [];
|
|
18810
19397
|
const existingRemoved = [];
|
|
18811
19398
|
const candidateUpserts = [];
|
|
@@ -18824,7 +19411,8 @@ function createIndexCore(host) {
|
|
|
18824
19411
|
existingRemoved,
|
|
18825
19412
|
candidateUpserts,
|
|
18826
19413
|
candidateRemoved,
|
|
18827
|
-
backlinkTargetsChanged: [...backlinkTargetsChanged]
|
|
19414
|
+
backlinkTargetsChanged: [...backlinkTargetsChanged],
|
|
19415
|
+
mapsChanged
|
|
18828
19416
|
};
|
|
18829
19417
|
}
|
|
18830
19418
|
/**
|
|
@@ -18952,10 +19540,19 @@ function createIndexCore(host) {
|
|
|
18952
19540
|
const before = conceptCandidatesForKeys(db, affected);
|
|
18953
19541
|
const previousSnapshot = visibleSnapshot;
|
|
18954
19542
|
visibleSnapshot = void 0;
|
|
18955
|
-
|
|
18956
|
-
for (const
|
|
19543
|
+
let mapsChanged = false;
|
|
19544
|
+
for (const key of removed) {
|
|
19545
|
+
mapsChanged ||= pageHasMapItems(db, key);
|
|
19546
|
+
removeDocument(db, key);
|
|
19547
|
+
}
|
|
19548
|
+
for (const doc of changed) {
|
|
19549
|
+
const key = conceptKey$1(doc.concept);
|
|
19550
|
+
const hadMaps = pageHasMapItems(db, key);
|
|
19551
|
+
ingestOne(db, doc);
|
|
19552
|
+
mapsChanged ||= hadMaps || pageHasMapItems(db, key);
|
|
19553
|
+
}
|
|
18957
19554
|
const after = conceptCandidatesForKeys(db, affected);
|
|
18958
|
-
const delta = deltaFor(advanceIndexRevision(db), before, after, backlinkTargetsChanged);
|
|
19555
|
+
const delta = deltaFor(advanceIndexRevision(db), before, after, backlinkTargetsChanged, mapsChanged);
|
|
18959
19556
|
applyDeltaToSnapshot(previousSnapshot, delta);
|
|
18960
19557
|
return [{
|
|
18961
19558
|
type: "delta",
|
|
@@ -19054,6 +19651,17 @@ function createIndexCore(host) {
|
|
|
19054
19651
|
graph
|
|
19055
19652
|
}];
|
|
19056
19653
|
}
|
|
19654
|
+
case "map-items": {
|
|
19655
|
+
const result = db ? mapItems(db, request.concept) : {
|
|
19656
|
+
items: [],
|
|
19657
|
+
truncated: false
|
|
19658
|
+
};
|
|
19659
|
+
return [{
|
|
19660
|
+
type: "map-items",
|
|
19661
|
+
id: request.id,
|
|
19662
|
+
result
|
|
19663
|
+
}];
|
|
19664
|
+
}
|
|
19057
19665
|
case "semantic-status": {
|
|
19058
19666
|
const status = db ? semanticStatus(db, request.model) : {
|
|
19059
19667
|
available: false,
|
|
@@ -19268,7 +19876,8 @@ function createRemoteGraphIndex(source, transport, options) {
|
|
|
19268
19876
|
for (const listener of updated) listener({
|
|
19269
19877
|
full: true,
|
|
19270
19878
|
changedConceptKeys,
|
|
19271
|
-
backlinkTargetsChanged: /* @__PURE__ */ new Set()
|
|
19879
|
+
backlinkTargetsChanged: /* @__PURE__ */ new Set(),
|
|
19880
|
+
mapsChanged: true
|
|
19272
19881
|
});
|
|
19273
19882
|
if (response.rebuildId && activeRebuildId) {
|
|
19274
19883
|
const shouldRetry = rebuildInvalidated;
|
|
@@ -19304,7 +19913,8 @@ function createRemoteGraphIndex(source, transport, options) {
|
|
|
19304
19913
|
const update = {
|
|
19305
19914
|
full: false,
|
|
19306
19915
|
changedConceptKeys,
|
|
19307
|
-
backlinkTargetsChanged: new Set(response.backlinkTargetsChanged)
|
|
19916
|
+
backlinkTargetsChanged: new Set(response.backlinkTargetsChanged),
|
|
19917
|
+
mapsChanged: response.mapsChanged
|
|
19308
19918
|
};
|
|
19309
19919
|
performanceRecorder.mark("index.delta.received", {
|
|
19310
19920
|
bytes: new TextEncoder().encode(JSON.stringify(response)).byteLength,
|
|
@@ -19853,6 +20463,13 @@ function createRemoteGraphIndex(source, transport, options) {
|
|
|
19853
20463
|
id
|
|
19854
20464
|
}))).graph;
|
|
19855
20465
|
},
|
|
20466
|
+
async mapItems(concept) {
|
|
20467
|
+
return (await request((id) => ({
|
|
20468
|
+
type: "map-items",
|
|
20469
|
+
id,
|
|
20470
|
+
concept
|
|
20471
|
+
}))).result;
|
|
20472
|
+
},
|
|
19856
20473
|
async searchText(query, offset, limit, filters) {
|
|
19857
20474
|
const response = await request((id) => ({
|
|
19858
20475
|
type: "search-text",
|
|
@@ -24485,9 +25102,9 @@ var USAGE = `etherpk-mcp - EtherPK Headless Client (an MCP server over one synce
|
|
|
24485
25102
|
removed - everything else in it is left alone. Publishing writes files - deploying them
|
|
24486
25103
|
(a git push, say) is yours. Run it from cron for a site that republishes itself.
|
|
24487
25104
|
${CMD} diagrams setup
|
|
24488
|
-
Let a publish draw Mermaid diagrams. Downloads a Chromium (about 170 MB) into the
|
|
24489
|
-
directory with Playwright's installer, once per computer - a publish whose pages hold
|
|
24490
|
-
diagrams refuses until this has run. Set ETHERPK_CHROMIUM=<path> to use a browser
|
|
25105
|
+
Let a publish draw Mermaid diagrams and maps. Downloads a Chromium (about 170 MB) into the
|
|
25106
|
+
cache directory with Playwright's installer, once per computer - a publish whose pages hold
|
|
25107
|
+
diagrams or maps refuses until this has run. Set ETHERPK_CHROMIUM=<path> to use a browser
|
|
24491
25108
|
already on this machine instead (NixOS needs this).
|
|
24492
25109
|
${CMD} diagrams status
|
|
24493
25110
|
Which browser a publish would use, if any.
|
|
@@ -24874,7 +25491,7 @@ async function diagramsCommand(what) {
|
|
|
24874
25491
|
return;
|
|
24875
25492
|
case "status": {
|
|
24876
25493
|
const status = await chromiumStatus(process.env, CMD);
|
|
24877
|
-
if (status.executable) console.log(`Chromium: ${status.executable} (${status.source === "env" ? "from ETHERPK_CHROMIUM" : "installed by diagrams setup"}). A publish can draw Mermaid diagrams.`);
|
|
25494
|
+
if (status.executable) console.log(`Chromium: ${status.executable} (${status.source === "env" ? "from ETHERPK_CHROMIUM" : "installed by diagrams setup"}). A publish can draw Mermaid diagrams and maps.`);
|
|
24878
25495
|
else console.log(status.source === "env" ? `ETHERPK_CHROMIUM is set but names no file: ${process.env.ETHERPK_CHROMIUM}` : `No browser is set up. Run: ${status.setupCommand}`);
|
|
24879
25496
|
return;
|
|
24880
25497
|
}
|
|
@@ -25218,7 +25835,7 @@ async function hostCommand(args) {
|
|
|
25218
25835
|
process.on("SIGHUP", () => {});
|
|
25219
25836
|
chromiumStatus(process.env, CMD).then((chromium) => {
|
|
25220
25837
|
if (chromium.executable) return;
|
|
25221
|
-
console.error(`etherpk-mcp: no browser for diagrams (run: ${chromium.setupCommand}, or set ETHERPK_CHROMIUM) - a publish or theme preview with
|
|
25838
|
+
console.error(`etherpk-mcp: no browser for diagrams or maps (run: ${chromium.setupCommand}, or set ETHERPK_CHROMIUM) - a publish with Mermaid diagrams or maps, or a theme preview with diagrams, refuses until then.`);
|
|
25222
25839
|
});
|
|
25223
25840
|
}
|
|
25224
25841
|
/**
|