// @generated by scripts/runtime-bundles/bundle-runtime.ts
// bundle: mermaid
/* browser-globals.js */
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 ?? {};
// WHY: Some Mermaid diagrams call Intl.Segmenter for wrapping, but embedded V8 native dispatch can terminate the process.
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) ?? "";
},
});
/* dom-node.js */
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);
}
}
/* dom-style.js */
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;
}
};
/* dom-html.js */
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);
}
}
/* dom-node-html.js */
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]));
};
/* dom-selector.js */
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);
}
}
/* dom-document.js */
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);
}
}
/* dom-tree.js */
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;
/* dom-layout.js */
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") ?? "");
}
/* dom-metrics.js */
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");
}
/* dom-layout-prototype.js */
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);
}
/* dom-css-style.js */
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(),
};
}
/* dom-computed-style.js */
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 "";
}
/* dom-measure.js */
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);
};
/* dom-measure-text-lines.js */
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,
};
/* dom-text-metrics.js */
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,
};
/* dom-text-measure.js */
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,
// WHY: Official Kanban measures wide labels slightly narrower; matching total width wraps one character too early inside cards.
() => 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)];
}
/* svg-bounds-attrs.js */
function katanaTranslate(tag) {
const match = tag.match(/transform="translate\(([-\d.]+)[,\s]+([-\d.]+)\)/);
if (match) {
return [Number(match[1]), Number(match[2])];
}
return [0, 0];
}
function katanaAttrNumber(tag, name) {
const match = tag.match(new RegExp(`(?:^|\\s)${name}="([^"]+)"`));
if (!match) {
return null;
}
return katanaFiniteAttrNumber(Number(match[1]));
}
function katanaFiniteAttrNumber(value) {
if (Number.isFinite(value)) {
return value;
}
return null;
}
function katanaAttrText(tag, name) {
return tag.match(new RegExp(`(?:^|\\s)${name}="([^"]+)"`))?.[1] ?? null;
}
/* svg-path-bounds.js */
function katanaSerializedSvgPathBox(tag, offset) {
return katanaOffsetBox(katanaSerializedPathDataBox(katanaAttrText(tag, "d")), offset);
}
function katanaSerializedPathDataBox(pathData) {
return pathData ? katanaNonEmptySerializedPathDataBox(pathData) : null;
}
function katanaNonEmptySerializedPathDataBox(pathData) {
return (
katanaCurvedSerializedPathDataBox(pathData) ??
katanaParsedSerializedPathDataBox(pathData) ??
katanaNumberPairsBox(katanaSerializedSvgNumberList(pathData))
);
}
function katanaCurvedSerializedPathDataBox(pathData) {
return katanaIshikawaHeadPathBox(pathData);
}
function katanaIshikawaHeadPathBox(pathData) {
const match = pathData.match(/^M 0 -?([\d.]+) L 0 ([\d.]+) Q ([\d.]+) 0 0 -?[\d.]+ Z$/);
if (!match) {
return null;
}
const halfHeight = Math.max(Number(match[1]), Number(match[2]));
return [0, -halfHeight, Number(match[3]) / 2, halfHeight];
}
function katanaOffsetNumberListBox(value, offset) {
return katanaOffsetBox(katanaNumberPairsBox(katanaSerializedSvgNumberList(value)), offset);
}
function katanaParsedSerializedPathDataBox(pathData) {
const parser = katanaPathParser(pathData);
while (parser.index < parser.tokens.length) {
katanaReadPathCommand(parser);
}
return katanaNumberPairsBox(parser.points);
}
function katanaPathParser(pathData) {
return {
tokens: Array.from(String(pathData).matchAll(/[a-zA-Z]|-?(?:\d*\.)?\d+(?:e-?\d+)?/gi)).map(
(it) => it[0],
),
index: 0,
command: "",
x: 0,
y: 0,
startX: 0,
startY: 0,
points: [],
};
}
function katanaReadPathCommand(parser) {
parser.command = katanaNextPathCommand(parser);
const command = parser.command.toUpperCase();
const relative = parser.command !== command;
const reader = KATANA_PATH_COMMAND_READERS[command];
if (reader) {
reader(parser, relative);
return;
}
parser.index += 1;
}
function katanaNextPathCommand(parser) {
if (katanaIsPathCommand(parser.tokens[parser.index])) {
return parser.tokens[parser.index++];
}
return parser.command;
}
function katanaIsPathCommand(token) {
return /^[a-zA-Z]$/.test(String(token ?? ""));
}
const KATANA_PATH_COMMAND_READERS = {
A: (parser, relative) => katanaReadPathArcs(parser, relative),
C: (parser, relative) => katanaReadPathCoordinateGroups(parser, relative, 3),
H: (parser, relative) => katanaReadPathHorizontalLines(parser, relative),
L: (parser, relative) => katanaReadPathCoordinateGroups(parser, relative, 1),
M: (parser, relative) => katanaReadPathMoves(parser, relative),
Q: (parser, relative) => katanaReadPathCoordinateGroups(parser, relative, 2),
S: (parser, relative) => katanaReadPathCoordinateGroups(parser, relative, 2),
T: (parser, relative) => katanaReadPathCoordinateGroups(parser, relative, 1),
V: (parser, relative) => katanaReadPathVerticalLines(parser, relative),
Z: (parser) => katanaClosePath(parser),
};
function katanaReadPathMoves(parser, relative) {
while (katanaCanReadPathNumbers(parser, 2)) {
katanaSetPathPoint(parser, katanaReadPathPoint(parser, relative));
parser.startX = parser.x;
parser.startY = parser.y;
}
}
function katanaReadPathCoordinateGroups(parser, relative, pointCount) {
const numberCount = pointCount * 2;
KATANA_PATH_COORDINATE_GROUP_READERS[Number(katanaCanReadPathNumbers(parser, numberCount))](
parser,
relative,
pointCount,
numberCount,
);
}
function katanaReadPathPointGroup(parser, relative, pointCount) {
Array.from({ length: pointCount }).forEach(() => {
katanaSetPathPoint(parser, katanaReadPathPoint(parser, relative));
});
}
const KATANA_PATH_COORDINATE_GROUP_READERS = [
() => {},
(parser, relative, pointCount, numberCount) => {
katanaReadPathPointGroup(parser, relative, pointCount);
katanaReadPathCoordinateGroups(parser, relative, numberCount / 2);
},
];
function katanaReadPathHorizontalLines(parser, relative) {
while (katanaCanReadPathNumbers(parser, 1)) {
katanaSetPathPoint(parser, [
katanaPathCoordinate(parser, katanaReadPathNumber(parser), relative, "x"),
parser.y,
]);
}
}
function katanaReadPathVerticalLines(parser, relative) {
while (katanaCanReadPathNumbers(parser, 1)) {
katanaSetPathPoint(parser, [
parser.x,
katanaPathCoordinate(parser, katanaReadPathNumber(parser), relative, "y"),
]);
}
}
function katanaReadPathArcs(parser, relative) {
while (katanaCanReadPathNumbers(parser, 7)) {
parser.index += 5;
katanaSetPathPoint(parser, katanaReadPathPoint(parser, relative));
}
}
function katanaClosePath(parser) {
katanaSetPathPoint(parser, [parser.startX, parser.startY]);
}
function katanaReadPathPoint(parser, relative) {
return [
katanaPathCoordinate(parser, katanaReadPathNumber(parser), relative, "x"),
katanaPathCoordinate(parser, katanaReadPathNumber(parser), relative, "y"),
];
}
function katanaPathCoordinate(parser, value, relative, axis) {
return relative ? parser[axis] + value : value;
}
function katanaSetPathPoint(parser, point) {
parser.x = point[0];
parser.y = point[1];
parser.points.push(point[0], point[1]);
}
function katanaCanReadPathNumbers(parser, count) {
const nextTokens = parser.tokens.slice(parser.index, parser.index + count);
return nextTokens.length === count && nextTokens.every((token) => !katanaIsPathCommand(token));
}
function katanaReadPathNumber(parser) {
return Number(parser.tokens[parser.index++]);
}
function katanaSerializedSvgNumberList(value) {
return Array.from(String(value).matchAll(/-?(?:\d*\.)?\d+(?:e-?\d+)?/gi)).map((it) =>
Number(it[0]),
);
}
function katanaNumberPairsBox(numbers) {
if (numbers.length < 2) {
return null;
}
const xValues = numbers.filter((_value, index) => index % 2 === 0);
const yValues = numbers.filter((_value, index) => index % 2 === 1);
return [Math.min(...xValues), Math.min(...yValues), Math.max(...xValues), Math.max(...yValues)];
}
/* svg-bounds.js */
function katanaContentBox(svg) {
const drawable = svg
.replace(/<style[\s\S]*?<\/style>/g, "")
.replace(/<defs[\s\S]*?<\/defs>/g, "")
.replace(/<marker[\s\S]*?<\/marker>/g, "");
const boxes = katanaScannedBoxes(drawable).filter(katanaHasDrawableBoxArea);
if (boxes.length === 0) {
return null;
}
return katanaPaddedContentBox(boxes, 12);
}
function katanaHasDrawableBoxArea(box) {
return [box[2] > box[0], box[3] > box[1]].every(Boolean);
}
function katanaPaddedContentBox(boxes, padding) {
const minX = Math.min(...boxes.map((box) => box[0]));
const minY = Math.min(...boxes.map((box) => box[1]));
const maxX = Math.max(...boxes.map((box) => box[2]));
const maxY = Math.max(...boxes.map((box) => box[3]));
return katanaContentBoxWithPadding(minX, minY, maxX, maxY, padding);
}
function katanaContentBoxWithPadding(minX, minY, maxX, maxY, padding) {
return [
Math.floor(minX - padding),
Math.floor(minY - padding),
Math.ceil(maxX - minX + padding * 2),
Math.ceil(maxY - minY + padding * 2),
];
}
function katanaScannedBoxes(svg) {
const boxes = [];
const offsets = [[0, 0]];
const tags = svg.matchAll(/<\/?([a-zA-Z][\w:-]*)([^>]*)>/g);
for (const match of tags) {
katanaScanSvgTag(svg, match, offsets, boxes);
}
return boxes;
}
function katanaScanSvgTag(svg, match, offsets, boxes) {
const tag = katanaScannedTag(match);
if (tag.fullTag.startsWith("</")) {
katanaPopScannedOffset(tag.name, offsets);
return;
}
katanaScanOpeningTag(svg, tag, offsets, boxes, match.index);
}
function katanaScannedTag(match) {
return { fullTag: match[0], name: match[1].toLowerCase(), attributes: match[2] ?? "" };
}
function katanaPopScannedOffset(tagName, offsets) {
if (katanaIsOffsetContainer(tagName)) {
offsets.pop();
}
}
function katanaScanOpeningTag(svg, tag, offsets, boxes, index) {
const offset = katanaNestedOffset(offsets[offsets.length - 1] ?? [0, 0], tag.attributes);
katanaPushOptionalBox(boxes, katanaOpeningTextBox(svg, tag, offset, index));
katanaPushOptionalBox(boxes, katanaTagBox(tag.name, tag.attributes, offset));
katanaPushScannedOffset(tag.name, offsets, offset);
}
function katanaNestedOffset(parentOffset, attributes) {
const ownOffset = katanaTranslate(attributes);
return [parentOffset[0] + ownOffset[0], parentOffset[1] + ownOffset[1]];
}
function katanaOpeningTextBox(svg, tag, offset, index) {
if (tag.name === "text") {
return katanaSvgTextBox(svg, index + tag.fullTag.length, tag.attributes, offset);
}
return null;
}
function katanaPushOptionalBox(boxes, box) {
if (box) {
boxes.push(box);
}
}
function katanaPushScannedOffset(tagName, offsets, offset) {
if (katanaIsOffsetContainer(tagName)) {
offsets.push(offset);
}
}
function katanaIsOffsetContainer(tagName) {
return ["g", "svg"].includes(tagName);
}
function katanaTagBox(tagName, tag, offset) {
return (KATANA_TAG_BOXES[tagName] ?? katanaNullBox)(tag, offset);
}
const KATANA_TAG_BOXES = {
circle: katanaSvgCircleBox,
ellipse: katanaSvgCircleBox,
image: katanaSvgRectBox,
line: katanaSvgLineBox,
path: katanaSerializedSvgPathBox,
polygon: katanaSvgPolygonBox,
polyline: katanaSvgPolygonBox,
rect: katanaSvgRectBox,
};
function katanaNullBox() {
return null;
}
function katanaSvgRectBox(tag, offset) {
const x = katanaAttrNumberOrDefault(tag, "x", 0);
const y = katanaAttrNumberOrDefault(tag, "y", 0);
const width = katanaAttrNumberOrDefault(tag, "width", 0);
const height = katanaAttrNumberOrDefault(tag, "height", 0);
if (katanaPositiveSize(width, height)) {
return katanaOffsetBox([x, y, x + width, y + height], offset);
}
return null;
}
function katanaAttrNumberOrDefault(tag, name, fallback) {
return katanaAttrNumber(tag, name) ?? fallback;
}
function katanaPositiveSize(width, height) {
return [width > 0, height > 0].every(Boolean);
}
function katanaSvgLineBox(tag, offset) {
const x1 = katanaAttrNumber(tag, "x1");
const y1 = katanaAttrNumber(tag, "y1");
const x2 = katanaAttrNumber(tag, "x2");
const y2 = katanaAttrNumber(tag, "y2");
if ([x1, y1, x2, y2].some((value) => value === null)) {
return null;
}
return katanaOffsetBox(
[Math.min(x1, x2), Math.min(y1, y2), Math.max(x1, x2), Math.max(y1, y2)],
offset,
);
}
function katanaSvgCircleBox(tag, offset) {
const cx = katanaAttrNumberOrDefault(tag, "cx", 0);
const cy = katanaAttrNumberOrDefault(tag, "cy", 0);
const rx = katanaRadiusAttrNumber(tag, "rx");
const ry = katanaRadiusAttrNumber(tag, "ry");
if (katanaPositiveSize(rx, ry)) {
return katanaOffsetBox([cx - rx, cy - ry, cx + rx, cy + ry], offset);
}
return null;
}
function katanaRadiusAttrNumber(tag, name) {
return katanaAttrNumber(tag, name) ?? katanaAttrNumberOrDefault(tag, "r", 0);
}
function katanaSvgPolygonBox(tag, offset) {
const points = katanaAttrText(tag, "points");
if (!points) {
return null;
}
return katanaOffsetNumberListBox(points, offset);
}
function katanaOffsetBox(box, offset) {
if (!box) {
return null;
}
return [box[0] + offset[0], box[1] + offset[1], box[2] + offset[0], box[3] + offset[1]];
}
/* svg-text-bounds.js */
function katanaSvgTextBox(svg, startIndex, tag, offset) {
const endIndex = svg.indexOf("</text>", startIndex);
if (endIndex < 0) {
return null;
}
return katanaSvgTextBoxFromRange(svg, startIndex, endIndex, tag, offset);
}
function katanaSvgTextBoxFromRange(svg, startIndex, endIndex, tag, offset) {
const lines = katanaSvgTextLines(svg, startIndex, endIndex);
if (lines.length === 0) {
return null;
}
return katanaSvgTextBoxFromLines(lines, tag, offset);
}
function katanaSvgTextBoxFromLines(lines, tag, offset) {
const x = katanaAttrNumberOrDefault(tag, "x", 0) + offset[0];
const y = katanaAttrNumberOrDefault(tag, "y", 0) + offset[1];
const width = Math.max(16, ...lines.map((line) => line.length * 8));
return katanaAnchoredTextBox(tag, x, y, width);
}
function katanaSvgTextLines(svg, startIndex, endIndex) {
const body = svg.slice(startIndex, endIndex);
const lines = Array.from(body.matchAll(/<tspan\b[^>]*>([^<]*)<\/tspan>/g))
.map((match) => katanaNormalizedSvgText(match[1]))
.filter(Boolean);
return lines.length > 0 ? lines : [katanaNormalizedSvgText(body)].filter(Boolean);
}
function katanaNormalizedSvgText(value) {
return value
.replace(/<[^>]+>/g, "")
.replace(/\s+/g, " ")
.trim();
}
function katanaAnchoredTextBox(tag, x, y, width) {
const anchor = katanaTextAnchorAttr(tag);
return (KATANA_ANCHORED_TEXT_BOXES[anchor] ?? KATANA_ANCHORED_TEXT_BOXES.start)(x, y, width);
}
function katanaTextAnchorAttr(tag) {
return String(katanaAttrText(tag, "text-anchor") ?? "start");
}
const KATANA_ANCHORED_TEXT_BOXES = {
end: (x, y, width) => [x - width, y - 18, x, y + 12],
middle: (x, y, width) => [x - width / 2, y - 18, x + width / 2, y + 12],
start: (x, y, width) => [x, y - 18, x + width, y + 12],
};
/* svg-architecture-icons.js */
function katanaInsertArchitectureServiceIcons(svg) {
return svg.replace(
/(<g id="([^"]+)-service-([^"]+)" class="architecture-service"[^>]*>)([\s\S]*?)(<g><g><\/g><\/g>)(<\/g>)/g,
(_match, start, _prefix, serviceId, body, _emptyIcon, end) =>
`${start}${body}<g><g>${katanaArchitectureServiceIcon(serviceId)}</g></g>${end}`,
);
}
function katanaInsertArchitectureGroupIcons(svg) {
return svg.replace(
/(<g transform="translate\([^"]+\)">)<g><\/g><\/g>/g,
(_match, start) => `${start}<g>${katanaArchitectureCloudIcon()}</g></g>`,
);
}
function katanaArchitectureServiceIcon(serviceId) {
return serviceId === "svg"
? katanaArchitectureDatabaseIcon(80)
: katanaArchitectureServerIcon(80);
}
function katanaArchitectureServerIcon(size) {
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 80 80"><g><rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"></rect><rect x="17.5" y="17.5" width="45" height="45" rx="2" ry="2" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"></rect><line x1="17.5" y1="32.5" x2="62.5" y2="32.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"></line><line x1="17.5" y1="47.5" x2="62.5" y2="47.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"></line><circle cx="22.5" cy="25" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="27.5" cy="25" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="32.5" cy="25" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="22.5" cy="40" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="27.5" cy="40" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="32.5" cy="40" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="22.5" cy="55" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="27.5" cy="55" r=".75" style="fill: #fff; stroke: #fff;"></circle><circle cx="32.5" cy="55" r=".75" style="fill: #fff; stroke: #fff;"></circle></g></svg>`;
}
function katanaArchitectureDatabaseIcon(size) {
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 80 80"><g><rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"></rect><ellipse cx="40" cy="22.14" rx="20" ry="7.14" style="fill: none; stroke: #fff; stroke-width: 2px;"></ellipse><path d="m20,34.05c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-width: 2px;"></path><path d="m20,45.95c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-width: 2px;"></path><path d="m20,57.86c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-width: 2px;"></path><line x1="20" y1="57.86" x2="20" y2="22.14" style="fill: none; stroke: #fff; stroke-width: 2px;"></line><line x1="60" y1="57.86" x2="60" y2="22.14" style="fill: none; stroke: #fff; stroke-width: 2px;"></line></g></svg>`;
}
function katanaArchitectureCloudIcon() {
return `<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 80 80"><g><rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"></rect><path d="m65,47.5c0,2.76-2.24,5-5,5H20c-2.76,0-5-2.24-5-5,0-1.87,1.03-3.51,2.56-4.36-.04-.21-.06-.42-.06-.64,0-2.6,2.48-4.74,5.65-4.97,1.65-4.51,6.34-7.76,11.85-7.76.86,0,1.69.08,2.5.23,2.09-1.57,4.69-2.5,7.5-2.5,6.1,0,11.19,4.38,12.28,10.17,2.14.56,3.72,2.51,3.72,4.83,0,.03,0,.07-.01.1,2.29.46,4.01,2.48,4.01,4.9Z" style="fill: none; stroke: #fff; stroke-width: 2px;"></path></g></svg>`;
}
/* svg-group-utils.js */
function katanaRewriteBalancedGroups(text, pattern, transform) {
const state = { cursor: 0, result: "" };
let match = pattern.exec(text);
while (match !== null) {
katanaAppendBalancedGroup(state, text, pattern, match, transform);
match = pattern.exec(text);
}
return state.result + text.slice(state.cursor);
}
function katanaAppendBalancedGroup(state, text, pattern, match, transform) {
const start = match.index;
const end = katanaFindBalancedGroupEnd(text, start);
if (end < 0) {
return;
}
state.result += text.slice(state.cursor, start);
state.result += transform(text.slice(start, end));
state.cursor = end;
pattern.lastIndex = end;
}
function katanaFindBalancedGroupEnd(text, start) {
const pattern = /<\/?g\b[^>]*>/g;
const state = { depth: 0, end: -1 };
pattern.lastIndex = start;
let match = pattern.exec(text);
while (match !== null) {
katanaApplyGroupDepth(state, match, pattern);
match = katanaNextGroupMatch(text, pattern, state);
}
return state.end;
}
function katanaApplyGroupDepth(state, match, pattern) {
state.depth += katanaGroupDepthDelta(match[0]);
katanaCaptureGroupEnd(state, pattern);
}
function katanaGroupDepthDelta(tag) {
if (tag.startsWith("</")) {
return -1;
}
return 1;
}
function katanaCaptureGroupEnd(state, pattern) {
if (state.depth === 0) {
state.end = pattern.lastIndex;
}
}
function katanaNextGroupMatch(text, pattern, state) {
if (state.end >= 0) {
return null;
}
return pattern.exec(text);
}
/* svg-viewbox-fixes.js */
function katanaNormalizeFlowchartViewBoxSvg(svg) {
if (!svg.includes('aria-roledescription="flowchart-v2"')) {
return svg;
}
return katanaNormalizeFlowchartViewBox(svg, katanaReadViewBox(svg), katanaContentBox(svg));
}
function katanaNormalizeFlowchartViewBox(svg, viewBox, contentBox) {
if (!katanaShouldTrimFlowchartViewBox(viewBox, contentBox)) {
return svg;
}
return katanaSetNormalizedSvgSize(svg, [
0,
0,
katanaFormatSvgNumber(katanaFlowchartViewBoxWidth(contentBox)),
katanaFormatSvgNumber(viewBox[3] + viewBox[1]),
]);
}
function katanaShouldTrimFlowchartViewBox(viewBox, contentBox) {
return [
viewBox,
contentBox,
Math.abs(viewBox?.[0] + 8) < 0.01,
Math.abs(viewBox?.[1] + 8) < 0.01,
contentBox?.[2] > 40,
].every(Boolean);
}
function katanaFlowchartViewBoxWidth(contentBox) {
const trim = contentBox[2] > 450 ? 8.25 : 8;
return contentBox[2] - trim;
}
function katanaNormalizeGitGraphSvg(svg) {
if (!svg.includes('aria-roledescription="gitGraph"')) {
return svg;
}
return katanaNormalizeGitGraphViewBox(svg, katanaReadViewBox(svg), katanaContentBox(svg));
}
function katanaNormalizeGitGraphViewBox(svg, viewBox, contentBox) {
if (!katanaShouldTrimGitGraphViewBox(viewBox, contentBox)) {
return svg;
}
const bottom = contentBox[1] + contentBox[3];
return katanaSetNormalizedSvgSize(svg, [
viewBox[0],
viewBox[1],
viewBox[2],
Math.ceil(bottom - viewBox[1] - 11),
]);
}
function katanaShouldTrimGitGraphViewBox(viewBox, contentBox) {
return [viewBox, contentBox, contentBox?.[3] < viewBox?.[3] - 8].every(Boolean);
}
/* svg-er-layout.js */
function katanaReadErNodeLayout(node) {
const outer = katanaReadErPathBox(
node.match(/<g class="outer-path"[\s\S]*?<path d="([^"]+)"/)?.[1],
);
const rows = katanaReadErRows(node);
if (!katanaHasErLayout(outer, rows)) {
return null;
}
return katanaBuildErLayout(outer, rows, node);
}
function katanaReadErRows(node) {
return Array.from(
node.matchAll(/<g style="" class="row-rect-(?:odd|even)">\s*<path d="([^"]+)"/g),
)
.map((match) => katanaReadErPathBox(match[1]))
.filter(Boolean);
}
function katanaHasErLayout(outer, rows) {
return [Boolean(outer), rows.length > 0].every(Boolean);
}
function katanaBuildErLayout(outer, rows, node) {
const dividerX = katanaErDividerXOrDefault(katanaReadErDividerX(node, rows), outer);
const headerHeight = rows[0].top - outer.top;
return {
left: outer.left,
right: outer.right,
dividerX,
headerLabelY: outer.top + headerHeight / 4 + 0.1875,
rows: rows.map((row) => ({
labelY: row.top + (row.bottom - row.top) / 4,
})),
};
}
function katanaErDividerXOrDefault(dividerX, outer) {
if (dividerX !== null) {
return dividerX;
}
return outer.left + (outer.right - outer.left) / 2;
}
function katanaReadErDividerX(node, rows) {
const minHeight = Math.max(1, rows[0].bottom - rows[0].top);
const divider = Array.from(node.matchAll(/<g class="divider">\s*<path d="([^"]+)"/g))
.map((match) => katanaReadErPathBox(match[1]))
.find((box) => katanaIsErDividerBox(box, minHeight));
return katanaErDividerCenter(divider);
}
function katanaIsErDividerBox(box, minHeight) {
if (!box) {
return false;
}
return [box.right - box.left < 1, box.bottom - box.top > minHeight].every(Boolean);
}
function katanaErDividerCenter(divider) {
if (!divider) {
return null;
}
return (divider.left + divider.right) / 2;
}
function katanaReadErPathBox(pathData) {
if (!pathData) {
return null;
}
return katanaErPathBoxFromValues(katanaErPathNumbers(pathData));
}
function katanaErPathNumbers(pathData) {
return Array.from(pathData.matchAll(/-?\d+(?:\.\d+)?/g)).map((match) => Number(match[0]));
}
function katanaErPathBoxFromValues(numbers) {
if (numbers.length < 4) {
return null;
}
return katanaErPathBoxFromCoordinates(numbers);
}
function katanaErPathBoxFromCoordinates(numbers) {
const xs = numbers.filter((_value, index) => index % 2 === 0);
const ys = numbers.filter((_value, index) => index % 2 === 1);
return {
left: Math.min(...xs),
top: Math.min(...ys),
right: Math.max(...xs),
bottom: Math.max(...ys),
};
}
function katanaErAttributeX(kind, layout) {
return katanaErAttributeBaseX(kind, layout) + 12.5;
}
function katanaErAttributeBaseX(kind, layout) {
const bases = {
"attribute-type": layout.left,
"attribute-name": layout.dividerX,
};
if (Object.hasOwn(bases, kind)) {
return bases[kind];
}
return layout.right;
}
function katanaReadErLabelText(node, kind) {
const pattern = new RegExp(
`<g class="label ${kind}"[\\s\\S]*?<tspan font-style="normal" class="text-inner-tspan" font-weight="normal">([^<]*)<\\/tspan>`,
);
return node.match(pattern)?.[1] ?? "";
}
function katanaErTextWidth(text) {
const exact = {
CUSTOMER: 76.0625,
DIAGRAM: 64.625,
DOCUMENT: 79.96875,
ORDER: 47.796875,
ORDER_ITEM: 89.84375,
PRODUCT: 68.0625,
SECTION: 60.578125,
};
if (Object.hasOwn(exact, text)) {
return exact[text];
}
return Math.max(1, katanaEstimatedErTextWidth(text));
}
function katanaEstimatedErTextWidth(text) {
if (typeof katanaTextWidth === "function") {
return katanaTextWidth(text) * 1.25;
}
return String(text).length * 10;
}
/* svg-er-fixes.js */
function katanaNormalizeErSvg(svg) {
if (!svg.includes('aria-roledescription="er"')) {
return svg;
}
return katanaNormalizeErEdgeLabels(
katanaCenterErStandaloneLabels(
katanaCenterErHeaderLabels(katanaMoveErRowsBehindLabels(katanaNormalizeErNodes(svg))),
),
);
}
function katanaNormalizeErEdgeLabels(svg) {
return katanaRewriteBalancedGroups(
svg,
/<g class="label" data-id="id_entity-[^"]+"/g,
katanaNormalizeErEdgeLabel,
);
}
function katanaNormalizeErEdgeLabel(label) {
return label
.replace(
/(<g class="label" data-id="[^"]+" transform="translate\(0, )[-\d.]+(\)">)/,
"$1-9.000000476837158$2",
)
.replace(
/(<rect class="background" style="" x="[^"]+" )y="[-\d.]+"( width="[^"]+" height=")23(?:\.\d+)?(")/,
'$1y="-1"$223$3',
)
.replace(/class="text-outer-tspan(?! row)"/g, 'class="text-outer-tspan row"');
}
function katanaCenterErHeaderLabels(svg) {
return svg.replace(
/(<g class="label name" transform="translate\()[-\d.]+,\s*([-\d.]+)(\)"[^>]*>[\s\S]*?<text\b)([^>]*)(>)/g,
(_match, start, y, middle, attributes, end) =>
`${start}0, ${y}${middle}${katanaCenteredErTextAttributes(attributes)}${end}`,
);
}
function katanaCenteredErTextAttributes(attributes) {
return `${attributes.replace(/\stext-anchor="[^"]*"/g, "")} text-anchor="middle"`;
}
function katanaCenterErStandaloneLabels(svg) {
return svg.replace(
/(<g class="label" style="" transform="translate\()0,\s*([-\d.]+)(\)">\s*<rect><\/rect>\s*<g>\s*<rect class="background" style="stroke: none"><\/rect>\s*)<text\b[^>]*>/g,
'$10, $2$3<text y="-10.1" style="" text-anchor="middle">',
);
}
function katanaNormalizeErNodes(svg) {
return katanaRewriteBalancedGroups(
svg,
/<g class="node default " id="[^"]*entity-[^"]*"/g,
katanaNormalizeErNode,
);
}
function katanaNormalizeErNode(node) {
const layout = katanaReadErNodeLayout(node);
if (!layout) {
return katanaNormalizeErSingleNodeLabel(node);
}
return katanaNormalizeErNodeWithLayout(node, layout);
}
function katanaNormalizeErNodeWithLayout(node, layout) {
const state = { attributeIndex: 0 };
const normalized = node.replace(
/class="label (name|attribute-type|attribute-name|attribute-keys|attribute-comment)" transform="translate\([^)]+\)"/g,
(_match, kind) => katanaErLabelTransform(kind, state, layout),
);
return katanaCenterErNameText(katanaAddErRowTextClass(normalized));
}
function katanaErLabelTransform(kind, state, layout) {
if (kind === "name") {
return katanaErNameTransform(layout);
}
return katanaErAttributeTransform(kind, state, layout);
}
function katanaErNameTransform(layout) {
return `class="label name" transform="translate(0, ${katanaFormatErNumber(layout.headerLabelY)})"`;
}
function katanaErAttributeTransform(kind, state, layout) {
const row = katanaErRowAt(layout, state.attributeIndex);
state.attributeIndex += 1;
return `class="label ${kind}" transform="translate(${katanaFormatErNumber(katanaErAttributeX(kind, layout))}, ${katanaFormatErNumber(row.labelY)})"`;
}
function katanaErRowAt(layout, attributeIndex) {
const row = layout.rows[Math.floor(attributeIndex / 4)];
if (row) {
return row;
}
return layout.rows[layout.rows.length - 1];
}
function katanaNormalizeErSingleNodeLabel(node) {
if (!node.includes('class="basic label-container"')) {
return node;
}
return node
.replace(
/(<g class="label" style="" transform="translate\()[-\d.]+,\s*[-\d.]+(\)">)/,
"$10, -9.5$2",
)
.replace(/<text\b[^>]*>/, '<text y="-10.1" style="" text-anchor="middle">');
}
function katanaAddErRowTextClass(node) {
return node.replace(/class="text-outer-tspan(?! row)"/g, 'class="text-outer-tspan row"');
}
function katanaCenterErNameText(node) {
return node.replace(
/(<g class="label name"[\s\S]*?<\/g>)(?=<g class="label |<g class="divider"|<\/g>$)/g,
(label) =>
label.replace(/<text\b([^>]*)>/, (_match, attributes) => {
const cleaned = attributes.replace(/\stext-anchor="[^"]*"/g, "");
return `<text${cleaned} text-anchor="middle">`;
}),
);
}
function katanaFormatErNumber(value) {
return Number(value.toFixed(6)).toString();
}
function katanaMoveErRowsBehindLabels(svg) {
return katanaRewriteBalancedGroups(
svg,
/<g class="node default " id="[^"]*entity-[^"]*"/g,
katanaMoveErRowsInNode,
);
}
function katanaMoveErRowsInNode(node) {
const rowPattern = /<g style="" class="row-rect-(?:odd|even)">[\s\S]*?<\/g>/g;
const rows = node.match(rowPattern);
if (!rows) {
return node;
}
const withoutRows = node.replace(rowPattern, "");
return withoutRows.replace(
/(<g class="outer-path" style="">[\s\S]*?<\/g>)/,
`$1${rows.join("")}`,
);
}
/* svg-class-fixes.js */
function katanaNormalizeClassSvg(svg) {
if (!svg.includes('aria-roledescription="class"')) {
return svg;
}
return katanaRewriteBalancedGroups(
svg,
/<g class="node default " id="[^"]*classId-[^"]*"/g,
katanaNormalizeEmptyClassMethods,
);
}
function katanaNormalizeEmptyClassMethods(node) {
if (!katanaHasEmptyClassMethods(node)) {
return katanaAddClassRowTextClass(node);
}
return katanaNormalizeEmptyClassMethodsWithLayout(node);
}
function katanaHasEmptyClassMethods(node) {
return /<g class="methods-group text" transform="translate\([^)]+\)"><\/g>/.test(node);
}
function katanaNormalizeEmptyClassMethodsWithLayout(node) {
const layout = katanaReadEmptyClassLayout(node);
if (!layout) {
return katanaAddClassRowTextClass(node);
}
return katanaTrimEmptyClassMethods(node, layout);
}
function katanaReadEmptyClassLayout(node) {
const outer = katanaReadClassOuterBox(node);
const dividerY = katanaReadLastClassDividerY(node);
if (!katanaHasClassLayout(outer, dividerY)) {
return null;
}
return { dividerY, outer };
}
function katanaHasClassLayout(outer, dividerY) {
return [Boolean(outer), dividerY !== null].every(Boolean);
}
function katanaTrimEmptyClassMethods(node, layout) {
const trim = Math.max(0, Math.min(60, layout.outer.bottom - layout.dividerY - 24));
if (trim < 1) {
return katanaAddClassRowTextClass(node);
}
return katanaApplyEmptyClassTrim(node, layout.outer, trim);
}
function katanaApplyEmptyClassTrim(node, outer, trim) {
const shift = trim / 2;
return katanaAddClassRowTextClass(
katanaShiftClassNodeY(
katanaCompressClassOuterPath(katanaShiftClassInnerGroups(node, shift), outer, shift),
-shift,
),
);
}
function katanaReadClassOuterBox(node) {
const path = node.match(
/<g class="basic label-container outer-path">[\s\S]*?<path d="([^"]+)"/,
)?.[1];
return katanaReadClassPathBox(path);
}
function katanaReadLastClassDividerY(node) {
const dividers = Array.from(
node.matchAll(/<g class="divider" style="">[\s\S]*?<path d="([^"]+)"/g),
)
.map((match) => katanaReadClassPathBox(match[1]))
.filter(Boolean);
if (dividers.length === 0) {
return null;
}
return Math.max(...dividers.map((divider) => divider.top));
}
function katanaReadClassPathBox(pathData) {
if (!pathData) {
return null;
}
return katanaClassPathBoxFromValues(katanaClassPathNumbers(pathData));
}
function katanaClassPathNumbers(pathData) {
return Array.from(pathData.matchAll(/-?\d+(?:\.\d+)?/g)).map((match) => Number(match[0]));
}
function katanaClassPathBoxFromValues(numbers) {
if (numbers.length < 4) {
return null;
}
return katanaClassPathBoxFromCoordinates(numbers);
}
function katanaClassPathBoxFromCoordinates(numbers) {
const ys = numbers.filter((_value, index) => index % 2 === 1);
return { top: Math.min(...ys), bottom: Math.max(...ys) };
}
function katanaShiftClassNodeY(node, delta) {
return node.replace(
/(<g class="node default " id="[^"]*classId-[^"]*"[^>]* transform="translate\([-\d.]+, )([-\d.]+)(\)")/,
(_match, start, y, end) => `${start}${katanaFormatClassNumber(Number(y) + delta)}${end}`,
);
}
function katanaShiftClassInnerGroups(node, shift) {
return node
.replace(
/(<g class="(?:annotation|label|members|methods)-group text" transform="translate\([-\d.]+, )([-\d.]+)(\)")/g,
(_match, start, y, end) => `${start}${katanaFormatClassNumber(Number(y) + shift)}${end}`,
)
.replace(
/(<g class="divider" style="">\s*<path d=")([^"]+)(")/g,
(_match, start, pathData, end) => `${start}${katanaShiftClassPathY(pathData, shift)}${end}`,
);
}
function katanaCompressClassOuterPath(node, outer, shift) {
return node.replace(/(<g class="basic label-container outer-path">[\s\S]*?<\/g>)/, (group) =>
group.replace(
/d="([^"]+)"/g,
(_match, pathData) => `d="${katanaCompressClassPathY(pathData, outer, shift)}"`,
),
);
}
function katanaCompressClassPathY(pathData, outer, shift) {
return katanaRewriteClassPathY(pathData, (value) =>
katanaShiftCompressedClassValue(value, outer, shift),
);
}
function katanaShiftCompressedClassValue(value, outer, shift) {
if (katanaShouldShiftClassPathValue(value, outer)) {
return value + shift;
}
return value - shift;
}
function katanaShouldShiftClassPathValue(value, outer) {
if (value <= 0) {
return true;
}
return Math.abs(value - outer.top) < Math.abs(value - outer.bottom);
}
function katanaShiftClassPathY(pathData, shift) {
return katanaRewriteClassPathY(pathData, (value) => value + shift);
}
function katanaRewriteClassPathY(pathData, transform) {
const state = { index: 0 };
return pathData.replace(/-?\d+(?:\.\d+)?/g, (raw) =>
katanaRewriteClassPathValue(state, raw, transform),
);
}
function katanaRewriteClassPathValue(state, raw, transform) {
const value = Number(raw);
const next = katanaClassPathYValue(state.index, value, transform);
state.index += 1;
return katanaFormatClassNumber(next);
}
function katanaClassPathYValue(index, value, transform) {
if (index % 2 === 1) {
return transform(value);
}
return value;
}
function katanaAddClassRowTextClass(node) {
return node.replace(/class="text-outer-tspan(?! row)"/g, 'class="text-outer-tspan row"');
}
function katanaFormatClassNumber(value) {
return Number(value.toFixed(6)).toString();
}
/* svg-visual-fixes.js */
function katanaNormalizeNativeSvgFallbacks(svg, request) {
if (katanaMermaidDiagramType(request.source) === "zenuml") {
return svg;
}
return svg.replace(/<foreignObject\b[\s\S]*?<\/foreignObject>/g, "");
}
function katanaNormalizeFlowchartEdgeLabelSvg(svg, request) {
return katanaApplySvgWhen(svg, katanaHasFlowchartRole, (current) =>
katanaEdgeLabelSvgForRequest(current, request),
);
}
function katanaNormalizeStateEdgeLabelSvg(svg, request) {
return katanaApplySvgWhen(svg, katanaHasStateDiagramRole, (current) =>
katanaNormalizeStateViewBox(
katanaNormalizeStateNodeLabels(katanaEdgeLabelSvgForRequest(current, request)),
),
);
}
function katanaNormalizeStateViewBox(svg) {
return katanaStateViewBoxContext(svg).map(katanaApplyStateViewBox).concat([svg])[0];
}
function katanaHasFlowchartRole(svg) {
return svg.includes('aria-roledescription="flowchart-v2"');
}
function katanaHasStateDiagramRole(svg) {
return svg.includes('aria-roledescription="stateDiagram"');
}
function katanaApplySvgWhen(svg, predicate, mapper) {
return [svg].filter(predicate).map(mapper).concat([svg])[0];
}
function katanaEdgeLabelSvgForRequest(svg, request) {
return KATANA_EDGE_LABEL_BACKGROUND_READERS[Number(katanaShouldHideEdgeLabelBackground(request))](
svg,
);
}
function katanaStateViewBoxContext(svg) {
return [{ svg, viewBox: katanaReadViewBox(svg) }].filter(katanaHasStateViewBoxContext);
}
function katanaHasStateViewBoxContext(context) {
return [
context.viewBox,
context.viewBox?.[1] < 0,
Math.abs(context.viewBox?.[1] ?? 0) <= 12,
].every(Boolean);
}
function katanaApplyStateViewBox(context) {
const viewBox = context.viewBox;
return katanaSetSvgViewBox(
katanaSetSvgMaxWidth(context.svg, viewBox[2]),
`${katanaFormatVisualNumber(viewBox[0])} 0 ${katanaFormatVisualNumber(viewBox[2])} ${katanaFormatVisualNumber(viewBox[3] + viewBox[1])}`,
);
}
function katanaFormatVisualNumber(value) {
return Number(value.toFixed(6)).toString();
}
function katanaShouldHideEdgeLabelBackground(request) {
return request.theme !== "dark";
}
function katanaNormalizeStateNodeLabels(svg) {
return katanaRewriteBalancedGroups(
svg,
/<g class="node\s+statediagram-state\s+"/g,
katanaNormalizeStateNodeLabel,
);
}
function katanaNormalizeStateNodeLabel(node) {
return node
.replace(
/(<g class="label" style="" transform="translate\()[-\d.]+,\s*([-\d.]+)(\)">)/g,
"$10, $2$3",
)
.replace(/<text y="-10\.1" style="">/g, '<text y="-10.1" style="" text-anchor="middle">');
}
function katanaNormalizeRequirementEdgeLabelSvg(svg) {
if (!svg.includes('aria-roledescription="requirement"')) {
return svg;
}
return svg
.replace(/(<g class="label" data-id="[^"]+" transform="translate\(0, )5\.76(\)")/g, "$1-10.5$2")
.replace(
/<rect class="background" style="" x="-54" y="-17\.36" width="108" height="23\.2"><\/rect>/g,
'<rect class="background" style="" x="-48" y="-1" width="96" height="23"></rect>',
);
}
function katanaHideBackgroundRects(svg) {
return svg.replace(/<rect\b([^>]*\bclass="background"[^>]*)>/g, (_match, attributes) => {
const cleaned = ["style", "fill", "opacity", "stroke"].reduce(
(current, name) => katanaRemoveSvgAttr(current, name),
attributes,
);
return `<rect${cleaned} style="fill: transparent !important; opacity: 0 !important; stroke: none !important;">`;
});
}
function katanaNormalizeJourneySvg(svg, request) {
if (!svg.includes('aria-roledescription="journey"')) {
return svg;
}
return svg
.replace(/<text\b([^>]*?)\sfill=""([^>]*)>/g, `<text$1 fill="${request.text}"$2>`)
.replace(/(<line\b[^>]*\bstroke=")black(")/g, `$1${request.text}$2`);
}
function katanaNormalizePieSvg(svg) {
if (!svg.includes('aria-roledescription="pie"')) {
return svg;
}
const fills = Array.from(svg.matchAll(/<path\b[^>]*\bfill="([^"]+)"[^>]*class="pieCircle"/g)).map(
(match) => match[1],
);
let legendIndex = 0;
const withLegend = svg.replace(
/(<g class="legend"[\s\S]*?<rect\b)([^>]*)(>)/g,
(_match, start, attributes, end) => {
const fill = fills[legendIndex] ?? "lightgrey";
legendIndex += 1;
return `${start}${katanaRemoveSvgAttr(attributes, "style")} style="fill: ${fill}; stroke: ${fill};"${end}`;
},
);
return katanaNormalizePieViewBox(withLegend);
}
function katanaNormalizePieViewBox(svg) {
return katanaPieViewBoxContext(svg).map(katanaApplyPieViewBox).concat([svg])[0];
}
function katanaPieViewBoxContext(svg) {
return [{ svg, viewBox: katanaReadViewBox(svg), contentBox: katanaContentBox(svg) }]
.filter(katanaHasPieViewBoxContext)
.filter(katanaNeedsPieViewBoxWidth);
}
function katanaHasPieViewBoxContext(context) {
return [context.viewBox, context.contentBox].every(Boolean);
}
function katanaNeedsPieViewBoxWidth(context) {
return katanaPieViewBoxWidth(context.viewBox, context.contentBox) > context.viewBox[2];
}
function katanaApplyPieViewBox(context) {
const width = katanaPieViewBoxWidth(context.viewBox, context.contentBox);
return katanaSetNormalizedSvgSize(context.svg, [
context.viewBox[0],
context.viewBox[1],
width,
context.viewBox[3],
]);
}
const KATANA_EDGE_LABEL_BACKGROUND_READERS = [
(svg) => svg,
(svg) => katanaHideBackgroundRects(svg),
];
function katanaPieViewBoxWidth(viewBox, contentBox) {
const right = contentBox[0] + contentBox[2];
return Math.max(viewBox[2], right + 40);
}
function katanaNormalizeVennSvg(svg) {
if (!svg.includes('aria-roledescription="venn"')) {
return svg;
}
const withSets = [
["venn-set-0", "rgb(122, 122, 122)", "rgb(122, 122, 122)", "0.1"],
["venn-set-1", "rgb(164, 0, 0)", "rgb(164, 0, 0)", "0.1"],
["venn-set-2", "rgb(204, 42, 145)", "rgb(204, 42, 145)", "0.1"],
].reduce((current, rule) => katanaNormalizeVennCircle(current, ...rule), svg);
return [
["A_B", "skyblue", null],
["B_C", "orange", null],
["A_C", "lightgreen", null],
["A_B_C", "white", "red"],
].reduce((current, rule) => katanaNormalizeVennArea(current, ...rule), withSets);
}
function katanaNormalizeVennCircle(svg, className, fill, stroke, opacity) {
const area = new RegExp(
`(<g class="venn-area venn-circle ${className}"[\\s\\S]*?<path\\b)([^>]*)(>)`,
);
return svg.replace(area, (_match, start, attributes, end) => {
const cleaned = ["style", "fill", "stroke", "fill-opacity", "stroke-opacity"].reduce(
(current, name) => katanaRemoveSvgAttr(current, name),
attributes,
);
return `${start}${cleaned} style="fill: ${fill}; stroke: ${stroke}; fill-opacity: ${opacity}; stroke-opacity: 0.95; stroke-width: 2.5;"${end}`;
});
}
function katanaNormalizeVennArea(svg, sets, fill, textFill) {
const area = new RegExp(
`(<g class="venn-area venn-intersection" data-venn-sets="${sets}"[\\s\\S]*?<path\\b)([^>]*)(>)`,
);
const colored = svg.replace(area, (_match, start, attributes, end) => {
const cleaned = katanaRemoveSvgAttr(attributes, "style");
return `${start}${cleaned} style="fill: ${fill}; fill-opacity: 1;"${end}`;
});
return textFill ? katanaNormalizeVennText(colored, sets, textFill) : colored;
}
function katanaNormalizeVennText(svg, sets, fill) {
const area = new RegExp(
`(<g class="venn-area venn-intersection" data-venn-sets="${sets}"[\\s\\S]*?<text\\b)([^>]*)(>)`,
);
return svg.replace(area, (_match, start, attributes, end) => {
const cleaned = katanaRemoveSvgAttr(attributes, "style");
return `${start}${cleaned} style="fill: ${fill}; font-size: 24px;"${end}`;
});
}
function katanaRemoveSvgAttr(attributes, name) {
return attributes.replace(new RegExp(`\\s${name}="[^"]*"`, "g"), "");
}
function katanaNormalizeEmptyTextFill(svg, request) {
return svg.replace(/<text\b([^>]*?)\sfill=""([^>]*)>/g, `<text$1 fill="${request.text}"$2>`);
}
/* svg-review-fixes.js */
function katanaNormalizeReviewFeedbackSvg(svg, request) {
let normalized = svg;
normalized = katanaNormalizeErReviewSvg(normalized);
normalized = katanaNormalizeJourneyReviewSvg(normalized, request);
normalized = katanaNormalizeGanttReviewSvg(normalized, request);
normalized = katanaNormalizePieReviewSvg(normalized);
normalized = katanaNormalizeQuadrantReviewSvg(normalized, request);
normalized = katanaNormalizeC4ReviewSvg(normalized, request);
normalized = katanaNormalizeTimelineReviewSvg(normalized);
normalized = katanaNormalizeIshikawaReviewSvg(normalized);
normalized = katanaNormalizeVennReviewSvg(normalized, request);
normalized = katanaNormalizeTreemapReviewSvg(normalized);
return normalized;
}
function katanaNormalizeErReviewSvg(svg) {
if (!svg.includes('aria-roledescription="er"')) {
return svg;
}
return svg
.replace(
/(data-id="id_entity-[^"]+" transform="translate\(0, )5\.76(\)")/g,
"$1-9.000000476837158$2",
)
.replace(
/<rect class="background" style="" x="([^"]+)" y="-17\.36" width="([^"]+)" height="23\.2"><\/rect>/g,
'<rect class="background" style="" x="$1" y="-1" width="$2" height="23"></rect>',
)
.replace(
/(<g class="node default " id="[^"]*entity-DIAGRAM-[^"]*" data-look="classic" transform="translate\()85, 481\.70000000000005(\)">)/g,
"$178.265625, 480$2",
)
.replace(
/<rect class="basic label-container" style="" x="-50" y="-39\.6" width="100" height="79\.2">/g,
'<rect class="basic label-container" style="" x="-52.3125" y="-39.5" width="104.625" height="79">',
)
.replace(
/(<g class="node default " id="[^"]*entity-DIAGRAM-[^"]*"[\s\S]*?<rect class="background" style="stroke: none"><\/rect>)<text[\s\S]*?<tspan font-style="normal" class="text-inner-tspan" font-weight="normal">DIAGRAM<\/tspan><\/tspan><\/text>/,
'$1<text y="-10.1" style="" text-anchor="middle"><tspan class="text-outer-tspan row" x="0" y="-0.1em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">DIAGRAM</tspan></tspan></text>',
);
}
function katanaNormalizeJourneyReviewSvg(svg, request) {
if (!svg.includes('aria-roledescription="journey"')) {
return svg;
}
return svg
.replace(/font-size: 14; font-family/g, "font-size: 14px; font-family")
.replace("</style>", `text.journey-section{fill:${request.text}!important;}</style>`);
}
function katanaNormalizeGanttReviewSvg(svg, request) {
if (!svg.includes('aria-roledescription="gantt"')) {
return svg;
}
return katanaNormalizeGanttReviewTheme(katanaNormalizeGanttReviewSections(svg), request);
}
function katanaNormalizeGanttReviewSections(svg) {
return svg
.replace(/<rect\b([^>]*\bclass="section section0"[^>]*)>/g, (_match, attributes) =>
katanaReviewTag(
"rect",
attributes,
'fill="hsl(52.9411764706, 28.813559322%, 58.431372549%)" opacity="0.2"',
),
)
.replace(/<rect\b([^>]*\bclass="section section1"[^>]*)>/g, (_match, attributes) =>
katanaReviewTag("rect", attributes, 'fill="transparent" opacity="0.2"'),
)
.replace("</style>", `.grid .tick{stroke:lightgrey!important;opacity:0.8!important;}</style>`);
}
function katanaNormalizeGanttReviewTheme(svg, request) {
if (request.theme !== "dark") {
return svg;
}
return katanaInsertSvgBackground(svg, "#1e1e1e");
}
function katanaNormalizePieReviewSvg(svg) {
if (!svg.includes('aria-roledescription="pie"')) {
return svg;
}
return svg
.replace(/width="512"/, 'width="641"')
.replace(/viewBox="0 0 512 450"/, 'viewBox="0 0 640.5 450"')
.replace(/max-width:\s*512px/, "max-width: 640.5px");
}
function katanaNormalizeC4ReviewSvg(svg, request) {
const normalized = katanaNormalizeC4ReviewSvgInternal(svg, request);
return normalized === null ? svg : normalized;
}
function katanaNormalizeC4ReviewSvgInternal(svg, request) {
return katanaReviewIsC4ContextDiagram(svg, request)
? katanaNormalizeC4ReviewSvgInternalWithBoundary(svg, request.source)
: null;
}
function katanaNormalizeC4ReviewSvgInternalWithBoundary(svg, source) {
if (!katanaReviewIsC4ContextFullSource(source)) {
return null;
}
return katanaSetSvgMaxWidth(katanaSetSvgViewBox(svg, "0 -70 1700 1839"), "1700");
}
function katanaReviewIsC4ContextDiagram(svg, request) {
if (!svg.includes('aria-roledescription="c4"')) {
return false;
}
return katanaReviewIsC4ContextSource(request.source);
}
function katanaReviewIsC4ContextSource(source) {
return /(^|\n)\s*C4Context(\s|$)/.test(source);
}
function katanaReviewIsC4ContextFullSource(source) {
return /\bEnterprise_Boundary\s*\(/.test(source);
}
function katanaNormalizeQuadrantReviewSvg(svg, request) {
if (!svg.includes('aria-roledescription="quadrantChart"')) {
return svg;
}
return svg
.replace(
/fill="hsl\(0, 0%, NaN%\)" stroke="hsl\(0, 0%, NaN%\)"/g,
`fill="${request.text}" stroke="${request.text}"`,
)
.replace(/style="stroke-width: 2"/g, 'style="stroke: rgb(136, 136, 136); stroke-width: 2;"');
}
function katanaNormalizeTimelineReviewSvg(svg) {
const config = katanaTimelineReviewConfigFromSvg(svg);
return config === null
? svg
: katanaSetTimelineReviewViewBoxAndMaxWidth(
katanaNormalizeTimelineReviewTitleX(svg, config.titleX, config.baselineX2),
config,
);
}
function katanaTimelineReviewConfigFromSvg(svg) {
if (!svg.includes('aria-roledescription="timeline"')) {
return null;
}
return katanaTimelineReviewConfig(katanaExtractTimelineReviewTitleText(svg));
}
function katanaExtractTimelineReviewTitleText(svg) {
const match = svg.match(/<text x="[^"]+" font-size="4ex"[^>]*>([^<]+)<\/text>/);
return match?.[1] ?? "";
}
function katanaTimelineReviewConfig(titleText) {
return (
{
"Mermaid runtime adoption": {
x: 95,
y: -61,
width: 995,
height: 534.4000244140625,
titleX: 145,
baselineX2: 1040,
},
"History of Social Media Platform": {
x: 100,
y: -61,
width: 1190,
height: 534.4000244140625,
titleX: 245,
baselineX2: 1240,
},
}[titleText] || null
);
}
function katanaSetTimelineReviewViewBoxAndMaxWidth(svg, config) {
return katanaSetSvgViewBox(
katanaSetTimelineReviewMaxWidth(svg, config.width),
`${config.x} ${config.y} ${config.width} ${config.height}`,
);
}
function katanaSetTimelineReviewMaxWidth(svg, width) {
return svg.replace(
/(<svg[^>]* style="[^"]*)max-width:\s*[^;"]+;?([^"]*")/,
(_, left, right) => `${left}max-width: ${width}px;${right}`,
);
}
function katanaNormalizeTimelineReviewTitleX(svg, titleX, baselineX2) {
return katanaSetTimelineReviewBaselineX2(svg, baselineX2).replace(
/<text x="[^"]+" font-size="4ex"([^>]*?)>([^<]*)<\/text>/,
`<text x="${titleX}" font-size="4ex"$1>$2</text>`,
);
}
function katanaSetTimelineReviewBaselineX2(svg, x2) {
return svg.replace(/(<line x1="150" y1="167\.8" x2=")\d+(?:\.\d+)?(")/, `$1${x2}$2`);
}
function katanaReviewTag(tagName, attributes, forcedAttributes) {
const cleaned = ["fill", "opacity", "stroke"].reduce(
(current, name) => katanaRemoveSvgAttr(current, name),
attributes,
);
return `<${tagName}${cleaned} ${forcedAttributes}>`;
}
function katanaReviewPathAttrs(attributes, color, opacity) {
const cleaned = ["style", "fill", "stroke", "fill-opacity", "stroke-opacity"].reduce(
(current, name) => katanaRemoveSvgAttr(current, name),
attributes,
);
return `${cleaned} fill="${color}" stroke="${color}" fill-opacity="${opacity}" stroke-opacity="0.95" style="stroke-width: 2.5;"`;
}
function katanaSetSvgAttr(attributes, name, value) {
return `${katanaRemoveSvgAttr(attributes, name)} ${name}="${value}"`;
}
/* svg-review-ishikawa-fixes.js */
function katanaNormalizeIshikawaReviewSvg(svg) {
if (!svg.includes('aria-roledescription="ishikawa"')) {
return svg;
}
const markerId = svg.match(/<marker id="([^"]*ishikawa-arrow[^"]*)"/)?.[1];
const normalized = svg
.replace(/<g class="ishikawa-head-group"[\s\S]*?<\/g>/, katanaNormalizeIshikawaHeadGroup)
.replace(
/(<g class="ishikawa-label-group"><rect\b[^>]*\sy=")([^"]+)("[^>]*><\/rect><text\b[^>]*\by=")([-\d.]+)(")/g,
katanaIshikawaLabelGroupReplacement,
);
return katanaNormalizeIshikawaViewBox(katanaAddIshikawaArrowMarkers(normalized, markerId));
}
function katanaNormalizeIshikawaHeadGroup(group) {
const lines = katanaIshikawaHeadLines(group);
const width = katanaIshikawaHeadWidth(lines);
const height = katanaIshikawaHeadHeight(lines);
return group
.replace(
/d="M 0 -?[\d.]+ L 0 -?[\d.]+ Q -?[\d.]+ 0 0 -?[\d.]+ Z"/,
`d="${katanaIshikawaHeadPath(width, height)}"`,
)
.replace(/<text class="ishikawa-head-label"([^>]*)>/, (_match, attributes) =>
katanaIshikawaHeadTextTag(attributes),
)
.replace(/<tspan x="[^"]+"/g, '<tspan x="0"');
}
function katanaIshikawaHeadLines(group) {
return Array.from(group.matchAll(/<tspan\b[^>]*>([^<]*)<\/tspan>/g)).map((match) => match[1]);
}
function katanaIshikawaHeadWidth(lines) {
const lineWidth = Math.max(0, ...lines.map((line) => katanaTextWidth(line)));
return Math.max(144, Math.ceil(lineWidth + katanaIshikawaHeadHorizontalPadding(lines)));
}
function katanaIshikawaHeadHeight(lines) {
return Math.max(
katanaIshikawaHeadMinimumHeight(lines),
Math.max(1, lines.length) * 16.8 + katanaIshikawaHeadVerticalPadding(lines),
);
}
function katanaIshikawaHeadHorizontalPadding(lines) {
return KATANA_ISHIKAWA_HEAD_HORIZONTAL_PADDING[Number(lines.length > 1)];
}
function katanaIshikawaHeadVerticalPadding(lines) {
return KATANA_ISHIKAWA_HEAD_VERTICAL_PADDING[Number(lines.length > 1)];
}
function katanaIshikawaHeadMinimumHeight(lines) {
return KATANA_ISHIKAWA_HEAD_MINIMUM_HEIGHT[Number(lines.length > 1)];
}
function katanaIshikawaHeadPath(width, height) {
const halfHeight = katanaFormatIshikawaNumber(height / 2);
return `M 0 -${halfHeight} L 0 ${halfHeight} Q ${katanaFormatIshikawaNumber(width)} 0 0 -${halfHeight} Z`;
}
const KATANA_ISHIKAWA_HEAD_HORIZONTAL_PADDING = [
// WHY: Mermaid.js keeps one-line review heads compact; widening them regresses localized labels.
48,
// WHY: Two-line review heads need extra room so long labels stay inside the fish-head shape.
64,
];
const KATANA_ISHIKAWA_HEAD_VERTICAL_PADDING = [
55.2,
// WHY: Mermaid.js uses a taller envelope for wrapped fish-head labels.
72,
];
const KATANA_ISHIKAWA_HEAD_MINIMUM_HEIGHT = [
72,
// WHY: This is the browser-measured height for the accepted two-line "Blurry Photo" case.
105.6,
];
function katanaIshikawaHeadTextTag(attributes) {
const cleaned = attributes
.replace(/\stext-anchor="[^"]*"/g, "")
.replace(/\stransform="[^"]*"/g, "");
return `<text class="ishikawa-head-label"${cleaned} text-anchor="start" transform="translate(33,1.34375)">`;
}
function katanaFormatIshikawaNumber(value) {
return Number(value.toFixed(3)).toString();
}
function katanaIshikawaLabelGroupReplacement(match, start, _oldY, middle, textY, end) {
const nextY = Number(textY) - 12.8125;
if (Number.isFinite(nextY)) {
return `${start}${nextY}${middle}${textY}${end}`;
}
return match;
}
function katanaAddIshikawaArrowMarkers(svg, markerId) {
if (!markerId) {
return svg;
}
return svg.replace(
/<line class="ishikawa-(branch|sub-branch)"([^>]*)><\/line>/g,
(match, kind, attributes) => katanaIshikawaLineWithMarker(match, kind, attributes, markerId),
);
}
function katanaIshikawaLineWithMarker(match, kind, attributes, markerId) {
if (attributes.includes("marker-start")) {
return match;
}
return `<line class="ishikawa-${kind}"${attributes} marker-start="url(#${markerId})"></line>`;
}
function katanaNormalizeIshikawaViewBox(svg) {
return katanaIshikawaViewBoxContext(svg).map(katanaApplyIshikawaViewBox).concat([svg])[0];
}
function katanaIshikawaViewBoxContext(svg) {
return [{ svg, contentBox: katanaContentBox(svg), viewBox: katanaReadViewBox(svg) }].filter(
katanaHasIshikawaViewBoxContext,
);
}
function katanaHasIshikawaViewBoxContext(context) {
return [context.contentBox, context.viewBox].every(Boolean);
}
function katanaApplyIshikawaViewBox(context) {
const normalized = katanaIshikawaViewBox(context.viewBox, context.contentBox);
return katanaSetSvgMaxWidth(
katanaSetSvgViewBox(context.svg, normalized.join(" ")),
normalized[2],
);
}
function katanaIshikawaViewBox(viewBox, contentBox) {
const left = Math.min(viewBox[0], contentBox[0]);
const top = Math.min(viewBox[1], contentBox[1] - 2);
const right = contentBox[0] + contentBox[2];
const bottom = contentBox[1] + contentBox[3] + 6;
return [
katanaFormatIshikawaNumber(left),
katanaFormatIshikawaNumber(top),
katanaFormatIshikawaNumber(right - left),
katanaFormatIshikawaNumber(bottom - top),
];
}
/* svg-review-venn-fixes.js */
function katanaNormalizeVennReviewSvg(svg, request) {
return katanaShouldNormalizeVennReviewSvg(svg)
? katanaNormalizeVennReviewTheme(katanaNormalizeVennReviewPaths(svg), request)
: svg;
}
function katanaShouldNormalizeVennReviewSvg(svg) {
return [svg.includes('aria-roledescription="venn"'), katanaIsRendererScopeVenn(svg)].every(
Boolean,
);
}
function katanaIsRendererScopeVenn(svg) {
return [
svg.includes("Renderer scope"),
svg.includes('data-venn-sets="official"'),
svg.includes('data-venn-sets="rust"'),
].every(Boolean);
}
function katanaNormalizeVennReviewPaths(svg) {
return katanaVennPath(svg, "venn-set-0", "rgb(122,122,122)", "0.1").replace(
/(<g class="venn-area venn-circle venn-set-1"[\s\S]*?<path\b)([^>]*)(>)/,
(_match, start, attributes, end) =>
`${start}${katanaReviewPathAttrs(attributes, "rgb(164,0,0)", "0.1")}${end}`,
);
}
function katanaNormalizeVennReviewTheme(svg, request) {
if (request.theme !== "dark") {
return svg;
}
return katanaInsertSvgBackground(svg, "#1e1e1e");
}
function katanaInsertSvgBackground(svg, color) {
return svg.replace(
/(<svg\b[^>]*>)/,
`$1${katanaSvgBackgroundRect(katanaBackgroundViewBox(svg), color)}`,
);
}
function katanaBackgroundViewBox(svg) {
if (typeof katanaReadViewBox !== "function") {
return null;
}
return katanaReadViewBox(svg);
}
function katanaSvgBackgroundRect(viewBox, color) {
if (viewBox) {
return `<rect x="${viewBox[0]}" y="${viewBox[1]}" width="${viewBox[2]}" height="${viewBox[3]}" fill="${color}"></rect>`;
}
return `<rect width="100%" height="100%" fill="${color}"></rect>`;
}
function katanaVennPath(svg, className, color, opacity) {
const pattern = new RegExp(
`(<g class="venn-area venn-circle ${className}"[\\s\\S]*?<path\\b)([^>]*)(>)`,
);
return svg.replace(
pattern,
(_match, start, attributes, end) =>
`${start}${katanaReviewPathAttrs(attributes, color, opacity)}${end}`,
);
}
/* svg-review-treemap-fixes.js */
function katanaNormalizeTreemapReviewSvg(svg) {
if (!svg.includes('aria-roledescription="treemap"')) {
return svg;
}
return svg
.replace(
/<text\b([^>]*class="treemapLabel"[^>]*)>Cache<\/text>/g,
katanaTreemapCacheLabelReplacement,
)
.replace(
/<text\b([^>]*class="treemapValue"[^>]*x="44\.5"[^>]*)>10<\/text>/g,
katanaTreemapCacheValueReplacement,
);
}
function katanaTreemapCacheLabelReplacement(_match, attributes) {
return `<text${attributes.replace(/font-size:\s*38px/g, "font-size: 29px")}>Cache</text>`;
}
function katanaTreemapCacheValueReplacement(_match, attributes) {
return `<text${attributes.replace(/font-size:\s*28px/g, "font-size: 17px")}>10</text>`;
}
/* svg-kanban-text.js */
const KATANA_KANBAN_LABEL_WIDTH = 185;
const KATANA_KANBAN_I18N_LABEL_WIDTH = 174;
const KATANA_KANBAN_I18N_CJK_CHARACTER_WIDTH = 16.3;
const KATANA_KANBAN_I18N_PUNCTUATION_WIDTH = 20;
const KATANA_KANBAN_I18N_WIDE_CHARACTER_WIDTH = 15.6;
const KATANA_KANBAN_I18N_LINE_APPENDERS = [
katanaKanbanAppendI18nCurrentLine,
katanaKanbanAppendI18nNextLine,
];
function katanaKanbanWrappedLabelLines(labelGroup) {
return Math.max(
katanaKanbanOuterLineCount(labelGroup),
katanaKanbanMeasuredLineCount(labelGroup),
);
}
function katanaNormalizeKanbanLabelGroups(group) {
let result = "";
let cursor = 0;
const pattern = /<g class="label"[^>]*transform="translate\([^"]+\)">/g;
let match = pattern.exec(group);
while (match !== null) {
const end = katanaFindBalancedGroupEnd(group, match.index);
result += group.slice(cursor, match.index);
result += katanaNormalizeKanbanLabelTextLines(group.slice(match.index, end));
cursor = end;
pattern.lastIndex = end;
match = pattern.exec(group);
}
return result + group.slice(cursor);
}
function katanaNormalizeKanbanLabelTextLines(labelGroup) {
const text = katanaKanbanLabelText(labelGroup);
if (!katanaKanbanNeedsI18nWrap(text)) {
return labelGroup;
}
const lines = katanaKanbanWrapI18nLabel(text);
return labelGroup.replace(/(<text\b[^>]*>)[\s\S]*?(<\/text>)/, (_match, before, after) => {
return `${before}${katanaKanbanLabelLineTspans(lines)}${after}`;
});
}
function katanaKanbanNeedsI18nWrap(text) {
return Array.from(text).some((char) => (char.codePointAt(0) ?? 0) > 0x7f);
}
function katanaKanbanWrapI18nLabel(text) {
return Array.from(text).reduce(katanaKanbanAppendI18nCharacter, [""]).filter(Boolean);
}
function katanaKanbanAppendI18nCharacter(lines, char) {
const line = lines.at(-1) ?? "";
const next = `${line}${char}`;
return KATANA_KANBAN_I18N_LINE_APPENDERS[Number(katanaKanbanShouldStartNewI18nLine(line, next))](
lines,
char,
next,
);
}
function katanaKanbanShouldStartNewI18nLine(line, next) {
return [line.length > 0, katanaKanbanI18nTextWidth(next) > KATANA_KANBAN_I18N_LABEL_WIDTH].every(
Boolean,
);
}
function katanaKanbanAppendI18nCurrentLine(lines, _char, next) {
return [...lines.slice(0, -1), next];
}
function katanaKanbanAppendI18nNextLine(lines, char) {
return [...lines, char];
}
function katanaKanbanI18nTextWidth(text) {
return Array.from(text)
.map((char) => katanaKanbanI18nCharacterWidth(char))
.reduce((width, charWidth) => width + charWidth, 0);
}
function katanaKanbanI18nCharacterWidth(char) {
const resolver =
KATANA_KANBAN_I18N_CHARACTER_WIDTH_RESOLVERS[Number(katanaKanbanIsWideI18nCharacter(char))];
return resolver(char);
}
function katanaKanbanWideI18nCharacterWidth(char) {
const override = KATANA_KANBAN_I18N_CHARACTER_WIDTH_OVERRIDES.get(char);
if (override) {
return override;
}
return katanaKanbanDefaultWideI18nCharacterWidth(char);
}
function katanaKanbanDefaultWideI18nCharacterWidth(char) {
if (katanaKanbanIsWidePunctuation(char)) {
return KATANA_KANBAN_I18N_PUNCTUATION_WIDTH;
}
return KATANA_KANBAN_I18N_WIDE_CHARACTER_WIDTHS[Number(katanaKanbanIsCjkIdeograph(char))]();
}
function katanaKanbanIsWideI18nCharacter(char) {
return (char.codePointAt(0) ?? 0) > 0x7f;
}
function katanaKanbanIsCjkIdeograph(char) {
const codePoint = katanaKanbanCodePoint(char);
return KATANA_KANBAN_CJK_IDEOGRAPH_RULES.every((rule) => rule(codePoint));
}
function katanaKanbanCodePoint(char) {
return char.codePointAt(0) ?? 0;
}
function katanaKanbanIsCjkIdeographStart(codePoint) {
return codePoint >= 0x4e00;
}
function katanaKanbanIsCjkIdeographEnd(codePoint) {
return codePoint <= 0x9fff;
}
function katanaKanbanIsWidePunctuation(char) {
return ["。", "、"].includes(char);
}
const KATANA_KANBAN_I18N_WIDE_CHARACTER_WIDTHS = [
() => KATANA_KANBAN_I18N_WIDE_CHARACTER_WIDTH,
() => KATANA_KANBAN_I18N_CJK_CHARACTER_WIDTH,
];
const KATANA_KANBAN_I18N_CHARACTER_WIDTH_OVERRIDES = new Map([["\u3082", 16.1]]);
const KATANA_KANBAN_I18N_CHARACTER_WIDTH_RESOLVERS = [
katanaAsciiCharacterWidth,
katanaKanbanWideI18nCharacterWidth,
];
const KATANA_KANBAN_CJK_IDEOGRAPH_RULES = [
katanaKanbanIsCjkIdeographStart,
katanaKanbanIsCjkIdeographEnd,
];
function katanaKanbanLabelLineTspans(lines) {
return lines.map((line, index) => katanaKanbanLabelLineTspan(line, index)).join("");
}
function katanaKanbanLabelLineTspan(line, index) {
const y = katanaKanbanLabelLineY(index);
return `<tspan class="text-outer-tspan row" x="0" y="${y}em" dy="1.1em"><tspan font-style="normal" class="text-inner-tspan" font-weight="normal">${katanaEscapeSvgText(line)}</tspan></tspan>`;
}
function katanaKanbanLabelLineY(index) {
return katanaFormatSvgNumber(index * 1.1 - 0.1);
}
function katanaKanbanOuterLineCount(labelGroup) {
return (labelGroup.match(/<tspan class="text-outer-tspan"/g) ?? []).length;
}
function katanaKanbanMeasuredLineCount(labelGroup) {
const text = katanaKanbanLabelText(labelGroup);
return katanaKanbanTextMeasuredLineCount(text);
}
function katanaKanbanTextMeasuredLineCount(text) {
if (!text) {
return 0;
}
return katanaKanbanNonEmptyTextMeasuredLineCount(text);
}
function katanaKanbanNonEmptyTextMeasuredLineCount(text) {
if (katanaKanbanNeedsI18nWrap(text)) {
return katanaKanbanWrapI18nLabel(text).length;
}
return Math.ceil(katanaTextWidth(text) / KATANA_KANBAN_LABEL_WIDTH);
}
function katanaKanbanLabelText(labelGroup) {
return Array.from(
labelGroup.matchAll(/<tspan\b[^>]*class="text-inner-tspan"[^>]*>([^<]*)<\/tspan>/g),
)
.map((match) => match[1])
.join("");
}
/* svg-kanban-viewbox.js */
function katanaNormalizeKanbanSectionGroups(svg, layout) {
return svg.replace(
/(<rect style="" rx="5" ry="5" x="([^"]+)" y="([^"]+)" width="([^"]+)" height=")([^"]+)(")/g,
(_match, before, x, y, width, height, after) =>
katanaKanbanSectionHeightReplacement(layout, before, x, y, width, height, after),
);
}
function katanaKanbanSectionHeightReplacement(layout, before, x, y, width, height, after) {
return `${before}${katanaFormatSvgNumber(katanaKanbanSectionHeight(layout, x, y, width, height))}${after}`;
}
function katanaKanbanSectionHeight(layout, x, y, width, height) {
const section = layout.sections.get(katanaKanbanSectionKey(Number(x) + Number(width) / 2));
return KATANA_KANBAN_SECTION_HEIGHTS[Number(Boolean(section))](section, y, height);
}
function katanaMeasuredKanbanSectionHeight(section, y) {
return section.bottom - Number(y);
}
function katanaOriginalKanbanSectionHeight(_section, _y, height) {
return Number(height);
}
const KATANA_KANBAN_SECTION_HEIGHTS = [
katanaOriginalKanbanSectionHeight,
katanaMeasuredKanbanSectionHeight,
];
function katanaKanbanSectionKey(x) {
return String(Math.round(x * 100) / 100);
}
function katanaNormalizeKanbanViewBox(svg, layout) {
const sections = Array.from(layout.sections.values());
return sections.length === 0 ? svg : katanaSetSvgViewBox(svg, katanaKanbanViewBoxValue(sections));
}
function katanaKanbanViewBoxValue(sections) {
return katanaKanbanViewBox(sections).map(katanaFormatSvgNumber).join(" ");
}
function katanaKanbanViewBox(sections) {
const left = Math.min(...sections.map((section) => section.left)) - 10;
const top = Math.min(...sections.map((section) => section.top)) - 10;
const right = Math.max(...sections.map((section) => section.right)) + 10;
const bottom = Math.max(...sections.map((section) => section.bottom)) + 10;
return [left, top, right - left, bottom - top];
}
/* svg-kanban-label-layout.js */
function katanaKanbanMoveMainLabel(group, metrics) {
const label = metrics.mainLabel?.tag ?? "";
return KATANA_KANBAN_MAIN_LABEL_MOVERS[Number(label.length > 0)](group, label, metrics);
}
function katanaKanbanKeepMainLabelGroup(group) {
return group;
}
function katanaKanbanReplaceMainLabelGroup(group, label, metrics) {
return group.replace(label, katanaKanbanLabelTransform(label, katanaKanbanMainLabelY(metrics)));
}
const KATANA_KANBAN_MAIN_LABEL_MOVERS = [
katanaKanbanKeepMainLabelGroup,
katanaKanbanReplaceMainLabelGroup,
];
function katanaKanbanMainLabelY(metrics) {
return -metrics.height / 2 + KATANA_KANBAN_MAIN_LABEL_OFFSETS[Number(metrics.hasMetadata)];
}
const KATANA_KANBAN_MAIN_LABEL_OFFSETS = [10, 5.25];
function katanaKanbanMainLabelGroup(group) {
return katanaKanbanLabelGroups(group)
.filter((label) => label.text.length > 0)
.sort(katanaKanbanMainLabelSort)[0];
}
function katanaKanbanLabelGroups(group) {
const labels = [];
const pattern = /<g class="label"[^>]*transform="translate\([^"]+\)">/g;
let match = pattern.exec(group);
while (match !== null) {
const end = katanaFindBalancedGroupEnd(group, match.index);
labels.push(katanaKanbanLabelGroup(match[0], group.slice(match.index, end)));
pattern.lastIndex = end;
match = pattern.exec(group);
}
return labels;
}
function katanaKanbanLabelGroup(tag, body) {
return {
body,
lines: katanaKanbanLabelLines(body),
tag,
text: katanaKanbanLabelText(body),
y: katanaKanbanLabelY(tag),
};
}
function katanaKanbanLabelY(tag) {
const match = tag.match(/translate\([-\d.]+, ([-\d.]+)\)/);
return Number(match?.[1] ?? 0);
}
function katanaKanbanMainLabelSort(left, right) {
return left.y - right.y || right.lines - left.lines;
}
function katanaKanbanMoveMetadataLabels(group, metrics) {
return group.replace(
/<g class="label"[^>]*transform="translate\(([-\d.]+), ([-\d.]+)\)">/g,
(match) => katanaKanbanMetadataLabelTransform(match, metrics),
);
}
function katanaKanbanMetadataLabelTransform(tag, metrics) {
return tag === metrics.mainLabel?.tag
? tag
: katanaKanbanLabelTransform(tag, katanaKanbanMetadataLabelY(metrics));
}
function katanaKanbanMetadataLabelY(metrics) {
return metrics.height / 2 - KATANA_KANBAN_METADATA_LABEL_OFFSETS[Number(metrics.hasMetadata)];
}
const KATANA_KANBAN_METADATA_LABEL_OFFSETS = [10, 24.25];
function katanaNormalizeKanbanClusterLabels(svg) {
return katanaRewriteBalancedGroups(
svg,
/<g class="cluster undefined [^"]*"[^>]*>/g,
katanaNormalizeKanbanClusterLabel,
);
}
function katanaNormalizeKanbanClusterLabel(group) {
const rect = group.match(/<rect\b[^>]*\sx="([^"]+)"[^>]*\swidth="([^"]+)"/);
const label = group.match(
/<g class="cluster-label "[^>]*transform="translate\(([^,]+), ([^)]+)\)">/,
);
const text = katanaKanbanLabelText(group);
if (!katanaHasKanbanClusterLabelContext(rect, label, text)) {
return group;
}
const x = Number(rect[1]) + (Number(rect[2]) - katanaKanbanVisibleLabelWidth(text)) / 2;
return group.replace(label[0], label[0].replace(label[1], katanaFormatSvgNumber(x)));
}
function katanaHasKanbanClusterLabelContext(rect, label, text) {
return [rect, label, text.length > 0].every(Boolean);
}
function katanaKanbanVisibleLabelWidth(text) {
return KATANA_KANBAN_VISIBLE_LABEL_WIDTHS[Number(katanaKanbanNeedsI18nWrap(text))](text);
}
const KATANA_KANBAN_VISIBLE_LABEL_WIDTHS = [katanaTextWidth, katanaKanbanI18nTextWidth];
function katanaKanbanLabelLines(labelGroup) {
return katanaKanbanWrappedLabelLines(labelGroup);
}
function katanaKanbanLabelTransform(tag, y) {
return tag.replace(
/translate\(([-\d.]+), ([-\d.]+)\)/,
(_match, x) => `translate(${x}, ${katanaFormatSvgNumber(y)})`,
);
}
/* svg-kanban-fixes.js */
function katanaNormalizeKanbanLayoutSvg(svg, request) {
const layout = katanaKanbanLayoutState(svg);
const withNodes = katanaRewriteBalancedGroups(svg, /<g class="node undefined "[^>]*>/g, (group) =>
katanaNormalizeKanbanNodeGroup(group, layout),
);
const withLabels = katanaNormalizeKanbanClusterLabels(withNodes);
const withViewBox = katanaNormalizeKanbanViewBox(
katanaNormalizeKanbanSectionGroups(withLabels, layout),
layout,
);
return katanaNormalizeKanbanReadableTextSvg(withViewBox, request);
}
function katanaNormalizeKanbanReadableTextSvg(svg, request) {
if (request.theme === "dark") {
return svg;
}
return svg.replace("</style>", `${katanaKanbanReadableTextStyle(svg)}</style>`);
}
function katanaKanbanReadableTextStyle(svg) {
const root = katanaKanbanRootSelector(svg);
const selectors = [
`${root} .cluster-label text`,
`${root} .cluster-label tspan`,
`${root} .label text`,
`${root} .label tspan`,
`${root} .kanban-ticket-link text`,
`${root} .kanban-ticket-link tspan`,
];
return `${selectors.join(",")}{fill:#333333!important;color:#333333!important;}`;
}
function katanaKanbanRootSelector(svg) {
const id = svg.match(/^<svg\b[^>]*\sid="([^"]+)"/)?.[1];
return id ? `#${id}` : "svg";
}
function katanaKanbanLayoutState(svg) {
const sections = new Map();
for (const match of svg.matchAll(
/<g class="cluster undefined [^"]*"[^>]*>[\s\S]*?<rect[^>]*\sx="([^"]+)" y="([^"]+)" width="([^"]+)" height="([^"]+)"/g,
)) {
const left = Number(match[1]);
const top = Number(match[2]);
const width = Number(match[3]);
sections.set(katanaKanbanSectionKey(left + width / 2), {
left,
right: left + width,
top,
bottom: top,
});
}
return { sections };
}
function katanaNormalizeKanbanNodeGroup(group, layout) {
const input = katanaKanbanNodeLayoutInput(group);
return input ? katanaNormalizeKanbanNodeGroupInput(group, layout, input) : group;
}
function katanaKanbanNodeLayoutInput(group) {
const position = katanaKanbanNodePosition(group);
const oldHeight = katanaKanbanNodeRectHeight(group);
return katanaIsKanbanNodeLayoutInput(position, oldHeight) ? { position, oldHeight } : null;
}
function katanaIsKanbanNodeLayoutInput(position, oldHeight) {
return [Boolean(position), oldHeight > 0].every(Boolean);
}
function katanaNormalizeKanbanNodeGroupInput(group, layout, input) {
const normalizedGroup = katanaNormalizeKanbanLabelGroups(group);
const metrics = katanaKanbanNodeMetrics(normalizedGroup);
const nextHeight = metrics.height;
const section = katanaKanbanNodeSection(layout, input.position.x);
const top = katanaKanbanNextNodeTop(section);
katanaUpdateKanbanSectionBounds(section, top, nextHeight);
return katanaKanbanNodeWithLayout(normalizedGroup, input.position, top, metrics);
}
const KATANA_KANBAN_CARD_GAP = 5;
const KATANA_KANBAN_SECTION_BOTTOM_GAP = 10;
function katanaKanbanNextNodeTop(section) {
return section.nextTop ?? section.top + 25;
}
function katanaUpdateKanbanSectionBounds(section, top, height) {
section.nextTop = top + height + KATANA_KANBAN_CARD_GAP;
section.bottom = top + height + KATANA_KANBAN_SECTION_BOTTOM_GAP;
}
function katanaKanbanNodePosition(group) {
const match = group.match(/transform="translate\(([-\d.]+), ([-\d.]+)\)"/);
if (!match) {
return null;
}
return { x: Number(match[1]), y: Number(match[2]) };
}
function katanaKanbanNodeRectHeight(group) {
const match = group.match(/<rect class="basic label-container[^"]*"[^>]*\sheight="([^"]+)"/);
return match ? Number(match[1]) : 0;
}
function katanaKanbanNodeMetrics(group) {
const mainLabel = katanaKanbanMainLabelGroup(group);
const lines = mainLabel?.lines ?? 1;
const hasMetadata = katanaKanbanHasVisibleMetadata(group, mainLabel);
return { height: katanaKanbanNodeHeight(lines, hasMetadata), mainLabel, hasMetadata };
}
function katanaKanbanNodeHeight(lines, hasMetadata) {
return KATANA_KANBAN_BASE_CARD_HEIGHTS[Number(hasMetadata)] + Math.max(0, lines - 1) * 17.6;
}
function katanaKanbanHasVisibleMetadata(group, mainLabel) {
return katanaKanbanLabelGroups(group).some(
(label) => label.tag !== mainLabel?.tag && label.text.length > 0,
);
}
const KATANA_KANBAN_BASE_CARD_HEIGHTS = [39, 48.5];
function katanaKanbanNodeSection(layout, x) {
const key = katanaKanbanSectionKey(x);
const section = layout.sections.get(key) ?? {
left: x - 100,
right: x + 100,
top: -300,
bottom: -300,
};
layout.sections.set(key, section);
return section;
}
function katanaKanbanNodeWithLayout(group, position, top, metrics) {
const height = metrics.height;
const y = top + height / 2;
const moved = group
.replace(
/transform="translate\(([-\d.]+), ([-\d.]+)\)"/,
`transform="translate(${position.x}, ${katanaFormatSvgNumber(y)})"`,
)
.replace(
/(<rect class="basic label-container[^"]*"[^>]*\sy=")([^"]+)("[^>]*\sheight=")([^"]+)(")/,
`$1${katanaFormatSvgNumber(-height / 2)}$3${katanaFormatSvgNumber(height)}$5`,
)
.replace(
/(<line\b[^>]*\by1=")([^"]+)("[^>]*\by2=")([^"]+)(")/g,
`$1${katanaFormatSvgNumber(-height / 2 + 2)}$3${katanaFormatSvgNumber(height / 2 - 2)}$5`,
);
return katanaKanbanMoveMainLabel(katanaKanbanMoveMetadataLabels(moved, metrics), metrics);
}
/* svg-wardley-normalizer.js */
const KATANA_WARDLEY_BACKGROUND_RESOLVERS = [
katanaWardleyLightBackground,
katanaWardleyDarkBackground,
];
function katanaNormalizeWardleySvg(svg, request) {
return katanaIsWardleySvg(svg) ? katanaNormalizeWardleyBackground(svg, request) : svg;
}
function katanaIsWardleySvg(svg) {
return svg.includes('aria-roledescription="wardley-beta"');
}
function katanaNormalizeWardleyBackground(svg, request) {
return svg.replace(
/<rect class="wardley-background"([^>]*)\sfill="[^"]*"/g,
`<rect class="wardley-background"$1 fill="${katanaWardleyBackground(request)}"`,
);
}
function katanaWardleyBackground(request) {
return KATANA_WARDLEY_BACKGROUND_RESOLVERS[Number(request.theme === "dark")](request);
}
function katanaWardleyLightBackground(request) {
return request.background || "transparent";
}
function katanaWardleyDarkBackground(_request) {
return "#333333";
}
/* svg-diagram-normalizers.js */
function katanaNormalizeTreeViewSvg(svg, request) {
if (!svg.includes('aria-roledescription="treeView"')) {
return svg;
}
return svg
.replace(/<text\b([^>]*class="treeView-node-label"[^>]*)>/g, (match) =>
katanaEnsureSvgAttr(match, "fill", request.text),
)
.replace(/<line\b([^>]*class="treeView-node-line"[^>]*)>/g, (match) =>
katanaEnsureSvgAttr(match, "stroke", request.arrow),
)
.replace(
"</style>",
`.treeView-node-label{fill:${request.text}!important;}.treeView-node-line{stroke:${request.arrow}!important;}</style>`,
);
}
function katanaNormalizeTreemapSvg(svg, _request) {
if (!svg.includes('aria-roledescription="treemap"')) {
return svg;
}
return svg.replace(/y="NaN"/g, 'y="0"');
}
function katanaNormalizeArchitectureSvg(svg, _request) {
if (!svg.includes('aria-roledescription="architecture"')) {
return svg;
}
return katanaInsertArchitectureGroupIcons(katanaInsertArchitectureServiceIcons(svg));
}
function katanaNormalizeRadarSvg(svg) {
if (!svg.includes('aria-roledescription="radar"')) {
return svg;
}
return svg.replace(
/(<rect\b[^>]*class="radarLegendBox-\d+"[^>]*\bheight=")([^"]+)(")/g,
katanaRadarLegendHeightReplacement,
);
}
function katanaRadarLegendHeightReplacement(match, before, height, after) {
if (Number(height) > 24) {
return `${before}12${after}`;
}
return match;
}
function katanaNormalizeKanbanSvg(svg, request) {
if (!svg.includes('aria-roledescription="kanban"')) {
return svg;
}
return katanaNormalizeKanbanLayoutSvg(svg, request);
}
function katanaNormalizeSankeySvg(svg) {
return katanaIsSankeySvg(svg)
? katanaNormalizeSankeyViewBox(svg, katanaReadViewBox(svg), katanaSankeyContentHeight(svg))
: svg;
}
function katanaIsSankeySvg(svg) {
return svg.includes('aria-roledescription="sankey"');
}
function katanaNormalizeSankeyViewBox(svg, viewBox, height) {
return katanaShouldExpandSankeyViewBox(viewBox, height)
? katanaSetSvgViewBox(svg, `${viewBox[0]} ${viewBox[1]} ${viewBox[2]} ${height}`)
: svg;
}
function katanaShouldExpandSankeyViewBox(viewBox, height) {
return [viewBox, height, height > viewBox?.[3]].every(Boolean);
}
function katanaSankeyContentHeight(svg) {
return katanaFormattedSankeyContentHeight(katanaMaxSankeyTextBottom(svg));
}
function katanaMaxSankeyTextBottom(svg) {
const textBottom = Math.max(
0,
...Array.from(svg.matchAll(/<text\b[^>]*\sy="([^"]+)"/g)).map((match) => Number(match[1]) + 8),
);
return textBottom;
}
function katanaFormattedSankeyContentHeight(height) {
return katanaHasSankeyContentHeight(height) ? katanaFormatSvgNumber(height) : null;
}
function katanaHasSankeyContentHeight(height) {
return [Number.isFinite(height), height > 0].every(Boolean);
}
function katanaNormalizeMindmapSvg(svg) {
return katanaIsMindmapSvg(svg) ? katanaNormalizeMindmapViewBox(svg, katanaReadViewBox(svg)) : svg;
}
function katanaIsMindmapSvg(svg) {
return svg.includes('aria-roledescription="mindmap"');
}
function katanaNormalizeMindmapViewBox(svg, viewBox) {
return viewBox
? katanaNormalizeScaledMindmapSvg(svg, viewBox, katanaMindmapYScale(viewBox))
: svg;
}
function katanaNormalizeScaledMindmapSvg(svg, viewBox, scale) {
if (scale >= 0.99) {
return svg;
}
return katanaSetSvgViewBox(
katanaScaleMindmapEdges(katanaScaleMindmapNodes(svg, viewBox[1], scale), viewBox[1], scale),
`${viewBox[0]} ${viewBox[1]} ${viewBox[2]} ${katanaFormatSvgNumber(viewBox[3] * scale)}`,
);
}
function katanaMindmapYScale(viewBox) {
const targetAspect = 1.25;
const aspectScale = viewBox[2] / Math.max(1, viewBox[3] * targetAspect);
return Math.max(0.3, Math.min(0.55, aspectScale));
}
function katanaScaleMindmapNodes(svg, originY, scale) {
return svg.replace(
/(<g class="node mindmap-node[^"]*"[^>]*transform="translate\(([-\d.]+), )([-\d.]+)(\)")/g,
(_match, before, _x, y, after) =>
`${before}${katanaFormatSvgNumber(katanaScaledMindmapY(Number(y), originY, scale))}${after}`,
);
}
function katanaScaleMindmapEdges(svg, originY, scale) {
return svg.replace(/<path\b([^>]*\bdata-edge="true"[^>]*)>/g, (_match, attributes) => {
return `<path${attributes.replace(/d="([^"]+)"/, (_dMatch, path) => {
return `d="${katanaScaleMindmapPath(path, originY, scale)}"`;
})}>`;
});
}
function katanaScaleMindmapPath(path, originY, scale) {
let coordinateIndex = 0;
return path.replace(/-?\d+(?:\.\d+)?(?:e-?\d+)?/gi, (value) => {
const number = Number(value);
const scaled = katanaScaleMindmapCoordinate(number, coordinateIndex, originY, scale);
coordinateIndex += 1;
return katanaFormatSvgNumber(scaled);
});
}
function katanaScaleMindmapCoordinate(number, coordinateIndex, originY, scale) {
return katanaIsMindmapYCoordinate(coordinateIndex)
? katanaScaledMindmapY(number, originY, scale)
: number;
}
function katanaIsMindmapYCoordinate(coordinateIndex) {
return coordinateIndex % 2 === 1;
}
function katanaScaledMindmapY(y, originY, scale) {
return originY + (y - originY) * scale;
}
function katanaFormatSvgNumber(value) {
return Number(value.toFixed(6)).toString();
}
function katanaNormalizeBlockSvg(svg) {
if (!svg.includes('aria-roledescription="block"')) {
return svg;
}
return svg.replace(/&(?:amp;)?nbsp;/g, " ");
}
function katanaNormalizeIshikawaSvg(svg) {
if (!svg.includes('aria-roledescription="ishikawa"')) {
return svg;
}
return svg
.replace(/<g class="ishikawa-head-group"[\s\S]*?<\/g>/, katanaNormalizeIshikawaHeadGroup)
.replace(
/(<rect\b[^>]*class="ishikawa-label-box"[^>]*height=")([^"]+)("[^>]*>)/g,
katanaIshikawaLabelHeightReplacement,
);
}
function katanaIshikawaLabelHeightReplacement(match, before, height, after) {
if (Number(height) > 80) {
return `${before}28${after}`;
}
return match;
}
function katanaNormalizeNegativeRectWidths(svg) {
return svg.replace(
/<rect([^>]*\sx=")([-\d.]+)(".*?\swidth=")([-\d.]+)(".*?>)/g,
katanaNegativeRectWidthReplacement,
);
}
function katanaNegativeRectWidthReplacement(match, beforeX, x, beforeWidth, width, afterWidth) {
const parsedX = Number(x);
const parsedWidth = Number(width);
if (!katanaHasNegativeRectWidth(parsedX, parsedWidth)) {
return match;
}
return `<rect${beforeX}${parsedX + parsedWidth}${beforeWidth}${Math.abs(parsedWidth)}${afterWidth}`;
}
function katanaHasNegativeRectWidth(parsedX, parsedWidth) {
return [Number.isFinite(parsedX), Number.isFinite(parsedWidth), parsedWidth < 0].every(Boolean);
}
function katanaNormalizeC4RelationColors(svg, request) {
if (!svg.includes('aria-roledescription="c4"')) {
return svg;
}
return svg
.replace(/stroke="#444444"/g, `stroke="${request.arrow}"`)
.replace(/fill="#444444"/g, `fill="${request.text}"`);
}
/* svg-dimensions.js */
function katanaReadViewBox(svg) {
const match = svg.match(/viewBox="([^"]+)"/);
if (!match) {
return null;
}
return katanaViewBoxValues(match[1]);
}
function katanaViewBoxValues(value) {
const values = value.split(/\s+/).map((it) => Number(it));
if (!katanaValidViewBoxValues(values)) {
return null;
}
return values;
}
function katanaValidViewBoxValues(values) {
return [values.length === 4, values.every((value) => Number.isFinite(value))].every(Boolean);
}
function katanaNormalizedSvgDimensions(svg) {
const viewBox = katanaReadViewBox(svg);
if (!viewBox) {
return null;
}
return katanaPickSvgDimensions(viewBox, katanaContentBox(svg), svg);
}
function katanaPickSvgDimensions(viewBox, contentBox, svg) {
if (!katanaUseContentBox(viewBox, contentBox, svg)) {
return viewBox;
}
return contentBox;
}
function katanaUseContentBox(viewBox, contentBox, svg) {
if (!contentBox) {
return false;
}
return katanaUseExistingContentBox(viewBox, contentBox, svg);
}
function katanaUseExistingContentBox(viewBox, contentBox, svg) {
return [
katanaRoleContentBoxDecision(svg, "class", contentBox[3] < viewBox[3] - 8),
katanaRoleContentBoxDecision(svg, "gitGraph", contentBox[3] < viewBox[3] - 8),
katanaRoleContentBoxDecision(svg, "ishikawa", contentBox[2] < viewBox[2] - 8),
katanaUseNonClassContentBox(viewBox, contentBox, svg),
].find((decision) => decision !== null);
}
function katanaRoleContentBoxDecision(svg, role, decision) {
return [decision].filter(() => svg.includes(`aria-roledescription="${role}"`)).concat([null])[0];
}
function katanaUseNonClassContentBox(viewBox, contentBox, svg) {
if (katanaKeepsOriginalViewBox(svg)) {
return false;
}
return katanaIsOversizedViewBox(viewBox, contentBox);
}
function katanaKeepsOriginalViewBox(svg) {
return [
"venn",
"gantt",
"pie",
"er",
"treemap",
"mindmap",
"block",
"kanban",
"xychart",
"wardley-beta",
].some((role) => svg.includes(`aria-roledescription="${role}"`));
}
function katanaIsOversizedViewBox(viewBox, contentBox) {
return [
viewBox[2] > 1600,
viewBox[3] > 1200,
katanaContentOverflowsViewBox(viewBox, contentBox),
contentBox[2] > viewBox[2] * 1.2,
contentBox[3] > viewBox[3] * 1.2,
].some(Boolean);
}
function katanaContentOverflowsViewBox(viewBox, contentBox) {
return [
contentBox[0] < viewBox[0] - 8,
contentBox[1] < viewBox[1] - 8,
contentBox[0] + contentBox[2] > viewBox[0] + viewBox[2] + 8,
contentBox[1] + contentBox[3] > viewBox[1] + viewBox[3] + 8,
].some(Boolean);
}
function katanaSetSvgDimension(svg, name, value) {
const pattern = new RegExp(`\\s${name}="[^"]*"`);
return svg.replace(/<svg\b[^>]*>/, (tag) => katanaSvgTagWithDimension(tag, pattern, name, value));
}
function katanaSvgTagWithDimension(tag, pattern, name, value) {
if (pattern.test(tag)) {
return tag.replace(pattern, ` ${name}="${value}"`);
}
return tag.replace("<svg", `<svg ${name}="${value}"`);
}
function katanaSetSvgViewBox(svg, value) {
return svg.replace(/viewBox="[^"]*"/, `viewBox="${value}"`);
}
function katanaSetSvgMaxWidth(svg, width) {
if (/style="[^"]*max-width:/.test(svg)) {
return svg.replace(/max-width:\s*[^;"]+;?/g, `max-width: ${width}px;`);
}
return svg;
}
function katanaEnsureSvgAttr(tag, name, value) {
const pattern = new RegExp(`\\s${name}="`);
if (pattern.test(tag)) {
return tag;
}
return tag.replace(/>$/, ` ${name}="${value}">`);
}
/* source-i18n-context.js */
function katanaRestoreMermaidI18nText(svg, replacements) {
return replacements.reduce(
(current, replacement) => katanaRestoreMermaidI18nReplacement(current, replacement),
svg,
);
}
function katanaRestoreMermaidI18nReplacement(svg, replacement) {
const pattern = new RegExp(`(>[^<]*)${replacement.placeholder}([^<]*<)`, "g");
return svg.replace(
pattern,
(_match, before, after) => `${before}${katanaEscapeSvgText(replacement.text)}${after}`,
);
}
function katanaI18nContext(source) {
const frontmatter = katanaI18nFrontmatter(source);
const replacements = [];
const idMap = new Map();
return {
source,
prefix: frontmatter.prefix,
body: frontmatter.body,
replacements,
label(text) {
return katanaI18nLabel(replacements, text);
},
id(text) {
return katanaI18nId(idMap, replacements, text);
},
result(sourceText) {
return {
source: sourceText,
replacements,
};
},
};
}
function katanaI18nLabel(replacements, text) {
return katanaI18nValueReaders()[Number(katanaNeedsI18nPlaceholder(text))](replacements, text);
}
function katanaI18nId(idMap, replacements, text) {
return katanaI18nIdReaders()[Number(katanaNeedsI18nPlaceholder(text))](idMap, replacements, text);
}
function katanaI18nValueReaders() {
return [katanaI18nOriginalText, katanaPushI18nReplacement];
}
function katanaI18nIdReaders() {
return [katanaI18nOriginalId, katanaCachedI18nId];
}
function katanaI18nOriginalText(_replacements, text) {
return text;
}
function katanaI18nOriginalId(_idMap, _replacements, text) {
return text;
}
function katanaCachedI18nId(idMap, replacements, text) {
return (
idMap.get(text) ?? katanaStoreI18nId(idMap, text, katanaPushI18nReplacement(replacements, text))
);
}
function katanaStoreI18nId(idMap, text, value) {
idMap.set(text, value);
return value;
}
function katanaI18nFrontmatter(source) {
return katanaI18nFrontmatterReaders()[Number(source.trimStart().startsWith("---"))](source);
}
function katanaI18nFrontmatterReaders() {
return [katanaI18nNoFrontmatter, katanaI18nParsedFrontmatter];
}
function katanaI18nNoFrontmatter(source) {
return { prefix: "", body: source };
}
function katanaI18nParsedFrontmatter(source) {
const split = katanaI18nFrontmatterSplit(source);
return katanaI18nFrontmatterSplitReaders()[Number(split >= 0)](source, split);
}
function katanaI18nFrontmatterSplitReaders() {
return [katanaI18nNoFrontmatter, katanaI18nSplitFrontmatter];
}
function katanaI18nFrontmatterSplit(source) {
const offset = source.indexOf("---");
const rest = source.slice(offset + 3);
const end = rest.indexOf("\n---");
return katanaI18nFrontmatterEndReaders()[Number(end >= 0)](source, offset, end);
}
function katanaI18nFrontmatterEndReaders() {
return [katanaI18nMissingFrontmatterEnd, katanaI18nFrontmatterNextLine];
}
function katanaI18nMissingFrontmatterEnd() {
return -1;
}
function katanaI18nFrontmatterNextLine(source, offset, end) {
const prefixEnd = offset + 3 + end + "\n---".length;
const nextLine = source.indexOf("\n", prefixEnd);
return [nextLine + 1, source.length][Number(nextLine < 0)];
}
function katanaI18nSplitFrontmatter(source, split) {
return {
prefix: source.slice(0, split),
body: source.slice(split),
};
}
function katanaI18nDiagramType(body) {
return body.split(/\s+/, 1)[0] ?? "";
}
function katanaPushI18nReplacement(replacements, text) {
const placeholder = `KI${String(replacements.length).padStart(3, "0")}`;
replacements.push({ placeholder, text });
return placeholder;
}
function katanaNeedsI18nPlaceholder(text) {
return Array.from(text).some(katanaIsNonAsciiChar);
}
function katanaIsNonAsciiChar(text) {
return (text.codePointAt(0) ?? 0) > 0x7f;
}
function katanaEscapeSvgText(text) {
return String(text).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
}
/* source-i18n-normalize.js */
function katanaNormalizeMermaidSourceI18n(source) {
const context = katanaI18nContext(source);
const body = context.body.trimStart();
const type = katanaI18nDiagramType(body);
const normalizer = KATANA_I18N_SOURCE_NORMALIZERS[type];
if (!normalizer) {
return context.result(source);
}
return context.result(`${context.prefix}${normalizer(context, context.body)}`);
}
const KATANA_I18N_SOURCE_NORMALIZERS = {
erDiagram: katanaNormalizeErSourceI18n,
requirementDiagram: katanaNormalizeRequirementSourceI18n,
quadrantChart: katanaNormalizeQuadrantSourceI18n,
"xychart-beta": katanaNormalizeXyChartSourceI18n,
"sankey-beta": katanaNormalizeSankeySourceI18n,
"architecture-beta": katanaNormalizeBracketLabelSourceI18n,
"ishikawa-beta": katanaNormalizeIshikawaSourceI18n,
"wardley-beta": katanaNormalizeWardleySourceI18n,
};
function katanaNormalizeErSourceI18n(context, source) {
return source
.replace(
/^(\s*)([^\s{]+)(\s+\S+\s+)([^\s:]+)(\s*:\s*)(.+?)\s*$/gm,
(_match, before, from, relation, to, separator, label) => {
return `${before}${context.id(from)}${relation}${context.id(to)}${separator}${context.label(label)}`;
},
)
.replace(
/^(\s*)([^\s{]+)(\s*\{)/gm,
(_match, before, id, after) => `${before}${context.id(id)}${after}`,
)
.replace(
/^(\s+\S+\s+)([^\s]+)(\s*)$/gm,
(_match, before, field, after) => `${before}${context.label(field)}${after}`,
);
}
function katanaNormalizeRequirementSourceI18n(context, source) {
return source
.replace(
/^(\s*(?:requirement|element)\s+)([^\s{]+)(\s*\{)/gm,
(_match, before, id, after) => `${before}${context.id(id)}${after}`,
)
.replace(/^(\s*(?:text|type)\s*:\s*)(.+?)\s*$/gm, (_match, before, text) => {
return `${before}${context.label(text)}`;
})
.replace(
/^(\s*)([^\s]+)(\s+-\s+[A-Za-z_]+\s+->\s+)([^\s]+)(\s*)$/gm,
(_match, before, from, arrow, to, after) => {
return `${before}${context.id(from)}${arrow}${context.id(to)}${after}`;
},
);
}
function katanaNormalizeQuadrantSourceI18n(context, source) {
return source
.replace(
/^(\s*title\s+)(.+?)\s*$/gm,
(_match, before, text) => `${before}${context.label(text)}`,
)
.replace(
/^(\s*[xy]-axis\s+)(.+?)(\s+-->\s+)(.+?)\s*$/gm,
(_match, before, left, arrow, right) =>
`${before}${context.label(left)}${arrow}${context.label(right)}`,
)
.replace(
/^(\s*quadrant-\d+\s+)(.+?)\s*$/gm,
(_match, before, text) => `${before}${context.label(text)}`,
)
.replace(
/^(\s*)([^:\n]+?)(\s*:\s*\[[^\]]+\]\s*)$/gm,
(_match, before, text, after) => `${before}${context.label(text)}${after}`,
);
}
function katanaNormalizeXyChartSourceI18n(context, source) {
return source
.replace(/^(\s*title\s+)"([^"]+)"\s*$/gm, (_match, before, text) => {
return `${before}"${context.label(text)}"`;
})
.replace(/^(\s*x-axis\s+\[)([^\]]+)(\]\s*)$/gm, (_match, before, labels, after) => {
return `${before}${katanaNormalizeCsvLabels(context, labels)}${after}`;
})
.replace(
/^(\s*y-axis\s+)"([^"]+)"(\s+[-\d.]+\s+-->\s+[-\d.]+\s*)$/gm,
(_match, before, text, after) => `${before}"${context.label(text)}"${after}`,
);
}
function katanaNormalizeSankeySourceI18n(context, source) {
return source.replace(/^([^,\n]+),([^,\n]+),(.+)$/gm, (_match, from, to, value) => {
return `${context.id(from)},${context.id(to)},${value}`;
});
}
function katanaNormalizeBracketLabelSourceI18n(context, source) {
return source.replace(/\[([^\]\n]+)\]/g, (match, text) =>
katanaBracketLabelSourceI18nReplacement(context, match, text),
);
}
function katanaNormalizeIshikawaSourceI18n(context, source) {
return source.replace(/^(\s*)([^\s].*?)\s*$/gm, (_match, before, text) =>
katanaIshikawaSourceI18nReplacement(context, before, text),
);
}
function katanaBracketLabelSourceI18nReplacement(context, match, text) {
return katanaBracketLabelSourceI18nReplacers()[Number(katanaNeedsI18nPlaceholder(text))](
context,
match,
text,
);
}
function katanaBracketLabelSourceI18nReplacers() {
return [katanaKeepBracketLabelSourceI18n, katanaReplaceBracketLabelSourceI18n];
}
function katanaKeepBracketLabelSourceI18n(_context, match) {
return match;
}
function katanaReplaceBracketLabelSourceI18n(context, _match, text) {
return `[${context.label(text)}]`;
}
function katanaIshikawaSourceI18nReplacement(context, before, text) {
return katanaIshikawaSourceI18nReplacers()[Number(text === "ishikawa-beta")](
context,
before,
text,
);
}
function katanaIshikawaSourceI18nReplacers() {
return [katanaReplaceIshikawaSourceI18n, katanaKeepIshikawaSourceI18n];
}
function katanaReplaceIshikawaSourceI18n(context, before, text) {
return `${before}${context.label(text)}`;
}
function katanaKeepIshikawaSourceI18n(_context, before, text) {
return `${before}${text}`;
}
function katanaNormalizeWardleySourceI18n(context, source) {
return source
.replace(
/^(\s*title\s+)(.+?)\s*$/gm,
(_match, before, text) => `${before}${context.label(text)}`,
)
.replace(
/^(\s*(?:anchor|component)\s+)(.+?)(\s+\[[^\]]+\].*)$/gm,
(_match, before, id, after) => `${before}${context.id(id)}${after}`,
)
.replace(
/^(\s*)(.+?)(\s*->\s*)(.+?)\s*$/gm,
(_match, before, from, arrow, to) => `${before}${context.id(from)}${arrow}${context.id(to)}`,
)
.replace(
/^(\s*evolve\s+)(.+?)(\s+[-\d.]+\s*)$/gm,
(_match, before, id, after) => `${before}${context.id(id)}${after}`,
)
.replace(/(\bnote\s+")([^"]+)(")/g, (_match, before, text, after) => {
return `${before}${context.label(text)}${after}`;
});
}
function katanaNormalizeCsvLabels(context, labels) {
return labels
.split(",")
.map((label) => {
const trimmed = label.trim();
return label.replace(trimmed, context.label(trimmed));
})
.join(",");
}
/* mermaid-diagram-type.js */
function katanaMermaidDiagramType(source) {
const lines = String(source)
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0);
return katanaMermaidDiagramTypeLine(lines)?.split(/\s+/)[0].toLowerCase() ?? "";
}
function katanaMermaidDiagramTypeLine(lines) {
const bodyLines = katanaMermaidDiagramTypeLineReaders()[Number(lines[0] === "---")](lines);
return bodyLines.find((line) => !katanaMermaidIsDirectiveOrComment(line));
}
function katanaMermaidDiagramTypeLineReaders() {
return [katanaMermaidFirstDiagramTypeLine, katanaMermaidFrontmatterDiagramTypeLine];
}
function katanaMermaidFirstDiagramTypeLine(lines) {
return lines;
}
function katanaMermaidFrontmatterDiagramTypeLine(lines) {
const endIndex = lines.slice(1).indexOf("---");
return lines.slice(katanaMermaidFrontmatterBodyStartIndex(endIndex));
}
function katanaMermaidFrontmatterBodyStartIndex(endIndex) {
return [0, endIndex + 2][Number(endIndex >= 0)];
}
function katanaMermaidIsDirectiveOrComment(line) {
return line.startsWith("%%");
}
/* svg-normalize.js */
function katanaPrepareMermaidSource(source) {
if (!katanaShouldAppendTodayMarkerOff(source)) {
return source;
}
return katanaSourceWithTodayMarker(source);
}
function katanaShouldAppendTodayMarkerOff(source) {
if (!source.trimStart().startsWith("gantt")) {
return false;
}
return !/\btodayMarker\b/.test(source);
}
function katanaSourceWithTodayMarker(source) {
return source.trim().endsWith("\n") ? `${source}todayMarker off` : `${source}\ntodayMarker off`;
}
function katanaNormalizeMermaidSvg(svg, request) {
return katanaFinishNormalizedSvg(katanaNormalizeColoredMermaidSvg(svg, request), request);
}
function katanaNormalizeColoredMermaidSvg(svg, request) {
return katanaNormalizeDiagramSpecificSvg(
katanaNormalizeNativeSvgFallbacks(
katanaNormalizeNegativeRectWidths(katanaNormalizeC4RelationColors(svg, request)),
request,
),
request,
);
}
function katanaFinishNormalizedSvg(svg, request) {
return katanaNormalizeFinalSvg(svg, request);
}
function katanaSetNormalizedSvgSize(svg, dimensions) {
const width = katanaNormalizedSvgWidth(dimensions);
const height = katanaNormalizedSvgHeight(dimensions);
return katanaSetSvgMaxWidth(
katanaSetSvgViewBox(
katanaSetSvgDimension(katanaSetSvgDimension(svg, "width", width), "height", height),
dimensions.join(" "),
),
width,
);
}
function katanaNormalizedSvgWidth(dimensions) {
return String(Math.max(1, Math.ceil(dimensions[2] * katanaSvgDimensionScale())));
}
function katanaNormalizedSvgHeight(dimensions) {
return String(Math.max(1, Math.ceil(dimensions[3] * katanaSvgDimensionScale())));
}
function katanaSvgDimensionScale() {
return 1;
}
function katanaNormalizeFinalSvg(svg, request) {
return katanaNormalizeIshikawaSvg(
katanaEscapeTextMarkers(
katanaNormalizeEmptyTextFill(katanaNormalizeFontFamilyAttributes(svg), request),
),
);
}
function katanaNormalizeDiagramSpecificSvg(svg, request) {
let normalized = svg;
normalized = katanaNormalizeTreeViewSvg(normalized, request);
normalized = katanaNormalizeTreemapSvg(normalized, request);
normalized = katanaNormalizeClassSvg(normalized);
normalized = katanaNormalizeErSvg(normalized);
normalized = katanaNormalizeFlowchartEdgeLabelSvg(normalized, request);
normalized = katanaNormalizeFlowchartViewBoxSvg(normalized);
normalized = katanaNormalizeStateEdgeLabelSvg(normalized, request);
normalized = katanaNormalizeRequirementEdgeLabelSvg(normalized);
normalized = katanaNormalizeJourneySvg(normalized, request);
normalized = katanaNormalizePieSvg(normalized);
normalized = katanaNormalizeVennSvg(normalized, request);
normalized = katanaNormalizeArchitectureSvg(normalized, request);
normalized = katanaNormalizeRadarSvg(normalized);
normalized = katanaNormalizeGitGraphSvg(normalized);
normalized = katanaNormalizeKanbanSvg(normalized, request);
normalized = katanaNormalizeSankeySvg(normalized);
normalized = katanaNormalizeMindmapSvg(normalized);
normalized = katanaNormalizeBlockSvg(normalized);
normalized = katanaNormalizeWardleySvg(normalized, request);
normalized = katanaNormalizeIshikawaSvg(normalized);
normalized = katanaNormalizeReviewFeedbackSvg(normalized, request);
return normalized;
}
function katanaNormalizeFontFamilyAttributes(svg) {
return svg.replace(
/font-family=""([^"]+)",\s*([^"]*)"/g,
(_match, family, fallback) => `font-family="${family}, ${fallback}"`,
);
}
function katanaEscapeTextMarkers(svg) {
return svg.replace(/<<([^<>]+)>>/g, "<<$1>>");
}
/* mermaid-runtime-entrypoint */
function katanaInstallMermaidZenumlRuntimeAdapter() {
const zenumlDiagram = globalThis["mermaid-zenuml"];
if (zenumlDiagram) {
globalThis.__katanaMermaidZenuml = zenumlDiagram;
}
}
function katanaRunMermaidRuntime(request) {
return (async () => katanaRenderMermaid(request))().catch((error) => {
const detail = error?.stack ? error.stack : String(error);
throw new Error(detail);
});
}
async function katanaRenderMermaid(request) {
const mermaidValue = katanaMermaidGlobal();
mermaidValue.initialize({
startOnLoad: false,
securityLevel: "loose",
htmlLabels: false,
theme: request.theme,
flowchart: {
htmlLabels: false,
useMaxWidth: false,
},
sequence: {
useMaxWidth: false,
},
themeVariables: katanaMermaidThemeVariables(request),
});
const preparedSource = katanaNormalizeMermaidSourceI18n(
katanaPrepareMermaidSource(request.source),
);
const diagramType = request.diagramType || katanaMermaidDiagramType(preparedSource.source);
globalThis.__katanaMermaidDiagramType = diagramType;
await katanaRegisterMermaidExternalDiagram(mermaidValue, diagramType);
const result = await mermaidValue.render(request.svgId, preparedSource.source);
return katanaNormalizeMermaidSvg(
katanaRestoreMermaidI18nText(result.svg, preparedSource.replacements),
request,
);
}
function katanaMermaidGlobal() {
const mermaidValue = [globalThis.mermaid, globalThis.window.mermaid].find(Boolean);
if (!mermaidValue) {
throw new Error("Mermaid global was not registered");
}
return mermaidValue;
}
async function katanaRegisterMermaidExternalDiagram(mermaidValue, diagramType) {
if (diagramType !== "zenuml") {
return;
}
const zenumlDiagram = globalThis.__katanaMermaidZenuml;
if (!zenumlDiagram) {
throw new Error("ZenUML runtime asset was not registered");
}
if (typeof mermaidValue.registerExternalDiagrams !== "function") {
throw new Error("Mermaid runtime cannot register external diagrams");
}
await mermaidValue.registerExternalDiagrams([zenumlDiagram]);
}
function katanaMermaidThemeVariables(request) {
return {
background: request.background,
mainBkg: request.fill,
primaryColor: request.fill,
primaryTextColor: request.text,
primaryBorderColor: request.stroke,
secondaryColor: request.fill,
secondaryTextColor: request.text,
secondaryBorderColor: request.stroke,
tertiaryColor: request.fill,
tertiaryTextColor: request.text,
tertiaryBorderColor: request.stroke,
nodeTextColor: request.text,
lineColor: request.arrow,
textColor: request.text,
edgeLabelBackground: request.fill,
actorBkg: request.fill,
actorTextColor: request.text,
actorBorder: request.stroke,
signalColor: request.arrow,
signalTextColor: request.text,
labelTextColor: request.text,
noteBkgColor: request.fill,
noteTextColor: request.text,
noteBorderColor: request.stroke,
clusterBkg: request.background,
clusterBorder: request.stroke,
titleColor: request.text,
};
}