mirror of
https://github.com/LCE-Hub/LCE-Workshop.git
synced 2026-07-29 21:52:28 +00:00
310 lines
8.7 KiB
JavaScript
310 lines
8.7 KiB
JavaScript
(function () {
|
|
var STORAGE_KEY = "themeloader_themes";
|
|
var ATTR = "data-tl";
|
|
|
|
function load() {
|
|
try {
|
|
var d = api.storage.get(STORAGE_KEY);
|
|
return d ? JSON.parse(d) : [];
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function save(themes) {
|
|
api.storage.set(STORAGE_KEY, JSON.stringify(themes));
|
|
}
|
|
|
|
function linkId(url) {
|
|
return "tl_" + btoa(url).replace(/[/+=]/g, "_");
|
|
}
|
|
|
|
function inject(url) {
|
|
var id = linkId(url);
|
|
if (document.getElementById(id)) return;
|
|
fetch(url).then(function (r) { return r.text(); }).then(function (css) {
|
|
var el = document.getElementById(id);
|
|
if (el) return;
|
|
var style = document.createElement("style");
|
|
style.id = id;
|
|
style.setAttribute(ATTR, "");
|
|
style.textContent = css;
|
|
document.head.appendChild(style);
|
|
}).catch(function () {});
|
|
}
|
|
|
|
function eject(url) {
|
|
var el = document.getElementById(linkId(url));
|
|
if (el) el.remove();
|
|
}
|
|
|
|
function apply() {
|
|
var els = document.querySelectorAll("style[" + ATTR + "]");
|
|
for (var i = els.length - 1; i >= 0; i--) els[i].remove();
|
|
var themes = load();
|
|
for (var i = 0; i < themes.length; i++) {
|
|
if (themes[i].enabled) inject(themes[i].url);
|
|
}
|
|
}
|
|
|
|
api.views.register(
|
|
"themeloader",
|
|
function (api) {
|
|
var h = api.React.createElement;
|
|
var useState = api.React.useState;
|
|
var useEffect = api.React.useEffect;
|
|
var useCallback = api.React.useCallback;
|
|
return function ThemeLoaderView() {
|
|
var themesState = useState([]);
|
|
var themes = themesState[0];
|
|
var setThemes = themesState[1];
|
|
var nameState = useState("");
|
|
var name = nameState[0];
|
|
var setName = nameState[1];
|
|
var urlState = useState("");
|
|
var url = urlState[0];
|
|
var setUrl = urlState[1];
|
|
useEffect(function () {
|
|
setThemes(load());
|
|
}, []);
|
|
|
|
var add = useCallback(
|
|
function () {
|
|
if (!name.trim() || !url.trim()) return;
|
|
var updated = themes.concat([
|
|
{ name: name.trim(), url: url.trim(), enabled: true },
|
|
]);
|
|
save(updated);
|
|
setThemes(updated);
|
|
apply();
|
|
setName("");
|
|
setUrl("");
|
|
},
|
|
[themes, name, url],
|
|
);
|
|
|
|
var toggle = useCallback(
|
|
function (i) {
|
|
var updated = themes.map(function (t, j) {
|
|
return j === i
|
|
? { name: t.name, url: t.url, enabled: !t.enabled }
|
|
: t;
|
|
});
|
|
save(updated);
|
|
setThemes(updated);
|
|
apply();
|
|
},
|
|
[themes],
|
|
);
|
|
|
|
var remove = useCallback(
|
|
function (i) {
|
|
eject(themes[i].url);
|
|
var updated = themes.filter(function (_, j) {
|
|
return j !== i;
|
|
});
|
|
save(updated);
|
|
setThemes(updated);
|
|
},
|
|
[themes],
|
|
);
|
|
|
|
var handleNameKey = useCallback(
|
|
function (e) {
|
|
if (e.key === "Enter") add();
|
|
},
|
|
[add],
|
|
);
|
|
|
|
var handleUrlKey = useCallback(
|
|
function (e) {
|
|
if (e.key === "Enter") add();
|
|
},
|
|
[add],
|
|
);
|
|
|
|
var children = [];
|
|
children.push(
|
|
h(
|
|
"div",
|
|
{ key: "back", className: "mb-2" },
|
|
h(
|
|
"button",
|
|
{
|
|
className:
|
|
"text-sm text-[#A0A0A0] hover:text-[#FFFF55] mc-text-shadow cursor-pointer",
|
|
onClick: function () {
|
|
api.views.navigate("settings");
|
|
},
|
|
},
|
|
"\u2190 Back",
|
|
),
|
|
),
|
|
);
|
|
|
|
children.push(
|
|
h(
|
|
"div",
|
|
{ key: "header", className: "flex flex-col gap-0 mb-4" },
|
|
h(
|
|
"span",
|
|
{ className: "text-2xl font-bold mc-text-shadow tracking-wide" },
|
|
"ThemeLoader",
|
|
),
|
|
h(
|
|
"span",
|
|
{ className: "text-xs text-[#888] mc-text-shadow mt-1" },
|
|
"Load, enable and disable themes from raw CSS Themes.",
|
|
),
|
|
),
|
|
);
|
|
|
|
children.push(
|
|
h(
|
|
"div",
|
|
{
|
|
key: "add-form",
|
|
className:
|
|
"border border-[#555] bg-black/40 p-3 mb-4 flex flex-col gap-2",
|
|
},
|
|
h(
|
|
"div",
|
|
{ className: "flex gap-2" },
|
|
h("input", {
|
|
className:
|
|
"flex-1 px-2 py-1 text-sm bg-black/60 border border-[#555] text-white outline-none placeholder-[#666]",
|
|
placeholder: "Theme name",
|
|
value: name,
|
|
onChange: function (e) {
|
|
setName(e.target.value);
|
|
},
|
|
onKeyDown: handleNameKey,
|
|
}),
|
|
h("input", {
|
|
className:
|
|
"flex-[2] px-2 py-1 text-sm bg-black/60 border border-[#555] text-white outline-none placeholder-[#666]",
|
|
placeholder: "Raw CSS URL (e.g. https://example.com/theme.css)",
|
|
value: url,
|
|
onChange: function (e) {
|
|
setUrl(e.target.value);
|
|
},
|
|
onKeyDown: handleUrlKey,
|
|
}),
|
|
),
|
|
h(
|
|
"button",
|
|
{
|
|
className:
|
|
"self-start px-4 py-1 text-sm text-white mc-text-shadow cursor-pointer",
|
|
style: {
|
|
backgroundImage: "url('/images/Button_Background.png')",
|
|
backgroundSize: "100% 100%",
|
|
imageRendering: "pixelated",
|
|
},
|
|
onClick: add,
|
|
},
|
|
"Add Theme",
|
|
),
|
|
),
|
|
);
|
|
|
|
var themeItems;
|
|
if (themes.length === 0) {
|
|
themeItems = h(
|
|
"div",
|
|
{
|
|
className: "text-sm text-[#666] mc-text-shadow py-4 text-center",
|
|
},
|
|
"No themes added yet.",
|
|
);
|
|
} else {
|
|
themeItems = themes.map(function (t, i) {
|
|
var statusClass = t.enabled ? "text-[#55FF55]" : "text-[#FF5555]";
|
|
var statusText = t.enabled ? "Enabled" : "Disabled";
|
|
var btnLabel = t.enabled ? "Disable" : "Enable";
|
|
|
|
return h(
|
|
"div",
|
|
{
|
|
key: t.url,
|
|
className:
|
|
"flex items-center gap-3 border border-[#444] bg-black/30 p-2",
|
|
},
|
|
h("span", { className: "flex-1 text-sm mc-text-shadow" }, t.name),
|
|
h(
|
|
"span",
|
|
{
|
|
className:
|
|
"text-[10px] uppercase tracking-wider " + statusClass,
|
|
},
|
|
statusText,
|
|
),
|
|
h(
|
|
"button",
|
|
{
|
|
className:
|
|
"text-[10px] px-2 py-0.5 text-white mc-text-shadow cursor-pointer border border-[#555] bg-black/40 hover:bg-black/60",
|
|
onClick: function () {
|
|
toggle(i);
|
|
},
|
|
},
|
|
btnLabel,
|
|
),
|
|
h(
|
|
"button",
|
|
{
|
|
className:
|
|
"text-[10px] px-2 py-0.5 text-[#FF5555] mc-text-shadow cursor-pointer border border-[#555] bg-black/40 hover:bg-black/60",
|
|
onClick: function () {
|
|
remove(i);
|
|
},
|
|
},
|
|
"Remove",
|
|
),
|
|
);
|
|
});
|
|
|
|
themeItems = h(
|
|
"div",
|
|
{ className: "flex flex-col gap-1.5" },
|
|
h(
|
|
"span",
|
|
{
|
|
className:
|
|
"text-[10px] text-[#888] uppercase tracking-widest mb-1",
|
|
},
|
|
"Installed Themes (" + themes.length + ")",
|
|
),
|
|
themeItems,
|
|
);
|
|
}
|
|
|
|
children.push(h("div", { key: "theme-list" }, themeItems));
|
|
return h(
|
|
"div",
|
|
{
|
|
className: "w-full max-w-xl mx-auto p-4 flex flex-col",
|
|
style: {
|
|
minHeight: "100%",
|
|
backgroundSize: "100% 100%",
|
|
imageRendering: "pixelated",
|
|
},
|
|
},
|
|
children,
|
|
);
|
|
};
|
|
},
|
|
{ label: "Theme Loader" },
|
|
);
|
|
|
|
api.actions.register("settings-tab", {
|
|
id: "themeloader",
|
|
label: "Themes",
|
|
onClick: function () {
|
|
api.views.navigate("themeloader");
|
|
},
|
|
});
|
|
|
|
api.hooks.on("app:ready", apply);
|
|
})();
|