From 14d4451b408124042510d0ea1262a02c18b69702 Mon Sep 17 00:00:00 2001 From: /home/neo <158327205+neoapps-dev@users.noreply.github.com> Date: Sun, 14 Jun 2026 19:27:34 +0300 Subject: [PATCH] init --- .00plugins/themeloader/main.js | 306 +++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 .00plugins/themeloader/main.js diff --git a/.00plugins/themeloader/main.js b/.00plugins/themeloader/main.js new file mode 100644 index 0000000..7ace0fd --- /dev/null +++ b/.00plugins/themeloader/main.js @@ -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); +})();