mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
work on image and video backgrounds! Not laggy anymore with large files
This commit is contained in:
@@ -6,8 +6,8 @@ import logoDark from './assets/betterseqta-light-full.png';
|
|||||||
import Shortcuts from './pages/Shortcuts';
|
import Shortcuts from './pages/Shortcuts';
|
||||||
import { useSettingsContext } from './SettingsContext';
|
import { useSettingsContext } from './SettingsContext';
|
||||||
import Picker from './components/Picker';
|
import Picker from './components/Picker';
|
||||||
//import Themes from './pages/Themes';
|
import Themes from './pages/Themes';
|
||||||
import About from './pages/About';
|
//import About from './pages/About';
|
||||||
//import About from './pages/About';
|
//import About from './pages/About';
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
@@ -32,8 +32,8 @@ const App: React.FC = () => {
|
|||||||
content: <Shortcuts />
|
content: <Shortcuts />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'About',
|
title: 'Themes',
|
||||||
content: <About />
|
content: <Themes />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ const Settings: React.FC = () => {
|
|||||||
description: "Customise the overall theme colour of SEQTA Learn.",
|
description: "Customise the overall theme colour of SEQTA Learn.",
|
||||||
modifyElement: <PickerSwatch />
|
modifyElement: <PickerSwatch />
|
||||||
},
|
},
|
||||||
/* {
|
{
|
||||||
title: "Transparency Effects",
|
title: "Transparency Effects",
|
||||||
description: "Enables transparency effects on certain elements such as blur. (May impact battery life)",
|
description: "Enables transparency effects on certain elements such as blur. (May impact battery life)",
|
||||||
modifyElement: <Switch state={settingsState.betterSEQTAPlus} onChange={(isOn: boolean) => switchChange('betterSEQTAPlus', isOn)} />
|
modifyElement: <Switch state={settingsState.betterSEQTAPlus} onChange={(isOn: boolean) => switchChange('betterSEQTAPlus', isOn)} />
|
||||||
}, */
|
},
|
||||||
{
|
{
|
||||||
title: "BetterSEQTA+",
|
title: "BetterSEQTA+",
|
||||||
description: "Enables BetterSEQTA+ features",
|
description: "Enables BetterSEQTA+ features",
|
||||||
|
|||||||
@@ -15,14 +15,13 @@ const openDB = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const writeData = async (type: string, data: any) => {
|
const writeData = async (type: string, blob: Blob) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB().then(async db => {
|
openDB().then(async db => {
|
||||||
const tx = db.transaction('backgrounds', 'readwrite');
|
const tx = db.transaction('backgrounds', 'readwrite');
|
||||||
const store = tx.objectStore('backgrounds');
|
const store = tx.objectStore('backgrounds');
|
||||||
const request = store.put({ id: 'customBackground', type, data });
|
const request = store.put({ id: 'customBackground', type, blob });
|
||||||
|
|
||||||
// Wait for the transaction to complete
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
tx.oncomplete = () => res(request.result);
|
tx.oncomplete = () => res(request.result);
|
||||||
tx.onerror = () => rej(tx.error);
|
tx.onerror = () => rej(tx.error);
|
||||||
@@ -32,13 +31,12 @@ const writeData = async (type: string, data: any) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const readData = async () => {
|
const readData = async () => {
|
||||||
const db = await openDB();
|
const db = await openDB();
|
||||||
const tx = db.transaction('backgrounds', 'readonly');
|
const tx = db.transaction('backgrounds', 'readonly');
|
||||||
const store = tx.objectStore('backgrounds');
|
const store = tx.objectStore('backgrounds');
|
||||||
const request = store.get('customBackground');
|
const request = store.get('customBackground');
|
||||||
|
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
request.onsuccess = () => resolve(request.result);
|
request.onsuccess = () => resolve(request.result);
|
||||||
request.onerror = () => reject(request.error);
|
request.onerror = () => reject(request.error);
|
||||||
@@ -52,39 +50,35 @@ const Themes: React.FC = () => {
|
|||||||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
const fileType = file.type.split('/')[0];
|
const fileType = file.type.split('/')[0];
|
||||||
console.log(fileType);
|
const blob = new Blob([file], { type: file.type });
|
||||||
|
|
||||||
// Directly save the Blob object (file)
|
// Save blob to IndexedDB
|
||||||
await writeData(fileType, file);
|
await writeData(fileType, blob);
|
||||||
|
|
||||||
// For displaying purpose, you might still want to convert it to Data URL
|
// For displaying purpose
|
||||||
const reader = new FileReader();
|
const url = URL.createObjectURL(blob);
|
||||||
reader.onload = () => {
|
if (fileType === 'image') {
|
||||||
setImageSrc(reader.result as string);
|
setVideoSrc(null);
|
||||||
};
|
setImageSrc(url);
|
||||||
reader.readAsDataURL(file);
|
} else if (fileType === 'video') {
|
||||||
|
setImageSrc(null);
|
||||||
|
setVideoSrc(url);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const data = await readData();
|
const data = await readData();
|
||||||
|
const url = URL.createObjectURL(data.blob);
|
||||||
if (data?.type === 'image') {
|
if (data?.type === 'image') {
|
||||||
const reader = new FileReader();
|
setImageSrc(url);
|
||||||
reader.onload = () => {
|
|
||||||
setImageSrc(reader.result as string);
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(data.data);
|
|
||||||
} else if (data?.type === 'video') {
|
} else if (data?.type === 'video') {
|
||||||
const reader = new FileReader();
|
setVideoSrc(url);
|
||||||
reader.onload = () => {
|
|
||||||
setVideoSrc(reader.result as string);
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(data.data);
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col overflow-y-scroll divide-y divide-zinc-100/50 dark:divide-zinc-700/50">
|
<div className="flex flex-col overflow-y-scroll divide-y divide-zinc-100/50 dark:divide-zinc-700/50">
|
||||||
@@ -111,4 +105,4 @@ const Themes: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Themes;
|
export default Themes;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Background Fetcher</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Container for the media -->
|
||||||
|
<div id="media-container">
|
||||||
|
<!-- Will be populated dynamically -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="background.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Open the database
|
||||||
|
const openDB = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const request = indexedDB.open("MyDatabase", 1);
|
||||||
|
|
||||||
|
request.onerror = () => reject(request.error);
|
||||||
|
request.onsuccess = () => resolve(request.result);
|
||||||
|
|
||||||
|
request.onupgradeneeded = (event) => {
|
||||||
|
const db = event.target.result;
|
||||||
|
db.createObjectStore("backgrounds", { keyPath: "id" });
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read data from IndexedDB
|
||||||
|
const readData = async () => {
|
||||||
|
const db = await openDB();
|
||||||
|
const tx = db.transaction("backgrounds", "readonly");
|
||||||
|
const store = tx.objectStore("backgrounds");
|
||||||
|
const request = store.get("customBackground");
|
||||||
|
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
request.onsuccess = () => resolve(request.result);
|
||||||
|
request.onerror = () => reject(request.error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main function to run on page load
|
||||||
|
const main = async () => {
|
||||||
|
try {
|
||||||
|
const data = await readData();
|
||||||
|
if (!data) {
|
||||||
|
console.log("No data found in IndexedDB.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = URL.createObjectURL(data.blob);
|
||||||
|
const container = document.getElementById("media-container");
|
||||||
|
|
||||||
|
if (data.type === "image") {
|
||||||
|
const imgElement = document.createElement("img");
|
||||||
|
imgElement.src = url;
|
||||||
|
imgElement.alt = "Uploaded content";
|
||||||
|
container.appendChild(imgElement);
|
||||||
|
} else if (data.type === "video") {
|
||||||
|
const videoElement = document.createElement("video");
|
||||||
|
videoElement.src = url;
|
||||||
|
videoElement.autoplay = true;
|
||||||
|
videoElement.loop = true;
|
||||||
|
videoElement.muted = true;
|
||||||
|
container.appendChild(videoElement);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("An error occurred:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run the main function when the document is ready
|
||||||
|
document.addEventListener("DOMContentLoaded", main);
|
||||||
@@ -70,6 +70,10 @@
|
|||||||
{
|
{
|
||||||
"resources": ["resources/*"],
|
"resources": ["resources/*"],
|
||||||
"matches": ["*://*/*"]
|
"matches": ["*://*/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resources": ["backgrounds/*"],
|
||||||
|
"matches": ["*://*/*"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1330,6 +1330,9 @@ function AddBetterSEQTAElements(toggle) {
|
|||||||
if (element.getAttribute("excludeDarkCheck") == "true") {
|
if (element.getAttribute("excludeDarkCheck") == "true") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(element);
|
||||||
|
console.log(element.contentDocument.documentElement);
|
||||||
|
|
||||||
element.contentDocument.documentElement.childNodes[1].style.color =
|
element.contentDocument.documentElement.childNodes[1].style.color =
|
||||||
"white";
|
"white";
|
||||||
|
|||||||
+1
-6
@@ -132,12 +132,7 @@ function HandleIntexedDB(request, sendResponse) {
|
|||||||
|
|
||||||
case "read":
|
case "read":
|
||||||
readData().then((data) => {
|
readData().then((data) => {
|
||||||
const reader = new FileReader();
|
sendResponse(data);
|
||||||
reader.onload = () => {
|
|
||||||
const arrayBuffer = reader.result;
|
|
||||||
sendResponse({ data: arrayBuffer, type: data.type });
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(data.data);
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-7
@@ -431,7 +431,7 @@ td.colourBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ol > [data-message] {
|
ol > [data-message] {
|
||||||
padding-left: 4px !important;
|
padding-left: 8px !important;
|
||||||
padding-right: 4px !important;
|
padding-right: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,11 +872,11 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ais-description {
|
.ais-description {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ais-signature {
|
.ais-signature {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ais-btnSearch i {
|
.ais-btnSearch i {
|
||||||
@@ -1428,8 +1428,8 @@ ul {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header .days .title {
|
.title {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.entry.class {
|
div.entry.class {
|
||||||
@@ -1683,8 +1683,14 @@ body {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MessageList__MessageList___3DxoC > ol > li.MessageList__unread___3imtO {
|
.MessageList__MessageList___3DxoC > ol > li.MessageList__unread___3imtO::before {
|
||||||
box-shadow: inset 3px 0 var(--better-main);
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 3px; /* Same width as your box-shadow */
|
||||||
|
background: var(--better-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.connectedNotificationsWrapper > div > button {
|
.connectedNotificationsWrapper > div > button {
|
||||||
|
|||||||
@@ -26,4 +26,5 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
border: none !important;
|
||||||
}
|
}
|
||||||
@@ -1,82 +1,84 @@
|
|||||||
/* // global chrome */
|
/* global chrome */
|
||||||
/* function isValidBase64(str) {
|
|
||||||
const len = str.length;
|
|
||||||
if (len % 4 !== 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
if (!(/[A-Za-z0-9+/=]/.test(str[i]))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function base64ToArrayBuffer(base64) {
|
|
||||||
console.log(base64);
|
|
||||||
if (!isValidBase64(base64)) {
|
|
||||||
console.error("Invalid base64 string");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const binaryString = atob(base64);
|
|
||||||
const len = binaryString.length;
|
|
||||||
const bytes = new Uint8Array(len);
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
bytes[i] = binaryString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
return bytes.buffer;
|
|
||||||
} */
|
|
||||||
|
|
||||||
export async function appendBackgroundToUI() {
|
export async function appendBackgroundToUI() {
|
||||||
|
console.log("Starting appendBackgroundToUI...");
|
||||||
|
|
||||||
|
const parent = document.getElementById("container");
|
||||||
|
|
||||||
|
// embed background.html
|
||||||
|
const background = document.createElement("iframe");
|
||||||
|
background.id = "background";
|
||||||
|
background.classList.add("imageBackground");
|
||||||
|
background.setAttribute("excludeDarkCheck", "true");
|
||||||
|
background.src = chrome.runtime.getURL("backgrounds/background.html");
|
||||||
|
parent.appendChild(background);
|
||||||
|
|
||||||
/* const response = await new Promise((resolve, reject) => {
|
/* const response = await new Promise((resolve, reject) => {
|
||||||
|
console.log("Sending message to background script...");
|
||||||
chrome.runtime.sendMessage({ type: "IndexedDB", action: "read", fileName: "customBackground" }, (response) => {
|
chrome.runtime.sendMessage({ type: "IndexedDB", action: "read", fileName: "customBackground" }, (response) => {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error("Error from background script:", chrome.runtime.lastError);
|
||||||
return reject(chrome.runtime.lastError);
|
return reject(chrome.runtime.lastError);
|
||||||
}
|
}
|
||||||
|
console.log("Received response from background script:", response);
|
||||||
resolve(response);
|
resolve(response);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
console.log("response:", response);
|
|
||||||
|
|
||||||
let data = response.data; // response.data is the image base64 string
|
let data = response.data; // response.data is the image Data URL
|
||||||
|
|
||||||
|
// Extract the pure base64 string from the Data URL
|
||||||
|
const base64Marker = ";base64,";
|
||||||
|
const base64Index = data.indexOf(base64Marker) + base64Marker.length;
|
||||||
|
const base64 = data.substring(base64Index);
|
||||||
|
|
||||||
let type = response.type; // response.type is the image type [ video | image ]
|
let type = response.type; // response.type is the image type [ video | image ]
|
||||||
|
|
||||||
if (data) {
|
if (base64) {
|
||||||
// check if it is video from its base64 string
|
console.log("Data exists, proceeding...");
|
||||||
|
|
||||||
const mount = document.getElementById("container");
|
const mount = document.getElementById("container");
|
||||||
console.log("Starting to append background");
|
|
||||||
|
// Convert base64 to ArrayBuffer
|
||||||
|
console.log("Converting base64 to ArrayBuffer...");
|
||||||
|
const byteCharacters = atob(base64);
|
||||||
|
const byteNumbers = Array.from(byteCharacters).map(char => char.charCodeAt(0));
|
||||||
|
const byteArray = new Uint8Array(byteNumbers);
|
||||||
|
|
||||||
|
// Create blob
|
||||||
|
console.log("Creating blob...");
|
||||||
|
const blob = new Blob([byteArray], { type: type === "video" ? "video/mp4" : "image/jpeg" });
|
||||||
|
|
||||||
|
// Create blob URL
|
||||||
|
console.log("Creating blob URL...");
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
let backgroundElement;
|
let backgroundElement;
|
||||||
|
|
||||||
if (type === "video") {
|
if (type === "video") {
|
||||||
console.log("Data type:", typeof data);
|
console.log("Appending video element...");
|
||||||
console.log("Data instance:", data instanceof Blob);
|
|
||||||
|
|
||||||
if (data instanceof Blob) {
|
|
||||||
console.log("Blob size:", data.size);
|
|
||||||
console.log("Blob type:", data.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Starting blob.");
|
|
||||||
const blob = new Blob([new Uint8Array(response.data)]); // Adjust the MIME type accordingly
|
|
||||||
console.log("Made blob.");
|
|
||||||
const blobUrl = URL.createObjectURL(blob);
|
|
||||||
console.log(blobUrl);
|
|
||||||
backgroundElement = document.createElement("video");
|
backgroundElement = document.createElement("video");
|
||||||
backgroundElement.src = blobUrl;
|
backgroundElement.src = blobUrl;
|
||||||
backgroundElement.autoplay = true;
|
backgroundElement.autoplay = true;
|
||||||
backgroundElement.loop = true;
|
backgroundElement.loop = true;
|
||||||
backgroundElement.muted = true;
|
backgroundElement.muted = true;
|
||||||
console.log(backgroundElement);
|
|
||||||
mount.appendChild(backgroundElement);
|
// Revoke blob URL to free memory
|
||||||
// Remember to revoke the blob URL to avoid memory leaks
|
|
||||||
backgroundElement.addEventListener("ended", () => {
|
backgroundElement.addEventListener("ended", () => {
|
||||||
|
console.log("Video ended, revoking blob URL...");
|
||||||
URL.revokeObjectURL(blobUrl);
|
URL.revokeObjectURL(blobUrl);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
console.log("Appending image element...");
|
||||||
backgroundElement = document.createElement("img");
|
backgroundElement = document.createElement("img");
|
||||||
backgroundElement.src = data;
|
backgroundElement.src = blobUrl;
|
||||||
backgroundElement.alt = "Custom Background";
|
backgroundElement.alt = "Custom Background";
|
||||||
backgroundElement.classList.add("imageBackground");
|
backgroundElement.classList.add("imageBackground");
|
||||||
mount.appendChild(backgroundElement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Appending background element to the DOM...");
|
||||||
|
mount.appendChild(backgroundElement);
|
||||||
|
} else {
|
||||||
|
console.warn("No data received. Background not appended.");
|
||||||
} */
|
} */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ const setCSSVar = (varName, value) => document.documentElement.style.setProperty
|
|||||||
const getChromeURL = (path) => chrome.runtime.getURL(path);
|
const getChromeURL = (path) => chrome.runtime.getURL(path);
|
||||||
const applyProperties = (props) => Object.entries(props).forEach(([key, value]) => setCSSVar(key, value));
|
const applyProperties = (props) => Object.entries(props).forEach(([key, value]) => setCSSVar(key, value));
|
||||||
|
|
||||||
|
let DarkMode = null;
|
||||||
|
|
||||||
export function updateAllColors(storedSetting, newColor = null) {
|
export function updateAllColors(storedSetting, newColor = null) {
|
||||||
// Determine the color to use
|
// Determine the color to use
|
||||||
const selectedColor = newColor || storedSetting.selectedColor;
|
const selectedColor = newColor || storedSetting.selectedColor;
|
||||||
const DarkMode = storedSetting ? storedSetting.DarkMode : null;
|
DarkMode = storedSetting ? storedSetting.DarkMode : DarkMode;
|
||||||
|
|
||||||
// Common properties, always applied
|
// Common properties, always applied
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
@@ -22,7 +24,7 @@ export function updateAllColors(storedSetting, newColor = null) {
|
|||||||
|
|
||||||
// Mode-based properties, applied if storedSetting is provided
|
// Mode-based properties, applied if storedSetting is provided
|
||||||
let modeProps = {};
|
let modeProps = {};
|
||||||
console.log(DarkMode);
|
console.log("Darkmode: ", DarkMode);
|
||||||
if (DarkMode !== null) {
|
if (DarkMode !== null) {
|
||||||
modeProps = DarkMode ? {
|
modeProps = DarkMode ? {
|
||||||
"--background-primary": "#232323",
|
"--background-primary": "#232323",
|
||||||
|
|||||||
Reference in New Issue
Block a user