Skip to content

Instantly share code, notes, and snippets.

@mdashlw
Last active February 27, 2025 13:45
Show Gist options
  • Select an option

  • Save mdashlw/0a2b4b2f7e37c4e0f9f59946ec339491 to your computer and use it in GitHub Desktop.

Select an option

Save mdashlw/0a2b4b2f7e37c4e0f9f59946ec339491 to your computer and use it in GitHub Desktop.
Derpibooru more tag slots on upload page
// ==UserScript==
// @name Booru Tag Slots
// @version 1.0
// @author mdashlw
// @namespace Booru Scripts
// @match https://derpibooru.org/*
// @match https://trixiebooru.org/*
// @match https://tantabus.ai/*
// @match https://furbooru.org/*
// @grant none
// @description 2/27/2025
// @updateURL https://gist.github.com/mdashlw/0a2b4b2f7e37c4e0f9f59946ec339491/raw/booru-tag-slots.user.js
// @downloadURL https://gist.github.com/mdashlw/0a2b4b2f7e37c4e0f9f59946ec339491/raw/booru-tag-slots.user.js
// ==/UserScript==
if (location.pathname !== "/images/new") {
return;
}
function getTagInputs() {
const _firstTagInput = localStorage.getItem("tag_input");
const firstTagInput = {
name: "",
value: _firstTagInput ? JSON.parse(_firstTagInput) : "",
};
const _extraTagInputs = localStorage.getItem("tag_inputs");
const extraTagInputs = _extraTagInputs ? JSON.parse(_extraTagInputs) : [];
return [firstTagInput, ...extraTagInputs];
}
let tagInputList = getTagInputs();
function loadTagInput({ value }) {
const tagInputArea = document.getElementById("image_tag_input");
if (!tagInputArea) {
return;
}
for (const tag of value.split(",")) {
tagInputArea.dispatchEvent(
new CustomEvent("addtag", { detail: { name: tag }, bubbles: true }),
);
}
}
function saveTagInput(index, tagInput) {
localStorage.setItem(
"tag_inputs",
JSON.stringify(tagInputList.slice(1).filter((ti) => ti.name || ti.value)),
);
if (index === 0) {
localStorage.setItem("tag_input", JSON.stringify(tagInput.value));
}
}
document.getElementById("tagsinput-save")?.remove();
document.getElementById("tagsinput-load")?.remove();
const block = document.createElement("div");
block.className = "block";
const blockContent = document.createElement("div");
blockContent.className = "block__content";
block.appendChild(blockContent);
function createTagInputFlex(index, tagInput) {
const field = document.createElement("div");
field.className = "flex field";
const slotNameInput = document.createElement("input");
slotNameInput.type = "text";
slotNameInput.className = "input";
slotNameInput.value = tagInput.name;
if (index === 0) {
slotNameInput.placeholder = "default";
slotNameInput.setAttribute("disabled", "");
} else {
slotNameInput.addEventListener("keydown", (event) => {
if (event.keyCode === 38 || event.keyCode === 40) {
event.preventDefault();
}
});
slotNameInput.addEventListener("keyup", (event) => {
if (event.keyCode === 38) {
// arrow up
if (field.previousElementSibling?.previousElementSibling) {
const oldIndex = tagInputList.indexOf(tagInput);
const newIndex = oldIndex - 1;
const oldElement = tagInputList[newIndex];
tagInputList[newIndex] = tagInput;
tagInputList[oldIndex] = oldElement;
field.parentElement.insertBefore(
field.previousElementSibling,
field.nextElementSibling,
);
}
} else if (event.keyCode === 40) {
// arrow down
if (field.nextElementSibling?.nextElementSibling) {
const oldIndex = tagInputList.indexOf(tagInput);
const newIndex = oldIndex + 1;
const oldElement = tagInputList[newIndex];
tagInputList[newIndex] = tagInput;
tagInputList[oldIndex] = oldElement;
field.parentElement.insertBefore(field.nextElementSibling, field);
}
}
});
}
const loadButton = document.createElement("button");
loadButton.type = "button";
loadButton.className =
"button button--state-warning button--separate-left button--bold";
loadButton.textContent = "Load";
loadButton.addEventListener("click", () => {
loadTagInput(tagInput);
});
const valueInput = document.createElement("input");
valueInput.type = "text";
valueInput.className = "input spacing-left flex__grow";
valueInput.value = tagInput.value;
const saveButton = document.createElement("button");
saveButton.type = "button";
saveButton.className =
"button button--state-success button--separate-left button--bold";
saveButton.textContent = "Save";
saveButton.addEventListener("click", () => {
tagInput.name = slotNameInput.value;
tagInput.value = valueInput.value;
saveTagInput(index, tagInput);
});
const deleteButtonWrapper = document.createElement("label");
deleteButtonWrapper.className = "flex flex--centered button--separate-left";
const deleteButton = document.createElement("a");
const faTrash = document.createElement("i");
faTrash.className = "fa fa-trash";
deleteButton.href = "#";
deleteButton.append(faTrash, new Text(" Delete"));
deleteButton.addEventListener("click", (event) => {
event.preventDefault();
field.remove();
tagInputList = tagInputList.filter((ti) => ti !== tagInput);
});
deleteButtonWrapper.appendChild(deleteButton);
if (index === 0) {
deleteButtonWrapper.style.visibility = "hidden";
}
field.append(
slotNameInput,
loadButton,
valueInput,
saveButton,
deleteButtonWrapper,
);
return field;
}
for (const [index, tagInput] of tagInputList.entries()) {
blockContent.appendChild(createTagInputFlex(index, tagInput));
}
const addButton = document.createElement("button");
addButton.type = "button";
addButton.className = "button";
addButton.addEventListener("click", () => {
const tagInput = { name: "", value: "" };
addButton.insertAdjacentElement(
"beforebegin",
createTagInputFlex(tagInputList.length, tagInput),
);
tagInputList.push(tagInput);
});
const faPlus = document.createElement("i");
faPlus.className = "fa fa-plus";
addButton.append(faPlus, new Text(" Add"));
blockContent.appendChild(addButton);
document
.getElementById("tagsinput-clear")
?.insertAdjacentElement("afterend", block);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment