Sleazy Fork is available in English.
Keeps the story illustrations onscreen, story keyboard navigation (1-5 for prompts, "a" and "s" to swap images, "e" to edit images), "ESC" can be used to close singe-button dialogs, adds arrow key navigation to community worlds, widens "Illustration details" window, and closes Discord popup. Keyboard navigation only works when the focus is not on an "input" element (use "ESC" to leave input focus).
// ==UserScript==
// @name Infinite Worlds tweaks
// @namespace http://tampermonkey.net/
// @version 1.8
// @description Keeps the story illustrations onscreen, story keyboard navigation (1-5 for prompts, "a" and "s" to swap images, "e" to edit images), "ESC" can be used to close singe-button dialogs, adds arrow key navigation to community worlds, widens "Illustration details" window, and closes Discord popup. Keyboard navigation only works when the focus is not on an "input" element (use "ESC" to leave input focus).
// @author HiEv
// @license MIT
// @match https://infiniteworlds.app/
// @icon https://www.google.com/s2/favicons?sz=64&domain=infiniteworlds.app
// @grant none
// @require http://code.jquery.com/jquery-3.7.1.min.js
// ==/UserScript==
/*
global $
*/
(function() {
'use strict';
// Styling tweaks
$('html > head').append($(
'<style>\n' + // Illustration image tweaks.
'.anvil-panel-section .has-components .anvil-spacing-above-small[style="min-height: 20px; text-align: center; overflow: clip;"] > img {\n' +
' transition: margin-top 0.5s ease-out;\n' +
' width: auto !important;\n' +
' max-height: calc(100vh - 47px) !important;\n' +
'}\n' +
'@media (min-width: 768px) {\n' + // Widen the "Illustration details" window.
' .modal-sm {\n' +
' width: 800px;\n' +
' max-width: 90vw;\n' +
' }\n' +
'}</style>'));
// Keyboard navigation tweaks
setTimeout(() => {
$(document).on("keyup", function (event) { // Keyboard handler
if (($("input:focus").length === 0) && ($("textarea:focus").length === 0) && ($("div[contenteditable='true']:focus").length == 0)) {
if (event.key === "ArrowLeft") { // Community Worlds navigate previous
$('[data-page="prev"]').last().trigger("click");
}
if (event.key === "ArrowRight") { // Community Worlds navigate next
$('[data-page="next"]').last().trigger("click");
}
if (event.key === "e") { // Open the edit dialog
if ($("div.modal.alert-modal").length == 0) {
let links = $('[title="View/modify image instructions"] button');
if (links.length >= 1) {
links[0].click();
}
} else { // Close the edit dialog
$("button.close").click();
}
}
if (event.key === "a") { // Swap to previous image
let links = $('div[title="Previous image"] button');
if (links.length >= 1) {
links[0].click();
}
}
if (event.key === "s") { // Swap to next image
// let links = $('button').children("i.fa-rotate"); // old version
let links = $('div[title="Next image"] button');
if (links.length >= 1) {
// $(links[0]).parent().click(); // old version
links[0].click();
}
}
if (event.key === "1") { // Option #1
let links = $('[anvil-role="option-button"] button');
if (links.length >= 1) {
links[0].click();
}
}
if (event.key === "2") { // Option #2
let links = $('[anvil-role="option-button"] button');
if (links.length >= 2) {
links[1].click();
}
}
if (event.key === "3") { // Option #3
let links = $('[anvil-role="option-button"] button');
if (links.length >= 3) {
links[2].click();
}
}
if (event.key === "4") { // Action input
let links = $('textarea');
if (links.length >= 1) {
links[0].focus();
}
}
if (event.key === "5") { // AI input
let links = $('textarea');
if (links.length >= 2) {
links[1].focus();
}
}
if (event.key === "Escape") { // Close dialog with one option
if ($('[class="modal-content"] button:visible').length === 1) {
$('[class="modal-content"] button:visible').click();
}
}
} else { // Focus is on an input.
if (event.key === "Escape") {
document.activeElement.blur(); // Remove input focus.
}
}
});
}, 500);
// Illustration positioning tweaks
let check = 0;
let skip = 0;
setInterval(() => { // Periodically check for updates.
let imageContainers = $("div.column-panel.col-padding-huge").filter(function () { return $(this).find("img").length && !$(this).hasClass("anvil-role-full-height-form"); });
if (imageContainers.length > 0) { // If we're in a story with images...
++check;
let img = $(imageContainers[0]).find("img"); // This should be the illustration image.
let textTop = Math.ceil($(".anvil-role-full-height-form > div").filter(function () { return $(this).find(img).length; }).position().top);
let pos = Math.max(0, $("html").scrollTop() - textTop);
let mtop = parseFloat(img.css("margin-top"));
let diff = pos - mtop;
let limit = Infinity;
let turnOld = img.data("turn");
let turnCur = $(".anvil-role-secondary-text .label-text").filter(function () { return $(this).text().includes("Turn number"); }).text(); // Get turn text.
if (turnOld != turnCur) { // Different turn, so set the turn and clear the limit.
img.data("turn", turnCur);
img.removeData("limit");
skip = 3; // Skip the next three limit update checks.
} else {
if (diff < -80) { // Scrolled up, so clear the limit.
img.removeData("limit");
} else {
limit = img.data("limit") ?? Infinity;
}
}
let loaded = $("i.left.fa-redo:visible").length > 0 || $("i.left.fa-floppy-o:visible").length > 0;
if (!loaded) {
limit = Infinity;
}
if ((pos <= limit || mtop < limit) && Math.abs(diff) >= 4) {
// If (the target position isn't greater than the limit or the current top is less than the limit) and the target position is more than 4 pixels different from the current position, then update the position.
let footer = $('[anvil-role="footer-panel"]').offset();
img.css({
marginTop: Math.min(pos, limit)
});
if (loaded && skip-- <= 0 && diff > 0) { // If not a recently new turn and scrolled down...
setTimeout(() => { // ...wait to see if the footer is moved by repositioning the image.
if ($('[anvil-role="footer-panel"]').offset()?.top > (footer?.top ?? Infinity) + 4) { // If the footer moved down by more than 4 pixels, then we moved the image down too much.
let newLimit = Math.round(parseFloat(img.css("margin-top")) + footer.top - $('[anvil-role="footer-panel"]').offset().top);
img.data("limit", newLimit).css("margin-top", newLimit); // Set the limit and fix the position.
}
}, 100);
}
}
}
// Popup tweaks
if ($('.modal-content a[href="https://discord.gg/eJKbKwdArY"]').length > 0) { // Close Discord popup.
$(".modal-content .btn-success").click();
}
}, 300);
})();