Merge branch 'main' into dev
This commit is contained in:
commit
4bcbd24d99
6 changed files with 699 additions and 699 deletions
|
|
@ -1,34 +1,34 @@
|
||||||
(function(){
|
(function(){
|
||||||
if(window.__AVIA_CATEGORY_SETTINGS__) return;
|
if(window.__AVIA_CATEGORY_SETTINGS__) return;
|
||||||
window.__AVIA_CATEGORY_SETTINGS__ = true;
|
window.__AVIA_CATEGORY_SETTINGS__ = true;
|
||||||
|
|
||||||
function inject(){
|
function inject(){
|
||||||
|
|
||||||
if(document.getElementById('avia-cloned-settings')) return;
|
if(document.getElementById('avia-cloned-settings')) return;
|
||||||
|
|
||||||
const spans = [...document.querySelectorAll('span')];
|
const spans = [...document.querySelectorAll('span')];
|
||||||
const target = spans.find(s => s.textContent.trim() === "User Settings");
|
const target = spans.find(s => s.textContent.trim() === "User Settings");
|
||||||
if(!target) return;
|
if(!target) return;
|
||||||
|
|
||||||
const container = target.closest('.d_flex.flex-d_column');
|
const container = target.closest('.d_flex.flex-d_column');
|
||||||
if(!container) return;
|
if(!container) return;
|
||||||
|
|
||||||
const clone = container.cloneNode(true);
|
const clone = container.cloneNode(true);
|
||||||
clone.id = "avia-cloned-settings";
|
clone.id = "avia-cloned-settings";
|
||||||
|
|
||||||
const header = clone.querySelector('span');
|
const header = clone.querySelector('span');
|
||||||
if(header) header.textContent = "AVIA CLIENT SETTINGS";
|
if(header) header.textContent = "AVIA CLIENT SETTINGS";
|
||||||
|
|
||||||
const list = clone.querySelector('.d_flex.flex-d_column.gap_var\\(--gap-s\\)');
|
const list = clone.querySelector('.d_flex.flex-d_column.gap_var\\(--gap-s\\)');
|
||||||
if(list) list.innerHTML = "";
|
if(list) list.innerHTML = "";
|
||||||
|
|
||||||
container.parentNode.insertBefore(clone, container.nextSibling);
|
container.parentNode.insertBefore(clone, container.nextSibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
new MutationObserver(() => {
|
new MutationObserver(() => {
|
||||||
inject();
|
inject();
|
||||||
}).observe(document.body, { childList: true, subtree: true });
|
}).observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
inject();
|
inject();
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
@ -1,349 +1,349 @@
|
||||||
(function () {
|
(function () {
|
||||||
|
|
||||||
if (window.__AVIA_FAVORITES_LOADED__) return;
|
if (window.__AVIA_FAVORITES_LOADED__) return;
|
||||||
window.__AVIA_FAVORITES_LOADED__ = true;
|
window.__AVIA_FAVORITES_LOADED__ = true;
|
||||||
|
|
||||||
const STORAGE_KEY = "avia_favorites";
|
const STORAGE_KEY = "avia_favorites";
|
||||||
|
|
||||||
const getFavorites = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
const getFavorites = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
||||||
const setFavorites = (data) => localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
const setFavorites = (data) => localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
||||||
|
|
||||||
function extractYouTubeID(url) {
|
function extractYouTubeID(url) {
|
||||||
const reg = /(?:youtube\.com\/(?:watch\?v=|shorts\/)|youtu\.be\/)([^&?/]+)/;
|
const reg = /(?:youtube\.com\/(?:watch\?v=|shorts\/)|youtu\.be\/)([^&?/]+)/;
|
||||||
const match = url.match(reg);
|
const match = url.match(reg);
|
||||||
return match ? match[1] : null;
|
return match ? match[1] : null;
|
||||||
}
|
|
||||||
|
|
||||||
function toggleFavoritesPanel() {
|
|
||||||
|
|
||||||
let panel = document.getElementById("avia-favorites-panel");
|
|
||||||
if (panel) {
|
|
||||||
panel.style.display = panel.style.display === "none" ? "flex" : "none";
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panel = document.createElement("div");
|
function toggleFavoritesPanel() {
|
||||||
panel.id = "avia-favorites-panel";
|
|
||||||
|
|
||||||
Object.assign(panel.style, {
|
let panel = document.getElementById("avia-favorites-panel");
|
||||||
position: "fixed",
|
if (panel) {
|
||||||
bottom: "40px",
|
panel.style.display = panel.style.display === "none" ? "flex" : "none";
|
||||||
right: "40px",
|
return;
|
||||||
width: "640px",
|
}
|
||||||
height: "580px",
|
|
||||||
background: "#1e1e1e",
|
|
||||||
color: "#fff",
|
|
||||||
borderRadius: "20px",
|
|
||||||
boxShadow: "0 12px 35px rgba(0,0,0,0.45)",
|
|
||||||
zIndex: 999999,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
overflow: "hidden",
|
|
||||||
border: "1px solid rgba(255,255,255,0.08)"
|
|
||||||
});
|
|
||||||
|
|
||||||
const header = document.createElement("div");
|
panel = document.createElement("div");
|
||||||
header.textContent = "Favorites";
|
panel.id = "avia-favorites-panel";
|
||||||
Object.assign(header.style, {
|
|
||||||
padding: "18px",
|
|
||||||
fontWeight: "600",
|
|
||||||
fontSize: "16px",
|
|
||||||
background: "rgba(255,255,255,0.04)",
|
|
||||||
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
|
||||||
cursor: "move",
|
|
||||||
position: "relative",
|
|
||||||
userSelect: "none"
|
|
||||||
});
|
|
||||||
|
|
||||||
const close = document.createElement("div");
|
Object.assign(panel.style, {
|
||||||
close.textContent = "✕";
|
position: "fixed",
|
||||||
Object.assign(close.style, {
|
bottom: "40px",
|
||||||
position: "absolute",
|
right: "40px",
|
||||||
right: "18px",
|
width: "640px",
|
||||||
top: "16px",
|
height: "580px",
|
||||||
cursor: "pointer"
|
background: "#1e1e1e",
|
||||||
});
|
color: "#fff",
|
||||||
close.onclick = () => panel.style.display = "none";
|
borderRadius: "20px",
|
||||||
header.appendChild(close);
|
boxShadow: "0 12px 35px rgba(0,0,0,0.45)",
|
||||||
|
zIndex: 999999,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
overflow: "hidden",
|
||||||
|
border: "1px solid rgba(255,255,255,0.08)"
|
||||||
|
});
|
||||||
|
|
||||||
const inputRow = document.createElement("div");
|
const header = document.createElement("div");
|
||||||
Object.assign(inputRow.style, {
|
header.textContent = "Favorites";
|
||||||
display: "flex",
|
Object.assign(header.style, {
|
||||||
gap: "8px",
|
padding: "18px",
|
||||||
padding: "14px 18px"
|
fontWeight: "600",
|
||||||
});
|
fontSize: "16px",
|
||||||
|
background: "rgba(255,255,255,0.04)",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
||||||
|
cursor: "move",
|
||||||
|
position: "relative",
|
||||||
|
userSelect: "none"
|
||||||
|
});
|
||||||
|
|
||||||
const urlInput = document.createElement("input");
|
const close = document.createElement("div");
|
||||||
urlInput.placeholder = "Paste link...";
|
close.textContent = "✕";
|
||||||
Object.assign(urlInput.style, {
|
Object.assign(close.style, {
|
||||||
flex: "2",
|
|
||||||
padding: "10px",
|
|
||||||
borderRadius: "10px",
|
|
||||||
border: "none",
|
|
||||||
outline: "none"
|
|
||||||
});
|
|
||||||
|
|
||||||
const titleInput = document.createElement("input");
|
|
||||||
titleInput.placeholder = "Optional title...";
|
|
||||||
Object.assign(titleInput.style, {
|
|
||||||
flex: "1",
|
|
||||||
padding: "10px",
|
|
||||||
borderRadius: "10px",
|
|
||||||
border: "none",
|
|
||||||
outline: "none"
|
|
||||||
});
|
|
||||||
|
|
||||||
const addBtn = document.createElement("button");
|
|
||||||
addBtn.textContent = "Add";
|
|
||||||
Object.assign(addBtn.style, {
|
|
||||||
padding: "10px 16px",
|
|
||||||
borderRadius: "10px",
|
|
||||||
border: "none",
|
|
||||||
cursor: "pointer"
|
|
||||||
});
|
|
||||||
|
|
||||||
inputRow.appendChild(urlInput);
|
|
||||||
inputRow.appendChild(titleInput);
|
|
||||||
inputRow.appendChild(addBtn);
|
|
||||||
|
|
||||||
const grid = document.createElement("div");
|
|
||||||
Object.assign(grid.style, {
|
|
||||||
flex: "1",
|
|
||||||
minHeight: "0",
|
|
||||||
overflowY: "auto",
|
|
||||||
padding: "18px",
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "repeat(auto-fill, 120px)",
|
|
||||||
gap: "14px",
|
|
||||||
alignContent: "start"
|
|
||||||
});
|
|
||||||
|
|
||||||
panel.appendChild(header);
|
|
||||||
panel.appendChild(inputRow);
|
|
||||||
panel.appendChild(grid);
|
|
||||||
document.body.appendChild(panel);
|
|
||||||
|
|
||||||
let isDragging = false, offsetX, offsetY;
|
|
||||||
|
|
||||||
header.addEventListener("mousedown", e => {
|
|
||||||
isDragging = true;
|
|
||||||
offsetX = e.clientX - panel.offsetLeft;
|
|
||||||
offsetY = e.clientY - panel.offsetTop;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("mouseup", () => isDragging = false);
|
|
||||||
|
|
||||||
document.addEventListener("mousemove", e => {
|
|
||||||
if (!isDragging) return;
|
|
||||||
panel.style.left = (e.clientX - offsetX) + "px";
|
|
||||||
panel.style.top = (e.clientY - offsetY) + "px";
|
|
||||||
panel.style.right = "auto";
|
|
||||||
panel.style.bottom = "auto";
|
|
||||||
});
|
|
||||||
|
|
||||||
function showToast(card) {
|
|
||||||
const toast = document.createElement("div");
|
|
||||||
toast.textContent = "Copied to clipboard";
|
|
||||||
Object.assign(toast.style, {
|
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: "6px",
|
right: "18px",
|
||||||
left: "50%",
|
top: "16px",
|
||||||
transform: "translateX(-50%)",
|
cursor: "pointer"
|
||||||
background: "rgba(0,0,0,0.85)",
|
|
||||||
padding: "6px 10px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
fontSize: "11px",
|
|
||||||
opacity: "0",
|
|
||||||
transition: "opacity 0.2s",
|
|
||||||
pointerEvents: "none"
|
|
||||||
});
|
});
|
||||||
card.appendChild(toast);
|
close.onclick = () => panel.style.display = "none";
|
||||||
requestAnimationFrame(() => toast.style.opacity = "1");
|
header.appendChild(close);
|
||||||
setTimeout(() => {
|
|
||||||
toast.style.opacity = "0";
|
|
||||||
setTimeout(() => toast.remove(), 200);
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fallbackCopy(text) {
|
const inputRow = document.createElement("div");
|
||||||
const textarea = document.createElement("textarea");
|
Object.assign(inputRow.style, {
|
||||||
textarea.value = text;
|
display: "flex",
|
||||||
textarea.style.position = "fixed";
|
gap: "8px",
|
||||||
textarea.style.opacity = "0";
|
padding: "14px 18px"
|
||||||
document.body.appendChild(textarea);
|
});
|
||||||
textarea.focus();
|
|
||||||
textarea.select();
|
|
||||||
try { document.execCommand("copy"); } catch {}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
|
|
||||||
function render() {
|
const urlInput = document.createElement("input");
|
||||||
|
urlInput.placeholder = "Paste link...";
|
||||||
|
Object.assign(urlInput.style, {
|
||||||
|
flex: "2",
|
||||||
|
padding: "10px",
|
||||||
|
borderRadius: "10px",
|
||||||
|
border: "none",
|
||||||
|
outline: "none"
|
||||||
|
});
|
||||||
|
|
||||||
grid.innerHTML = "";
|
const titleInput = document.createElement("input");
|
||||||
const favorites = getFavorites();
|
titleInput.placeholder = "Optional title...";
|
||||||
|
Object.assign(titleInput.style, {
|
||||||
|
flex: "1",
|
||||||
|
padding: "10px",
|
||||||
|
borderRadius: "10px",
|
||||||
|
border: "none",
|
||||||
|
outline: "none"
|
||||||
|
});
|
||||||
|
|
||||||
favorites.forEach(item => {
|
const addBtn = document.createElement("button");
|
||||||
|
addBtn.textContent = "Add";
|
||||||
|
Object.assign(addBtn.style, {
|
||||||
|
padding: "10px 16px",
|
||||||
|
borderRadius: "10px",
|
||||||
|
border: "none",
|
||||||
|
cursor: "pointer"
|
||||||
|
});
|
||||||
|
|
||||||
const card = document.createElement("div");
|
inputRow.appendChild(urlInput);
|
||||||
Object.assign(card.style, {
|
inputRow.appendChild(titleInput);
|
||||||
position: "relative",
|
inputRow.appendChild(addBtn);
|
||||||
width: "120px",
|
|
||||||
height: "120px",
|
|
||||||
borderRadius: "14px",
|
|
||||||
overflow: "hidden",
|
|
||||||
background: "rgba(255,255,255,0.05)",
|
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
});
|
|
||||||
|
|
||||||
const remove = document.createElement("div");
|
const grid = document.createElement("div");
|
||||||
remove.textContent = "✕";
|
Object.assign(grid.style, {
|
||||||
Object.assign(remove.style, {
|
flex: "1",
|
||||||
|
minHeight: "0",
|
||||||
|
overflowY: "auto",
|
||||||
|
padding: "18px",
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(auto-fill, 120px)",
|
||||||
|
gap: "14px",
|
||||||
|
alignContent: "start"
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.appendChild(header);
|
||||||
|
panel.appendChild(inputRow);
|
||||||
|
panel.appendChild(grid);
|
||||||
|
document.body.appendChild(panel);
|
||||||
|
|
||||||
|
let isDragging = false, offsetX, offsetY;
|
||||||
|
|
||||||
|
header.addEventListener("mousedown", e => {
|
||||||
|
isDragging = true;
|
||||||
|
offsetX = e.clientX - panel.offsetLeft;
|
||||||
|
offsetY = e.clientY - panel.offsetTop;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("mouseup", () => isDragging = false);
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", e => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
panel.style.left = (e.clientX - offsetX) + "px";
|
||||||
|
panel.style.top = (e.clientY - offsetY) + "px";
|
||||||
|
panel.style.right = "auto";
|
||||||
|
panel.style.bottom = "auto";
|
||||||
|
});
|
||||||
|
|
||||||
|
function showToast(card) {
|
||||||
|
const toast = document.createElement("div");
|
||||||
|
toast.textContent = "Copied to clipboard";
|
||||||
|
Object.assign(toast.style, {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "6px",
|
bottom: "6px",
|
||||||
right: "8px",
|
left: "50%",
|
||||||
fontSize: "12px",
|
transform: "translateX(-50%)",
|
||||||
cursor: "pointer",
|
background: "rgba(0,0,0,0.85)",
|
||||||
background: "rgba(0,0,0,0.6)",
|
padding: "6px 10px",
|
||||||
padding: "2px 6px",
|
borderRadius: "8px",
|
||||||
borderRadius: "6px",
|
fontSize: "11px",
|
||||||
zIndex: 2
|
opacity: "0",
|
||||||
|
transition: "opacity 0.2s",
|
||||||
|
pointerEvents: "none"
|
||||||
});
|
});
|
||||||
|
card.appendChild(toast);
|
||||||
|
requestAnimationFrame(() => toast.style.opacity = "1");
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.style.opacity = "0";
|
||||||
|
setTimeout(() => toast.remove(), 200);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
remove.onclick = (e) => {
|
function fallbackCopy(text) {
|
||||||
e.stopPropagation();
|
const textarea = document.createElement("textarea");
|
||||||
setFavorites(favorites.filter(f => f.url !== item.url));
|
textarea.value = text;
|
||||||
render();
|
textarea.style.position = "fixed";
|
||||||
};
|
textarea.style.opacity = "0";
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.focus();
|
||||||
|
textarea.select();
|
||||||
|
try { document.execCommand("copy"); } catch {}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
|
||||||
card.appendChild(remove);
|
function render() {
|
||||||
|
|
||||||
let mediaAdded = false;
|
grid.innerHTML = "";
|
||||||
|
const favorites = getFavorites();
|
||||||
|
|
||||||
const ytID = extractYouTubeID(item.url);
|
favorites.forEach(item => {
|
||||||
if (ytID) {
|
|
||||||
const img = new Image();
|
|
||||||
img.src = `https://img.youtube.com/vi/${ytID}/hqdefault.jpg`;
|
|
||||||
Object.assign(img.style, { width:"100%", height:"100%", objectFit:"cover" });
|
|
||||||
card.appendChild(img);
|
|
||||||
mediaAdded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mediaAdded) {
|
const card = document.createElement("div");
|
||||||
const ext = item.url.split(".").pop().split("?")[0].toLowerCase();
|
Object.assign(card.style, {
|
||||||
const isVideo = ["mp4","webm","mov","gifv"].includes(ext);
|
position: "relative",
|
||||||
|
width: "120px",
|
||||||
|
height: "120px",
|
||||||
|
borderRadius: "14px",
|
||||||
|
overflow: "hidden",
|
||||||
|
background: "rgba(255,255,255,0.05)",
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center"
|
||||||
|
});
|
||||||
|
|
||||||
if (isVideo) {
|
const remove = document.createElement("div");
|
||||||
const video = document.createElement("video");
|
remove.textContent = "✕";
|
||||||
video.src = item.url.replace(".gifv",".mp4");
|
Object.assign(remove.style, {
|
||||||
video.autoplay = true;
|
position: "absolute",
|
||||||
video.loop = true;
|
top: "6px",
|
||||||
video.muted = true;
|
right: "8px",
|
||||||
video.playsInline = true;
|
fontSize: "12px",
|
||||||
Object.assign(video.style, { width:"100%", height:"100%", objectFit:"cover" });
|
cursor: "pointer",
|
||||||
video.onerror = fallback;
|
background: "rgba(0,0,0,0.6)",
|
||||||
card.appendChild(video);
|
padding: "2px 6px",
|
||||||
} else {
|
borderRadius: "6px",
|
||||||
const img = new Image();
|
zIndex: 2
|
||||||
img.src = item.url;
|
});
|
||||||
Object.assign(img.style, { width:"100%", height:"100%", objectFit:"cover" });
|
|
||||||
img.onerror = fallback;
|
remove.onclick = (e) => {
|
||||||
card.appendChild(img);
|
e.stopPropagation();
|
||||||
}
|
setFavorites(favorites.filter(f => f.url !== item.url));
|
||||||
}
|
render();
|
||||||
|
};
|
||||||
|
|
||||||
function fallback() {
|
|
||||||
card.innerHTML = "";
|
|
||||||
card.appendChild(remove);
|
card.appendChild(remove);
|
||||||
const text = document.createElement("div");
|
|
||||||
text.textContent = item.title || item.url;
|
|
||||||
Object.assign(text.style, {
|
|
||||||
padding:"8px",
|
|
||||||
fontSize:"11px",
|
|
||||||
textAlign:"center",
|
|
||||||
wordBreak:"break-word"
|
|
||||||
});
|
|
||||||
card.appendChild(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.title) {
|
let mediaAdded = false;
|
||||||
const titleOverlay = document.createElement("div");
|
|
||||||
titleOverlay.textContent = item.title;
|
|
||||||
Object.assign(titleOverlay.style, {
|
|
||||||
position:"absolute",
|
|
||||||
bottom:"0",
|
|
||||||
width:"100%",
|
|
||||||
background:"rgba(0,0,0,0.6)",
|
|
||||||
fontSize:"11px",
|
|
||||||
padding:"4px",
|
|
||||||
textAlign:"center",
|
|
||||||
whiteSpace:"nowrap",
|
|
||||||
overflow:"hidden",
|
|
||||||
textOverflow:"ellipsis"
|
|
||||||
});
|
|
||||||
card.appendChild(titleOverlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
card.onclick = () => {
|
const ytID = extractYouTubeID(item.url);
|
||||||
const doToast = () => showToast(card);
|
if (ytID) {
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
const img = new Image();
|
||||||
navigator.clipboard.writeText(item.url)
|
img.src = `https://img.youtube.com/vi/${ytID}/hqdefault.jpg`;
|
||||||
.then(doToast)
|
Object.assign(img.style, { width:"100%", height:"100%", objectFit:"cover" });
|
||||||
.catch(() => {
|
card.appendChild(img);
|
||||||
fallbackCopy(item.url);
|
mediaAdded = true;
|
||||||
doToast();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
fallbackCopy(item.url);
|
|
||||||
doToast();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
grid.appendChild(card);
|
if (!mediaAdded) {
|
||||||
});
|
const ext = item.url.split(".").pop().split("?")[0].toLowerCase();
|
||||||
|
const isVideo = ["mp4","webm","mov","gifv"].includes(ext);
|
||||||
|
|
||||||
|
if (isVideo) {
|
||||||
|
const video = document.createElement("video");
|
||||||
|
video.src = item.url.replace(".gifv",".mp4");
|
||||||
|
video.autoplay = true;
|
||||||
|
video.loop = true;
|
||||||
|
video.muted = true;
|
||||||
|
video.playsInline = true;
|
||||||
|
Object.assign(video.style, { width:"100%", height:"100%", objectFit:"cover" });
|
||||||
|
video.onerror = fallback;
|
||||||
|
card.appendChild(video);
|
||||||
|
} else {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = item.url;
|
||||||
|
Object.assign(img.style, { width:"100%", height:"100%", objectFit:"cover" });
|
||||||
|
img.onerror = fallback;
|
||||||
|
card.appendChild(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fallback() {
|
||||||
|
card.innerHTML = "";
|
||||||
|
card.appendChild(remove);
|
||||||
|
const text = document.createElement("div");
|
||||||
|
text.textContent = item.title || item.url;
|
||||||
|
Object.assign(text.style, {
|
||||||
|
padding:"8px",
|
||||||
|
fontSize:"11px",
|
||||||
|
textAlign:"center",
|
||||||
|
wordBreak:"break-word"
|
||||||
|
});
|
||||||
|
card.appendChild(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.title) {
|
||||||
|
const titleOverlay = document.createElement("div");
|
||||||
|
titleOverlay.textContent = item.title;
|
||||||
|
Object.assign(titleOverlay.style, {
|
||||||
|
position:"absolute",
|
||||||
|
bottom:"0",
|
||||||
|
width:"100%",
|
||||||
|
background:"rgba(0,0,0,0.6)",
|
||||||
|
fontSize:"11px",
|
||||||
|
padding:"4px",
|
||||||
|
textAlign:"center",
|
||||||
|
whiteSpace:"nowrap",
|
||||||
|
overflow:"hidden",
|
||||||
|
textOverflow:"ellipsis"
|
||||||
|
});
|
||||||
|
card.appendChild(titleOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
card.onclick = () => {
|
||||||
|
const doToast = () => showToast(card);
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
navigator.clipboard.writeText(item.url)
|
||||||
|
.then(doToast)
|
||||||
|
.catch(() => {
|
||||||
|
fallbackCopy(item.url);
|
||||||
|
doToast();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fallbackCopy(item.url);
|
||||||
|
doToast();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
grid.appendChild(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addBtn.onclick = () => {
|
||||||
|
const url = urlInput.value.trim();
|
||||||
|
const title = titleInput.value.trim();
|
||||||
|
if (!url) return;
|
||||||
|
const favorites = getFavorites();
|
||||||
|
if (favorites.some(f => f.url === url)) return;
|
||||||
|
favorites.push({ url, title, addedAt: Date.now() });
|
||||||
|
setFavorites(favorites);
|
||||||
|
urlInput.value = "";
|
||||||
|
titleInput.value = "";
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
|
||||||
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
addBtn.onclick = () => {
|
function injectButton() {
|
||||||
const url = urlInput.value.trim();
|
|
||||||
const title = titleInput.value.trim();
|
|
||||||
if (!url) return;
|
|
||||||
const favorites = getFavorites();
|
|
||||||
if (favorites.some(f => f.url === url)) return;
|
|
||||||
favorites.push({ url, title, addedAt: Date.now() });
|
|
||||||
setFavorites(favorites);
|
|
||||||
urlInput.value = "";
|
|
||||||
titleInput.value = "";
|
|
||||||
render();
|
|
||||||
};
|
|
||||||
|
|
||||||
render();
|
if (document.getElementById("avia-favorites-btn")) return;
|
||||||
}
|
|
||||||
|
|
||||||
function injectButton() {
|
const gifSpan = [...document.querySelectorAll("span.material-symbols-outlined")]
|
||||||
|
.find(s => s.textContent.trim() === "gif");
|
||||||
|
|
||||||
if (document.getElementById("avia-favorites-btn")) return;
|
if (!gifSpan) return;
|
||||||
|
|
||||||
const gifSpan = [...document.querySelectorAll("span.material-symbols-outlined")]
|
const wrapper = gifSpan.closest("div.flex-sh_0");
|
||||||
.find(s => s.textContent.trim() === "gif");
|
if (!wrapper) return;
|
||||||
|
|
||||||
if (!gifSpan) return;
|
const clone = wrapper.cloneNode(true);
|
||||||
|
clone.id = "avia-favorites-btn";
|
||||||
|
clone.querySelector("span.material-symbols-outlined").textContent = "star";
|
||||||
|
clone.querySelector("button").onclick = toggleFavoritesPanel;
|
||||||
|
|
||||||
const wrapper = gifSpan.closest("div.flex-sh_0");
|
wrapper.parentElement.insertBefore(clone, wrapper.nextSibling);
|
||||||
if (!wrapper) return;
|
}
|
||||||
|
|
||||||
const clone = wrapper.cloneNode(true);
|
new MutationObserver(injectButton)
|
||||||
clone.id = "avia-favorites-btn";
|
.observe(document.body, { childList: true, subtree: true });
|
||||||
clone.querySelector("span.material-symbols-outlined").textContent = "star";
|
|
||||||
clone.querySelector("button").onclick = toggleFavoritesPanel;
|
|
||||||
|
|
||||||
wrapper.parentElement.insertBefore(clone, wrapper.nextSibling);
|
injectButton();
|
||||||
}
|
|
||||||
|
|
||||||
new MutationObserver(injectButton)
|
|
||||||
.observe(document.body, { childList: true, subtree: true });
|
|
||||||
|
|
||||||
injectButton();
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,40 @@
|
||||||
(function () {
|
(function () {
|
||||||
|
|
||||||
if (window.__AVIA_VERSION_PATCH__) return;
|
if (window.__AVIA_VERSION_PATCH__) return;
|
||||||
window.__AVIA_VERSION_PATCH__ = true;
|
window.__AVIA_VERSION_PATCH__ = true;
|
||||||
|
|
||||||
function patchVersion() {
|
function patchVersion() {
|
||||||
|
|
||||||
document
|
document
|
||||||
.querySelectorAll("span.lh_1rem.fs_0\\.75rem.ls_0\\.03125rem.fw_500")
|
.querySelectorAll("span.lh_1rem.fs_0\\.75rem.ls_0\\.03125rem.fw_500")
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
|
|
||||||
if (el.dataset.aviaPatched) return;
|
if (el.dataset.aviaPatched) return;
|
||||||
|
|
||||||
const match = el.textContent.match(/Stoat for Desktop\s+([0-9.]+)/);
|
const match = el.textContent.match(/Stoat for Desktop\s+([0-9.]+)/);
|
||||||
|
|
||||||
if (!match) return;
|
if (!match) return;
|
||||||
|
|
||||||
const stoatVersion = match[1];
|
const stoatVersion = match[1];
|
||||||
|
|
||||||
el.dataset.aviaPatched = "true";
|
el.dataset.aviaPatched = "true";
|
||||||
|
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
Avia Client Desktop<br>
|
Avia Client Desktop<br>
|
||||||
<span style="font-size:10px;opacity:0.7;">
|
<span style="font-size:10px;opacity:0.7;">
|
||||||
Based on Stoat ${stoatVersion}
|
Based on Stoat ${stoatVersion}
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
const observer = new MutationObserver(patchVersion);
|
||||||
|
|
||||||
const observer = new MutationObserver(patchVersion);
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
observer.observe(document.body, {
|
patchVersion();
|
||||||
childList: true,
|
|
||||||
subtree: true
|
|
||||||
});
|
|
||||||
|
|
||||||
patchVersion();
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -243,15 +243,15 @@
|
||||||
const appearanceBtn = Array.from(document.querySelectorAll('a')).find(a => a.textContent.trim() === 'Appearance');
|
const appearanceBtn = Array.from(document.querySelectorAll('a')).find(a => a.textContent.trim() === 'Appearance');
|
||||||
if (!appearanceBtn) return;
|
if (!appearanceBtn) return;
|
||||||
|
|
||||||
const aviaHeader = [...document.querySelectorAll('span')]
|
const aviaHeader = [...document.querySelectorAll('span')]
|
||||||
.find(s => s.textContent.trim() === "AVIA CLIENT SETTINGS");
|
.find(s => s.textContent.trim() === "AVIA CLIENT SETTINGS");
|
||||||
if (!aviaHeader) return;
|
if (!aviaHeader) return;
|
||||||
|
|
||||||
const aviaContainer = aviaHeader.closest('.d_flex.flex-d_column');
|
const aviaContainer = aviaHeader.closest('.d_flex.flex-d_column');
|
||||||
if (!aviaContainer) return;
|
if (!aviaContainer) return;
|
||||||
|
|
||||||
const targetParent = aviaContainer.querySelector('.d_flex.flex-d_column.gap_var\\(--gap-s\\)');
|
const targetParent = aviaContainer.querySelector('.d_flex.flex-d_column.gap_var\\(--gap-s\\)');
|
||||||
if (!targetParent) return;
|
if (!targetParent) return;
|
||||||
|
|
||||||
if (!document.getElementById('stoat-fake-linktree')) {
|
if (!document.getElementById('stoat-fake-linktree')) {
|
||||||
const linktreeBtn = appearanceBtn.cloneNode(true);
|
const linktreeBtn = appearanceBtn.cloneNode(true);
|
||||||
|
|
@ -305,7 +305,8 @@ if (!targetParent) return;
|
||||||
document.getElementById('stoat-fake-loadfont') ||
|
document.getElementById('stoat-fake-loadfont') ||
|
||||||
document.getElementById('stoat-fake-stoatserver') ||
|
document.getElementById('stoat-fake-stoatserver') ||
|
||||||
document.getElementById('stoat-fake-linktree');
|
document.getElementById('stoat-fake-linktree');
|
||||||
targetParent.appendChild(quickCssBtn); }
|
targetParent.appendChild(quickCssBtn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyQuickCSS(css) {
|
function applyQuickCSS(css) {
|
||||||
|
|
|
||||||
678
src/themes.js
678
src/themes.js
|
|
@ -1,12 +1,12 @@
|
||||||
(function () {
|
(function () {
|
||||||
|
|
||||||
if (window.__AVIA_THEMES_LOADED__) return;
|
if (window.__AVIA_THEMES_LOADED__) return;
|
||||||
window.__AVIA_THEMES_LOADED__ = true;
|
window.__AVIA_THEMES_LOADED__ = true;
|
||||||
|
|
||||||
const STORAGE_KEY = "avia_themes";
|
const STORAGE_KEY = "avia_themes";
|
||||||
let editingTheme = null;
|
let editingTheme = null;
|
||||||
|
|
||||||
const TEMPLATE = `/*
|
const TEMPLATE = `/*
|
||||||
@name Whatever name here
|
@name Whatever name here
|
||||||
@author Whatever Author Here
|
@author Whatever Author Here
|
||||||
@version 1.0
|
@version 1.0
|
||||||
|
|
@ -15,367 +15,367 @@ const TEMPLATE = `/*
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const getThemes = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
const getThemes = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
||||||
const setThemes = (data) => localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
const setThemes = (data) => localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
||||||
|
|
||||||
function parseMeta(css){
|
function parseMeta(css){
|
||||||
const name = css.match(/@name\s+(.+)/)?.[1] || "Unknown Theme";
|
const name = css.match(/@name\s+(.+)/)?.[1] || "Unknown Theme";
|
||||||
const author = css.match(/@author\s+(.+)/)?.[1] || "Unknown";
|
const author = css.match(/@author\s+(.+)/)?.[1] || "Unknown";
|
||||||
const version = css.match(/@version\s+(.+)/)?.[1] || "1.0";
|
const version = css.match(/@version\s+(.+)/)?.[1] || "1.0";
|
||||||
const rawDescription = css.match(/@description\s+(.+)/)?.[1] || "No Description Available";
|
const rawDescription = css.match(/@description\s+(.+)/)?.[1] || "No Description Available";
|
||||||
const description = rawDescription.trim() === "*/" ? "No Description Available" : rawDescription;
|
const description = rawDescription.trim() === "*/" ? "No Description Available" : rawDescription;
|
||||||
return {name,author,version,description};
|
return {name,author,version,description};
|
||||||
}
|
|
||||||
|
|
||||||
function applyThemes(){
|
|
||||||
document.querySelectorAll(".avia-theme-style").forEach(e=>e.remove());
|
|
||||||
const themes = getThemes();
|
|
||||||
themes.forEach(theme=>{
|
|
||||||
if(!theme.enabled) return;
|
|
||||||
const style=document.createElement("style");
|
|
||||||
style.className="avia-theme-style";
|
|
||||||
style.textContent=theme.css;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function styleBtn(btn, bg) {
|
|
||||||
Object.assign(btn.style, {
|
|
||||||
padding: "5px 12px",
|
|
||||||
borderRadius: "8px",
|
|
||||||
border: "none",
|
|
||||||
background: bg || "rgba(255,255,255,0.08)",
|
|
||||||
color: "#fff",
|
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: "12px",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
fontWeight: "500"
|
|
||||||
});
|
|
||||||
btn.onmouseenter = () => btn.style.opacity = "0.75";
|
|
||||||
btn.onmouseleave = () => btn.style.opacity = "1";
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeDraggable(panel, handle){
|
|
||||||
let dragging=false,offsetX,offsetY;
|
|
||||||
handle.addEventListener("mousedown",e=>{
|
|
||||||
dragging=true;
|
|
||||||
offsetX=e.clientX-panel.offsetLeft;
|
|
||||||
offsetY=e.clientY-panel.offsetTop;
|
|
||||||
document.body.style.userSelect="none";
|
|
||||||
});
|
|
||||||
document.addEventListener("mouseup",()=>{dragging=false;document.body.style.userSelect="";});
|
|
||||||
document.addEventListener("mousemove",e=>{
|
|
||||||
if(!dragging) return;
|
|
||||||
panel.style.left=(e.clientX-offsetX)+"px";
|
|
||||||
panel.style.top=(e.clientY-offsetY)+"px";
|
|
||||||
panel.style.right="auto";
|
|
||||||
panel.style.bottom="auto";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openThemeEditor(theme){
|
|
||||||
editingTheme = theme;
|
|
||||||
let panel = document.getElementById('avia-theme-editor');
|
|
||||||
if(panel){
|
|
||||||
panel.style.display="flex";
|
|
||||||
panel.querySelector("textarea").value = theme.css;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
panel=document.createElement("div");
|
|
||||||
panel.id="avia-theme-editor";
|
|
||||||
Object.assign(panel.style,{
|
|
||||||
position:"fixed",
|
|
||||||
bottom:"24px",
|
|
||||||
right:"24px",
|
|
||||||
width:"420px",
|
|
||||||
height:"340px",
|
|
||||||
background:"var(--md-sys-color-surface,#1e1e1e)",
|
|
||||||
color:"var(--md-sys-color-on-surface,#fff)",
|
|
||||||
borderRadius:"16px",
|
|
||||||
boxShadow:"0 8px 28px rgba(0,0,0,0.35)",
|
|
||||||
zIndex:999999,
|
|
||||||
display:"flex",
|
|
||||||
flexDirection:"column",
|
|
||||||
overflow:"hidden",
|
|
||||||
border:"1px solid rgba(255,255,255,0.08)",
|
|
||||||
backdropFilter:"blur(12px)"
|
|
||||||
});
|
|
||||||
const header=document.createElement("div");
|
|
||||||
header.textContent="Theme Editor";
|
|
||||||
Object.assign(header.style,{
|
|
||||||
padding:"14px 16px",
|
|
||||||
fontWeight:"600",
|
|
||||||
fontSize:"14px",
|
|
||||||
background:"var(--md-sys-color-surface-container,rgba(255,255,255,0.04))",
|
|
||||||
borderBottom:"1px solid rgba(255,255,255,0.08)",
|
|
||||||
cursor:"move"
|
|
||||||
});
|
|
||||||
makeDraggable(panel,header);
|
|
||||||
const close=document.createElement("div");
|
|
||||||
close.textContent="✕";
|
|
||||||
Object.assign(close.style,{
|
|
||||||
position:"absolute",
|
|
||||||
right:"16px",
|
|
||||||
top:"12px",
|
|
||||||
cursor:"pointer",
|
|
||||||
opacity:"0.6",
|
|
||||||
fontSize:"15px",
|
|
||||||
lineHeight:"1",
|
|
||||||
padding:"2px 4px"
|
|
||||||
});
|
|
||||||
close.onmouseenter=()=>close.style.opacity="1";
|
|
||||||
close.onmouseleave=()=>close.style.opacity="0.6";
|
|
||||||
close.onclick=()=>panel.style.display="none";
|
|
||||||
const textarea=document.createElement("textarea");
|
|
||||||
Object.assign(textarea.style,{
|
|
||||||
flex:"1",
|
|
||||||
border:"none",
|
|
||||||
outline:"none",
|
|
||||||
resize:"none",
|
|
||||||
padding:"16px",
|
|
||||||
background:"transparent",
|
|
||||||
color:"inherit",
|
|
||||||
fontFamily:"monospace",
|
|
||||||
fontSize:"13px"
|
|
||||||
});
|
|
||||||
textarea.value=theme.css;
|
|
||||||
textarea.addEventListener("input",()=>{
|
|
||||||
const themes=getThemes();
|
|
||||||
const t=themes.find(x=>x.id===editingTheme.id);
|
|
||||||
if(!t) return;
|
|
||||||
t.css=textarea.value;
|
|
||||||
setThemes(themes);
|
|
||||||
applyThemes();
|
|
||||||
if(window.__avia_refresh_themes_panel){window.__avia_refresh_themes_panel();}
|
|
||||||
});
|
|
||||||
panel.appendChild(header);
|
|
||||||
panel.appendChild(close);
|
|
||||||
panel.appendChild(textarea);
|
|
||||||
document.body.appendChild(panel);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleThemesPanel(){
|
function applyThemes(){
|
||||||
let panel=document.getElementById("avia-themes-panel");
|
document.querySelectorAll(".avia-theme-style").forEach(e=>e.remove());
|
||||||
if(panel){
|
const themes = getThemes();
|
||||||
panel.style.display = panel.style.display==="none"?"flex":"none";
|
themes.forEach(theme=>{
|
||||||
return;
|
if(!theme.enabled) return;
|
||||||
|
const style=document.createElement("style");
|
||||||
|
style.className="avia-theme-style";
|
||||||
|
style.textContent=theme.css;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
panel=document.createElement("div");
|
|
||||||
panel.id="avia-themes-panel";
|
|
||||||
Object.assign(panel.style,{
|
|
||||||
position:"fixed",
|
|
||||||
bottom:"40px",
|
|
||||||
right:"40px",
|
|
||||||
width:"500px",
|
|
||||||
height:"460px",
|
|
||||||
background:"var(--md-sys-color-surface,#1e1e1e)",
|
|
||||||
color:"var(--md-sys-color-on-surface,#fff)",
|
|
||||||
borderRadius:"16px",
|
|
||||||
boxShadow:"0 8px 28px rgba(0,0,0,0.35)",
|
|
||||||
zIndex:999999,
|
|
||||||
display:"flex",
|
|
||||||
flexDirection:"column",
|
|
||||||
overflow:"hidden",
|
|
||||||
border:"1px solid rgba(255,255,255,0.08)",
|
|
||||||
backdropFilter:"blur(12px)"
|
|
||||||
});
|
|
||||||
|
|
||||||
const header=document.createElement("div");
|
function styleBtn(btn, bg) {
|
||||||
header.textContent="Themes";
|
Object.assign(btn.style, {
|
||||||
Object.assign(header.style,{
|
padding: "5px 12px",
|
||||||
padding:"14px 16px",
|
borderRadius: "8px",
|
||||||
fontWeight:"600",
|
border: "none",
|
||||||
fontSize:"14px",
|
background: bg || "rgba(255,255,255,0.08)",
|
||||||
background:"var(--md-sys-color-surface-container,rgba(255,255,255,0.04))",
|
color: "#fff",
|
||||||
borderBottom:"1px solid rgba(255,255,255,0.08)",
|
cursor: "pointer",
|
||||||
cursor:"move"
|
fontSize: "12px",
|
||||||
});
|
whiteSpace: "nowrap",
|
||||||
makeDraggable(panel,header);
|
fontWeight: "500"
|
||||||
|
});
|
||||||
|
btn.onmouseenter = () => btn.style.opacity = "0.75";
|
||||||
|
btn.onmouseleave = () => btn.style.opacity = "1";
|
||||||
|
}
|
||||||
|
|
||||||
const close=document.createElement("div");
|
function makeDraggable(panel, handle){
|
||||||
close.textContent="✕";
|
let dragging=false,offsetX,offsetY;
|
||||||
Object.assign(close.style,{
|
handle.addEventListener("mousedown",e=>{
|
||||||
position:"absolute",
|
dragging=true;
|
||||||
right:"16px",
|
offsetX=e.clientX-panel.offsetLeft;
|
||||||
top:"12px",
|
offsetY=e.clientY-panel.offsetTop;
|
||||||
cursor:"pointer",
|
document.body.style.userSelect="none";
|
||||||
opacity:"0.6",
|
});
|
||||||
fontSize:"15px",
|
document.addEventListener("mouseup",()=>{dragging=false;document.body.style.userSelect="";});
|
||||||
lineHeight:"1",
|
document.addEventListener("mousemove",e=>{
|
||||||
padding:"2px 4px"
|
if(!dragging) return;
|
||||||
});
|
panel.style.left=(e.clientX-offsetX)+"px";
|
||||||
close.onmouseenter=()=>close.style.opacity="1";
|
panel.style.top=(e.clientY-offsetY)+"px";
|
||||||
close.onmouseleave=()=>close.style.opacity="0.6";
|
panel.style.right="auto";
|
||||||
close.onclick=()=>panel.style.display="none";
|
panel.style.bottom="auto";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const btnRow=document.createElement("div");
|
function openThemeEditor(theme){
|
||||||
Object.assign(btnRow.style,{
|
editingTheme = theme;
|
||||||
display:"flex",
|
let panel = document.getElementById('avia-theme-editor');
|
||||||
gap:"8px",
|
if(panel){
|
||||||
padding:"12px 16px",
|
panel.style.display="flex";
|
||||||
borderBottom:"1px solid rgba(255,255,255,0.08)",
|
panel.querySelector("textarea").value = theme.css;
|
||||||
flex:"0 0 auto"
|
|
||||||
});
|
|
||||||
|
|
||||||
const importBtn=document.createElement("button");
|
|
||||||
importBtn.textContent="Import Theme";
|
|
||||||
styleBtn(importBtn);
|
|
||||||
importBtn.style.flex="1";
|
|
||||||
importBtn.style.padding="8px 12px";
|
|
||||||
|
|
||||||
const newBtn=document.createElement("button");
|
|
||||||
newBtn.textContent="+ New";
|
|
||||||
styleBtn(newBtn);
|
|
||||||
newBtn.style.flex="1";
|
|
||||||
newBtn.style.padding="8px 12px";
|
|
||||||
|
|
||||||
btnRow.appendChild(importBtn);
|
|
||||||
btnRow.appendChild(newBtn);
|
|
||||||
|
|
||||||
const list=document.createElement("div");
|
|
||||||
Object.assign(list.style,{
|
|
||||||
flex:"1",
|
|
||||||
overflowY:"auto",
|
|
||||||
padding:"16px",
|
|
||||||
display:"flex",
|
|
||||||
flexDirection:"column",
|
|
||||||
gap:"8px"
|
|
||||||
});
|
|
||||||
|
|
||||||
panel.appendChild(header);
|
|
||||||
panel.appendChild(close);
|
|
||||||
panel.appendChild(btnRow);
|
|
||||||
panel.appendChild(list);
|
|
||||||
document.body.appendChild(panel);
|
|
||||||
|
|
||||||
function render(){
|
|
||||||
list.innerHTML="";
|
|
||||||
const themes=getThemes();
|
|
||||||
|
|
||||||
if(themes.length === 0){
|
|
||||||
const empty=document.createElement("div");
|
|
||||||
empty.textContent="No themes yet. Import or create one above.";
|
|
||||||
Object.assign(empty.style,{opacity:"0.4",fontSize:"13px"});
|
|
||||||
list.appendChild(empty);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
panel=document.createElement("div");
|
||||||
|
panel.id="avia-theme-editor";
|
||||||
|
Object.assign(panel.style,{
|
||||||
|
position:"fixed",
|
||||||
|
bottom:"24px",
|
||||||
|
right:"24px",
|
||||||
|
width:"420px",
|
||||||
|
height:"340px",
|
||||||
|
background:"var(--md-sys-color-surface,#1e1e1e)",
|
||||||
|
color:"var(--md-sys-color-on-surface,#fff)",
|
||||||
|
borderRadius:"16px",
|
||||||
|
boxShadow:"0 8px 28px rgba(0,0,0,0.35)",
|
||||||
|
zIndex:999999,
|
||||||
|
display:"flex",
|
||||||
|
flexDirection:"column",
|
||||||
|
overflow:"hidden",
|
||||||
|
border:"1px solid rgba(255,255,255,0.08)",
|
||||||
|
backdropFilter:"blur(12px)"
|
||||||
|
});
|
||||||
|
const header=document.createElement("div");
|
||||||
|
header.textContent="Theme Editor";
|
||||||
|
Object.assign(header.style,{
|
||||||
|
padding:"14px 16px",
|
||||||
|
fontWeight:"600",
|
||||||
|
fontSize:"14px",
|
||||||
|
background:"var(--md-sys-color-surface-container,rgba(255,255,255,0.04))",
|
||||||
|
borderBottom:"1px solid rgba(255,255,255,0.08)",
|
||||||
|
cursor:"move"
|
||||||
|
});
|
||||||
|
makeDraggable(panel,header);
|
||||||
|
const close=document.createElement("div");
|
||||||
|
close.textContent="✕";
|
||||||
|
Object.assign(close.style,{
|
||||||
|
position:"absolute",
|
||||||
|
right:"16px",
|
||||||
|
top:"12px",
|
||||||
|
cursor:"pointer",
|
||||||
|
opacity:"0.6",
|
||||||
|
fontSize:"15px",
|
||||||
|
lineHeight:"1",
|
||||||
|
padding:"2px 4px"
|
||||||
|
});
|
||||||
|
close.onmouseenter=()=>close.style.opacity="1";
|
||||||
|
close.onmouseleave=()=>close.style.opacity="0.6";
|
||||||
|
close.onclick=()=>panel.style.display="none";
|
||||||
|
const textarea=document.createElement("textarea");
|
||||||
|
Object.assign(textarea.style,{
|
||||||
|
flex:"1",
|
||||||
|
border:"none",
|
||||||
|
outline:"none",
|
||||||
|
resize:"none",
|
||||||
|
padding:"16px",
|
||||||
|
background:"transparent",
|
||||||
|
color:"inherit",
|
||||||
|
fontFamily:"monospace",
|
||||||
|
fontSize:"13px"
|
||||||
|
});
|
||||||
|
textarea.value=theme.css;
|
||||||
|
textarea.addEventListener("input",()=>{
|
||||||
|
const themes=getThemes();
|
||||||
|
const t=themes.find(x=>x.id===editingTheme.id);
|
||||||
|
if(!t) return;
|
||||||
|
t.css=textarea.value;
|
||||||
|
setThemes(themes);
|
||||||
|
applyThemes();
|
||||||
|
if(window.__avia_refresh_themes_panel){window.__avia_refresh_themes_panel();}
|
||||||
|
});
|
||||||
|
panel.appendChild(header);
|
||||||
|
panel.appendChild(close);
|
||||||
|
panel.appendChild(textarea);
|
||||||
|
document.body.appendChild(panel);
|
||||||
|
}
|
||||||
|
|
||||||
themes.forEach(theme=>{
|
function toggleThemesPanel(){
|
||||||
const meta=parseMeta(theme.css);
|
let panel=document.getElementById("avia-themes-panel");
|
||||||
|
if(panel){
|
||||||
|
panel.style.display = panel.style.display==="none"?"flex":"none";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
panel=document.createElement("div");
|
||||||
|
panel.id="avia-themes-panel";
|
||||||
|
Object.assign(panel.style,{
|
||||||
|
position:"fixed",
|
||||||
|
bottom:"40px",
|
||||||
|
right:"40px",
|
||||||
|
width:"500px",
|
||||||
|
height:"460px",
|
||||||
|
background:"var(--md-sys-color-surface,#1e1e1e)",
|
||||||
|
color:"var(--md-sys-color-on-surface,#fff)",
|
||||||
|
borderRadius:"16px",
|
||||||
|
boxShadow:"0 8px 28px rgba(0,0,0,0.35)",
|
||||||
|
zIndex:999999,
|
||||||
|
display:"flex",
|
||||||
|
flexDirection:"column",
|
||||||
|
overflow:"hidden",
|
||||||
|
border:"1px solid rgba(255,255,255,0.08)",
|
||||||
|
backdropFilter:"blur(12px)"
|
||||||
|
});
|
||||||
|
|
||||||
const card=document.createElement("div");
|
const header=document.createElement("div");
|
||||||
Object.assign(card.style,{
|
header.textContent="Themes";
|
||||||
display:"flex",
|
Object.assign(header.style,{
|
||||||
justifyContent:"space-between",
|
padding:"14px 16px",
|
||||||
alignItems:"center",
|
fontWeight:"600",
|
||||||
padding:"10px 12px",
|
fontSize:"14px",
|
||||||
borderRadius:"10px",
|
background:"var(--md-sys-color-surface-container,rgba(255,255,255,0.04))",
|
||||||
background:"rgba(255,255,255,0.04)",
|
borderBottom:"1px solid rgba(255,255,255,0.08)",
|
||||||
border:"1px solid rgba(255,255,255,0.06)",
|
cursor:"move"
|
||||||
marginBottom:"0"
|
});
|
||||||
|
makeDraggable(panel,header);
|
||||||
|
|
||||||
|
const close=document.createElement("div");
|
||||||
|
close.textContent="✕";
|
||||||
|
Object.assign(close.style,{
|
||||||
|
position:"absolute",
|
||||||
|
right:"16px",
|
||||||
|
top:"12px",
|
||||||
|
cursor:"pointer",
|
||||||
|
opacity:"0.6",
|
||||||
|
fontSize:"15px",
|
||||||
|
lineHeight:"1",
|
||||||
|
padding:"2px 4px"
|
||||||
|
});
|
||||||
|
close.onmouseenter=()=>close.style.opacity="1";
|
||||||
|
close.onmouseleave=()=>close.style.opacity="0.6";
|
||||||
|
close.onclick=()=>panel.style.display="none";
|
||||||
|
|
||||||
|
const btnRow=document.createElement("div");
|
||||||
|
Object.assign(btnRow.style,{
|
||||||
|
display:"flex",
|
||||||
|
gap:"8px",
|
||||||
|
padding:"12px 16px",
|
||||||
|
borderBottom:"1px solid rgba(255,255,255,0.08)",
|
||||||
|
flex:"0 0 auto"
|
||||||
|
});
|
||||||
|
|
||||||
|
const importBtn=document.createElement("button");
|
||||||
|
importBtn.textContent="Import Theme";
|
||||||
|
styleBtn(importBtn);
|
||||||
|
importBtn.style.flex="1";
|
||||||
|
importBtn.style.padding="8px 12px";
|
||||||
|
|
||||||
|
const newBtn=document.createElement("button");
|
||||||
|
newBtn.textContent="+ New";
|
||||||
|
styleBtn(newBtn);
|
||||||
|
newBtn.style.flex="1";
|
||||||
|
newBtn.style.padding="8px 12px";
|
||||||
|
|
||||||
|
btnRow.appendChild(importBtn);
|
||||||
|
btnRow.appendChild(newBtn);
|
||||||
|
|
||||||
|
const list=document.createElement("div");
|
||||||
|
Object.assign(list.style,{
|
||||||
|
flex:"1",
|
||||||
|
overflowY:"auto",
|
||||||
|
padding:"16px",
|
||||||
|
display:"flex",
|
||||||
|
flexDirection:"column",
|
||||||
|
gap:"8px"
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.appendChild(header);
|
||||||
|
panel.appendChild(close);
|
||||||
|
panel.appendChild(btnRow);
|
||||||
|
panel.appendChild(list);
|
||||||
|
document.body.appendChild(panel);
|
||||||
|
|
||||||
|
function render(){
|
||||||
|
list.innerHTML="";
|
||||||
|
const themes=getThemes();
|
||||||
|
|
||||||
|
if(themes.length === 0){
|
||||||
|
const empty=document.createElement("div");
|
||||||
|
empty.textContent="No themes yet. Import or create one above.";
|
||||||
|
Object.assign(empty.style,{opacity:"0.4",fontSize:"13px"});
|
||||||
|
list.appendChild(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
themes.forEach(theme=>{
|
||||||
|
const meta=parseMeta(theme.css);
|
||||||
|
|
||||||
|
const card=document.createElement("div");
|
||||||
|
Object.assign(card.style,{
|
||||||
|
display:"flex",
|
||||||
|
justifyContent:"space-between",
|
||||||
|
alignItems:"center",
|
||||||
|
padding:"10px 12px",
|
||||||
|
borderRadius:"10px",
|
||||||
|
background:"rgba(255,255,255,0.04)",
|
||||||
|
border:"1px solid rgba(255,255,255,0.06)",
|
||||||
|
marginBottom:"0"
|
||||||
|
});
|
||||||
|
|
||||||
|
const left=document.createElement("div");
|
||||||
|
Object.assign(left.style,{display:"flex",alignItems:"center",gap:"10px"});
|
||||||
|
|
||||||
|
const dot=document.createElement("div");
|
||||||
|
Object.assign(dot.style,{
|
||||||
|
width:"10px",
|
||||||
|
height:"10px",
|
||||||
|
borderRadius:"50%",
|
||||||
|
flexShrink:"0",
|
||||||
|
background: theme.enabled ? "#4dff88" : "#777",
|
||||||
|
boxShadow: theme.enabled ? "0 0 6px #4dff88" : "none"
|
||||||
|
});
|
||||||
|
|
||||||
|
const info=document.createElement("div");
|
||||||
|
info.innerHTML=`<div style="font-weight:600;font-size:13px">${meta.name}</div><div style="font-size:11px;opacity:.5">${meta.author} • v${meta.version}</div><div style="font-size:11px;opacity:.4">${meta.description}</div>`;
|
||||||
|
|
||||||
|
left.appendChild(dot);
|
||||||
|
left.appendChild(info);
|
||||||
|
|
||||||
|
const controls=document.createElement("div");
|
||||||
|
Object.assign(controls.style,{display:"flex",gap:"6px"});
|
||||||
|
|
||||||
|
const toggle=document.createElement("button");
|
||||||
|
toggle.textContent=theme.enabled?"Disable":"Enable";
|
||||||
|
styleBtn(toggle);
|
||||||
|
toggle.onclick=()=>{
|
||||||
|
theme.enabled=!theme.enabled;
|
||||||
|
setThemes(themes);
|
||||||
|
applyThemes();
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
|
||||||
|
const edit=document.createElement("button");
|
||||||
|
edit.textContent="Edit";
|
||||||
|
styleBtn(edit, "rgba(100,160,255,0.15)");
|
||||||
|
edit.onclick=()=>openThemeEditor(theme);
|
||||||
|
|
||||||
|
const del=document.createElement("button");
|
||||||
|
del.textContent="✕";
|
||||||
|
styleBtn(del, "rgba(255,80,80,0.15)");
|
||||||
|
del.onclick=()=>{
|
||||||
|
const updated=themes.filter(t=>t.id!==theme.id);
|
||||||
|
setThemes(updated);
|
||||||
|
applyThemes();
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
|
||||||
|
controls.appendChild(toggle);
|
||||||
|
controls.appendChild(edit);
|
||||||
|
controls.appendChild(del);
|
||||||
|
card.appendChild(left);
|
||||||
|
card.appendChild(controls);
|
||||||
|
list.appendChild(card);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const left=document.createElement("div");
|
window.__avia_refresh_themes_panel = render;
|
||||||
Object.assign(left.style,{display:"flex",alignItems:"center",gap:"10px"});
|
|
||||||
|
|
||||||
const dot=document.createElement("div");
|
importBtn.onclick=()=>{
|
||||||
Object.assign(dot.style,{
|
const input=document.createElement("input");
|
||||||
width:"10px",
|
input.type="file";
|
||||||
height:"10px",
|
input.accept=".css,.txt";
|
||||||
borderRadius:"50%",
|
input.onchange=async()=>{
|
||||||
flexShrink:"0",
|
const file=input.files[0];
|
||||||
background: theme.enabled ? "#4dff88" : "#777",
|
if(!file) return;
|
||||||
boxShadow: theme.enabled ? "0 0 6px #4dff88" : "none"
|
const css=await file.text();
|
||||||
});
|
const themes=getThemes();
|
||||||
|
themes.push({id:crypto.randomUUID(),css,enabled:true});
|
||||||
const info=document.createElement("div");
|
|
||||||
info.innerHTML=`<div style="font-weight:600;font-size:13px">${meta.name}</div><div style="font-size:11px;opacity:.5">${meta.author} • v${meta.version}</div><div style="font-size:11px;opacity:.4">${meta.description}</div>`;
|
|
||||||
|
|
||||||
left.appendChild(dot);
|
|
||||||
left.appendChild(info);
|
|
||||||
|
|
||||||
const controls=document.createElement("div");
|
|
||||||
Object.assign(controls.style,{display:"flex",gap:"6px"});
|
|
||||||
|
|
||||||
const toggle=document.createElement("button");
|
|
||||||
toggle.textContent=theme.enabled?"Disable":"Enable";
|
|
||||||
styleBtn(toggle);
|
|
||||||
toggle.onclick=()=>{
|
|
||||||
theme.enabled=!theme.enabled;
|
|
||||||
setThemes(themes);
|
setThemes(themes);
|
||||||
applyThemes();
|
applyThemes();
|
||||||
render();
|
render();
|
||||||
};
|
};
|
||||||
|
input.click();
|
||||||
|
};
|
||||||
|
|
||||||
const edit=document.createElement("button");
|
newBtn.onclick=()=>{
|
||||||
edit.textContent="Edit";
|
|
||||||
styleBtn(edit, "rgba(100,160,255,0.15)");
|
|
||||||
edit.onclick=()=>openThemeEditor(theme);
|
|
||||||
|
|
||||||
const del=document.createElement("button");
|
|
||||||
del.textContent="✕";
|
|
||||||
styleBtn(del, "rgba(255,80,80,0.15)");
|
|
||||||
del.onclick=()=>{
|
|
||||||
const updated=themes.filter(t=>t.id!==theme.id);
|
|
||||||
setThemes(updated);
|
|
||||||
applyThemes();
|
|
||||||
render();
|
|
||||||
};
|
|
||||||
|
|
||||||
controls.appendChild(toggle);
|
|
||||||
controls.appendChild(edit);
|
|
||||||
controls.appendChild(del);
|
|
||||||
card.appendChild(left);
|
|
||||||
card.appendChild(controls);
|
|
||||||
list.appendChild(card);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__avia_refresh_themes_panel = render;
|
|
||||||
|
|
||||||
importBtn.onclick=()=>{
|
|
||||||
const input=document.createElement("input");
|
|
||||||
input.type="file";
|
|
||||||
input.accept=".css,.txt";
|
|
||||||
input.onchange=async()=>{
|
|
||||||
const file=input.files[0];
|
|
||||||
if(!file) return;
|
|
||||||
const css=await file.text();
|
|
||||||
const themes=getThemes();
|
const themes=getThemes();
|
||||||
themes.push({id:crypto.randomUUID(),css,enabled:true});
|
themes.push({id:crypto.randomUUID(),css:TEMPLATE,enabled:true});
|
||||||
setThemes(themes);
|
setThemes(themes);
|
||||||
applyThemes();
|
applyThemes();
|
||||||
render();
|
render();
|
||||||
};
|
};
|
||||||
input.click();
|
|
||||||
};
|
|
||||||
|
|
||||||
newBtn.onclick=()=>{
|
|
||||||
const themes=getThemes();
|
|
||||||
themes.push({id:crypto.randomUUID(),css:TEMPLATE,enabled:true});
|
|
||||||
setThemes(themes);
|
|
||||||
applyThemes();
|
|
||||||
render();
|
render();
|
||||||
};
|
}
|
||||||
|
|
||||||
render();
|
function injectButton(){
|
||||||
}
|
if(document.getElementById("avia-themes-btn")) return;
|
||||||
|
const appearanceBtn=[...document.querySelectorAll("a")].find(a=>a.textContent.trim()==="Appearance");
|
||||||
|
const quickCSS=document.getElementById("stoat-fake-quickcss");
|
||||||
|
if(!appearanceBtn || !quickCSS) return;
|
||||||
|
const clone=appearanceBtn.cloneNode(true);
|
||||||
|
clone.id="avia-themes-btn";
|
||||||
|
const text=[...clone.querySelectorAll("div")].find(d=>d.children.length===0);
|
||||||
|
if(text) text.textContent="(Avia) Themes";
|
||||||
|
clone.onclick=toggleThemesPanel;
|
||||||
|
quickCSS.parentElement.insertBefore(clone, quickCSS.nextSibling);
|
||||||
|
}
|
||||||
|
|
||||||
function injectButton(){
|
new MutationObserver(injectButton).observe(document.body,{childList:true,subtree:true});
|
||||||
if(document.getElementById("avia-themes-btn")) return;
|
injectButton();
|
||||||
const appearanceBtn=[...document.querySelectorAll("a")].find(a=>a.textContent.trim()==="Appearance");
|
applyThemes();
|
||||||
const quickCSS=document.getElementById("stoat-fake-quickcss");
|
|
||||||
if(!appearanceBtn || !quickCSS) return;
|
|
||||||
const clone=appearanceBtn.cloneNode(true);
|
|
||||||
clone.id="avia-themes-btn";
|
|
||||||
const text=[...clone.querySelectorAll("div")].find(d=>d.children.length===0);
|
|
||||||
if(text) text.textContent="(Avia) Themes";
|
|
||||||
clone.onclick=toggleThemesPanel;
|
|
||||||
quickCSS.parentElement.insertBefore(clone, quickCSS.nextSibling);
|
|
||||||
}
|
|
||||||
|
|
||||||
new MutationObserver(injectButton).observe(document.body,{childList:true,subtree:true});
|
|
||||||
injectButton();
|
|
||||||
applyThemes();
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue