This commit is contained in:
/home/neo
2026-06-14 19:27:34 +03:00
committed by GitHub
parent 0ec2fc2d3f
commit 14d4451b40

View File

@@ -0,0 +1,306 @@
(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;
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = url;
link.id = id;
link.setAttribute(ATTR, "");
document.head.appendChild(link);
}
function eject(url) {
var el = document.getElementById(linkId(url));
if (el) el.remove();
}
function apply() {
var els = document.querySelectorAll("link[" + 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);
})();