UserScript:cheat-sheet
code:js
import { setupCheatSheetMenu } from "/api/code/suto3/UserScript:cheat-sheet/script.js";
setupCheatSheetMenu("/api/table/suto3/cheat-sheet/list.csv");
code:script.js
// NOTE: This file is an ES Module.
/**
* Creates a Scrapbox page menu to set a wallpaper from a list of images in a CSV file.
* @param {string} csvUrl - The URL of the CSV file containing image names and URLs.
*/
export function setupCheatSheetMenu(csvUrl) {
// Run only in a Scrapbox environment.
if (typeof scrapbox === "undefined") {
console.log("This script is intended to run in a Scrapbox environment.");
return;
}
scrapbox.PageMenu.addMenu({
title: "cheat-sheet",
//image: "/api/pages/suto3/cheat-sheet/icon",
onClick: async () => {
const menu = scrapbox.PageMenu("cheat-sheet");
menu.addItem({ title: "Now loading...", onClick: () => null });
try {
const text = await fetchLists(csvUrl);
const urls = [];
const menuItems = [];
// 1. Prepare menu items and a list of valid URLs from the fetched text.
text.split("\n").forEach(line => {
if (!line) return;
const res = line.split(",");
if (res.length < 2 || !res1) return; const name = res0.length ? res0 : "unknown"; const imageUrl = res1.replace(/\\\/g, ""); if (imageUrlRegex.test(imageUrl)) {
urls.push(imageUrl);
menuItems.push({
title: name,
onClick: () => { changeCss(imageUrl); }
});
}
});
// 2. Clear the "Now loading..." message.
menu.removeAllItems();
// 3. Add the new menu items.
menu.addItem({
title: "初期値",
onClick: () => { changeCss(null); }
});
menuItems.forEach(item => menu.addItem(item));
if (urls.length > 0) {
menu.addItem({
title: "ランダム",
onClick: () => { changeCss(choice(urls)); }
});
}
} catch (error) {
console.error("Failed to load wallpaper list:", error);
menu.removeAllItems();
menu.addItem({ title: "Error: Failed to load", onClick: () => null });
}
}
});
}
// --- Helper Functions (exported for testing) ---
export const fetchLists = async (url) => {
const response = await fetch(url);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const text = await response.text();
// Assuming the CSV has a header row to be skipped (slice(1))
// and lines might start with a comma to be removed.
return text.split("\n").slice(1)
.map(line => line.replace(/^,/, ""))
.join("\n");
};
export const changeCss = (imageUrl) => {
const styleId = "__cheat-sheet__";
let style = document.getElementById(styleId);
if (style) {
style.remove();
}
// If imageUrl is not provided, simply remove the style and exit.
if (!imageUrl) {
return;
}
const css = `
body {
--cheat-sheet-image: url("${imageUrl}");
}
`;
style = document.createElement("style");
style.setAttribute("id", styleId);
style.appendChild(document.createTextNode(css.trim()));
document.head.appendChild(style);
};
export const choice = (arr) => {
if (!arr || arr.length === 0) {
return null;
}
};
// A more robust regex for image URLs.
export const imageUrlRegex = /(https?:\/\/[\w\-\.\/?\#\:\%\u3000-\u30FE\u4E00-\u9FA0\uFF01-\uFFE3\\]+)\.(jpg|jpeg|gif|png|webp|svg)/i;