163 lines
4.7 KiB
JavaScript
163 lines
4.7 KiB
JavaScript
import { app } from "/scripts/app.js";
|
|
|
|
const NODE_IDS = new Set(["mrmth_ScriptInput"]);
|
|
const NODE_NAMES = new Set(["ScriptTextInput"]);
|
|
const STYLE_ID = "mrmth-script-line-numbers";
|
|
|
|
function hasScriptInput(nodeData) {
|
|
const input = nodeData?.input || {};
|
|
const required = input.required || {};
|
|
const optional = input.optional || {};
|
|
return Object.prototype.hasOwnProperty.call(required, "script") || Object.prototype.hasOwnProperty.call(optional, "script");
|
|
}
|
|
|
|
function isTargetNode(nodeData) {
|
|
if (!nodeData) {
|
|
return false;
|
|
}
|
|
if (NODE_IDS.has(nodeData.node_id)) {
|
|
return true;
|
|
}
|
|
if (NODE_IDS.has(nodeData.name)) {
|
|
return true;
|
|
}
|
|
if (NODE_NAMES.has(nodeData.name)) {
|
|
return true;
|
|
}
|
|
if (NODE_NAMES.has(nodeData.display_name)) {
|
|
return true;
|
|
}
|
|
return hasScriptInput(nodeData);
|
|
}
|
|
|
|
function ensureStyles() {
|
|
if (document.getElementById(STYLE_ID)) {
|
|
return;
|
|
}
|
|
|
|
const style = document.createElement("style");
|
|
style.id = STYLE_ID;
|
|
style.textContent = `
|
|
.mrmth-line-editor {
|
|
display: flex;
|
|
align-items: stretch;
|
|
width: 100%;
|
|
gap: 0;
|
|
}
|
|
.mrmth-line-numbers {
|
|
padding: 6px 8px;
|
|
text-align: right;
|
|
user-select: none;
|
|
color: #9aa0a6;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
line-height: 1.4;
|
|
white-space: pre;
|
|
overflow: hidden;
|
|
box-sizing: border-box;
|
|
}
|
|
.mrmth-line-numbers-content {
|
|
white-space: pre;
|
|
}
|
|
.mrmth-line-input {
|
|
flex: 1;
|
|
font-family: monospace;
|
|
line-height: 1.4;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
function attachLineNumbers(widget) {
|
|
const inputEl = widget?.inputEl;
|
|
if (!inputEl || inputEl.dataset.mrmthLineNumbers) {
|
|
return false;
|
|
}
|
|
|
|
const parent = inputEl.parentElement;
|
|
if (!parent) {
|
|
return false;
|
|
}
|
|
|
|
ensureStyles();
|
|
|
|
const wrapper = document.createElement("div");
|
|
wrapper.className = "mrmth-line-editor";
|
|
|
|
const gutter = document.createElement("div");
|
|
gutter.className = "mrmth-line-numbers";
|
|
|
|
const gutterContent = document.createElement("div");
|
|
gutterContent.className = "mrmth-line-numbers-content";
|
|
|
|
parent.replaceChild(wrapper, inputEl);
|
|
wrapper.appendChild(gutter);
|
|
gutter.appendChild(gutterContent);
|
|
wrapper.appendChild(inputEl);
|
|
|
|
inputEl.classList.add("mrmth-line-input");
|
|
inputEl.dataset.mrmthLineNumbers = "true";
|
|
|
|
const inputStyle = getComputedStyle(inputEl);
|
|
gutter.style.fontFamily = inputStyle.fontFamily;
|
|
gutter.style.fontSize = inputStyle.fontSize;
|
|
gutter.style.lineHeight = inputStyle.lineHeight;
|
|
gutter.style.paddingTop = inputStyle.paddingTop;
|
|
gutter.style.paddingBottom = inputStyle.paddingBottom;
|
|
|
|
const updateNumbers = () => {
|
|
const lineCount = Math.max(1, inputEl.value.split("\n").length);
|
|
gutterContent.textContent = Array.from({ length: lineCount }, (_, i) => String(i + 1)).join("\n");
|
|
gutter.style.height = `${inputEl.clientHeight}px`;
|
|
gutterContent.style.transform = `translateY(${-inputEl.scrollTop}px)`;
|
|
};
|
|
|
|
inputEl.addEventListener("input", updateNumbers);
|
|
inputEl.addEventListener("scroll", () => {
|
|
gutterContent.style.transform = `translateY(${-inputEl.scrollTop}px)`;
|
|
});
|
|
|
|
if (window.ResizeObserver) {
|
|
const resizeObserver = new ResizeObserver(() => updateNumbers());
|
|
resizeObserver.observe(inputEl);
|
|
}
|
|
|
|
requestAnimationFrame(updateNumbers);
|
|
return true;
|
|
}
|
|
|
|
function attachLineNumbersWithRetry(node) {
|
|
const widget = node.widgets?.find((w) => w.name === "script");
|
|
if (attachLineNumbers(widget)) {
|
|
return;
|
|
}
|
|
|
|
requestAnimationFrame(() => {
|
|
const retryWidget = node.widgets?.find((w) => w.name === "script");
|
|
attachLineNumbers(retryWidget);
|
|
});
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "mrmth.ScriptTextInput.LineNumbers",
|
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
|
if (!isTargetNode(nodeData)) {
|
|
return;
|
|
}
|
|
|
|
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
|
nodeType.prototype.onNodeCreated = function () {
|
|
onNodeCreated?.apply(this, arguments);
|
|
attachLineNumbersWithRetry(this);
|
|
};
|
|
|
|
const onConfigure = nodeType.prototype.onConfigure;
|
|
nodeType.prototype.onConfigure = function () {
|
|
onConfigure?.apply(this, arguments);
|
|
attachLineNumbersWithRetry(this);
|
|
};
|
|
},
|
|
});
|