major updates to popup dev, manifest fix
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,75 @@
|
||||
// App.tsx
|
||||
import { useState } from 'react';
|
||||
import TabbedContainer from './components/TabbedContainer';
|
||||
import Settings from './pages/Settings';
|
||||
import logo from './assets/betterseqta-dark-full.png';
|
||||
import logoDark from './assets/betterseqta-light-full.png';
|
||||
import Shortcuts from './pages/Shortcuts';
|
||||
import About from './pages/About';
|
||||
|
||||
export interface SettingsState {
|
||||
notificationCollector: boolean;
|
||||
lessonAlerts: boolean;
|
||||
animatedBackground: boolean;
|
||||
animatedBackgroundSpeed: boolean;
|
||||
customThemeColor: string;
|
||||
betterSEQTAPlus: boolean;
|
||||
}
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [settingsState, setSettingsState] = useState<SettingsState>({
|
||||
notificationCollector: false,
|
||||
lessonAlerts: false,
|
||||
animatedBackground: false,
|
||||
animatedBackgroundSpeed: false,
|
||||
customThemeColor: "#db6969",
|
||||
betterSEQTAPlus: true
|
||||
});
|
||||
|
||||
// Handler for Switches
|
||||
const switchChange = (key: string, isOn: boolean) => {
|
||||
setSettingsState({
|
||||
...settingsState,
|
||||
[key]: isOn,
|
||||
});
|
||||
};
|
||||
|
||||
// Handler for ColorPicker
|
||||
const colorChange = (color: string) => {
|
||||
setSettingsState({
|
||||
...settingsState,
|
||||
customThemeColor: color,
|
||||
});
|
||||
};
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
title: 'Settings',
|
||||
content: <Settings settingsState={settingsState} switchChange={switchChange} colorChange={colorChange} />
|
||||
},
|
||||
{
|
||||
title: 'Shortcuts',
|
||||
content: <Shortcuts />
|
||||
},
|
||||
{
|
||||
title: 'About',
|
||||
content: <About />
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-screen pt-4 overflow-hidden" style={{ background: settingsState.customThemeColor }}>
|
||||
|
||||
<div className="flex flex-col w-[24rem] shadow-2xl gap-2 bg-white rounded-xl h-4/6 dark:bg-zinc-800 dark:text-white">
|
||||
<div className="grid border-b border-b-zinc-200/40 place-items-center">
|
||||
<img src={logo} className="w-4/5 dark:hidden" />
|
||||
<img src={logoDark} className="hidden w-4/5 dark:block" />
|
||||
</div>
|
||||
<TabbedContainer themeColor={settingsState.customThemeColor} tabs={tabs} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,45 @@
|
||||
// TODO: Create types for ColorPicker
|
||||
// @ts-expect-error No typescript declarations available
|
||||
import ColorPicker from 'react-best-gradient-color-picker';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
|
||||
interface ColorPickerProps {
|
||||
color: string;
|
||||
onChange: (color: string) => void;
|
||||
}
|
||||
|
||||
const Picker = ({ color, onChange }: ColorPickerProps) => {
|
||||
const [showPicker, setShowPicker] = useState<boolean>(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent): void => {
|
||||
if (ref.current && !ref.current.contains(event.target as Node)) {
|
||||
setShowPicker(false);
|
||||
}
|
||||
};
|
||||
if (showPicker) {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
}
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, [showPicker]);
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<button
|
||||
onClick={() => setShowPicker(!showPicker)}
|
||||
style={{ background: color }}
|
||||
className="w-16 h-8 rounded-md"
|
||||
></button>
|
||||
{showPicker && (
|
||||
<div ref={ref} className="fixed top-0 left-0 z-50 p-4 bg-white border rounded-lg shadow-lg border-zinc-00">
|
||||
<ColorPicker value={color} onChange={onChange} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Picker;
|
||||
@@ -1,4 +1,3 @@
|
||||
.switch[data-ison="true"] {
|
||||
justify-content: end;
|
||||
background-color: #30D259;
|
||||
}
|
||||
@@ -1,29 +1,28 @@
|
||||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import "./Switch.css";
|
||||
|
||||
interface SwitchProps {
|
||||
onChange: (isOn: boolean) => void;
|
||||
state: boolean;
|
||||
}
|
||||
|
||||
export default function Switch(props: SwitchProps) {
|
||||
const [isOn, setIsOn] = useState(false);
|
||||
|
||||
const toggleSwitch = () => {
|
||||
const newIsOn = !isOn;
|
||||
setIsOn(newIsOn);
|
||||
const newIsOn = !props.state;
|
||||
props.onChange(newIsOn);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex w-14 p-1 cursor-pointer rounded-full dark:bg-[#38373D] bg-[#DDDDDD] switch"
|
||||
data-isOn={isOn}
|
||||
data-isOn={props.state}
|
||||
onClick={toggleSwitch}
|
||||
>
|
||||
<motion.div
|
||||
|
||||
className="w-6 h-6 bg-white dark:bg-[#FEFEFE] rounded-full drop-shadow-md"
|
||||
layout
|
||||
initial={{ x: props.state ? 0 : 0 }}
|
||||
animate={{ x: props.state ? 24 : 0 }}
|
||||
transition={spring}
|
||||
/>
|
||||
</div>
|
||||
@@ -0,0 +1,92 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
interface Tab {
|
||||
title: string;
|
||||
content: JSX.Element;
|
||||
}
|
||||
|
||||
interface TabbedContainerProps {
|
||||
tabs: Tab[];
|
||||
themeColor: string;
|
||||
}
|
||||
|
||||
const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) => {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const [hoveredTab, setHoveredTab] = useState<number | null>(null);
|
||||
const [tabWidth, setTabWidth] = useState(0);
|
||||
const [position, setPosition] = useState(0);
|
||||
const positionRef = useRef(position);
|
||||
|
||||
useEffect(() => {
|
||||
const newPosition = -activeTab * 100;
|
||||
setPosition(newPosition);
|
||||
positionRef.current = newPosition;
|
||||
}, [activeTab]);
|
||||
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const springTransition = { type: 'spring', stiffness: 250, damping: 25 };
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
// @ts-expect-error for some reason its giving an error in TS but it works...
|
||||
const width = containerRef.current.getBoundingClientRect().width;
|
||||
setTabWidth(width / tabs.length);
|
||||
}
|
||||
}, [tabs.length]);
|
||||
|
||||
const calcXPos = (index: number | null) => {
|
||||
if (index !== null) {
|
||||
return tabWidth * index;
|
||||
}
|
||||
return tabWidth * activeTab;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full px-4 overflow-y-scroll overflow-x-clip">
|
||||
<div ref={containerRef} className="sticky top-0 z-10 text-[0.875rem] mb-2 pb-2 bg-white">
|
||||
<div className="relative flex">
|
||||
<motion.div
|
||||
className="absolute top-0 left-0 z-0 h-full rounded-full opacity-40"
|
||||
style={{ width: `${tabWidth}px`, background: themeColor }}
|
||||
initial={false}
|
||||
animate={{ x: calcXPos(hoveredTab) }}
|
||||
transition={springTransition}
|
||||
/>
|
||||
{tabs.map((tab, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className="relative z-10 flex-1 px-4 py-2"
|
||||
onClick={() => setActiveTab(index)}
|
||||
onMouseEnter={() => setHoveredTab(index)}
|
||||
onMouseLeave={() => setHoveredTab(null)}
|
||||
>
|
||||
{tab.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<motion.div
|
||||
initial={false}
|
||||
animate={{ x: `${position}%` }}
|
||||
transition={springTransition}
|
||||
>
|
||||
<div className="absolute flex w-full" style={{ left: `${-position}%` }}>
|
||||
{tabs.map((tab, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`w-full ${activeTab === index ? '' : 'hidden'}`}
|
||||
>
|
||||
{tab.content}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabbedContainer;
|
||||
@@ -0,0 +1,10 @@
|
||||
const About: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col overflow-y-scroll divide-y divide-zinc-100">
|
||||
<h2>About</h2>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -0,0 +1,68 @@
|
||||
import Switch from '../components/Switch';
|
||||
import ColorPicker from '../components/ColorPicker';
|
||||
import { SettingsState } from '../App';
|
||||
|
||||
interface ISetting {
|
||||
title: string;
|
||||
description: string;
|
||||
modifyElement: JSX.Element;
|
||||
}
|
||||
|
||||
interface SettingsProps {
|
||||
settingsState: SettingsState;
|
||||
switchChange: (key: string, isOn: boolean) => void;
|
||||
colorChange: (color: string) => void;
|
||||
}
|
||||
|
||||
const Settings: React.FC<SettingsProps> = ({ settingsState, switchChange, colorChange }) => {
|
||||
const settings: ISetting[] = [
|
||||
{
|
||||
title: "Notification Collector",
|
||||
description: "Uncaps the 9+ limit for notifications, showing the real number.",
|
||||
modifyElement: <Switch state={settingsState.notificationCollector} onChange={(isOn: boolean) => switchChange('notificationCollector', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Lesson Alerts",
|
||||
description: "Sends a native browser notification ~5 minutes prior to lessons.",
|
||||
modifyElement: <Switch state={settingsState.lessonAlerts} onChange={(isOn: boolean) => switchChange('lessonAlerts', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Animated Background",
|
||||
description: "Adds an animated background to BetterSEQTA. (May impact battery life)",
|
||||
modifyElement: <Switch state={settingsState.animatedBackground} onChange={(isOn: boolean) => switchChange('animatedBackground', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Animated Background Speed",
|
||||
description: "Controls the speed of the animated background.",
|
||||
modifyElement: <Switch state={settingsState.animatedBackgroundSpeed} onChange={(isOn: boolean) => switchChange('animatedBackgroundSpeed', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Custom Theme Colour",
|
||||
description: "Customise the overall theme colour of SEQTA Learn.",
|
||||
modifyElement: <ColorPicker color={settingsState.customThemeColor} onChange={(color: string) => colorChange(color)} />
|
||||
},
|
||||
{
|
||||
title: "BetterSEQTA+",
|
||||
description: "Unlocks premium features.",
|
||||
modifyElement: <Switch state={settingsState.betterSEQTAPlus} onChange={(isOn: boolean) => switchChange('betterSEQTAPlus', isOn)} />
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col overflow-y-scroll divide-y divide-zinc-100">
|
||||
{settings.map((setting, index) => (
|
||||
<div className="flex items-center justify-between px-4 py-3" key={index}>
|
||||
<div className="pr-4">
|
||||
<h2 className="text-sm font-bold">{setting.title}</h2>
|
||||
<p className="text-xs">{setting.description}</p>
|
||||
</div>
|
||||
<div>
|
||||
{setting.modifyElement}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
@@ -0,0 +1,95 @@
|
||||
import { useState } from "react";
|
||||
import Switch from "../components/Switch";
|
||||
|
||||
export default function Shortcuts() {
|
||||
const [shortcutState, setShortcutState] = useState({
|
||||
youtube: false,
|
||||
outlook: false,
|
||||
office: false,
|
||||
spotify: false,
|
||||
google: false,
|
||||
duckduckgo: false,
|
||||
coolmathgames: false,
|
||||
sace: false,
|
||||
googlescholar: false,
|
||||
gmail: false,
|
||||
netflix: false
|
||||
});
|
||||
|
||||
// Handler for Switches
|
||||
const switchChange = (key: string, isOn: boolean) => {
|
||||
setShortcutState({
|
||||
...shortcutState,
|
||||
[key]: isOn,
|
||||
});
|
||||
};
|
||||
|
||||
const DefaultShortcuts = [
|
||||
{
|
||||
title: "YouTube",
|
||||
link: "https://youtube.com",
|
||||
modifyElement: <Switch state={shortcutState.youtube} onChange={(isOn: boolean) => switchChange('youtube', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Outlook",
|
||||
link: "https://outlook.office.com/mail/inbox",
|
||||
modifyElement: <Switch state={shortcutState.outlook} onChange={(isOn: boolean) => switchChange('outlook', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Office",
|
||||
link: "https://www.office.com/",
|
||||
modifyElement: <Switch state={shortcutState.office} onChange={(isOn: boolean) => switchChange('office', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Spotify",
|
||||
link: "https://www.spotify.com/",
|
||||
modifyElement: <Switch state={shortcutState.spotify} onChange={(isOn: boolean) => switchChange('spotify', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Google",
|
||||
link: "https://www.google.com/",
|
||||
modifyElement: <Switch state={shortcutState.google} onChange={(isOn: boolean) => switchChange('google', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "DuckDuckGo",
|
||||
link: "https://duckduckgo.com/",
|
||||
modifyElement: <Switch state={shortcutState.duckduckgo} onChange={(isOn: boolean) => switchChange('duckduckgo', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Cool Math Games",
|
||||
link: "https://www.coolmathgames.com/",
|
||||
modifyElement: <Switch state={shortcutState.coolmathgames} onChange={(isOn: boolean) => switchChange('coolmathgames', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "SACE",
|
||||
link: "https://www.sace.sa.edu.au/",
|
||||
modifyElement: <Switch state={shortcutState.sace} onChange={(isOn: boolean) => switchChange('sace', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Google Scholar",
|
||||
link: "https://scholar.google.com/",
|
||||
modifyElement: <Switch state={shortcutState.googlescholar} onChange={(isOn: boolean) => switchChange('googlescholar', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Gmail",
|
||||
link: "https://mail.google.com/",
|
||||
modifyElement: <Switch state={shortcutState.gmail} onChange={(isOn: boolean) => switchChange('gmail', isOn)} />
|
||||
},
|
||||
{
|
||||
title: "Netflix",
|
||||
link: "https://www.netflix.com/",
|
||||
modifyElement: <Switch state={shortcutState.netflix} onChange={(isOn: boolean) => switchChange('netflix', isOn)} />
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col divide-y divide-zinc-100">
|
||||
{DefaultShortcuts.map((shortcut, index) => (
|
||||
<div className="flex items-center justify-between px-4 py-3" key={index}>
|
||||
{shortcut.title}
|
||||
{shortcut.modifyElement}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
fontSize: {
|
||||
'xs': '.65rem',
|
||||
'sm': '.775rem',
|
||||
'base': '0.65rem', // 16px
|
||||
'md': '0.65rem', // 16px
|
||||
'lg': '1rem', // 18px
|
||||
'xl': '1.25rem', // 20px
|
||||
'2xl': '1.5rem', // 24px
|
||||
'3xl': '1.875rem', // 30px
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import react from '@vitejs/plugin-react'
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
outDir: '../../public/popup-dist',
|
||||
//outDir: '../../public/popup-dist',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
assetFileNames: 'client/rsc/[ext]/[name][extname]',
|
||||
@@ -20,8 +20,7 @@
|
||||
"permissions": ["tabs", "notifications", "storage"],
|
||||
"host_permissions": ["https://newsapi.org/", "*://*/*"],
|
||||
"background": {
|
||||
"content_scripts": "background.js",
|
||||
"persistent": false
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"optional_permissions": ["declarativeContent"],
|
||||
"content_scripts": [
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import './App.css'
|
||||
import Switch from './components/Switch'
|
||||
import logo from './assets/betterseqta-dark-full.png'
|
||||
import logoDark from './assets/betterseqta-light-full.png'
|
||||
import ColorPicker from './components/ColorPicker'
|
||||
|
||||
const switchChange = (isOn: boolean) => {
|
||||
console.log(isOn)
|
||||
}
|
||||
|
||||
const settings = [
|
||||
{
|
||||
title: "Notification Collector",
|
||||
description: "Uncaps the 9+ limit for notifications, showing the real number.",
|
||||
modifyElement: <Switch onChange={switchChange} />
|
||||
},
|
||||
{
|
||||
title: "Lesson Alerts",
|
||||
description: "Sends a native browser notification ~5 minutes prior to lessons.",
|
||||
modifyElement: <Switch onChange={switchChange} />
|
||||
},
|
||||
{
|
||||
title: "Animated Background",
|
||||
description: "Adds an animated background to BetterSEQTA. (May impact battery life)",
|
||||
modifyElement: <Switch onChange={switchChange} />
|
||||
},
|
||||
{
|
||||
title: "Animated Background Speed",
|
||||
description: "Controls the speed of the animated background.",
|
||||
modifyElement: <Switch onChange={switchChange} />
|
||||
},
|
||||
{
|
||||
title: "Custom Theme Colour",
|
||||
description: "Customise the overall theme colour of SEQTA Learn.",
|
||||
modifyElement: <ColorPicker />
|
||||
},
|
||||
{
|
||||
title: "BetterSEQTA+",
|
||||
description: "Unlocks premium features.",
|
||||
modifyElement: <Switch onChange={switchChange} />
|
||||
}
|
||||
]
|
||||
|
||||
function App() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 dark:bg-zinc-800">
|
||||
<div className="grid border-b border-b-zinc-200 place-items-center h-1/2">
|
||||
<img src={logo} className="w-4/5 dark:hidden" />
|
||||
<img src={logoDark} className="hidden w-4/5 dark:block" />
|
||||
</div>
|
||||
{settings.map((setting, index) => (
|
||||
<div className="flex justify-between px-4 place-items-center" key={index}>
|
||||
<div className="dark:text-white">
|
||||
<h2 className="text-sm font-bold">{setting.title}</h2>
|
||||
<p className="text-xs">{setting.description}</p>
|
||||
</div>
|
||||
<div>
|
||||
{setting.modifyElement}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,43 +0,0 @@
|
||||
// TODO: Create types for ColorPicker
|
||||
// @ts-expect-error No typescript declarations available
|
||||
import ColorPicker from 'react-best-gradient-color-picker';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
|
||||
const Picker = (): JSX.Element => {
|
||||
const [color, setColor] = useState<string>('rgba(255,20,255,1)');
|
||||
const [showPicker, setShowPicker] = useState<boolean>(false);
|
||||
const pickerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent): void => {
|
||||
if (pickerRef.current && !pickerRef.current.contains(event.target as Node)) {
|
||||
setShowPicker(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, [pickerRef]);
|
||||
|
||||
return (
|
||||
<div className="relative" ref={pickerRef}>
|
||||
<button
|
||||
onClick={() => setShowPicker(!showPicker)}
|
||||
style={{
|
||||
'background': color
|
||||
}}
|
||||
className="w-16 h-8 rounded-md"
|
||||
></button>
|
||||
<div className="absolute top-0 right-0 z-10 p-2 bg-white border rounded-lg shadow-2xl border-zinc-200">
|
||||
{ showPicker &&
|
||||
<ColorPicker value={color} onChange={setColor} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Picker;
|
||||
@@ -1,24 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"background-primary-dark": "",
|
||||
"background-primary-light": "",
|
||||
"background-secondary-dark": "",
|
||||
"background-secondary-light": "",
|
||||
"forground-primary-dark": "",
|
||||
"forground-primary-light": "",
|
||||
"forground-secondary-dark": "",
|
||||
"forground-secondary-light": ""
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||