dikdimon's picture
Upload extensions using SD-Hub extension
3dabe4a verified
class ForgeCoupleDataframe {
static #default_mapping = [
[0.0, 0.5, 0.0, 1.0, 1.0],
[0.5, 1.0, 0.0, 1.0, 1.0]
];
static get #columns() { return this.#tableHeader.length; }
static #tableHeader = ["x1", "x2", "y1", "y2", "w", "prompt"];
static #tableWidth = ["6%", "6%", "6%", "6%", "6%", "70%"];
static #colors = [0, 30, 60, 120, 240, 280, 320];
static #color(i) { return `hsl(${ForgeCoupleDataframe.#colors[i % 7]}, 36%, 36%)` }
/** "t2i" | "i2i" */
#mode = undefined;
#promptField = undefined;
#separatorField = undefined;
get #sep() {
var sep = this.#separatorField.value.trim();
if (!sep) sep = "\n";
return sep;
}
#body = undefined;
#selection = -1;
/** @param {Element} div @param {string} mode @param {Element} separator */
constructor(div, mode, separator) {
this.#mode = mode;
this.#promptField = document.getElementById(`${mode === "t2i" ? "txt" : "img"}2img_prompt`).querySelector("textarea");
this.#separatorField = separator;
const table = document.createElement('table');
const colgroup = document.createElement('colgroup');
for (let c = 0; c < ForgeCoupleDataframe.#columns; c++) {
const col = document.createElement('col');
col.style.width = ForgeCoupleDataframe.#tableWidth[c];
colgroup.appendChild(col);
}
table.appendChild(colgroup);
const thead = document.createElement('thead');
const thr = thead.insertRow();
for (let c = 0; c < ForgeCoupleDataframe.#columns; c++) {
const th = document.createElement('th');
th.textContent = ForgeCoupleDataframe.#tableHeader[c];
thr.appendChild(th);
}
table.appendChild(thead);
const tbody = document.createElement('tbody');
for (let r = 0; r < ForgeCoupleDataframe.#default_mapping.length; r++) {
const tr = tbody.insertRow();
for (let c = 0; c < ForgeCoupleDataframe.#columns; c++) {
const td = tr.insertCell();
const isPrompt = (c === ForgeCoupleDataframe.#columns - 1);
td.contentEditable = true;
td.textContent = isPrompt ? "" : Number(ForgeCoupleDataframe.#default_mapping[r][c]).toFixed(2);
td.addEventListener("keydown", (e) => {
if (e.key == 'Enter') {
e.preventDefault();
td.blur();
}
});
td.addEventListener("blur", () => { this.#onSubmit(td, isPrompt); })
td.onclick = () => { this.#onSelect(r); }
}
}
table.appendChild(tbody);
div.appendChild(table);
this.#body = tbody;
}
/** @param {number} row */
#onSelect(row) {
this.#selection = (row === this.#selection) ? -1 : row;
ForgeCouple.onSelect(this.#mode);
}
/** @param {Element} cell @param {boolean} isPrompt */
#onSubmit(cell, isPrompt) {
if (isPrompt) {
const prompts = [];
const rows = this.#body.querySelectorAll("tr");
rows.forEach((row) => {
const prompt = row.querySelector("td:last-child").textContent.trim();
prompts.push(prompt);
});
const oldPrompts = this.#promptField.value.split(this.#sep).map(line => line.trim());
const modified = prompts.length;
if (modified >= oldPrompts.length)
this.#promptField.value = prompts.join(this.#sep);
else {
const newPrompts = [...prompts, ...(oldPrompts.slice(modified))]
this.#promptField.value = newPrompts.join(this.#sep);
}
updateInput(this.#promptField);
} else {
var val = this.#clamp01(cell.textContent,
Array.from(cell.parentElement.children).indexOf(cell) === 4
);
val = Math.round(val / 0.01) * 0.01;
cell.textContent = Number(val).toFixed(2);
ForgeCouple.onSelect(this.#mode);
ForgeCouple.onEntry(this.#mode);
}
}
/** @param {number[][]} vals */
onPaste(vals) {
while (this.#body.querySelector("tr"))
this.#body.deleteRow(0);
const count = vals.length;
for (let r = 0; r < count; r++) {
const tr = this.#body.insertRow();
for (let c = 0; c < ForgeCoupleDataframe.#columns; c++) {
const td = tr.insertCell();
const prompt = (c === ForgeCoupleDataframe.#columns - 1);
td.contentEditable = true;
td.textContent = prompt ? "" : Number(vals[r][c]).toFixed(2);
td.addEventListener("keydown", (e) => {
if (e.key == 'Enter') {
e.preventDefault();
td.blur();
}
});
td.addEventListener("blur", () => { this.#onSubmit(td, prompt); })
td.onclick = () => { this.#onSelect(r); }
}
}
this.#selection = -1;
ForgeCouple.onSelect(this.#mode);
ForgeCouple.onEntry(this.#mode);
this.syncPrompt();
}
reset() {
while (this.#body.querySelector("tr"))
this.#body.deleteRow(0);
for (let r = 0; r < ForgeCoupleDataframe.#default_mapping.length; r++) {
const tr = this.#body.insertRow();
for (let c = 0; c < ForgeCoupleDataframe.#columns; c++) {
const td = tr.insertCell();
const prompt = (c === ForgeCoupleDataframe.#columns - 1);
td.contentEditable = true;
td.textContent = prompt ? "" : Number(ForgeCoupleDataframe.#default_mapping[r][c]).toFixed(2);
td.addEventListener("keydown", (e) => {
if (e.key == 'Enter') {
e.preventDefault();
td.blur();
}
});
td.addEventListener("blur", () => { this.#onSubmit(td, prompt); })
td.onclick = () => { this.#onSelect(r); }
}
}
this.#selection = -1;
ForgeCouple.onSelect(this.#mode);
ForgeCouple.onEntry(this.#mode);
this.syncPrompt();
}
/** @returns {number[][]} */
#newRow() {
const rows = this.#body.querySelectorAll("tr");
const count = rows.length;
const vals = Array.from(rows, row => {
return Array.from(row.querySelectorAll("td"))
.slice(0, -1).map(cell => parseFloat(cell.textContent));
});
const tr = this.#body.insertRow();
for (let c = 0; c < ForgeCoupleDataframe.#columns; c++) {
const td = tr.insertCell();
const prompt = (c === ForgeCoupleDataframe.#columns - 1);
td.contentEditable = true;
td.textContent = "";
td.addEventListener("keydown", (e) => {
if (e.key == 'Enter') {
e.preventDefault();
td.blur();
}
});
td.addEventListener("blur", () => { this.#onSubmit(td, prompt); })
td.onclick = () => { this.#onSelect(count); }
}
return vals;
}
/** @param {boolean} newline */
newRowAbove(newline) {
const vals = this.#newRow();
const newVals = [
...vals.slice(0, this.#selection),
[0.0, 1.0, 0.0, 1.0, 1.0],
...vals.slice(this.#selection)
];
const count = newVals.length;
const rows = this.#body.querySelectorAll("tr");
for (let r = 0; r < count; r++) {
const cells = rows[r].querySelectorAll("td");
for (let c = 0; c < ForgeCoupleDataframe.#columns - 1; c++)
cells[c].textContent = Number(newVals[r][c]).toFixed(2);
}
if (newline) {
const prompts = this.#promptField.value.split(this.#sep).map(line => line.trim());
const newPrompts = [
...prompts.slice(0, this.#selection),
"",
...prompts.slice(this.#selection)
];
this.#promptField.value = newPrompts.join(this.#sep);
updateInput(this.#promptField);
}
this.#selection += 1;
ForgeCouple.onSelect(this.#mode);
ForgeCouple.onEntry(this.#mode);
this.syncPrompt();
}
/** @param {boolean} newline */
newRowBelow(newline) {
const vals = this.#newRow();
const newVals = [
...vals.slice(0, this.#selection + 1),
[0.25, 0.75, 0.25, 0.75, 1.0],
...vals.slice(this.#selection + 1)
];
const count = newVals.length;
const rows = this.#body.querySelectorAll("tr");
for (let r = 0; r < count; r++) {
const cells = rows[r].querySelectorAll("td");
for (let c = 0; c < ForgeCoupleDataframe.#columns - 1; c++)
cells[c].textContent = Number(newVals[r][c]).toFixed(2);
}
if (newline) {
const prompts = this.#promptField.value.split(this.#sep).map(line => line.trim());
const newPrompts = [
...prompts.slice(0, this.#selection + 1),
"",
...prompts.slice(this.#selection + 1)
];
this.#promptField.value = newPrompts.join(this.#sep);
updateInput(this.#promptField);
}
ForgeCouple.onSelect(this.#mode);
ForgeCouple.onEntry(this.#mode);
this.syncPrompt();
}
/** @param {boolean} removeText */
deleteRow(removeText) {
const rows = this.#body.querySelectorAll("tr");
const count = rows.length;
const vals = Array.from(rows, row => {
return Array.from(row.querySelectorAll("td"))
.slice(0, -1).map(cell => parseFloat(cell.textContent));
});
vals.splice(this.#selection, 1);
this.#body.deleteRow(count - 1);
for (let r = 0; r < count - 1; r++) {
const cells = rows[r].querySelectorAll("td");
for (let c = 0; c < ForgeCoupleDataframe.#columns - 1; c++)
cells[c].textContent = Number(vals[r][c]).toFixed(2);
}
if (removeText) {
const prompts = this.#promptField.value.split(this.#sep).map(line => line.trim());
prompts.splice(this.#selection, 1);
this.#promptField.value = prompts.join(this.#sep);
updateInput(this.#promptField);
}
if (this.#selection == count - 1)
this.#selection -= 1;
ForgeCouple.onSelect(this.#mode);
ForgeCouple.onEntry(this.#mode);
this.syncPrompt();
}
/** @returns {[string, Element]} */
updateColors() {
const rows = this.#body.querySelectorAll("tr");
rows.forEach((row, i) => {
const color = ForgeCoupleDataframe.#color(i);
const stripe = (this.#selection === i) ? "var(--table-row-focus)" :
`var(--table-${(i % 2 == 0) ? "odd" : "even"}-background-fill)`;
row.style.background = `linear-gradient(to right, ${stripe} 80%, ${color})`;
});
if (this.#selection < 0 || this.#selection > rows.length)
return [null, null];
else
return [ForgeCoupleDataframe.#color(this.#selection), rows[this.#selection]];
}
syncPrompt() {
const prompt = this.#promptField.value;
const prompts = prompt.split(this.#sep).map(line => line.trim());
const rows = this.#body.querySelectorAll("tr");
const active = document.activeElement;
rows.forEach((row, i) => {
const promptCell = row.querySelector("td:last-child");
// Skip editing Cell
if (promptCell === active)
return;
if (i < prompts.length)
promptCell.textContent = prompts[i];
else
promptCell.textContent = "";
});
}
/** @param {number} @param {boolean} w @returns {number} */
#clamp01(v, w) {
var val = parseFloat(v);
if (Number.isNaN(val))
val = 0.0;
return Math.min(
Math.max(val, w ? -10.0 : 0.0),
w ? 10.0 : 1.0
);
}
}