@wizzlethorpe/vaults 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -15
- package/dist/build.js +80 -71
- package/dist/build.js.map +1 -1
- package/dist/commands/build.js +3 -4
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/init.js +13 -10
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/password.js +2 -0
- package/dist/commands/password.js.map +1 -1
- package/dist/commands/patreon.js +6 -0
- package/dist/commands/patreon.js.map +1 -1
- package/dist/commands/preview.js +6 -5
- package/dist/commands/preview.js.map +1 -1
- package/dist/commands/push.js +5 -2
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/role.js +5 -0
- package/dist/commands/role.js.map +1 -1
- package/dist/config.js +9 -5
- package/dist/config.js.map +1 -1
- package/dist/index.js +34 -2
- package/dist/index.js.map +1 -1
- package/dist/migrate/0.6-legacy-auth-settings.js +98 -0
- package/dist/migrate/0.6-legacy-auth-settings.js.map +1 -0
- package/dist/migrate/0.7-vaults-dir.js +70 -0
- package/dist/migrate/0.7-vaults-dir.js.map +1 -0
- package/dist/migrate/registry.js +12 -0
- package/dist/migrate/registry.js.map +1 -0
- package/dist/migrate/run.js +38 -0
- package/dist/migrate/run.js.map +1 -0
- package/dist/migrate/types.js +8 -0
- package/dist/migrate/types.js.map +1 -0
- package/dist/paths.js +51 -0
- package/dist/paths.js.map +1 -0
- package/dist/render/auth-template.js +3 -0
- package/dist/render/auth-template.js.map +1 -1
- package/dist/render/footer.js +37 -0
- package/dist/render/footer.js.map +1 -0
- package/dist/render/handlers/assets.js +90 -0
- package/dist/render/handlers/assets.js.map +1 -0
- package/dist/render/handlers/builtin/dice.js +78 -0
- package/dist/render/handlers/builtin/dice.js.map +1 -0
- package/dist/render/handlers/builtin/fm.js +89 -0
- package/dist/render/handlers/builtin/fm.js.map +1 -0
- package/dist/render/handlers/builtin/index.js +9 -0
- package/dist/render/handlers/builtin/index.js.map +1 -0
- package/dist/render/handlers/builtin/statblock.js +351 -0
- package/dist/render/handlers/builtin/statblock.js.map +1 -0
- package/dist/render/handlers/dispatch.js +187 -0
- package/dist/render/handlers/dispatch.js.map +1 -0
- package/dist/render/handlers/loader.js +90 -0
- package/dist/render/handlers/loader.js.map +1 -0
- package/dist/render/handlers/types.js +40 -0
- package/dist/render/handlers/types.js.map +1 -0
- package/dist/render/layout.js +2 -0
- package/dist/render/layout.js.map +1 -1
- package/dist/render/pipeline.js +16 -0
- package/dist/render/pipeline.js.map +1 -1
- package/dist/render/styles.js +30 -1
- package/dist/render/styles.js.map +1 -1
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/settings.js +9 -3
- package/dist/settings.js.map +1 -1
- package/package.json +11 -12
- package/dist/api.js +0 -42
- package/dist/api.js.map +0 -1
- package/dist/render/mcp-template.js +0 -239
- package/dist/render/mcp-template.js.map +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Read and concatenate browser-side assets declared by handlers.
|
|
2
|
+
//
|
|
3
|
+
// Built-in handler assets are delivered as { source, content } pairs (so
|
|
4
|
+
// vaults-cli's own bundle ships the runtime without filesystem lookups).
|
|
5
|
+
// User handler assets are file paths relative to the handler's module file
|
|
6
|
+
// — those get read here, validated to live under the vault's
|
|
7
|
+
// .vaults/handlers/ tree (no `../../etc/passwd`), and included verbatim.
|
|
8
|
+
//
|
|
9
|
+
// Output is two strings: a concatenated _handlers.js and _handlers.css.
|
|
10
|
+
// Each unique source is included exactly once even if multiple handlers
|
|
11
|
+
// reference it, so shared utility files don't duplicate.
|
|
12
|
+
import { readFile } from "node:fs/promises";
|
|
13
|
+
import { dirname, resolve, sep } from "node:path";
|
|
14
|
+
/**
|
|
15
|
+
* Built-in handlers attach their static assets to a side-table keyed on
|
|
16
|
+
* the handler reference. The bundler looks them up here.
|
|
17
|
+
*/
|
|
18
|
+
const BUILTIN_ASSETS = new WeakMap();
|
|
19
|
+
export function registerBuiltinAssets(handler, assets) {
|
|
20
|
+
BUILTIN_ASSETS.set(handler, assets);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build the concatenated _handlers.js and _handlers.css for a deploy.
|
|
24
|
+
*
|
|
25
|
+
* @param userHandlers handlers loaded from `.vaults/handlers/`
|
|
26
|
+
* @param builtinHandlers handlers shipped by vaults-cli core
|
|
27
|
+
* @param vaultPath absolute path to the vault root, used for path-validation
|
|
28
|
+
*/
|
|
29
|
+
export async function bundleHandlerAssets(userHandlers, builtinHandlers, vaultPath) {
|
|
30
|
+
const seenJs = new Set();
|
|
31
|
+
const seenCss = new Set();
|
|
32
|
+
const jsParts = [];
|
|
33
|
+
const cssParts = [];
|
|
34
|
+
const handlersRoot = resolve(vaultPath, ".vaults/handlers");
|
|
35
|
+
for (const h of builtinHandlers) {
|
|
36
|
+
const inline = BUILTIN_ASSETS.get(h);
|
|
37
|
+
if (!inline)
|
|
38
|
+
continue;
|
|
39
|
+
for (const a of inline.scripts ?? []) {
|
|
40
|
+
if (seenJs.has(a.source))
|
|
41
|
+
continue;
|
|
42
|
+
seenJs.add(a.source);
|
|
43
|
+
jsParts.push(`/* ${a.source} */\n${a.content}`);
|
|
44
|
+
}
|
|
45
|
+
for (const a of inline.styles ?? []) {
|
|
46
|
+
if (seenCss.has(a.source))
|
|
47
|
+
continue;
|
|
48
|
+
seenCss.add(a.source);
|
|
49
|
+
cssParts.push(`/* ${a.source} */\n${a.content}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const { handler, sourcePath } of userHandlers) {
|
|
53
|
+
const baseDir = dirname(sourcePath);
|
|
54
|
+
for (const rel of handler.assets?.scripts ?? []) {
|
|
55
|
+
const abs = resolveAssetPath(handlersRoot, baseDir, rel);
|
|
56
|
+
if (seenJs.has(abs))
|
|
57
|
+
continue;
|
|
58
|
+
seenJs.add(abs);
|
|
59
|
+
jsParts.push(`/* ${rel} (${abs}) */\n${await readFile(abs, "utf8")}`);
|
|
60
|
+
}
|
|
61
|
+
for (const rel of handler.assets?.styles ?? []) {
|
|
62
|
+
const abs = resolveAssetPath(handlersRoot, baseDir, rel);
|
|
63
|
+
if (seenCss.has(abs))
|
|
64
|
+
continue;
|
|
65
|
+
seenCss.add(abs);
|
|
66
|
+
cssParts.push(`/* ${rel} (${abs}) */\n${await readFile(abs, "utf8")}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { js: jsParts.join("\n\n"), css: cssParts.join("\n\n") };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a handler asset path and require it lives under .vaults/handlers/.
|
|
73
|
+
* Throws on escape attempts so a typo or malicious manifest fails the build
|
|
74
|
+
* instead of silently shipping nothing.
|
|
75
|
+
*/
|
|
76
|
+
function resolveAssetPath(handlersRoot, baseDir, rel) {
|
|
77
|
+
const abs = resolve(baseDir, rel);
|
|
78
|
+
if (!isWithin(handlersRoot, abs)) {
|
|
79
|
+
throw new Error(`handler asset path escapes .vaults/handlers/: ${rel} (resolved to ${abs}). ` +
|
|
80
|
+
`Move the asset under .vaults/handlers/ and reference it relatively.`);
|
|
81
|
+
}
|
|
82
|
+
return abs;
|
|
83
|
+
}
|
|
84
|
+
function isWithin(root, candidate) {
|
|
85
|
+
// resolve() normalises both, so a prefix check is sound. Append the
|
|
86
|
+
// platform separator to avoid `/foo/bar-evil` matching `/foo/bar`.
|
|
87
|
+
const rootWithSep = root.endsWith(sep) ? root : root + sep;
|
|
88
|
+
return candidate === root || candidate.startsWith(rootWithSep);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../src/render/handlers/assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Built-in inline `dice:` handler.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the syntax popularized by Obsidian's Dice Roller plugin:
|
|
4
|
+
//
|
|
5
|
+
// The goblin attacks: `dice: 1d20+5` to hit, `dice: 1d6+3` damage.
|
|
6
|
+
//
|
|
7
|
+
// At build time, the handler renders each formula as a clickable <button>
|
|
8
|
+
// with the formula stored in data-formula. The browser-side runtime
|
|
9
|
+
// (DICE_RUNTIME_SCRIPT below) ships as a built-in asset, gets concatenated
|
|
10
|
+
// into _handlers.js, and parses + rolls on click.
|
|
11
|
+
//
|
|
12
|
+
// Subset of Dice Roller's syntax: basic XdY +/- Z arithmetic, optional X.
|
|
13
|
+
// More complex modifiers (kh, kl, explode, etc.) are not supported.
|
|
14
|
+
import { htmlEscape } from "../types.js";
|
|
15
|
+
import { registerBuiltinAssets } from "../assets.js";
|
|
16
|
+
// Formula validation: optional X, then 'd', then Y, then optional ± integer.
|
|
17
|
+
// Only used at build time to decide whether render() emits a clickable
|
|
18
|
+
// button or a strikethrough <code> fallback. The browser-side parser is
|
|
19
|
+
// the source of truth at runtime — see DICE_RUNTIME_SCRIPT.
|
|
20
|
+
const FORMULA_RE = /^(\d*)d(\d+)\s*([+-]\s*\d+)?$/i;
|
|
21
|
+
export const diceHandler = {
|
|
22
|
+
inline: "dice",
|
|
23
|
+
render(content) {
|
|
24
|
+
const formula = content.trim();
|
|
25
|
+
const escaped = htmlEscape(formula);
|
|
26
|
+
if (!FORMULA_RE.test(formula)) {
|
|
27
|
+
// Not a formula we recognize: fall back to monospace text so the
|
|
28
|
+
// user sees what they wrote and can fix the syntax.
|
|
29
|
+
return {
|
|
30
|
+
html: `<code class="dice-roll dice-roll-invalid" title="unrecognized dice formula">${escaped}</code>`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
html: `<button type="button" class="dice-roll" data-formula="${escaped}" title="Click to roll">${escaped}</button>`,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
// ── Browser runtime ───────────────────────────────────────────────────────
|
|
39
|
+
// Click-to-reroll for buttons emitted by render(). Event-delegated so a
|
|
40
|
+
// single listener handles every dice button on the page; pages without
|
|
41
|
+
// any dice pay only the cost of the listener attachment.
|
|
42
|
+
const DICE_RUNTIME_SCRIPT = `
|
|
43
|
+
(function () {
|
|
44
|
+
var FORMULA_RE = /^(\\d*)d(\\d+)\\s*([+-]\\s*\\d+)?$/i;
|
|
45
|
+
function roll(formula) {
|
|
46
|
+
var m = FORMULA_RE.exec(formula.trim());
|
|
47
|
+
if (!m) return null;
|
|
48
|
+
var count = m[1] ? parseInt(m[1], 10) : 1;
|
|
49
|
+
var faces = parseInt(m[2], 10);
|
|
50
|
+
var mod = m[3] ? parseInt(m[3].replace(/\\s+/g, ''), 10) : 0;
|
|
51
|
+
if (count < 1 || faces < 1) return null;
|
|
52
|
+
var total = 0;
|
|
53
|
+
var rolls = [];
|
|
54
|
+
for (var i = 0; i < count; i++) {
|
|
55
|
+
var r = 1 + Math.floor(Math.random() * faces);
|
|
56
|
+
rolls.push(r);
|
|
57
|
+
total += r;
|
|
58
|
+
}
|
|
59
|
+
return { total: total + mod, rolls: rolls, mod: mod };
|
|
60
|
+
}
|
|
61
|
+
document.addEventListener('click', function (e) {
|
|
62
|
+
var btn = e.target;
|
|
63
|
+
if (!(btn instanceof HTMLButtonElement)) return;
|
|
64
|
+
if (!btn.classList.contains('dice-roll')) return;
|
|
65
|
+
var formula = btn.dataset.formula;
|
|
66
|
+
if (!formula) return;
|
|
67
|
+
var r = roll(formula);
|
|
68
|
+
if (!r) return;
|
|
69
|
+
var sign = r.mod === 0 ? '' : (r.mod > 0 ? ' + ' + r.mod : ' - ' + Math.abs(r.mod));
|
|
70
|
+
btn.textContent = r.total + ' (' + formula + ')';
|
|
71
|
+
btn.title = '[' + r.rolls.join(', ') + ']' + sign + ' = ' + r.total + ' — click to re-roll';
|
|
72
|
+
});
|
|
73
|
+
})();
|
|
74
|
+
`;
|
|
75
|
+
registerBuiltinAssets(diceHandler, {
|
|
76
|
+
scripts: [{ source: "builtin/dice.runtime.js", content: DICE_RUNTIME_SCRIPT }],
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=dice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dice.js","sourceRoot":"","sources":["../../../../src/render/handlers/builtin/dice.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,EAAE;AACF,mEAAmE;AACnE,EAAE;AACF,qEAAqE;AACrE,EAAE;AACF,0EAA0E;AAC1E,oEAAoE;AACpE,2EAA2E;AAC3E,kDAAkD;AAClD,EAAE;AACF,0EAA0E;AAC1E,oEAAoE;AAGpE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD,6EAA6E;AAC7E,uEAAuE;AACvE,wEAAwE;AACxE,4DAA4D;AAC5D,MAAM,UAAU,GAAG,gCAAgC,CAAC;AAEpD,MAAM,CAAC,MAAM,WAAW,GAAkB;IACxC,MAAM,EAAE,MAAM;IACd,MAAM,CAAC,OAAe;QACpB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,iEAAiE;YACjE,oDAAoD;YACpD,OAAO;gBACL,IAAI,EAAE,+EAA+E,OAAO,SAAS;aACtG,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,yDAAyD,OAAO,2BAA2B,OAAO,WAAW;SACpH,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,6EAA6E;AAC7E,wEAAwE;AACxE,uEAAuE;AACvE,yDAAyD;AAEzD,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC3B,CAAC;AAEF,qBAAqB,CAAC,WAAW,EAAE;IACjC,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;CAC/E,CAAC,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Built-in inline `fm:` handler — inserts a frontmatter value into prose.
|
|
2
|
+
//
|
|
3
|
+
// ---
|
|
4
|
+
// class: Wizard
|
|
5
|
+
// level: 7
|
|
6
|
+
// stats:
|
|
7
|
+
// hp: 22
|
|
8
|
+
// ---
|
|
9
|
+
// The `fm: class` is level `fm: level` with `fm: stats.hp` HP.
|
|
10
|
+
//
|
|
11
|
+
// Renders as: "The Wizard is level 7 with 22 HP."
|
|
12
|
+
//
|
|
13
|
+
// Dot-path lookups (`fm: a.b.c`) walk nested objects. Keys with literal
|
|
14
|
+
// dots in their names are unreachable, which we accept because such keys
|
|
15
|
+
// are vanishingly rare in vault frontmatter.
|
|
16
|
+
//
|
|
17
|
+
// String/number/boolean values render as HTML with a small inline-markup
|
|
18
|
+
// pass (**bold**, *italic*, `code`) so authors can put light formatting
|
|
19
|
+
// in frontmatter and have it survive. Returning HTML rather than markdown
|
|
20
|
+
// lets fm work when invoked recursively from another handler (e.g. inside
|
|
21
|
+
// a `statblock` field), since the dispatcher only re-pipelines top-level
|
|
22
|
+
// markdown returns. Wikilinks in frontmatter are NOT processed; cross-page
|
|
23
|
+
// reference belongs in prose.
|
|
24
|
+
//
|
|
25
|
+
// Dates (YAML auto-parses ISO 8601 strings to Date) format as YYYY-MM-DD.
|
|
26
|
+
// Arrays are joined with ", ". Missing keys and unsupported value shapes
|
|
27
|
+
// (plain objects, null) render a visible warning marker so the page
|
|
28
|
+
// surfaces the problem instead of silently emitting "undefined".
|
|
29
|
+
import { htmlEscape } from "../types.js";
|
|
30
|
+
export const fmHandler = {
|
|
31
|
+
inline: "fm",
|
|
32
|
+
render(content, ctx) {
|
|
33
|
+
const path = content.trim();
|
|
34
|
+
if (!path)
|
|
35
|
+
return missing("(empty)");
|
|
36
|
+
const value = lookup(ctx.frontmatter, path);
|
|
37
|
+
const formatted = formatScalar(value);
|
|
38
|
+
if (formatted !== null)
|
|
39
|
+
return { html: wrap(formatInline(formatted)) };
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
return { html: wrap(formatInline(value.map((v) => formatScalar(v) ?? "").join(", "))) };
|
|
42
|
+
}
|
|
43
|
+
return missing(path);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
// Bare-text HTML AST nodes get dropped by the markdown → HTML stage
|
|
47
|
+
// (rehype-raw needs an actual element to anchor onto in inline contexts);
|
|
48
|
+
// wrapping in a <span> keeps the value visible. The span is invisible in
|
|
49
|
+
// the final layout so it doesn't change typography.
|
|
50
|
+
function wrap(inner) { return `<span class="fm-value">${inner}</span>`; }
|
|
51
|
+
function lookup(root, path) {
|
|
52
|
+
const segments = path.split(".");
|
|
53
|
+
let cursor = root;
|
|
54
|
+
for (const seg of segments) {
|
|
55
|
+
if (cursor === null || cursor === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (typeof cursor !== "object")
|
|
58
|
+
return undefined;
|
|
59
|
+
cursor = cursor[seg];
|
|
60
|
+
}
|
|
61
|
+
return cursor;
|
|
62
|
+
}
|
|
63
|
+
// HTML-escape, then a tiny inline-markdown pass: bold (** **) before italic
|
|
64
|
+
// (* *) so bold doesn't get eaten, then code spans. Matches the same subset
|
|
65
|
+
// the statblock handler's formatInline supports — keep these in sync.
|
|
66
|
+
function formatInline(s) {
|
|
67
|
+
let out = htmlEscape(s);
|
|
68
|
+
out = out.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
69
|
+
out = out.replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, "$1<em>$2</em>");
|
|
70
|
+
out = out.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function formatScalar(v) {
|
|
74
|
+
if (v === null || v === undefined)
|
|
75
|
+
return null;
|
|
76
|
+
if (typeof v === "string")
|
|
77
|
+
return v;
|
|
78
|
+
if (typeof v === "number" || typeof v === "boolean")
|
|
79
|
+
return String(v);
|
|
80
|
+
if (v instanceof Date && !isNaN(v.getTime()))
|
|
81
|
+
return v.toISOString().slice(0, 10);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function missing(key) {
|
|
85
|
+
return {
|
|
86
|
+
html: `<code class="fm-missing" title="frontmatter key not found">{{${htmlEscape(key)}}}</code>`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=fm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fm.js","sourceRoot":"","sources":["../../../../src/render/handlers/builtin/fm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Registry of built-in handlers shipped with vaults-cli.
|
|
2
|
+
//
|
|
3
|
+
// User-defined handlers (loaded from .vaults/handlers/) override built-ins
|
|
4
|
+
// of the same name; see buildRegistry() in handlers/types.ts.
|
|
5
|
+
import { diceHandler } from "./dice.js";
|
|
6
|
+
import { fmHandler } from "./fm.js";
|
|
7
|
+
import { statblockHandler } from "./statblock.js";
|
|
8
|
+
export const BUILTIN_HANDLERS = [diceHandler, fmHandler, statblockHandler];
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/render/handlers/builtin/index.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,2EAA2E;AAC3E,8DAA8D;AAG9D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,CAAC,MAAM,gBAAgB,GAAc,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC"}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
// Built-in code-block `statblock` handler — D&D 5e creature statblocks.
|
|
2
|
+
//
|
|
3
|
+
// Schema is compatible with the Fantasy Statblocks Obsidian plugin so the
|
|
4
|
+
// same source renders in Obsidian (via the plugin) and on the published
|
|
5
|
+
// wiki (via this handler):
|
|
6
|
+
//
|
|
7
|
+
// ```statblock
|
|
8
|
+
// name: Goblin
|
|
9
|
+
// size: Small
|
|
10
|
+
// type: humanoid
|
|
11
|
+
// ac: 15
|
|
12
|
+
// hp: 7
|
|
13
|
+
// hit_dice: 2d6
|
|
14
|
+
// speed: 30 ft.
|
|
15
|
+
// stats: [8, 14, 10, 10, 8, 8]
|
|
16
|
+
// saves:
|
|
17
|
+
// - dexterity: 5
|
|
18
|
+
// skillsaves:
|
|
19
|
+
// - stealth: 6
|
|
20
|
+
// senses: darkvision 60 ft., passive Perception 9
|
|
21
|
+
// languages: Common, Goblin
|
|
22
|
+
// cr: "1/4"
|
|
23
|
+
// traits:
|
|
24
|
+
// - name: Nimble Escape
|
|
25
|
+
// desc: The goblin can take the Disengage or Hide action as a bonus action.
|
|
26
|
+
// actions:
|
|
27
|
+
// - name: Scimitar
|
|
28
|
+
// desc: "*Melee Weapon Attack:* +4 to hit. *Hit:* 5 (1d6 + 2) slashing damage."
|
|
29
|
+
// ```
|
|
30
|
+
//
|
|
31
|
+
// Coverage: name/size/type/subtype/alignment, ac (+ ac_class), hp (+ hit_dice),
|
|
32
|
+
// speed, six abilities (stats), saves, skillsaves, damage_*, condition_immunities,
|
|
33
|
+
// senses, languages, cr, traits, actions, reactions, legendary_actions
|
|
34
|
+
// (+ legendary_description). Inline markdown in `desc` fields supports **bold**,
|
|
35
|
+
// *italic*, and `code`. Other Fantasy Statblocks features (spells nested
|
|
36
|
+
// formatting, custom layouts, dice-roller integration, JS callbacks, image
|
|
37
|
+
// fields) are not supported in v1.
|
|
38
|
+
import yaml from "js-yaml";
|
|
39
|
+
import { htmlEscape } from "../types.js";
|
|
40
|
+
import { registerBuiltinAssets } from "../assets.js";
|
|
41
|
+
const ABILITY_NAMES = ["STR", "DEX", "CON", "INT", "WIS", "CHA"];
|
|
42
|
+
const ABILITY_SHORT = {
|
|
43
|
+
strength: "Str", dexterity: "Dex", constitution: "Con",
|
|
44
|
+
intelligence: "Int", wisdom: "Wis", charisma: "Cha",
|
|
45
|
+
};
|
|
46
|
+
function modifier(score) {
|
|
47
|
+
const mod = Math.floor((score - 10) / 2);
|
|
48
|
+
return mod >= 0 ? `+${mod}` : String(mod);
|
|
49
|
+
}
|
|
50
|
+
// Tiny inline-markdown formatter for desc fields. Pre-substituted handler
|
|
51
|
+
// HTML (e.g. `dice:` buttons) reaches us as sentinel tokens emitted by
|
|
52
|
+
// preprocessHandlers below. We html-escape and apply bold/italic/code
|
|
53
|
+
// first, then splice the original handler HTML back over the tokens.
|
|
54
|
+
// Wikilinks are still NOT processed (would require coupling to the
|
|
55
|
+
// broader render pipeline). Authors who need wikilinks should put them
|
|
56
|
+
// in a separate paragraph outside the statblock.
|
|
57
|
+
//
|
|
58
|
+
// Token format is a distinctive ASCII sentinel: htmlEscape leaves the
|
|
59
|
+
// underscores and digits alone, the bold/italic/code regexes can't
|
|
60
|
+
// match across it, and the chance a user writes this exact string in a
|
|
61
|
+
// desc is effectively zero.
|
|
62
|
+
const TOKEN_RE = /__VAULTSTATBLOCK_HANDLER_(\d+)__/g;
|
|
63
|
+
function makeToken(i) { return `__VAULTSTATBLOCK_HANDLER_${i}__`; }
|
|
64
|
+
function formatInline(s, spliceback) {
|
|
65
|
+
let out = htmlEscape(s);
|
|
66
|
+
out = out.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
67
|
+
out = out.replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, "$1<em>$2</em>");
|
|
68
|
+
out = out.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
69
|
+
out = out.replace(TOKEN_RE, (_, idx) => spliceback[Number(idx)] ?? "");
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Walk every string field in the monster, replace `` `prefix: …` ``
|
|
74
|
+
* inline-handler invocations with sentinel tokens, and remember the
|
|
75
|
+
* resulting HTML in `spliceback`. The synchronous formatInline pass below
|
|
76
|
+
* then weaves the tokens back in after escaping. Two-step so the rest of
|
|
77
|
+
* statblock rendering stays sync.
|
|
78
|
+
*
|
|
79
|
+
* Generic (Object.keys + typeof === "string") so future fields tokenize
|
|
80
|
+
* automatically — a new top-level string field doesn't need plumbing.
|
|
81
|
+
*/
|
|
82
|
+
async function preprocessHandlers(m, ctx) {
|
|
83
|
+
const spliceback = [];
|
|
84
|
+
const tokenize = async (s) => {
|
|
85
|
+
if (!s || !s.includes("`"))
|
|
86
|
+
return s;
|
|
87
|
+
// Local regex (not module-level) so /g state doesn't leak across calls.
|
|
88
|
+
const re = /`([a-z][\w-]*):\s*([^`]*)`/g;
|
|
89
|
+
let out = "";
|
|
90
|
+
let last = 0;
|
|
91
|
+
let match;
|
|
92
|
+
while ((match = re.exec(s)) !== null) {
|
|
93
|
+
const replaced = await ctx.applyInlineHandlers(match[0]);
|
|
94
|
+
if (replaced === match[0])
|
|
95
|
+
continue; // unhandled prefix; leave in place
|
|
96
|
+
out += s.slice(last, match.index);
|
|
97
|
+
out += makeToken(spliceback.length);
|
|
98
|
+
spliceback.push(replaced);
|
|
99
|
+
last = match.index + match[0].length;
|
|
100
|
+
}
|
|
101
|
+
if (last === 0)
|
|
102
|
+
return s; // nothing matched
|
|
103
|
+
out += s.slice(last);
|
|
104
|
+
return out;
|
|
105
|
+
};
|
|
106
|
+
// Top-level scalar string fields (name, ac, hp, speed, cr, senses, …).
|
|
107
|
+
const mAny = m;
|
|
108
|
+
for (const k of Object.keys(mAny)) {
|
|
109
|
+
if (typeof mAny[k] === "string")
|
|
110
|
+
mAny[k] = await tokenize(mAny[k]);
|
|
111
|
+
}
|
|
112
|
+
// Per-entry name and desc inside trait/action lists.
|
|
113
|
+
const list = async (xs) => {
|
|
114
|
+
if (!xs)
|
|
115
|
+
return;
|
|
116
|
+
for (const x of xs) {
|
|
117
|
+
if (typeof x.name === "string")
|
|
118
|
+
x.name = await tokenize(x.name);
|
|
119
|
+
if (typeof x.desc === "string")
|
|
120
|
+
x.desc = await tokenize(x.desc);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
await list(m.traits);
|
|
124
|
+
await list(m.actions);
|
|
125
|
+
await list(m.reactions);
|
|
126
|
+
await list(m.legendary_actions);
|
|
127
|
+
return spliceback;
|
|
128
|
+
}
|
|
129
|
+
function property(label, value, spliceback) {
|
|
130
|
+
if (value === undefined || value === "")
|
|
131
|
+
return "";
|
|
132
|
+
return `<p class="statblock-property"><strong>${label}</strong> ${formatInline(value, spliceback)}</p>`;
|
|
133
|
+
}
|
|
134
|
+
function header(m, spliceback) {
|
|
135
|
+
const name = m.name ?? "Unnamed";
|
|
136
|
+
const sub = [m.size, m.type, m.subtype && `(${m.subtype})`, m.alignment]
|
|
137
|
+
.filter(Boolean).join(" ");
|
|
138
|
+
// Rendered as a div, not an h2: the page sanitizer restricts <h2> to
|
|
139
|
+
// class="sr-only" only (GFM footnotes use it). The name is also not part
|
|
140
|
+
// of the page's article outline — pages can have multiple statblocks.
|
|
141
|
+
return (`<div class="statblock-header">` +
|
|
142
|
+
`<div class="statblock-name">${formatInline(name, spliceback)}</div>` +
|
|
143
|
+
(sub ? `<p class="statblock-subheading">${formatInline(sub, spliceback)}</p>` : "") +
|
|
144
|
+
`</div>`);
|
|
145
|
+
}
|
|
146
|
+
function topProperties(m, spliceback) {
|
|
147
|
+
const parts = [];
|
|
148
|
+
if (m.ac !== undefined) {
|
|
149
|
+
parts.push(property("Armor Class", m.ac_class ? `${m.ac} (${m.ac_class})` : String(m.ac), spliceback));
|
|
150
|
+
}
|
|
151
|
+
if (m.hp !== undefined) {
|
|
152
|
+
parts.push(property("Hit Points", m.hit_dice ? `${m.hp} (${m.hit_dice})` : String(m.hp), spliceback));
|
|
153
|
+
}
|
|
154
|
+
if (m.speed !== undefined)
|
|
155
|
+
parts.push(property("Speed", String(m.speed), spliceback));
|
|
156
|
+
return parts.join("");
|
|
157
|
+
}
|
|
158
|
+
function stats(m) {
|
|
159
|
+
if (!m.stats || m.stats.length !== 6)
|
|
160
|
+
return "";
|
|
161
|
+
const cells = m.stats.map((score, i) => `<div class="statblock-stat">` +
|
|
162
|
+
`<div class="statblock-stat-name">${ABILITY_NAMES[i]}</div>` +
|
|
163
|
+
`<div class="statblock-stat-value">${score} (${modifier(score)})</div>` +
|
|
164
|
+
`</div>`).join("");
|
|
165
|
+
return `<div class="statblock-stats">${cells}</div>`;
|
|
166
|
+
}
|
|
167
|
+
function bonusList(entries, labelFor) {
|
|
168
|
+
if (!entries?.length)
|
|
169
|
+
return "";
|
|
170
|
+
return entries.map(entry => {
|
|
171
|
+
const pair = Object.entries(entry)[0];
|
|
172
|
+
if (!pair)
|
|
173
|
+
return "";
|
|
174
|
+
const [key, val] = pair;
|
|
175
|
+
if (val === undefined)
|
|
176
|
+
return "";
|
|
177
|
+
const sign = val >= 0 ? "+" : "";
|
|
178
|
+
return `${labelFor(key)} ${sign}${val}`;
|
|
179
|
+
}).filter(Boolean).join(", ");
|
|
180
|
+
}
|
|
181
|
+
function midProperties(m, spliceback) {
|
|
182
|
+
const saves = bonusList(m.saves, k => ABILITY_SHORT[k.toLowerCase()] ?? k);
|
|
183
|
+
const skills = bonusList(m.skillsaves, k => k.charAt(0).toUpperCase() + k.slice(1));
|
|
184
|
+
return [
|
|
185
|
+
property("Saving Throws", saves || undefined, spliceback),
|
|
186
|
+
property("Skills", skills || undefined, spliceback),
|
|
187
|
+
property("Damage Vulnerabilities", m.damage_vulnerabilities, spliceback),
|
|
188
|
+
property("Damage Resistances", m.damage_resistances, spliceback),
|
|
189
|
+
property("Damage Immunities", m.damage_immunities, spliceback),
|
|
190
|
+
property("Condition Immunities", m.condition_immunities, spliceback),
|
|
191
|
+
property("Senses", m.senses, spliceback),
|
|
192
|
+
property("Languages", m.languages, spliceback),
|
|
193
|
+
property("Challenge", m.cr !== undefined ? String(m.cr) : undefined, spliceback),
|
|
194
|
+
].join("");
|
|
195
|
+
}
|
|
196
|
+
function trait(t, spliceback) {
|
|
197
|
+
return `<p class="statblock-trait">` +
|
|
198
|
+
`<strong><em>${formatInline(t.name ?? "", spliceback)}.</em></strong> ` +
|
|
199
|
+
formatInline(t.desc ?? "", spliceback) +
|
|
200
|
+
`</p>`;
|
|
201
|
+
}
|
|
202
|
+
function section(label, items, spliceback, intro) {
|
|
203
|
+
if (!items?.length)
|
|
204
|
+
return "";
|
|
205
|
+
// `label` is hard-coded ("Actions"/"Reactions"/…); escape rather than
|
|
206
|
+
// formatInline so a literal "*" in a future label doesn't get italicised.
|
|
207
|
+
return `<div class="statblock-section">` +
|
|
208
|
+
`<h3 class="statblock-section-heading">${htmlEscape(label)}</h3>` +
|
|
209
|
+
(intro ? `<p class="statblock-section-intro">${formatInline(intro, spliceback)}</p>` : "") +
|
|
210
|
+
items.map(t => trait(t, spliceback)).join("") +
|
|
211
|
+
`</div>`;
|
|
212
|
+
}
|
|
213
|
+
export const statblockHandler = {
|
|
214
|
+
codeBlock: "statblock",
|
|
215
|
+
async render(content, ctx) {
|
|
216
|
+
let m;
|
|
217
|
+
try {
|
|
218
|
+
m = yaml.load(content) ?? {};
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
return {
|
|
222
|
+
html: `<div class="statblock statblock-error">` +
|
|
223
|
+
`<strong>statblock parse error:</strong> ` +
|
|
224
|
+
htmlEscape(err instanceof Error ? err.message : String(err)) +
|
|
225
|
+
`</div>`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
// Pre-substitute any `prefix: …` inline-handler invocations in desc
|
|
229
|
+
// fields with sentinel tokens; spliceback holds the resulting HTML and
|
|
230
|
+
// formatInline weaves it back in after escaping.
|
|
231
|
+
const spliceback = await preprocessHandlers(m, ctx);
|
|
232
|
+
const traitsHtml = m.traits?.length
|
|
233
|
+
? `<div class="statblock-section">${m.traits.map(t => trait(t, spliceback)).join("")}</div>`
|
|
234
|
+
: "";
|
|
235
|
+
const body = [
|
|
236
|
+
header(m, spliceback),
|
|
237
|
+
`<div class="statblock-rule"></div>`,
|
|
238
|
+
`<div class="statblock-block">${topProperties(m, spliceback)}</div>`,
|
|
239
|
+
`<div class="statblock-rule"></div>`,
|
|
240
|
+
stats(m),
|
|
241
|
+
`<div class="statblock-rule"></div>`,
|
|
242
|
+
`<div class="statblock-block">${midProperties(m, spliceback)}</div>`,
|
|
243
|
+
`<div class="statblock-rule statblock-rule-tapered"></div>`,
|
|
244
|
+
traitsHtml,
|
|
245
|
+
section("Actions", m.actions, spliceback),
|
|
246
|
+
section("Reactions", m.reactions, spliceback),
|
|
247
|
+
section("Legendary Actions", m.legendary_actions, spliceback, m.legendary_description),
|
|
248
|
+
].join("");
|
|
249
|
+
return { html: `<div class="statblock">${body}</div>` };
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
// Visual style follows the standard D&D 5e statblock (parchment, scarlet
|
|
253
|
+
// rules, small-caps headings) but uses `.statblock-*` class names borrowed
|
|
254
|
+
// from the Fantasy Statblocks plugin where they overlap, so a vault user
|
|
255
|
+
// could supply their own theme by overriding the same selectors. CSS
|
|
256
|
+
// variables match Fantasy Statblocks' palette tokens for portability.
|
|
257
|
+
const STATBLOCK_CSS = `
|
|
258
|
+
.statblock {
|
|
259
|
+
--statblock-primary-color: #7a200d;
|
|
260
|
+
--statblock-rule-color: #922610;
|
|
261
|
+
--statblock-bg: #fdf1dc;
|
|
262
|
+
--statblock-heading-font: "Libre Baskerville", "Lora", Georgia, serif;
|
|
263
|
+
--statblock-content-font: "Noto Sans", Calibri, Helvetica, Arial, sans-serif;
|
|
264
|
+
|
|
265
|
+
background: var(--statblock-bg);
|
|
266
|
+
color: #000;
|
|
267
|
+
border: 1px solid #ddd;
|
|
268
|
+
box-shadow: 0 0 1.5em #ddd;
|
|
269
|
+
padding: 0.6rem 0.75rem;
|
|
270
|
+
margin: 1rem 0;
|
|
271
|
+
font-family: var(--statblock-content-font);
|
|
272
|
+
font-size: 14px;
|
|
273
|
+
line-height: 1.4;
|
|
274
|
+
}
|
|
275
|
+
.statblock p { margin: 0.15rem 0; }
|
|
276
|
+
.statblock-name {
|
|
277
|
+
font-family: var(--statblock-heading-font);
|
|
278
|
+
font-size: 1.6rem;
|
|
279
|
+
font-variant: small-caps;
|
|
280
|
+
font-weight: 700;
|
|
281
|
+
color: var(--statblock-primary-color);
|
|
282
|
+
margin: 0;
|
|
283
|
+
line-height: 1.1;
|
|
284
|
+
}
|
|
285
|
+
.statblock-subheading {
|
|
286
|
+
font-style: italic;
|
|
287
|
+
font-size: 0.9rem;
|
|
288
|
+
margin: 0.1rem 0 0;
|
|
289
|
+
}
|
|
290
|
+
.statblock-rule {
|
|
291
|
+
height: 5px;
|
|
292
|
+
background: var(--statblock-rule-color);
|
|
293
|
+
border: none;
|
|
294
|
+
margin: 0.4rem 0;
|
|
295
|
+
clip-path: polygon(0 0, 100% 0, 100% 30%, 0 100%);
|
|
296
|
+
}
|
|
297
|
+
.statblock-rule-tapered {
|
|
298
|
+
clip-path: polygon(0 0, 100% 0, 95% 100%, 5% 100%);
|
|
299
|
+
}
|
|
300
|
+
.statblock-block { margin: 0.3rem 0; }
|
|
301
|
+
.statblock-property {
|
|
302
|
+
color: var(--statblock-primary-color);
|
|
303
|
+
}
|
|
304
|
+
.statblock-property strong {
|
|
305
|
+
font-weight: bold;
|
|
306
|
+
}
|
|
307
|
+
.statblock-stats {
|
|
308
|
+
display: grid;
|
|
309
|
+
grid-template-columns: repeat(6, 1fr);
|
|
310
|
+
gap: 0.25rem;
|
|
311
|
+
text-align: center;
|
|
312
|
+
color: var(--statblock-primary-color);
|
|
313
|
+
margin: 0.3rem 0;
|
|
314
|
+
}
|
|
315
|
+
.statblock-stat-name {
|
|
316
|
+
font-weight: bold;
|
|
317
|
+
font-variant: small-caps;
|
|
318
|
+
}
|
|
319
|
+
.statblock-section { margin-top: 0.6rem; }
|
|
320
|
+
.statblock-section-heading {
|
|
321
|
+
font-family: var(--statblock-heading-font);
|
|
322
|
+
font-variant: small-caps;
|
|
323
|
+
font-weight: normal;
|
|
324
|
+
font-size: 1.3rem;
|
|
325
|
+
color: var(--statblock-primary-color);
|
|
326
|
+
border-bottom: 1px solid var(--statblock-primary-color);
|
|
327
|
+
margin: 0.5rem 0 0.3rem;
|
|
328
|
+
}
|
|
329
|
+
.statblock-trait {
|
|
330
|
+
margin: 0.3rem 0;
|
|
331
|
+
text-indent: -1rem;
|
|
332
|
+
padding-left: 1rem;
|
|
333
|
+
}
|
|
334
|
+
.statblock-trait strong em {
|
|
335
|
+
font-weight: bold;
|
|
336
|
+
font-style: italic;
|
|
337
|
+
}
|
|
338
|
+
.statblock-section-intro {
|
|
339
|
+
font-style: italic;
|
|
340
|
+
margin: 0.3rem 0;
|
|
341
|
+
}
|
|
342
|
+
.statblock-error {
|
|
343
|
+
background: #fde7e3;
|
|
344
|
+
color: #7a200d;
|
|
345
|
+
border-color: #7a200d;
|
|
346
|
+
}
|
|
347
|
+
`;
|
|
348
|
+
registerBuiltinAssets(statblockHandler, {
|
|
349
|
+
styles: [{ source: "builtin/statblock.css", content: STATBLOCK_CSS }],
|
|
350
|
+
});
|
|
351
|
+
//# sourceMappingURL=statblock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statblock.js","sourceRoot":"","sources":["../../../../src/render/handlers/builtin/statblock.ts"],"names":[],"mappings":"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"}
|