globalThis.window = globalThis;
globalThis.self = globalThis;
globalThis.navigator = { userAgent: "KatanA Rust-managed Mermaid runtime" };
globalThis.location = {
href: "https://katana.local/",
origin: "https://katana.local",
protocol: "https:",
host: "katana.local",
hostname: "katana.local",
pathname: "/",
search: "",
hash: "",
};
const KATANA_DETERMINISTIC_NOW = Date.parse("2026-01-01T00:00:00.000Z");
globalThis.Date.now = () => KATANA_DETERMINISTIC_NOW;
globalThis.performance = { now: () => Date.now() };
globalThis.devicePixelRatio = 1;
globalThis.innerWidth = 1520;
globalThis.innerHeight = 845;
globalThis.screen = {
width: globalThis.innerWidth,
height: globalThis.innerHeight,
availWidth: globalThis.innerWidth,
availHeight: globalThis.innerHeight,
};
globalThis.localStorage = katanaStorage();
globalThis.sessionStorage = katanaStorage();
class KatanaMessageChannel {
constructor() {
this.port1 = { onmessage: null };
this.port2 = {
postMessage: (data) => {
queueMicrotask(() => this.port1.onmessage?.({ data }));
},
};
}
}
class KatanaSegmenter {
segment(value) {
const input = String(value);
let index = 0;
return Array.from(input).map((segment) => {
const entry = { segment, index, input };
index += segment.length;
return entry;
});
}
}
const katanaIntl = globalThis.Intl ?? {};
katanaIntl.Segmenter = KatanaSegmenter;
globalThis.Intl = katanaIntl;
globalThis.crypto = {
getRandomValues(array) {
for (let index = 0; index < array.length; index += 1) {
array[index] = katanaDeterministicByte(index);
}
return array;
},
randomUUID() {
return "00000000-0000-4000-8000-000000000000";
},
};
Math.random = katanaDeterministicRandom;
let katanaRandomState = 0x12345678;
globalThis.queueMicrotask = (callback) => Promise.resolve().then(callback);
function katanaDeterministicRandom() {
katanaRandomState = (1664525 * katanaRandomState + 1013904223) >>> 0;
return katanaRandomState / 0x100000000;
}
function katanaDeterministicByte(index) {
return (index * 73 + 41) & 0xff;
}
function katanaStorage() {
const values = {};
return {
get length() {
return Object.keys(values).length;
},
clear() {
Object.keys(values).forEach((key) => delete values[key]);
},
getItem(key) {
return values[String(key)] ?? null;
},
key(index) {
return Object.keys(values)[index] ?? null;
},
removeItem(key) {
delete values[String(key)];
},
setItem(key, value) {
values[String(key)] = String(value);
},
};
}
globalThis.setTimeout = (callback, _delay, ...args) => callback(...args);
globalThis.clearTimeout = () => {};
globalThis.setInterval = (callback, _delay, ...args) => callback(...args);
globalThis.clearInterval = () => {};
globalThis.MessageChannel = KatanaMessageChannel;
let katanaAnimationFrameDepth = 0;
globalThis.requestAnimationFrame = (callback) => katanaRunAnimationFrame(callback);
function katanaRunAnimationFrame(callback) {
if (katanaAnimationFrameDepth > 4) {
return 0;
}
return katanaInvokeAnimationFrame(callback);
}
function katanaInvokeAnimationFrame(callback) {
katanaAnimationFrameDepth += 1;
try {
return callback(Date.now());
} finally {
katanaAnimationFrameDepth -= 1;
}
}
globalThis.cancelAnimationFrame = () => {};
globalThis.addEventListener = () => {};
globalThis.removeEventListener = () => {};
globalThis.__katanaMissingSelectors = [];
const KATANA_BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
globalThis.btoa = (value) => {
const utf8 = unescape(encodeURIComponent(String(value)));
return katanaBase64Triplets(utf8).map(katanaBase64Chunk).join("");
};
function katanaBase64Triplets(value) {
return value.match(/[\s\S]{1,3}/g) ?? [];
}
function katanaBase64Chunk(chunk) {
const first = chunk.charCodeAt(0);
const second = chunk.charCodeAt(1);
const third = chunk.charCodeAt(2);
return [
first >> 2,
((first & 3) << 4) | (second >> 4),
katanaThirdBase64Index(second, third),
katanaFourthBase64Index(third),
]
.map((index) => KATANA_BASE64_CHARS.charAt(index))
.join("");
}
function katanaThirdBase64Index(second, third) {
if (Number.isNaN(second)) {
return 64;
}
return ((second & 15) << 2) | (third >> 6);
}
function katanaFourthBase64Index(third) {
if (Number.isNaN(third)) {
return 64;
}
return third & 63;
}
globalThis.DOMPurify = {
sanitize(value) {
return String(value ?? "");
},
addHook() {},
removeHook() {},
};
class KatanaStyle {
constructor() {
this.values = {};
}
setProperty(name, value) {
this.values[String(name)] = String(value);
}
getPropertyValue(name) {
return this.values[String(name)] ?? "";
}
removeProperty(name) {
const value = this.getPropertyValue(name);
delete this.values[String(name)];
return value;
}
set mixBlendMode(value) {
this.setProperty("mix-blend-mode", value);
}
get mixBlendMode() {
return this.getPropertyValue("mix-blend-mode");
}
get cssText() {
return Object.entries(this.values)
.map(([key, value]) => `${key}: ${value};`)
.join(" ");
}
clone() {
const cloned = new KatanaStyle();
cloned.values = { ...this.values };
return cloned;
}
}
globalThis.getComputedStyle = (node) => ({
getPropertyValue(name) {
return node?.style?.getPropertyValue?.(name) ?? "";
},
});
function katanaEscapeSvgAttribute(value) {
return String(value)
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
}
function katanaEscapeSvgText(value) {
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
class KatanaNode {
constructor(tagName, namespaceURI = null) {
const rawName = katanaRawNodeName(tagName);
const preservesCase = katanaPreservesNodeCase(namespaceURI);
this.serializedName = katanaSerializedNodeName(rawName, preservesCase);
this.tagName = katanaTagNodeName(rawName, preservesCase);
this.nodeName = this.tagName;
this.localName = rawName.toLowerCase();
this.namespaceURI = namespaceURI;
this.children = [];
this.childNodes = this.children;
this.parentNode = null;
this.ownerDocument = null;
this.nodeType = katanaNodeType(tagName);
this.attributes = [];
this.attributeMap = {};
this.style = new KatanaStyle();
this.dataset = {};
this._textContent = "";
this.className = "";
this.id = "";
}
appendChild(child) {
child.parentNode = this;
child.ownerDocument = this.ownerDocument;
this.children.push(child);
this.childNodes = this.children;
return child;
}
insertBefore(child, reference) {
child.parentNode = this;
child.ownerDocument = this.ownerDocument;
const index = this.children.indexOf(reference);
if (index < 0) {
this.children.push(child);
this.childNodes = this.children;
return child;
}
this.children.splice(index, 0, child);
this.childNodes = this.children;
return child;
}
removeChild(child) {
this.children = this.children.filter((candidate) => candidate !== child);
this.childNodes = this.children;
return child;
}
remove() {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
}
get firstChild() {
return this.children[0] ?? null;
}
get lastChild() {
return this.children[this.children.length - 1] ?? null;
}
get parentElement() {
return this.parentNode;
}
getRootNode() {
return this.ownerDocument ?? document;
}
hasChildNodes() {
return this.children.length > 0;
}
compareDocumentPosition(other) {
if (this === other) {
return 0;
}
return 4;
}
get nextSibling() {
if (!this.parentNode) {
return null;
}
const index = this.parentNode.children.indexOf(this);
return this.parentNode.children[index + 1] ?? null;
}
get previousSibling() {
if (!this.parentNode) {
return null;
}
const index = this.parentNode.children.indexOf(this);
return this.parentNode.children[index - 1] ?? null;
}
setAttribute(name, value) {
const normalized = String(name);
const attribute = katanaUpsertAttribute(this, normalized);
attribute.value = String(value);
katanaSyncIdAttribute(this, normalized, value);
katanaSyncClassAttribute(this, normalized, value);
}
getAttribute(name) {
return this.attributeMap[String(name)]?.value ?? null;
}
removeAttribute(name) {
const normalized = String(name);
delete this.attributeMap[normalized];
this.attributes = this.attributes.filter((attribute) => attribute.name !== normalized);
}
setAttributeNS(_namespace, name, value) {
this.setAttribute(name, value);
}
getAttributeNS(_namespace, name) {
return this.getAttribute(name);
}
hasAttribute(name) {
return Object.hasOwn(this.attributeMap, String(name));
}
querySelector(selector) {
const result = katanaFirstQuerySelectorResult(this, selector);
if (result === null) {
globalThis.__katanaMissingSelectors.push(`${this.localName}:${selector}`);
}
return result;
}
querySelectorAll(selector) {
const compound = queryCompoundSelector(this, String(selector));
if (compound !== null) {
return compound;
}
return querySimpleSelector(this, String(selector));
}
getElementsByTagName(tagName) {
return this.querySelectorAll(String(tagName).toLowerCase());
}
addEventListener() {}
removeEventListener() {}
dispatchEvent() {
return true;
}
focus() {
document.activeElement = this;
}
click() {}
get isConnected() {
return this === document || document.documentElement.contains(this);
}
toJSON() {
return {};
}
}
function katanaFirstQuerySelectorResult(node, selector) {
return node.querySelectorAll(selector)[0] ?? null;
}
function katanaRawNodeName(tagName) {
return String(tagName || "");
}
function katanaPreservesNodeCase(namespaceURI) {
return [
katanaNamespaceIncludes(namespaceURI, "svg"),
katanaNamespaceIncludes(namespaceURI, "xml"),
].includes(true);
}
function katanaNamespaceIncludes(namespaceURI, value) {
return String(namespaceURI || "").includes(value);
}
function katanaSerializedNodeName(rawName, preservesCase) {
return preservesCase ? rawName : rawName.toLowerCase();
}
function katanaTagNodeName(rawName, preservesCase) {
return preservesCase ? rawName : rawName.toUpperCase();
}
function katanaNodeType(tagName) {
return tagName === "#text" ? 3 : 1;
}
function katanaUpsertAttribute(node, normalized) {
const attribute = node.attributeMap[normalized];
if (attribute) {
return attribute;
}
return katanaCreateAttribute(node, normalized);
}
function katanaCreateAttribute(node, normalized) {
const attribute = { name: normalized, nodeName: normalized, value: "" };
node.attributeMap[normalized] = attribute;
node.attributes.push(attribute);
return attribute;
}
function katanaSyncIdAttribute(node, normalized, value) {
if (normalized === "id") {
node.id = String(value);
}
}
function katanaSyncClassAttribute(node, normalized, value) {
if (normalized === "class") {
node.className = String(value);
}
}
function katanaStyleCamelName(name) {
return String(name).replace(/-([a-z])/g, (_match, char) => char.toUpperCase());
}
function katanaStyleKebabName(name) {
return String(name).replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
}
function katanaApplyCssText(style, value) {
katanaCssEntries(value).forEach((entry) => {
katanaApplyCssEntry(style, entry);
});
}
function katanaCssEntries(value) {
return String(value ?? "").split(";");
}
function katanaApplyCssEntry(style, entry) {
const separator = entry.indexOf(":");
if (separator < 0) return;
style.setProperty(entry.slice(0, separator).trim(), entry.slice(separator + 1).trim());
}
KatanaStyle.prototype.setProperty = function setProperty(name, value) {
const kebab = katanaStyleKebabName(name);
const camel = katanaStyleCamelName(name);
this.values[kebab] = String(value);
this.values[camel] = String(value);
if (!Object.getOwnPropertyDescriptor(KatanaStyle.prototype, camel)) {
this[camel] = String(value);
}
};
KatanaStyle.prototype.getPropertyValue = function getPropertyValue(name) {
const key = String(name);
const kebab = katanaStyleKebabName(key);
const camel = katanaStyleCamelName(key);
return this.values[key] ?? this.values[kebab] ?? this.values[camel] ?? this[camel] ?? "";
};
KatanaStyle.prototype.removeProperty = function removeProperty(name) {
const value = this.getPropertyValue(name);
delete this.values[String(name)];
delete this.values[katanaStyleKebabName(name)];
delete this.values[katanaStyleCamelName(name)];
delete this[katanaStyleCamelName(name)];
return value;
};
Object.defineProperty(KatanaStyle.prototype, "cssText", {
get() {
return Object.entries(this.values)
.filter(([key]) => key.includes("-"))
.map(([key, value]) => `${key}: ${value}`)
.join("; ");
},
set(value) {
this.values = {};
katanaApplyCssText(this, value);
},
});
const katanaSetAttributeBase = KatanaNode.prototype.setAttribute;
KatanaNode.prototype.setAttribute = function setAttribute(name, value) {
katanaSetAttributeBase.call(this, name, value);
if (String(name).toLowerCase() === "style") {
this.style.cssText = value;
}
};
function parseInnerHtml(source, xmlMode = false) {
const root = {
localName: "#root",
children: [],
appendChild(child) {
this.children.push(child);
},
};
const stack = [root];
const tokenRegex = /<\/?([a-zA-Z0-9:_-]+)([^>]*)>|([^<]+)/g;
Array.from(source.matchAll(tokenRegex)).forEach((match) => {
appendHtmlToken(stack, match, xmlMode);
});
return root.children;
}
function appendHtmlToken(stack, match, xmlMode) {
if (match[3] !== undefined) {
appendHtmlText(stack[stack.length - 1], match[3]);
return;
}
appendHtmlTag(stack, match, xmlMode);
}
function appendHtmlTag(stack, match, xmlMode) {
if (match[0].startsWith("</")) {
popHtmlStack(stack, match[1]);
return;
}
appendHtmlStartTag(stack, match, xmlMode);
}
function appendHtmlStartTag(stack, match, xmlMode) {
const node = new KatanaNode(match[1], katanaHtmlNamespace(xmlMode));
node.ownerDocument = document;
parseAttributes(match[2]).forEach(([name, value]) => {
node.setAttribute(name, value);
});
stack[stack.length - 1].appendChild(node);
pushHtmlElementIfOpen(stack, node, match[0], match[1]);
}
function katanaHtmlNamespace(xmlMode) {
return xmlMode ? "katana-xml" : null;
}
function pushHtmlElementIfOpen(stack, node, fullTag, tagName) {
if (katanaIsOpenHtmlTag(fullTag, tagName)) {
stack.push(node);
}
}
function katanaIsOpenHtmlTag(fullTag, tagName) {
return [!fullTag.endsWith("/>"), !isHtmlVoidTag(tagName)].every(Boolean);
}
function appendHtmlText(parent, value) {
const text = decodeHtmlEntities(value);
if (text.length > 0) {
parent.appendChild(new KatanaTextNode(text));
}
}
function popHtmlStack(stack, tagName) {
const normalized = String(tagName).toLowerCase();
const index = stack.findLastIndex((node) => node.localName === normalized);
if (index > 0) {
stack.splice(index);
}
}
function isHtmlVoidTag(tagName) {
return new Set(["br", "hr", "img", "input", "meta", "link"]).has(String(tagName).toLowerCase());
}
function parseAttributes(source) {
const attrRegex = /([a-zA-Z0-9:_-]+)="([^"]*)"/g;
return Array.from(source.matchAll(attrRegex)).map((match) => [
match[1],
decodeHtmlEntities(match[2]),
]);
}
function decodeHtmlEntities(value) {
return String(value)
.replace(/&nbsp;/g, "\u00A0")
.replace(/ /g, "\u00A0")
.replace(/
/gi, "\n")
.replace(/ /g, "\n")
.replace(/ /g, "\u00A0")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/&/g, "&")
.replace(/"/g, '"')
.replace(/'/g, "'");
}
class KatanaTextNode extends KatanaNode {
constructor(value) {
super("#text");
this.textContent = String(value);
}
get outerHTML() {
return katanaEscapeSvgText(this.textContent);
}
}
KatanaNode.prototype.getBBox = function getBBox() {
const text = katanaNodeTextForBox(this);
const childWidth = katanaNodeChildWidth(this);
const width = Math.max(80, childWidth, katanaTextWidth(text));
const height = Math.max(24, this.children.length * 24);
return { x: 0, y: 0, width, height };
};
function katanaNodeTextForBox(node) {
return String(katanaFirstTextValue([node.textContent, node.innerText]));
}
function katanaFirstTextValue(values) {
return values.find(Boolean) ?? "";
}
function katanaNodeChildWidth(node) {
return node.children.reduce((max, child) => Math.max(max, child.getBBox().width), 0);
}
KatanaNode.prototype.getBoundingClientRect = function getBoundingClientRect() {
const box = this.getBBox();
return {
...box,
top: box.y,
left: box.x,
right: box.x + box.width,
bottom: box.y + box.height,
};
};
KatanaNode.prototype.getComputedTextLength = function getComputedTextLength() {
return Math.max(16, katanaTextWidth(String(this.textContent || "")));
};
KatanaNode.prototype.getContext = function getContext() {
return {
font: "",
measureText(value) {
return { width: Math.max(16, katanaTextWidth(String(value || ""))) };
},
};
};
Object.defineProperty(KatanaNode.prototype, "innerHTML", {
get() {
if (this.children.length === 0) {
return this.textContent;
}
return this.children.map((child) => child.outerHTML ?? child.textContent ?? "").join("");
},
set(value) {
katanaClearInnerHtml(this);
katanaApplyInnerHtml(this, String(value), parseInnerHtml(String(value)));
},
});
function katanaClearInnerHtml(node) {
node.textContent = "";
node.children = [];
node.childNodes = node.children;
}
function katanaApplyInnerHtml(node, value, parsed) {
if (parsed.length === 0) {
node.textContent = value;
return;
}
katanaAppendParsedHtml(node, parsed);
}
function katanaAppendParsedHtml(node, parsed) {
for (const child of parsed) {
node.appendChild(child);
}
}
Object.defineProperty(KatanaNode.prototype, "outerHTML", {
get() {
const attrs = katanaSerializedAttributeText(this);
const body = katanaOuterHtmlBody(this);
return `<${this.serializedName}${attrs}>${body}</${this.serializedName}>`;
},
});
function katanaSerializedAttributeText(node) {
return Object.entries({ ...node.serializedAttributes(), ...katanaStyleAttribute(node) })
.map(([key, value]) => ` ${key}="${katanaEscapeSvgAttribute(value)}"`)
.join("");
}
function katanaStyleAttribute(node) {
if (katanaShouldSerializeStyle(node)) {
return { style: node.style.cssText };
}
return {};
}
function katanaShouldSerializeStyle(node) {
return [node.style.cssText, !node.hasAttribute("style")].every(Boolean);
}
function katanaOuterHtmlBody(node) {
if (node.children.length === 0) {
return katanaEscapeSvgText(node.textContent);
}
return node.innerHTML;
}
KatanaNode.prototype.cloneNode = function cloneNode(deep = false) {
const clone = katanaShallowCloneNode(this);
katanaCloneChildrenIfDeep(this, clone, deep);
return clone;
};
function katanaShallowCloneNode(node) {
const clone = new KatanaNode(node.serializedName, node.namespaceURI);
node.attributes.forEach((attribute) => {
clone.setAttribute(attribute.name, attribute.value);
});
clone.style = node.style.clone();
clone._textContent = node._textContent ?? "";
clone.className = node.className;
clone.id = node.id;
clone.ownerDocument = node.ownerDocument;
return clone;
}
function katanaCloneChildrenIfDeep(node, clone, deep) {
if (deep) {
katanaCloneChildren(node, clone);
}
}
function katanaCloneChildren(node, clone) {
for (const child of node.children) {
clone.appendChild(child.cloneNode(true));
}
}
KatanaNode.prototype.serializedAttributes = function serializedAttributes() {
return Object.fromEntries(this.attributes.map((attribute) => [attribute.name, attribute.value]));
};
function matchesSelector(node, selector) {
const matcher = KATANA_SELECTOR_MATCHERS.find((entry) => entry.applies(selector));
if (matcher) {
return matcher.matches(node, selector);
}
return katanaMatchesTag(node, selector);
}
const KATANA_SELECTOR_MATCHERS = [
{ applies: katanaIsBareFirstChildSelector, matches: katanaMatchesBareFirstChild },
{ applies: katanaIsNestedFirstChildSelector, matches: katanaMatchesNestedFirstChild },
{ applies: katanaIsWildcardSelector, matches: katanaMatchesWildcard },
{ applies: katanaIsTagIdSelector, matches: katanaMatchesTagId },
{ applies: katanaIsBodySelector, matches: katanaMatchesBody },
{ applies: katanaIsIdSelector, matches: katanaMatchesId },
{ applies: katanaIsIdAttributeSelector, matches: katanaMatchesIdAttribute },
{ applies: katanaIsClassSelector, matches: katanaMatchesClass },
];
function katanaIsBareFirstChildSelector(selector) {
return [":first-child", "::first-child"].includes(selector);
}
function katanaMatchesBareFirstChild(node) {
return node.parentNode?.firstElementChild === node;
}
function katanaIsNestedFirstChildSelector(selector) {
return selector.endsWith(":first-child");
}
function katanaMatchesNestedFirstChild(node, selector) {
const base = selector.replace(/:{1,2}first-child$/, "");
return [matchesSelector(node, base), katanaMatchesBareFirstChild(node)].every(Boolean);
}
function katanaIsWildcardSelector(selector) {
return selector === "*";
}
function katanaMatchesWildcard(node) {
return node.nodeType === Node.ELEMENT_NODE;
}
function katanaIsTagIdSelector(selector) {
return /^([a-zA-Z0-9:_-]+)#(.+)$/.test(selector);
}
function katanaMatchesTagId(node, selector) {
const match = selector.match(/^([a-zA-Z0-9:_-]+)#(.+)$/);
return [node.localName === match[1].toLowerCase(), node.id === match[2]].every(Boolean);
}
function katanaIsBodySelector(selector) {
return selector === "body";
}
function katanaMatchesBody(node) {
return node === document.body;
}
function katanaIsIdSelector(selector) {
return selector.startsWith("#");
}
function katanaMatchesId(node, selector) {
return node.id === selector.slice(1);
}
function katanaIsIdAttributeSelector(selector) {
return /^\[id="([^"]+)"\]$/.test(selector);
}
function katanaMatchesIdAttribute(node, selector) {
return node.id === selector.match(/^\[id="([^"]+)"\]$/)[1];
}
function katanaIsClassSelector(selector) {
return selector.startsWith(".");
}
function katanaMatchesClass(node, selector) {
return String(node.className).split(/\s+/).includes(selector.slice(1));
}
function katanaMatchesTag(node, selector) {
return node.localName === selector.toLowerCase();
}
function queryCompoundSelector(root, selector) {
const parts = selector.split(/\s+/).filter(Boolean);
if (parts.length <= 1) {
return null;
}
return katanaQuerySelectorParts([root], parts);
}
function katanaQuerySelectorParts(candidates, parts) {
return parts.reduce(
(current, part) => current.flatMap((candidate) => querySimpleSelector(candidate, part)),
candidates,
);
}
function querySimpleSelector(root, selector) {
const results = [];
root.children.forEach((node) => {
katanaVisitSelector(node, selector, results);
});
return results;
}
function katanaVisitSelector(node, selector, results) {
katanaAddMatchingSelector(node, selector, results);
node.children.forEach((child) => {
katanaVisitSelector(child, selector, results);
});
}
function katanaAddMatchingSelector(node, selector, results) {
if (matchesSelector(node, selector)) {
results.push(node);
}
}
const document = {
nodeType: 9,
currentScript: null,
activeElement: null,
addEventListener() {},
removeEventListener() {},
dispatchEvent() {
return true;
},
createElement(tagName) {
const node = new KatanaNode(tagName);
node.ownerDocument = document;
return node;
},
createElementNS(namespaceURI, tagName) {
const node = new KatanaNode(tagName, namespaceURI);
node.ownerDocument = document;
return node;
},
createTextNode(value) {
const node = new KatanaTextNode(value);
node.ownerDocument = document;
return node;
},
createComment(value) {
return this.createTextNode(value);
},
createDocumentFragment() {
const node = new KatanaNode("#document-fragment");
node.nodeType = 11;
node.ownerDocument = document;
return node;
},
createNodeIterator(root) {
const nodes = [];
const visit = (node) => {
nodes.push(node);
node.children.forEach(visit);
};
visit(root);
let index = 0;
return { nextNode: () => nodes[index++] ?? null };
},
getElementsByTagName(tagName) {
return this.documentElement.querySelectorAll(String(tagName).toLowerCase());
},
importNode(node, deep) {
const clone = node.cloneNode(deep);
clone.ownerDocument = document;
return clone;
},
getElementById(id) {
const result = this.documentElement.querySelector(`#${id}`);
if (result === null) {
globalThis.__katanaMissingSelectors.push(`document.getElementById:${id}`);
}
return result;
},
querySelector(selector) {
if (selector === "body") {
return this.body;
}
return this.documentElement.querySelector(selector);
},
querySelectorAll(selector) {
return this.documentElement.querySelectorAll(selector);
},
implementation: {
createHTMLDocument() {
return createDetachedDocument();
},
},
fonts: {
ready: Promise.resolve(),
},
};
document.documentElement = document.createElement("html");
document.head = document.createElement("head");
document.body = document.createElement("body");
document.documentElement.appendChild(document.head);
document.documentElement.appendChild(document.body);
document.defaultView = globalThis;
globalThis.document = document;
globalThis.Element = KatanaNode;
globalThis.HTMLElement = KatanaNode;
globalThis.SVGElement = KatanaNode;
globalThis.Node = KatanaNode;
globalThis.Node.ELEMENT_NODE = 1;
globalThis.Node.TEXT_NODE = 3;
globalThis.Node.DOCUMENT_NODE = 9;
globalThis.Node.DOCUMENT_FRAGMENT_NODE = 11;
globalThis.DocumentFragment = KatanaNode;
globalThis.HTMLTemplateElement = KatanaNode;
globalThis.HTMLFormElement = KatanaNode;
globalThis.HTMLButtonElement = KatanaNode;
globalThis.HTMLCanvasElement = KatanaNode;
globalThis.HTMLFieldSetElement = KatanaNode;
globalThis.HTMLIFrameElement = KatanaNode;
globalThis.HTMLImageElement = KatanaNode;
globalThis.HTMLInputElement = KatanaNode;
globalThis.HTMLLabelElement = KatanaNode;
globalThis.HTMLLegendElement = KatanaNode;
globalThis.HTMLSelectElement = KatanaNode;
globalThis.HTMLTextAreaElement = KatanaNode;
globalThis.HTMLVideoElement = KatanaNode;
globalThis.SVGImageElement = KatanaNode;
globalThis.NamedNodeMap = Array;
globalThis.NodeFilter = { SHOW_ELEMENT: 1, SHOW_TEXT: 4, SHOW_COMMENT: 128 };
globalThis.MutationObserver = class {
constructor(callback) {
this.callback = callback;
}
observe() {}
disconnect() {}
takeRecords() {
return [];
}
};
globalThis.trustedTypes = {
createPolicy() {
return {
createHTML: (value) => String(value),
createScriptURL: (value) => String(value),
};
},
};
globalThis.TextEncoder = class {
encode(value) {
return new Uint8Array(Array.from(String(value)).flatMap((char) => katanaUtf8Bytes(char)));
}
};
function katanaUtf8Bytes(char) {
const code = char.charCodeAt(0);
if (code < 0x80) {
return [code];
}
return katanaMultibyteUtf8Bytes(code);
}
function katanaMultibyteUtf8Bytes(code) {
if (code < 0x800) {
return [0xc0 | (code >> 6), 0x80 | (code & 0x3f)];
}
return [0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f)];
}
globalThis.TextDecoder = class {
decode(value) {
return Array.from(value)
.map((byte) => String.fromCharCode(byte))
.join("");
}
};
globalThis.structuredClone = (value) => JSON.parse(JSON.stringify(value));
globalThis.XMLSerializer = class {
serializeToString(node) {
return node.outerHTML ?? "";
}
};
globalThis.DOMParser = class {
parseFromString(source) {
const parsed = parseInnerHtml(String(source).trim(), true);
const detached = createDetachedDocument();
const root = parsed[0] ?? detached.createElement("xml");
attachOwnerDocument(root, detached);
detached.documentElement = root;
detached.body = root;
return detached;
}
};
function createDetachedDocument() {
const detached = Object.create(document);
detached.documentElement = document.createElement("html");
detached.head = document.createElement("head");
detached.body = document.createElement("body");
detached.documentElement.ownerDocument = detached;
detached.head.ownerDocument = detached;
detached.body.ownerDocument = detached;
detached.documentElement.appendChild(detached.head);
detached.documentElement.appendChild(detached.body);
detached.defaultView = globalThis;
detached.nodeType = 9;
return detached;
}
function attachOwnerDocument(node, ownerDocument) {
node.ownerDocument = ownerDocument;
for (const child of node.children) {
attachOwnerDocument(child, ownerDocument);
}
}
function katanaDetachChild(child) {
const parent = child?.parentNode;
if (!parent) return;
katanaReplaceChildList(
parent,
katanaChildList(parent).filter((candidate) => candidate !== child),
);
child.parentNode = null;
}
function katanaChildList(parent) {
return [parent.children, parent.childNodes, []].find(Array.isArray);
}
function katanaReplaceChildList(parent, children) {
parent.children = children;
parent.childNodes = children;
}
function katanaAdoptChild(parent, child) {
child.parentNode = parent;
child.ownerDocument = parent.ownerDocument ?? parent;
}
function katanaAppendFragment(parent, fragment) {
const nodes = [...fragment.childNodes];
for (const node of nodes) {
parent.appendChild(node);
}
return fragment;
}
KatanaNode.prototype.appendChild = function appendChild(child) {
if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
return katanaAppendFragment(this, child);
}
katanaDetachChild(child);
katanaAdoptChild(this, child);
this.children.push(child);
this.childNodes = this.children;
return child;
};
KatanaNode.prototype.insertBefore = function insertBefore(child, reference) {
if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
return katanaInsertFragmentBefore(this, child, reference);
}
return katanaInsertNodeBefore(this, child, reference);
};
function katanaInsertFragmentBefore(parent, fragment, reference) {
for (const node of fragment.childNodes) {
parent.insertBefore(node, reference);
}
return fragment;
}
function katanaInsertNodeBefore(parent, child, reference) {
if (child === reference) {
return child;
}
return katanaInsertDetachedNodeBefore(parent, child, reference);
}
function katanaInsertDetachedNodeBefore(parent, child, reference) {
katanaDetachChild(child);
katanaAdoptChild(parent, child);
katanaInsertAtReference(parent, child, reference);
parent.childNodes = parent.children;
return child;
}
function katanaInsertAtReference(parent, child, reference) {
const index = parent.children.indexOf(reference);
if (index < 0) {
parent.children.push(child);
return;
}
parent.children.splice(index, 0, child);
}
KatanaNode.prototype.removeChild = function removeChild(child) {
this.children = this.children.filter((candidate) => candidate !== child);
this.childNodes = this.children;
child.parentNode = null;
return child;
};
Object.defineProperty(KatanaNode.prototype, "firstElementChild", {
get() {
return this.children.find((child) => child.nodeType === Node.ELEMENT_NODE) ?? null;
},
});
function katanaDocumentPath(node) {
const path = [];
let current = node;
while (current?.parentNode) {
path.unshift(current.parentNode.children.indexOf(current));
current = current.parentNode;
}
return path;
}
function katanaComparePath(left, right) {
const mismatch = katanaFirstPathMismatch(left, right);
if (mismatch !== null) {
return katanaPathMismatchPosition(left, right, mismatch);
}
return katanaPathLengthPosition(left, right);
}
function katanaFirstPathMismatch(left, right) {
return katanaPathIndexes(left, right).find((index) => left[index] !== right[index]) ?? null;
}
function katanaPathIndexes(left, right) {
return Array.from({ length: Math.min(left.length, right.length) }, (_value, index) => index);
}
function katanaPathMismatchPosition(left, right, index) {
return left[index] < right[index] ? 4 : 2;
}
function katanaPathLengthPosition(left, right) {
if (left.length === right.length) {
return 0;
}
return katanaUnequalPathLengthPosition(left, right);
}
function katanaUnequalPathLengthPosition(left, right) {
return left.length < right.length ? 20 : 10;
}
KatanaNode.prototype.compareDocumentPosition = function compareDocumentPosition(other) {
if (this === other) return 0;
return katanaComparePath(katanaDocumentPath(this), katanaDocumentPath(other));
};
Node.DOCUMENT_POSITION_DISCONNECTED = 1;
Node.DOCUMENT_POSITION_PRECEDING = 2;
Node.DOCUMENT_POSITION_FOLLOWING = 4;
Node.DOCUMENT_POSITION_CONTAINS = 8;
Node.DOCUMENT_POSITION_CONTAINED_BY = 16;
const KATANA_HIDDEN_LAYOUT_TAGS = new Set([
"style",
"script",
"defs",
"marker",
"lineargradient",
"radialgradient",
"stop",
"filter",
"fedropshadow",
"clippath",
"mask",
"pattern",
]);
function katanaOwnText(node) {
return String(node?._textContent ?? "");
}
function katanaTextContent(node) {
return node ? `${katanaOwnText(node)}${katanaTextContentChildren(node)}` : "";
}
function katanaTextContentChildren(node) {
return (node.children ?? []).map((child) => katanaTextContent(child)).join("");
}
function katanaLayoutTextContent(node) {
if (katanaIsHiddenLayoutNode(node)) {
return "";
}
return `${katanaOwnText(node)}${katanaLayoutTextChildren(node)}`;
}
function katanaLayoutTextChildren(node) {
return (node.children ?? []).map((child) => katanaLayoutTextContent(child)).join("");
}
function katanaIsHiddenLayoutNode(node) {
return [!node, KATANA_HIDDEN_LAYOUT_TAGS.has(node?.localName)].includes(true);
}
function katanaNodeValue(value) {
if (value instanceof KatanaNode) {
return value;
}
return document.createTextNode(katanaStringValue(value));
}
function katanaStringValue(value) {
return String(value ?? "");
}
function katanaNumberAttr(node, name) {
return katanaNullableNumber(node?.getAttribute?.(name));
}
function katanaNullableNumber(rawValue) {
return katanaHasNumberValue(rawValue) ? katanaFiniteNumber(Number(rawValue)) : null;
}
function katanaHasNumberValue(rawValue) {
return ![null, undefined, ""].includes(rawValue);
}
function katanaFiniteNumber(value) {
return Number.isFinite(value) ? value : null;
}
function katanaBox(x, y, width, height) {
return { x, y, width, height, w: width, h: height };
}
function katanaUnionBox(boxes) {
if (boxes.length === 0) {
return katanaBox(0, 0, 0, 0);
}
const minX = Math.min(...boxes.map((box) => box.x));
const minY = Math.min(...boxes.map((box) => box.y));
const maxX = Math.max(...boxes.map((box) => box.x + box.width));
const maxY = Math.max(...boxes.map((box) => box.y + box.height));
return katanaBox(minX, minY, maxX - minX, maxY - minY);
}
function katanaVisibleChildBoxes(node) {
return (node.children ?? [])
.filter((child) => !KATANA_HIDDEN_LAYOUT_TAGS.has(child.localName))
.map((child) => {
const box = child.getBBox();
const offset = katanaNodeTranslate(child);
return katanaBox(box.x + offset[0], box.y + offset[1], box.width, box.height);
});
}
function katanaMeasuredBox(node) {
if (KATANA_HIDDEN_LAYOUT_TAGS.has(node.localName)) {
return katanaBox(0, 0, 0, 0);
}
return katanaVisibleMeasuredBox(node);
}
function katanaVisibleMeasuredBox(node) {
return (
katanaExplicitMeasuredBox(node) ??
katanaDirectShapeBox(node) ??
katanaChildTextMeasuredBox(node)
);
}
function katanaExplicitMeasuredBox(node) {
const attrs = katanaBoxAttributes(node);
if ([attrs.width > 0, attrs.height > 0].includes(true)) {
return katanaBox(attrs.x, attrs.y, attrs.width, attrs.height);
}
return null;
}
function katanaBoxAttributes(node) {
return {
x: katanaNumberAttrOrDefault(node, "x", 0),
y: katanaNumberAttrOrDefault(node, "y", 0),
width: katanaNumberAttrOrDefault(node, "width", 0),
height: katanaNumberAttrOrDefault(node, "height", 0),
};
}
function katanaNumberAttrOrDefault(node, name, fallback) {
const value = katanaNumberAttr(node, name);
if (value === null) {
return fallback;
}
return value;
}
function katanaDirectShapeBox(node) {
return katanaLineBox(node) ?? katanaCircleBox(node);
}
function katanaChildTextMeasuredBox(node) {
const childBox = katanaUnionBox(katanaVisibleChildBoxes(node));
const text = katanaMeasuredNodeText(node, childBox);
return katanaBox(
0,
0,
katanaMeasuredTextWidth(text, childBox),
katanaMeasuredTextHeight(text, childBox),
);
}
function katanaMeasuredNodeText(node, childBox) {
return katanaOwnText(node) || katanaFallbackLayoutText(node, childBox);
}
function katanaFallbackLayoutText(node, childBox) {
return childBox.width === 0 ? katanaLayoutTextContent(node) : "";
}
function katanaMeasuredTextWidth(text, childBox) {
return Math.max(16, katanaTextWidthFallback(text), childBox.width);
}
function katanaMeasuredTextHeight(text, childBox) {
return Math.max(24, katanaTextHeightFallback(text), childBox.height);
}
function katanaTextWidthFallback(text) {
return text.length === 0 ? 0 : Math.max(16, katanaTextWidth(text));
}
function katanaTextHeightFallback(text) {
return text.length === 0 ? 0 : 24;
}
function katanaLineBox(node) {
if (node.localName !== "line") {
return null;
}
return katanaLineShapeBox(node);
}
function katanaLineShapeBox(node) {
const x1 = katanaNumberAttrOrDefault(node, "x1", 0);
const y1 = katanaNumberAttrOrDefault(node, "y1", 0);
const x2 = katanaNumberAttrOrDefault(node, "x2", 0);
const y2 = katanaNumberAttrOrDefault(node, "y2", 0);
return katanaBox(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1));
}
function katanaCircleBox(node) {
if (!katanaIsRoundSvgShape(node)) {
return null;
}
return katanaRoundShapeBox(node);
}
function katanaRoundShapeBox(node) {
const cx = katanaNumberAttrOrDefault(node, "cx", 0);
const cy = katanaNumberAttrOrDefault(node, "cy", 0);
const rx = katanaRoundRadius(node, "rx");
const ry = katanaRoundRadius(node, "ry");
return katanaBox(cx - rx, cy - ry, rx * 2, ry * 2);
}
function katanaRoundRadius(node, name) {
return katanaNumberAttr(node, name) ?? katanaNumberAttrOrDefault(node, "r", 0);
}
function katanaIsRoundSvgShape(node) {
return ["circle", "ellipse"].includes(node.localName);
}
function katanaNodeTranslate(node) {
const match = katanaTransformValue(node).match(/translate\(([-\d.]+)[,\s]+([-\d.]+)\)/);
if (match) {
return [Number(match[1]), Number(match[2])];
}
return [0, 0];
}
function katanaTransformValue(node) {
return String(node?.getAttribute?.("transform") ?? "");
}
const KATANA_DEFAULT_CLIENT_TAGS = new Set(["body", "div", "main", "pre", "section", "article"]);
function katanaMeasuredClientBox(node) {
const context = katanaClientBoxContext(node);
return (
katanaDefaultExplicitZeroBox(context) ??
katanaExplicitClientBox(context) ??
katanaSvgClientBox(context) ??
katanaEmptyDefaultClientBox(context) ??
context.box
);
}
function katanaClientBoxContext(node) {
return {
node,
box: node.getBBox(),
explicitWidth: katanaExplicitClientWidth(node),
explicitHeight: katanaExplicitClientHeight(node),
};
}
function katanaExplicitClientWidth(node) {
return (
katanaCssLength(node.style?.getPropertyValue?.("width")) ?? katanaNumberAttr(node, "width")
);
}
function katanaExplicitClientHeight(node) {
return (
katanaCssLength(node.style?.getPropertyValue?.("height")) ?? katanaNumberAttr(node, "height")
);
}
function katanaDefaultExplicitZeroBox(context) {
if ([katanaNeedsDefaultClientBox(context.node), context.explicitWidth === 0].every(Boolean)) {
return katanaBox(
0,
0,
katanaDefaultViewportWidth(),
katanaPositiveOrDefault(context.explicitHeight, katanaDefaultViewportHeight()),
);
}
return null;
}
function katanaExplicitClientBox(context) {
if (katanaHasExplicitClientSize(context)) {
return katanaResolvedExplicitClientBox(context);
}
return null;
}
function katanaHasExplicitClientSize(context) {
return [context.explicitWidth !== null, context.explicitHeight !== null].includes(true);
}
function katanaResolvedExplicitClientBox(context) {
return katanaBox(
context.box.x,
context.box.y,
katanaExplicitWidthValue(context),
katanaExplicitHeightValue(context),
);
}
function katanaExplicitWidthValue(context) {
return context.explicitWidth ?? context.box.width;
}
function katanaExplicitHeightValue(context) {
return context.explicitHeight ?? context.box.height;
}
function katanaSvgClientBox(context) {
if (context.node.localName === "svg") {
return katanaSvgViewBoxClientBox(context);
}
return null;
}
function katanaSvgViewBoxClientBox(context) {
const viewBox = katanaViewBoxSize(context.node.getAttribute("viewBox"));
return katanaBox(
context.box.x,
context.box.y,
katanaViewBoxWidth(context, viewBox),
katanaViewBoxHeight(context, viewBox),
);
}
function katanaViewBoxWidth(context, viewBox) {
return viewBox?.[0] ?? Math.max(context.box.width, katanaDefaultViewportWidth());
}
function katanaViewBoxHeight(context, viewBox) {
return viewBox?.[1] ?? Math.max(context.box.height, katanaDefaultViewportHeight());
}
function katanaEmptyDefaultClientBox(context) {
if ([katanaNeedsDefaultClientBox(context.node), katanaIsEmptyBox(context.box)].every(Boolean)) {
return katanaBox(0, 0, katanaDefaultViewportWidth(), katanaDefaultViewportHeight());
}
return null;
}
function katanaDefaultViewportWidth() {
return Number(globalThis.innerWidth ?? globalThis.screen?.width ?? 800);
}
function katanaDefaultViewportHeight() {
return Number(globalThis.innerHeight ?? globalThis.screen?.height ?? 600);
}
function katanaNeedsDefaultClientBox(node) {
return [KATANA_DEFAULT_CLIENT_TAGS.has(node.localName), katanaHasSvgChild(node)].includes(true);
}
function katanaIsEmptyBox(box) {
return [box.width === 0, box.height === 0].every(Boolean);
}
function katanaPositiveOrDefault(value, fallback) {
return value > 0 ? value : fallback;
}
function katanaCssLength(value) {
if (!value) {
return null;
}
return katanaFiniteCssLength(value);
}
function katanaFiniteCssLength(value) {
const number = Number(String(value).replace("px", ""));
if (Number.isFinite(number)) {
return number;
}
return null;
}
function katanaViewBoxSize(value) {
const values = katanaViewBoxValues(value);
if (katanaIsValidViewBox(values)) {
return [values[2], values[3]];
}
return null;
}
function katanaViewBoxValues(value) {
return String(value ?? "")
.split(/\s+/)
.map((it) => Number(it));
}
function katanaIsValidViewBox(values) {
return [values.length === 4, values.every((it) => Number.isFinite(it))].every(Boolean);
}
function katanaHasSvgChild(node) {
return (node.children ?? []).some((child) => child.localName === "svg");
}
Object.defineProperty(KatanaNode.prototype, "textContent", {
get() {
return katanaTextContent(this);
},
set(value) {
this._textContent = String(value ?? "");
this.children = [];
this.childNodes = this.children;
},
});
Object.defineProperty(KatanaNode.prototype, "innerText", {
get() {
return this.textContent;
},
set(value) {
this.textContent = value;
},
});
Object.defineProperty(KatanaNode.prototype, "nodeValue", {
get() {
return this.nodeType === 3 ? this.textContent : null;
},
set(value) {
if (this.nodeType === 3) {
this.textContent = value;
}
},
});
KatanaNode.prototype.append = function append(...values) {
for (const value of values) {
this.appendChild(katanaNodeValue(value));
}
};
KatanaNode.prototype.prepend = function prepend(...values) {
for (const value of values.reverse()) {
this.insertBefore(katanaNodeValue(value), this.firstChild);
}
};
KatanaNode.prototype.replaceChildren = function replaceChildren(...values) {
this.children = [];
this.childNodes = this.children;
this.append(...values);
};
KatanaNode.prototype.contains = function contains(candidate) {
if (this === candidate) {
return true;
}
return katanaNodeChildren(this).some((child) => child.contains(candidate));
};
function katanaNodeChildren(node) {
return node.children ?? [];
}
KatanaNode.prototype.text = function text(...values) {
if (values.length === 0) return this.textContent;
this.textContent = values[0];
return this;
};
KatanaNode.prototype.getBBox = function getBBox() {
return katanaMeasuredBox(this);
};
KatanaNode.prototype.getBoundingClientRect = function getBoundingClientRect() {
const box = katanaElementClientBox(this);
return { ...box, top: box.y, left: box.x, right: box.x + box.width, bottom: box.y + box.height };
};
KatanaNode.prototype.getComputedTextLength = function getComputedTextLength() {
return Math.max(16, katanaTextContent(this).length * 8);
};
KatanaNode.prototype.getScreenCTM = function getScreenCTM() {
return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0, inverse: () => this.getScreenCTM() };
};
KatanaNode.prototype.createSVGPoint = function createSVGPoint() {
return {
x: 0,
y: 0,
matrixTransform() {
return { x: this.x, y: this.y };
},
};
};
Object.defineProperties(KatanaNode.prototype, {
clientWidth: {
get() {
return Math.ceil(katanaElementClientBox(this).width);
},
},
clientHeight: {
get() {
return Math.ceil(katanaElementClientBox(this).height);
},
},
offsetWidth: {
get() {
return Math.ceil(katanaElementClientBox(this).width);
},
},
offsetHeight: {
get() {
return Math.ceil(katanaElementClientBox(this).height);
},
},
scrollWidth: {
get() {
return Math.ceil(katanaElementClientBox(this).width);
},
},
scrollHeight: {
get() {
return Math.ceil(katanaElementClientBox(this).height);
},
},
});
function katanaElementClientBox(node) {
return katanaMeasuredClientBox(node);
}
function katanaCssComputedStyleValue(node, name) {
const classNames = katanaCssClassNames(node);
if (classNames.length === 0) {
return "";
}
return katanaCssRuleValue(katanaCssText(node), classNames, name);
}
function katanaCssClassNames(node) {
return String(node?.className ?? "")
.split(/\s+/)
.filter(Boolean);
}
function katanaCssText(node) {
const root = katanaCssRoot(node);
return katanaCssStyleNodes(root)
.map((styleNode) => styleNode.textContent)
.join("\n");
}
function katanaCssRoot(node) {
return katanaCssRootNode(node) || document.documentElement;
}
function katanaCssRootNode(node) {
return KATANA_CSS_ROOT_READERS[Number(Boolean(katanaCssParentNode(node)))](node);
}
function katanaCssParentNode(node) {
return node?.parentNode || null;
}
function katanaCssStyleNodes(root) {
return katanaCssDescendantNodes(root).filter(katanaIsCssStyleNode);
}
function katanaCssDescendantNodes(node) {
return [node].filter(Boolean).flatMap(katanaCssNodeWithDescendants);
}
function katanaCssNodeWithDescendants(node) {
return [node].concat(Array.from(node.children || []).flatMap(katanaCssNodeWithDescendants));
}
function katanaIsCssStyleNode(node) {
return node.localName === "style";
}
function katanaCssRuleValue(cssText, classNames, name) {
return (
Array.from(cssText.matchAll(/([^{}]+)\{([^{}]+)\}/g))
.map((rule) => katanaCssRuleDeclarationValue(rule, classNames, name))
.find(Boolean) ?? ""
);
}
function katanaCssRuleDeclarationValue(rule, classNames, name) {
return (
[rule]
.filter((it) => katanaCssRuleMatchesClass(it[1], classNames))
.map((it) => katanaCssDeclarationValue(it[2], name))[0] ?? ""
);
}
const KATANA_CSS_ROOT_READERS = [
(node) => node,
(node) => katanaCssRootNode(katanaCssParentNode(node)),
];
function katanaCssRuleMatchesClass(selectorText, classNames) {
return selectorText
.split(",")
.some((selector) =>
classNames.some((className) => katanaCssSelectorMatches(selector, className)),
);
}
function katanaCssSelectorMatches(selector, className) {
const classSelector = `.${className}`;
return String(selector)
.trim()
.split(/\s+/)
.some((part) => part === classSelector);
}
function katanaCssDeclarationValue(declarations, name) {
return (
String(declarations)
.split(";")
.map((declaration) => katanaCssDeclarationEntry(declaration))
.find((entry) => entry.name === name)?.value ?? ""
);
}
function katanaCssDeclarationEntry(declaration) {
const parts = String(declaration).split(":");
return {
name: String(parts.shift() ?? "").trim(),
value: parts.join(":").trim(),
};
}
globalThis.getComputedStyle = (node) => katanaComputedStyle(node);
function katanaComputedStyle(node) {
const box = katanaComputedStyleBox(node);
return {
width: `${box.width}px`,
height: `${box.height}px`,
display: "block",
fontSize: katanaComputedFontSize(node),
fontFamily: katanaComputedFontFamily(node),
getPropertyValue(name) {
return katanaComputedPropertyValue(node, this, name);
},
};
}
function katanaComputedFontSize(node) {
return katanaComputedStyleValue(node, "font-size") || "16px";
}
function katanaComputedFontFamily(node) {
return (
katanaComputedStyleValue(node, "font-family") || "trebuchet ms, verdana, arial, sans-serif"
);
}
function katanaComputedPropertyValue(node, style, name) {
return katanaComputedStyleValue(node, name) || style[name] || "";
}
function katanaComputedStyleBox(node) {
if (node) {
return katanaElementClientBox(node);
}
return katanaBox(0, 0, 0, 0);
}
function katanaComputedStyleValue(node, name) {
return (
[katanaRawComputedStyleValue(node, name), katanaCssComputedStyleValue(node, name)]
.filter(Boolean)
.concat([katanaDefaultComputedStyleValue(name)])[0] ?? ""
);
}
function katanaRawComputedStyleValue(node, name) {
return node?.style?.getPropertyValue?.(name) ?? "";
}
function katanaDefaultComputedStyleValue(name) {
if (/^(padding|margin|border).*/.test(name)) {
return "0px";
}
return "";
}
function katanaMeasuredBoxAccurate(node) {
if (KATANA_HIDDEN_LAYOUT_TAGS.has(node.localName)) {
return katanaBox(0, 0, 0, 0);
}
return katanaVisibleMeasuredBoxAccurate(node);
}
function katanaVisibleMeasuredBoxAccurate(node) {
return (
katanaTextElementMeasuredBox(node) ??
katanaTextFragmentMeasuredBox(node) ??
katanaExplicitMeasuredBox(node) ??
katanaDirectMeasuredBox(node) ??
katanaChildTextBox(node)
);
}
function katanaTextElementMeasuredBox(node) {
if (node.localName === "text") {
return katanaTextElementBox(node);
}
return null;
}
function katanaTextFragmentMeasuredBox(node) {
if (["tspan", "#text"].includes(node.localName)) {
return katanaTextFragmentBox(node);
}
return null;
}
function katanaDirectMeasuredBox(node) {
return katanaLineBox(node) ?? katanaCircleBox(node) ?? katanaPathBox(node);
}
function katanaChildTextBox(node) {
const childBox = katanaUnionBox(katanaVisibleChildBoxes(node));
const text = katanaMeasuredNodeText(node, childBox);
return katanaUnionBox([childBox, katanaOptionalTextBox(node, text)].filter(katanaHasArea));
}
function katanaOptionalTextBox(node, text) {
return text ? katanaAnchoredTextNodeBox(node, text, 0, 0) : katanaBox(0, 0, 0, 0);
}
function katanaHasArea(box) {
return [box.width > 0, box.height > 0].includes(true);
}
function katanaPathBox(node) {
return katanaSvgPathBox(node) ?? katanaSvgPointsBox(node);
}
function katanaSvgPathBox(node) {
if (node.localName === "path") {
return katanaOptionalNumberListBox(node.getAttribute("d"));
}
return null;
}
function katanaSvgPointsBox(node) {
if (["polygon", "polyline"].includes(node.localName)) {
return katanaOptionalNumberListBox(node.getAttribute("points"));
}
return null;
}
function katanaOptionalNumberListBox(value) {
return value ? katanaNumberListBox(value) : null;
}
function katanaNumberListBox(value) {
const numbers = Array.from(String(value).matchAll(/-?\d+(?:\.\d+)?(?:e-?\d+)?/gi)).map((match) =>
Number(match[0]),
);
if (numbers.length < 2) {
return null;
}
const xs = numbers.filter((_value, index) => index % 2 === 0);
const ys = numbers.filter((_value, index) => index % 2 === 1);
return katanaBox(
Math.min(...xs),
Math.min(...ys),
Math.max(...xs) - Math.min(...xs),
Math.max(...ys) - Math.min(...ys),
);
}
function katanaTextElementBox(node) {
const lines = katanaTextLines(node);
const text = katanaTextElementText(node, lines);
if (!text) {
return katanaBox(0, 0, 0, 0);
}
return katanaUnionBox(
katanaTextLineValues(node, text, lines).map((line, index) =>
katanaTextLineBox(node, line, index),
),
);
}
function katanaTextElementText(node, lines) {
return lines.length === 0 ? katanaTextContent(node) : lines.map((line) => line.text).join("");
}
function katanaTextLineValues(node, text, lines) {
if (lines.length === 0) {
return [{ text, x: katanaNumberAttr(node, "x"), y: katanaNumberAttr(node, "y") }];
}
return lines;
}
function katanaTextLineBox(node, line, index) {
const x = katanaLineValueOrDefault(line.x, katanaNumberAttrOrDefault(node, "x", 0));
const y = katanaLineValueOrDefault(line.y, katanaTextLineFallbackY(node, index));
return katanaAnchoredTextNodeBox(node, line.text, x, y);
}
function katanaLineValueOrDefault(value, fallback) {
return value ?? fallback;
}
function katanaTextLineFallbackY(node, index) {
return (katanaNumberAttr(node, "y") ?? 0) + index * katanaLineHeight(node);
}
function katanaTextFragmentBox(node) {
const text = katanaFragmentText(node);
if (text) {
return katanaAnchoredTextNodeBox(
katanaTextFragmentParent(node),
text,
katanaNumberAttrOrDefault(node, "x", 0),
katanaNumberAttrOrDefault(node, "y", 0),
);
}
return katanaBox(0, 0, 0, 0);
}
function katanaTextFragmentParent(node) {
return node.parentNode ?? node;
}
function katanaFragmentText(node) {
return [katanaOwnText(node), katanaTextContent(node)].find(Boolean) ?? "";
}
KatanaNode.prototype.getBBox = function getBBox() {
return katanaMeasuredBoxAccurate(this);
};
function katanaTextLines(node) {
return katanaTextTspans(node).reduce(katanaAppendTextLine, { lines: [], current: null }).lines;
}
function katanaTextTspans(node) {
return katanaDescendantElements(node, "tspan")
.filter((child) => katanaTextContent(child).length > 0)
.filter((child) => !katanaHasTextTspanAncestor(child, node));
}
function katanaHasTextTspanAncestor(child, root) {
return katanaTextTspanAncestors(child, root).some(katanaIsFilledTextTspan);
}
function katanaTextTspanAncestors(child, root) {
return katanaTextTspanAncestorList(child.parentNode, root);
}
function katanaTextTspanAncestorList(node, root) {
return KATANA_TEXT_TSPAN_ANCESTOR_READERS[Number(katanaHasTextAncestorNode(node, root))](
node,
root,
);
}
function katanaHasTextAncestorNode(node, root) {
return Boolean(node) && node !== root;
}
function katanaIsFilledTextTspan(node) {
return [node.localName === "tspan", katanaTextContent(node).length > 0].every(Boolean);
}
function katanaAppendTextLine(state, tspan, index) {
const next = katanaTextLineState(state, tspan, index);
next.current.text += katanaTextContent(tspan);
return next;
}
function katanaTextLineState(state, tspan, index) {
return KATANA_TEXT_LINE_STATE_READERS[Number(katanaStartsTextLine(tspan, index))](state, tspan);
}
function katanaStartsTextLine(tspan, index) {
return [
index === 0,
tspan.hasAttribute("x"),
Math.abs(katanaSvgTextLengthAttr(tspan, "dy") ?? 0) >= 1,
].includes(true);
}
function katanaStartTextLine(state, tspan) {
const line = { text: "", x: katanaNumberAttr(tspan, "x"), y: katanaTspanLineY(tspan) };
return { lines: [...state.lines, line], current: line };
}
function katanaTspanLineY(tspan) {
const values = [katanaSvgTextLengthAttr(tspan, "y"), katanaSvgTextLengthAttr(tspan, "dy")];
return KATANA_TSPAN_LINE_Y_READERS[Number(values.some(katanaIsSvgTextLengthValue))](values);
}
function katanaIsSvgTextLengthValue(value) {
return value !== null;
}
function katanaSvgTextLengthAttr(node, name) {
return [String(node.getAttribute?.(name) ?? "")]
.filter(Boolean)
.map((raw) => katanaSvgTextLengthValue(node, raw))
.concat([null])[0];
}
function katanaSvgTextLengthValue(node, raw) {
return [raw.match(/^(-?\d+(?:\.\d+)?)([a-z%]*)$/i)]
.filter(Boolean)
.map((match) => katanaSvgTextLengthMatchValue(node, match))
.concat([null])[0];
}
function katanaSvgTextLengthMatchValue(node, match) {
return [Number(match[1])]
.filter(Number.isFinite)
.map((value) => katanaSvgTextLengthPixels(node, value, match[2]))
.concat([null])[0];
}
function katanaSvgTextLengthPixels(node, value, unit) {
return (KATANA_SVG_TEXT_LENGTH_UNITS[unit] ?? KATANA_SVG_TEXT_LENGTH_UNITS.px)(node, value);
}
function katanaDescendantElements(node, tagName) {
return Array.from(node.children).flatMap((child) => katanaDescendantElement(child, tagName));
}
function katanaDescendantElement(child, tagName) {
return [katanaMatchingDescendantElement(child, tagName)]
.filter(Boolean)
.concat(Array.from(child.children).flatMap((node) => katanaDescendantElement(node, tagName)));
}
function katanaMatchingDescendantElement(child, tagName) {
return [child].filter((node) => node.localName === tagName)[0] ?? null;
}
const KATANA_TEXT_TSPAN_ANCESTOR_READERS = [
() => [],
(node, root) => [node].concat(katanaTextTspanAncestorList(node.parentNode, root)),
];
const KATANA_TEXT_LINE_STATE_READERS = [
(state) => state,
(state, tspan) => katanaStartTextLine(state, tspan),
];
function katanaSumTspanLineY(values) {
return katanaOptionalSvgTextLength(values[0]) + katanaOptionalSvgTextLength(values[1]);
}
function katanaOptionalSvgTextLength(value) {
return [value].filter(katanaIsSvgTextLengthValue).concat([0])[0];
}
const KATANA_TSPAN_LINE_Y_READERS = [() => null, (values) => katanaSumTspanLineY(values)];
const KATANA_SVG_TEXT_LENGTH_UNITS = {
em: (node, value) =>
value * katanaFiniteFontSize(Number(String(katanaLineHeightFontSize(node)).replace("px", ""))),
px: (_node, value) => value,
};
const KATANA_ASCII_KERNING_PAIRS = {
'"T': -2.805,
"n'": -0.993,
"'t": -0.985,
"'w": -0.984,
"<<": 0.781,
">>": 0.781,
"E]": -2.117,
"M]": -2.118,
"P]": -2.125,
Pa: -0.75,
Pe: -0.743,
Ph: -0.742,
Po: -0.742,
Pr: -0.75,
Re: -0.649,
Ro: -0.649,
Ta: -1.992,
Te: -1.985,
Ti: -0.665,
To: -1.984,
Tu: -2.062,
Tw: -2.211,
Ty: -1.836,
Ve: -1.031,
Wa: -0.883,
We: -0.735,
Ye: -1.68,
Yo: -1.836,
"e'": -0.993,
"e,": -2.118,
"e?": -2.118,
"k,": -2.117,
"n,": -2.117,
"p;": -2.117,
's"': -2.805,
"s,": -2.118,
"t!": -2.125,
"u!": -2.117,
"u?": -2.117,
"v.": -2.149,
"y?": -2.125,
"y]": -2.125,
};
const KATANA_ASCII_TEXT_WIDTHS = {
" ": 4.82,
"'": 3.547,
$: 8.391,
"(": 5.875,
")": 5.875,
"-": 5.875,
".": 5.875,
":": 5.875,
0: 8.391,
1: 8.391,
2: 8.391,
3: 8.391,
4: 8.391,
5: 8.391,
6: 8.391,
7: 8.391,
8: 8.391,
9: 8.391,
A: 9.438,
B: 9.055,
C: 9.57,
D: 9.813,
E: 8.57,
F: 8.398,
G: 10.82,
H: 10.469,
I: 4.453,
J: 7.625,
K: 9.211,
L: 8.102,
M: 11.352,
N: 10.211,
O: 10.781,
P: 8.922,
Q: 10.813,
R: 9.313,
S: 7.695,
T: 9.289,
U: 10.375,
V: 9.398,
W: 13.633,
X: 8.906,
Y: 9.125,
Z: 8.805,
_: 8.391,
a: 8.406,
b: 8.914,
c: 7.922,
d: 8.914,
e: 8.727,
f: 5.914,
g: 8.031,
h: 8.742,
i: 4.563,
j: 5.867,
k: 8.07,
l: 4.719,
m: 13.281,
n: 8.742,
o: 8.586,
p: 8.914,
q: 8.914,
r: 6.219,
s: 6.477,
t: 6.344,
u: 8.742,
v: 7.836,
w: 11.906,
x: 8.016,
y: 7.891,
z: 7.594,
};
function katanaAnchoredTextNodeBox(node, text, x, y) {
const width = Math.max(1, katanaTextNodeWidth(node, text));
const height = katanaLineHeight(node);
return katanaBox(
katanaAnchoredTextLeft(katanaTextAnchor(node), x, width),
y - height * 0.8,
width,
height,
);
}
function katanaAnchoredTextLeft(anchor, x, width) {
return (KATANA_TEXT_ANCHORS[anchor] ?? KATANA_TEXT_ANCHORS.start)(x, width);
}
const KATANA_TEXT_ANCHORS = {
end: (x, width) => x - width,
middle: (x, width) => x - width / 2,
start: (x) => x,
};
function katanaTextAnchor(node) {
return String(katanaTextAnchorValues(node).find(Boolean) ?? "start");
}
function katanaTextAnchorValues(node) {
return [
node.getAttribute?.("text-anchor"),
node.style?.getPropertyValue?.("text-anchor"),
node.parentNode?.getAttribute?.("text-anchor"),
];
}
function katanaLineHeight(node) {
const fontSize = Number(String(katanaLineHeightFontSize(node)).replace("px", ""));
return katanaBrowserTextBoxHeight(katanaFiniteFontSize(fontSize));
}
function katanaBrowserTextBoxHeight(fontSize) {
const measured = KATANA_TEXT_BOX_HEIGHTS[String(fontSize)];
if (measured) {
return measured;
}
return Math.max(12, Math.ceil(fontSize * 1.15));
}
const KATANA_TEXT_BOX_HEIGHTS = {
10: 11,
11: 12.25,
12: 14,
14: 16,
16: 19,
18: 21,
20: 23,
};
function katanaLineHeightFontSize(node) {
return katanaLineHeightFontSizeValues(node).find(Boolean) ?? "16";
}
function katanaLineHeightFontSizeValues(node) {
return [
node.style?.getPropertyValue?.("font-size"),
katanaCssComputedStyleValue(node, "font-size"),
node.getAttribute?.("font-size"),
node.parentNode?.style?.getPropertyValue?.("font-size"),
katanaCssComputedStyleValue(node.parentNode, "font-size"),
node.parentNode?.getAttribute?.("font-size"),
];
}
function katanaFiniteFontSize(fontSize) {
if (Number.isFinite(fontSize)) {
return fontSize;
}
return 16;
}
function katanaTextNodeWidth(node, text) {
return katanaTextWidth(text) * katanaTextWidthScale(node);
}
function katanaTextWidthScale(node) {
return (
katanaFiniteFontSize(Number(String(katanaLineHeightFontSize(node)).replace("px", ""))) / 16
);
}
function katanaTextWidth(text) {
const chars = Array.from(katanaMeasuredTextValue(text));
const characterWidth = chars
.map((char) => katanaCharacterWidth(char))
.reduce((width, charWidth) => width + charWidth, 0);
return characterWidth + katanaTextKerningWidth(chars);
}
function katanaMeasuredTextValue(text) {
return String(text).replace(/&(?:amp;)?nbsp;| /g, "\u00A0");
}
function katanaTextKerningWidth(chars) {
return chars
.slice(0, -1)
.map((char, index) => katanaKerningPairWidth(char, chars[index + 1]))
.reduce((width, pairWidth) => width + pairWidth, 0);
}
function katanaKerningPairWidth(left, right) {
return KATANA_KERNING_PAIR_WIDTH_READERS[Number(katanaHasWideKerningChar(left, right))](
left,
right,
);
}
function katanaHasWideKerningChar(left, right) {
return [left, right].some(katanaIsWideTextChar);
}
function katanaIsWideTextChar(char) {
return char.charCodeAt(0) > 255;
}
function katanaCharacterWidth(char) {
return KATANA_CHARACTER_WIDTHS[Number(char.charCodeAt(0) > 255)](char);
}
function katanaAsciiCharacterWidth(char) {
if (char.charCodeAt(0) === 160) {
return KATANA_ASCII_TEXT_WIDTHS[" "];
}
return [KATANA_ASCII_TEXT_WIDTHS[char]].filter(Boolean).concat([8])[0];
}
function katanaWideCharacterWidth(char) {
return KATANA_WIDE_CHARACTER_WIDTHS[Number(globalThis.__katanaMermaidDiagramType === "kanban")](
char,
);
}
const KATANA_WIDE_CHARACTER_WIDTHS = [
katanaDefaultWideCharacterWidth,
() => 12.5,
];
function katanaDefaultWideCharacterWidth(char) {
if (katanaIsWidePunctuation(char)) {
return 20;
}
return KATANA_DEFAULT_WIDE_CHARACTER_WIDTHS[Number(katanaIsCjkIdeograph(char))]();
}
function katanaIsCjkIdeograph(char) {
const codePoint = katanaCharacterCodePoint(char);
return KATANA_CJK_IDEOGRAPH_RULES.every((rule) => rule(codePoint));
}
function katanaCharacterCodePoint(char) {
return char.codePointAt(0) ?? 0;
}
function katanaIsCjkIdeographStart(codePoint) {
return codePoint >= 0x4e00;
}
function katanaIsCjkIdeographEnd(codePoint) {
return codePoint <= 0x9fff;
}
function katanaIsWidePunctuation(char) {
return ["。", "、"].includes(char);
}
const KATANA_DEFAULT_WIDE_CHARACTER_WIDTHS = [() => 15.8, () => 16.3];
const KATANA_CJK_IDEOGRAPH_RULES = [katanaIsCjkIdeographStart, katanaIsCjkIdeographEnd];
const KATANA_CHARACTER_WIDTHS = [katanaAsciiCharacterWidth, katanaWideCharacterWidth];
const KATANA_KERNING_PAIR_WIDTH_READERS = [
(left, right) => KATANA_ASCII_KERNING_PAIRS[`${left}${right}`] ?? 0,
() => 0,
];
KatanaNode.prototype.getComputedTextLength = function getComputedTextLength() {
const lines = katanaComputedTextLines(this);
return Math.max(1, ...lines.map((line) => katanaTextNodeWidth(this, line)));
};
function katanaComputedTextLines(node) {
if (node.localName === "text") {
return katanaTextLines(node).map((line) => line.text);
}
return [katanaTextContent(node)];
}
function katanaInstallMermaidZenumlRuntimeAdapter() {
const zenumlDiagram = globalThis["mermaid-zenuml"];
if (zenumlDiagram) {
globalThis.__katanaMermaidZenuml = zenumlDiagram;
}
}
function katanaRunZenumlRuntime(source, isDark) {
const stripped = String(source).replace(/^zenuml[^\n]*\n?/, "");
let svg = zenuml.renderToSvg(stripped).svg;
if (isDark) {
const darkStyle = '<style>' +
'.frame-border-outer{fill:#4a5170}' +
'.frame-border-inner{fill:#111628}' +
'.frame-header-bg{fill:#1d2240}' +
'.frame-header-line{stroke:#cecfd2}' +
'.frame-title{fill:#cecfd2}' +
'.participant-box{fill:#5964f2;stroke:#cecfd2}' +
'.participant-label{fill:#cecfd2}' +
'.participant-icon{color:#cecfd2}' +
'.participant-icon [fill="currentColor"]:not([stroke]){stroke:#cecfd2}' +
'.lifeline{stroke:#cecfd2}' +
'.message-line{stroke:#536fff}' +
'.message-label{fill:#cecfd2}' +
'.arrow-head{fill:#536fff;stroke:#536fff}' +
'.occurrence{fill:#5964f2;stroke:#cecfd2}' +
'.fragment-border{stroke:#cecfd2}' +
'.fragment-header{fill:#5964f2}' +
'.fragment-label{fill:#cecfd2}' +
'.fragment-condition{fill:#cecfd2}' +
'.fragment-separator{stroke:#cecfd2}' +
'.fragment-section-label{fill:#cecfd2}' +
'.return-line{stroke:#536fff}' +
'.return-arrow{stroke:#536fff}' +
'.return-label{fill:#cecfd2}' +
'.return-icon{fill:#cecfd2}' +
'.divider-line{stroke:#536fff}' +
'.divider-bg{fill:#1d2240;stroke:#536fff}' +
'.divider-label{fill:#cecfd2}' +
'.comment-text{fill:#cecfd2}' +
'.seq-number{fill:#8890b0}' +
'.group-outline{stroke:#cecfd2}' +
'.group-title-bg{fill:#111628}' +
'.group-title-text{fill:#cecfd2}' +
'</style>';
svg = svg.replace('</svg>', darkStyle + '</svg>');
}
return svg;
}
katanaInstallMermaidZenumlRuntimeAdapter();