undo redo

This commit is contained in:
Haoming 2026-06-01 17:57:35 +08:00
parent bb3fb1a935
commit b93f23efb7
6 changed files with 106 additions and 4 deletions

View File

@ -61,7 +61,7 @@
const counter = gradioApp().getElementById(id_counter);
if (textarea && counter)
onEdit(`${id_prompt}_BracketChecking`, textarea, 400, () => checkBrackets(textarea, counter));
onEdit(`${id_prompt}_BracketChecking`, textarea, 600, () => checkBrackets(textarea, counter));
}
onUiLoaded(function () {

101
javascript/history.js Normal file
View File

@ -0,0 +1,101 @@
(function () {
const editTimers = {};
const delay = 600;
const limit = 16;
class TextboxHistory {
/** @param {string} id */
constructor(id) {
/** @type {HTMLTextAreaElement} */
this.textarea = document.querySelector(`#${id} textarea`);
/** @type {string[]} */
this.undoStack = [];
/** @type {string[]} */
this.redoStack = [];
this.#snapshot();
this.textarea.addEventListener("keydown", (e) => {
const prev = editTimers[id];
if ((e.ctrlKey || e.metaKey) && e.key === "z") {
e.preventDefault();
if (prev) clearTimeout(prev);
this.#undo();
return false;
}
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "Z") {
e.preventDefault();
if (prev) clearTimeout(prev);
this.#redo();
return false;
}
if (["Control", "Meta", "Shift", "Alt"].includes(e.key)) return true;
if (prev) clearTimeout(prev);
editTimers[id] = setTimeout(() => this.#snapshot(), delay);
});
}
#undo() {
this.#snapshot(false);
if (this.undoStack.length < 2) return;
const current = this.undoStack.pop();
this.redoStack.push(current);
const prev = this.undoStack.at(-1);
this.textarea.value = prev;
updateInput(this.textarea);
}
#redo() {
if (this.redoStack.length < 1) return;
const current = this.textarea.value;
this.undoStack.push(current);
const prev = this.redoStack.pop();
this.textarea.value = prev;
updateInput(this.textarea);
}
#snapshot(reset = true) {
const current = this.textarea.value;
if (current === this.undoStack.at(-1)) return;
this.undoStack.push(current);
if (this.undoStack.length > limit) this.undoStack.shift();
if (reset) this.redoStack.length = 0;
}
}
function setup() {
const IDs = [
"txt2img_prompt",
"txt2img_neg_prompt",
"img2img_prompt",
"img2img_neg_prompt",
"hires_prompt",
"hires_neg_prompt",
];
for (const id of IDs) new TextboxHistory(id);
}
onUiLoaded(() => {
function checkSettings() {
if (Object.keys(opts).length === 0) {
setTimeout(checkSettings, 100);
return;
}
if (opts.undo_redo) setup();
}
checkSettings();
});
})();

View File

@ -21,7 +21,7 @@ function shushTextfield() {
onUiLoaded(() => {
function checkSettings() {
if (Object.keys(opts).length == 0) {
if (Object.keys(opts).length === 0) {
setTimeout(checkSettings, 100);
return;
}

View File

@ -29,7 +29,7 @@ onUiLoaded(function () {
);
let settings_tabs = gradioApp().querySelector("#settings div");
onEdit("settingsSearch", editTextarea, 250, function () {
onEdit("settingsSearch", editTextarea, 300, function () {
let searchText = (editTextarea.value || "").trim().toLowerCase();
gradioApp()

View File

@ -54,7 +54,7 @@ function setupTokenCounting(id, id_counter, id_button) {
prompt.parentElement.insertBefore(counter, prompt);
prompt.parentElement.style.position = "relative";
let func = onEdit(id, textarea, 800, function () {
let func = onEdit(id, textarea, 600, function () {
if (counter.classList.contains("token-counter-visible")) {
gradioApp().getElementById(id_button)?.click();
}

View File

@ -175,6 +175,7 @@ options_templates.update(
"list_hidden_files": OptionInfo(True, "List the models/files under hidden directories").info('directory is hidden if its name starts with "."'),
"dump_stacks_on_signal": OptionInfo(False, "Print the stack trace before terminating the webui via Ctrl + C"),
"no_spellcheck": OptionInfo(False, "Disable auto-correct / spellcheck for prompt fields").needs_reload_ui(),
"undo_redo": OptionInfo(False, "Enable undo / redo history for prompt fields").needs_reload_ui(),
},
)
)