mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
Merge pull request #6 from SethBurkart123/dev
Merge DEV (new popup) into main
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# npm
|
# npm
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
package.zip
|
package.zip
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
+85
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
.dark .switch[data-ison=true],.switch[data-ison=true]{background-color:#30d259}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React + TS</title>
|
||||||
|
<script type="module" crossorigin src="/client/public/client.js"></script>
|
||||||
|
<link rel="stylesheet" href="/client/rsc/css/index.css">
|
||||||
|
</head>
|
||||||
|
<body class="">
|
||||||
|
<div id="ExtensionPopup"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React + TS</title>
|
||||||
|
</head>
|
||||||
|
<body class="">
|
||||||
|
<div id="ExtensionPopup"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "popup",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite build --watch",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/chrome": "^0.0.246",
|
||||||
|
"framer-motion": "^10.16.4",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-best-gradient-color-picker": "^2.2.22",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.15",
|
||||||
|
"@types/react-dom": "^18.2.7",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
|
"@vitejs/plugin-react": "^4.0.3",
|
||||||
|
"autoprefixer": "^10.4.15",
|
||||||
|
"eslint": "^8.45.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.3",
|
||||||
|
"postcss": "^8.4.29",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
|
"typescript": "^5.0.2",
|
||||||
|
"vite": "^4.4.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-42
@@ -1,51 +1,18 @@
|
|||||||
// App.tsx
|
import React from 'react';
|
||||||
import { useState } from 'react';
|
|
||||||
import TabbedContainer from './components/TabbedContainer';
|
import TabbedContainer from './components/TabbedContainer';
|
||||||
import Settings from './pages/Settings';
|
import Settings from './pages/Settings';
|
||||||
import logo from './assets/betterseqta-dark-full.png';
|
import logo from './assets/betterseqta-dark-full.png';
|
||||||
import logoDark from './assets/betterseqta-light-full.png';
|
import logoDark from './assets/betterseqta-light-full.png';
|
||||||
import Shortcuts from './pages/Shortcuts';
|
import Shortcuts from './pages/Shortcuts';
|
||||||
import About from './pages/About';
|
import About from './pages/About';
|
||||||
|
import { SettingsContextProvider } from './SettingsContext';
|
||||||
export interface SettingsState {
|
|
||||||
notificationCollector: boolean;
|
|
||||||
lessonAlerts: boolean;
|
|
||||||
animatedBackground: boolean;
|
|
||||||
animatedBackgroundSpeed: boolean;
|
|
||||||
customThemeColor: string;
|
|
||||||
betterSEQTAPlus: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const App: React.FC = () => {
|
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 = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
title: 'Settings',
|
title: 'Settings',
|
||||||
content: <Settings settingsState={settingsState} switchChange={switchChange} colorChange={colorChange} />
|
content: <Settings />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Shortcuts',
|
title: 'Shortcuts',
|
||||||
@@ -59,13 +26,15 @@ const App: React.FC = () => {
|
|||||||
|
|
||||||
{/* <div className="flex justify-center w-screen h-screen pt-4 overflow-hidden" style={{ background: settingsState.customThemeColor }}> */}
|
{/* <div className="flex justify-center w-screen h-screen pt-4 overflow-hidden" style={{ background: settingsState.customThemeColor }}> */}
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col w-[24rem] shadow-2xl gap-2 bg-white rounded-xl h-[590px] dark:bg-zinc-800 dark:text-white">
|
<SettingsContextProvider>
|
||||||
<div className="grid border-b border-b-zinc-200/40 place-items-center">
|
<div className="flex flex-col w-[384px] shadow-2xl gap-2 bg-white rounded-xl h-[590px] dark:bg-zinc-800 dark:text-white">
|
||||||
<img src={logo} className="w-4/5 dark:hidden" />
|
<div className="grid border-b border-b-zinc-200/40 place-items-center">
|
||||||
<img src={logoDark} className="hidden w-4/5 dark:block" />
|
<img src={logo} className="w-4/5 dark:hidden" />
|
||||||
|
<img src={logoDark} className="hidden w-4/5 dark:block" />
|
||||||
|
</div>
|
||||||
|
<TabbedContainer tabs={tabs} />
|
||||||
</div>
|
</div>
|
||||||
<TabbedContainer themeColor={settingsState.customThemeColor} tabs={tabs} />
|
</SettingsContextProvider>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// SettingsContext.tsx
|
||||||
|
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||||
|
import { SettingsState } from './types/AppProps';
|
||||||
|
import useSettingsState from './hooks/settingsState';
|
||||||
|
|
||||||
|
// Create a context with an initial state
|
||||||
|
const SettingsContext = createContext<{
|
||||||
|
settingsState: SettingsState;
|
||||||
|
setSettingsState: React.Dispatch<React.SetStateAction<SettingsState>>;
|
||||||
|
} | undefined>(undefined);
|
||||||
|
|
||||||
|
export const SettingsContextProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const [settingsState, setSettingsState] = useState<SettingsState>({
|
||||||
|
notificationCollector: false,
|
||||||
|
lessonAlerts: false,
|
||||||
|
animatedBackground: false,
|
||||||
|
animatedBackgroundSpeed: "0",
|
||||||
|
customThemeColor: "#db6969",
|
||||||
|
betterSEQTAPlus: true,
|
||||||
|
shortcuts: []
|
||||||
|
});
|
||||||
|
|
||||||
|
useSettingsState({ settingsState, setSettingsState });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsContext.Provider value={{ settingsState, setSettingsState }}>
|
||||||
|
{children}
|
||||||
|
</SettingsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export const useSettingsContext = () => {
|
||||||
|
const context = useContext(SettingsContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useSettingsContext must be used within a SettingsContextProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
// TODO: Create types for ColorPicker
|
// @ts-expect-error There aren't any types for the below library
|
||||||
// @ts-expect-error No typescript declarations available
|
|
||||||
import ColorPicker from 'react-best-gradient-color-picker';
|
import ColorPicker from 'react-best-gradient-color-picker';
|
||||||
import { useState, useRef, useEffect } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
|
import type { ColorPickerProps } from '../types/ColorPickerProps';
|
||||||
interface ColorPickerProps {
|
|
||||||
color: string;
|
|
||||||
onChange: (color: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Picker = ({ color, onChange }: ColorPickerProps) => {
|
const Picker = ({ color, onChange }: ColorPickerProps) => {
|
||||||
const [showPicker, setShowPicker] = useState<boolean>(false);
|
const [showPicker, setShowPicker] = useState<boolean>(false);
|
||||||
@@ -27,18 +22,20 @@ const Picker = ({ color, onChange }: ColorPickerProps) => {
|
|||||||
}, [showPicker]);
|
}, [showPicker]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowPicker(!showPicker)}
|
onClick={() => setShowPicker(!showPicker)}
|
||||||
style={{ background: color }}
|
style={{ background: color }}
|
||||||
className="w-16 h-8 rounded-md"
|
className="w-16 h-8 rounded-md"
|
||||||
></button>
|
></button>
|
||||||
{showPicker && (
|
{showPicker && (
|
||||||
<div ref={ref} className="fixed top-0 left-0 z-50 p-4 bg-white border rounded-lg shadow-lg border-zinc-00">
|
<div className="absolute top-0 left-0 w-full h-full bg-black/20">
|
||||||
<ColorPicker value={color} onChange={onChange} />
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import "./Slider.css";
|
import "./Slider.css";
|
||||||
|
import type { Slider } from '../types/SliderProps';
|
||||||
interface Slider {
|
|
||||||
onValueChange: (value: number) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Slider: React.FC<Slider> = ({ onValueChange }) => {
|
const Slider: React.FC<Slider> = ({ onValueChange }) => {
|
||||||
const [sliderValue, setSliderValue] = useState(0);
|
const [sliderValue, setSliderValue] = useState(0);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
.dark .switch[data-ison="true"],
|
||||||
.switch[data-ison="true"] {
|
.switch[data-ison="true"] {
|
||||||
background-color: #30D259;
|
background-color: #30D259;
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import "./Switch.css";
|
import "./Switch.css";
|
||||||
|
import type { SwitchProps } from "../types/SwitchProps";
|
||||||
interface SwitchProps {
|
|
||||||
onChange: (isOn: boolean) => void;
|
|
||||||
state: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Switch(props: SwitchProps) {
|
export default function Switch(props: SwitchProps) {
|
||||||
const toggleSwitch = () => {
|
const toggleSwitch = () => {
|
||||||
|
|||||||
@@ -1,22 +1,15 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import type { TabbedContainerProps } from '../types/TabbedContainerProps';
|
||||||
|
import { useSettingsContext } from '../SettingsContext';
|
||||||
|
|
||||||
interface Tab {
|
const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs }) => {
|
||||||
title: string;
|
|
||||||
content: JSX.Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TabbedContainerProps {
|
|
||||||
tabs: Tab[];
|
|
||||||
themeColor: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) => {
|
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
const [hoveredTab, setHoveredTab] = useState<number | null>(null);
|
const [hoveredTab, setHoveredTab] = useState<number | null>(null);
|
||||||
const [tabWidth, setTabWidth] = useState(0);
|
const [tabWidth, setTabWidth] = useState(0);
|
||||||
const [position, setPosition] = useState(0);
|
const [position, setPosition] = useState(0);
|
||||||
const positionRef = useRef(position);
|
const positionRef = useRef(position);
|
||||||
|
const themeColor = useSettingsContext().settingsState.customThemeColor;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newPosition = -activeTab * 100;
|
const newPosition = -activeTab * 100;
|
||||||
@@ -28,6 +21,13 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) =
|
|||||||
|
|
||||||
const springTransition = { type: 'spring', stiffness: 250, damping: 25 };
|
const springTransition = { type: 'spring', stiffness: 250, damping: 25 };
|
||||||
|
|
||||||
|
const contentVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: { opacity: 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const fastOpacityTransition = { duration: 0.2 };
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (containerRef.current) {
|
if (containerRef.current) {
|
||||||
// @ts-expect-error for some reason its giving an error in TS but it works...
|
// @ts-expect-error for some reason its giving an error in TS but it works...
|
||||||
@@ -44,48 +44,57 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) =
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 ref={containerRef} className="top-0 z-10 text-[0.875rem] mb-2 pb-2 mx-4">
|
||||||
<div className="relative flex">
|
<div className="relative flex">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute top-0 left-0 z-0 h-full rounded-full opacity-40"
|
className="absolute top-0 left-0 z-0 h-full rounded-full opacity-40"
|
||||||
style={{ width: `${tabWidth}px`, background: themeColor }}
|
style={{ width: `${tabWidth}px`, background: themeColor }}
|
||||||
initial={false}
|
initial={false}
|
||||||
animate={{ x: calcXPos(hoveredTab) }}
|
animate={{ x: calcXPos(hoveredTab) }}
|
||||||
transition={springTransition}
|
transition={springTransition}
|
||||||
/>
|
/>
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
<button
|
<button
|
||||||
key={index}
|
key={index}
|
||||||
className="relative z-10 flex-1 px-4 py-2"
|
className="relative z-10 flex-1 px-4 py-2"
|
||||||
onClick={() => setActiveTab(index)}
|
onClick={() => setActiveTab(index)}
|
||||||
onMouseEnter={() => setHoveredTab(index)}
|
onMouseEnter={() => setHoveredTab(index)}
|
||||||
onMouseLeave={() => setHoveredTab(null)}
|
onMouseLeave={() => setHoveredTab(null)}
|
||||||
>
|
>
|
||||||
{tab.title}
|
{tab.title}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
</div>
|
||||||
|
<div className="h-full px-4 overflow-y-scroll overflow-x-clip">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={false}
|
initial={false}
|
||||||
animate={{ x: `${position}%` }}
|
animate={{ x: `${position}%` }}
|
||||||
transition={springTransition}
|
transition={springTransition}
|
||||||
>
|
>
|
||||||
<div className="absolute flex w-full" style={{ left: `${-position}%` }}>
|
<div className="absolute flex w-full" style={{ left: `${-position}%` }}>
|
||||||
{tabs.map((tab, index) => (
|
<AnimatePresence>
|
||||||
<div
|
{tabs.map((tab, index) => (
|
||||||
key={index}
|
activeTab === index && (
|
||||||
className={`w-full ${activeTab === index ? '' : 'hidden'}`}
|
<motion.div
|
||||||
>
|
key={index}
|
||||||
{tab.content}
|
className="absolute w-full"
|
||||||
</div>
|
initial="hidden"
|
||||||
))}
|
animate="visible"
|
||||||
|
exit="hidden"
|
||||||
|
transition={fastOpacityTransition}
|
||||||
|
variants={contentVariants}
|
||||||
|
>
|
||||||
|
{tab.content}
|
||||||
|
</motion.div>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*global chrome*/
|
||||||
|
import { useEffect, useMemo } from "react";
|
||||||
|
import { SettingsProps } from "../types/SettingsProps";
|
||||||
|
import { MainConfig, SettingsState } from "../types/AppProps";
|
||||||
|
|
||||||
|
let RanOnce = false;
|
||||||
|
let previousSettingsState: SettingsState
|
||||||
|
|
||||||
|
const useSettingsState = ({ settingsState, setSettingsState }: SettingsProps) => {
|
||||||
|
useEffect(() => {
|
||||||
|
if (RanOnce) return;
|
||||||
|
RanOnce = true;
|
||||||
|
|
||||||
|
// get the current settings state
|
||||||
|
chrome.storage.local.get(function(result: MainConfig) {
|
||||||
|
console.log(result);
|
||||||
|
setSettingsState({
|
||||||
|
notificationCollector: result.notificationcollector,
|
||||||
|
lessonAlerts: result.lessonalert,
|
||||||
|
animatedBackground: result.animatedbk,
|
||||||
|
animatedBackgroundSpeed: result.bksliderinput,
|
||||||
|
customThemeColor: result.selectedColor,
|
||||||
|
betterSEQTAPlus: result.onoff,
|
||||||
|
shortcuts: result.shortcuts,
|
||||||
|
customshortcuts: result.customshortcuts,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.DarkMode) {
|
||||||
|
document.body.classList.add('dark');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const keyToStateMap = useMemo(() => ({
|
||||||
|
"notificationcollector": "notificationCollector",
|
||||||
|
"lessonalert": "lessonAlerts",
|
||||||
|
"animatedbk": "animatedBackground",
|
||||||
|
"bksliderinput": "animatedBackgroundSpeed",
|
||||||
|
"selectedColor": "customThemeColor",
|
||||||
|
"onoff": "betterSEQTAPlus",
|
||||||
|
"shortcuts": "shortcuts",
|
||||||
|
"customshortcuts": "customshortcuts",
|
||||||
|
}), []);
|
||||||
|
|
||||||
|
const storageChangeListener = (changes: chrome.storage.StorageChange) => {
|
||||||
|
console.log(settingsState);
|
||||||
|
for (const [key, { newValue }] of Object.entries(changes)) {
|
||||||
|
if (key === "DarkMode") {
|
||||||
|
if (key === "DarkMode" && newValue) {
|
||||||
|
document.body.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove('dark');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error - TODO: Fix this
|
||||||
|
const stateKey = keyToStateMap[key as keyof MainConfig];
|
||||||
|
if (stateKey) {
|
||||||
|
setSettingsState((prevState: SettingsState) => ({
|
||||||
|
...prevState,
|
||||||
|
[stateKey]: newValue
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
chrome.storage.onChanged.addListener(storageChangeListener);
|
||||||
|
return () => {
|
||||||
|
chrome.storage.onChanged.removeListener(storageChangeListener);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const setStorage = (key: keyof MainConfig, value: any) => {
|
||||||
|
chrome.storage.local.set({ [key]: value });
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (previousSettingsState) {
|
||||||
|
for (const [key, value] of Object.entries(settingsState)) {
|
||||||
|
// @ts-expect-error - TODO: Fix this
|
||||||
|
const storageKey = Object.keys(keyToStateMap).find(k => keyToStateMap[k] === key);
|
||||||
|
// @ts-expect-error - TODO: Fix this
|
||||||
|
if (storageKey && value !== previousSettingsState[key]) {
|
||||||
|
setStorage(storageKey as keyof MainConfig, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousSettingsState = settingsState;
|
||||||
|
}, [settingsState, keyToStateMap])
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useSettingsState;
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
import './index.css';
|
||||||
|
|
||||||
|
declare module "*.png";
|
||||||
|
declare module "*.svg";
|
||||||
|
declare module "*.jpeg";
|
||||||
|
declare module "*.jpg";
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom/client'
|
import ReactDOM from 'react-dom/client'
|
||||||
import App from './App'
|
import App from './App.js'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('ExtensionPopup')!);
|
const root = ReactDOM.createRoot(document.getElementById('ExtensionPopup')!);
|
||||||
@@ -9,4 +9,4 @@ root.render(
|
|||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
)
|
);
|
||||||
@@ -1,21 +1,26 @@
|
|||||||
import Switch from '../components/Switch';
|
import Switch from '../components/Switch';
|
||||||
import ColorPicker from '../components/ColorPicker';
|
import ColorPicker from '../components/ColorPicker';
|
||||||
import { SettingsState } from '../App';
|
import { SettingsList } from '../types/SettingsProps';
|
||||||
|
import { useSettingsContext } from '../SettingsContext';
|
||||||
|
|
||||||
interface ISetting {
|
const Settings: React.FC = () => {
|
||||||
title: string;
|
const { settingsState, setSettingsState } = useSettingsContext();
|
||||||
description: string;
|
|
||||||
modifyElement: JSX.Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SettingsProps {
|
const switchChange = (key: string, isOn: boolean) => {
|
||||||
settingsState: SettingsState;
|
setSettingsState({
|
||||||
switchChange: (key: string, isOn: boolean) => void;
|
...settingsState,
|
||||||
colorChange: (color: string) => void;
|
[key]: isOn,
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const Settings: React.FC<SettingsProps> = ({ settingsState, switchChange, colorChange }) => {
|
const colorChange = (color: string) => {
|
||||||
const settings: ISetting[] = [
|
setSettingsState({
|
||||||
|
...settingsState,
|
||||||
|
customThemeColor: color,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const settings: SettingsList[] = [
|
||||||
{
|
{
|
||||||
title: "Notification Collector",
|
title: "Notification Collector",
|
||||||
description: "Uncaps the 9+ limit for notifications, showing the real number.",
|
description: "Uncaps the 9+ limit for notifications, showing the real number.",
|
||||||
@@ -34,7 +39,7 @@ const Settings: React.FC<SettingsProps> = ({ settingsState, switchChange, colorC
|
|||||||
{
|
{
|
||||||
title: "Animated Background Speed",
|
title: "Animated Background Speed",
|
||||||
description: "Controls the speed of the animated background.",
|
description: "Controls the speed of the animated background.",
|
||||||
modifyElement: <Switch state={settingsState.animatedBackgroundSpeed} onChange={(isOn: boolean) => switchChange('animatedBackgroundSpeed', isOn)} />
|
modifyElement: <div>Insert Slider Please</div>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Custom Theme Colour",
|
title: "Custom Theme Colour",
|
||||||
@@ -43,13 +48,13 @@ const Settings: React.FC<SettingsProps> = ({ settingsState, switchChange, colorC
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "BetterSEQTA+",
|
title: "BetterSEQTA+",
|
||||||
description: "Unlocks premium features.",
|
description: "Enables BetterSEQTA+ features",
|
||||||
modifyElement: <Switch state={settingsState.betterSEQTAPlus} onChange={(isOn: boolean) => switchChange('betterSEQTAPlus', isOn)} />
|
modifyElement: <Switch state={settingsState.betterSEQTAPlus} onChange={(isOn: boolean) => switchChange('betterSEQTAPlus', isOn)} />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col overflow-y-scroll divide-y divide-zinc-100">
|
<div className="flex flex-col -mt-4 overflow-y-scroll divide-y divide-zinc-100">
|
||||||
{settings.map((setting, index) => (
|
{settings.map((setting, index) => (
|
||||||
<div className="flex items-center justify-between px-4 py-3" key={index}>
|
<div className="flex items-center justify-between px-4 py-3" key={index}>
|
||||||
<div className="pr-4">
|
<div className="pr-4">
|
||||||
|
|||||||
@@ -1,95 +1,158 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Switch from "../components/Switch";
|
import Switch from "../components/Switch";
|
||||||
|
import { useSettingsContext } from "../SettingsContext";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
|
||||||
|
interface Shortcut {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export default function Shortcuts() {
|
export default function Shortcuts() {
|
||||||
const [shortcutState, setShortcutState] = useState({
|
const { settingsState, setSettingsState } = useSettingsContext();
|
||||||
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 = (shortcutName: string, isOn: boolean): void => {
|
||||||
const switchChange = (key: string, isOn: boolean) => {
|
const updatedShortcuts = settingsState.shortcuts.map((shortcut) => {
|
||||||
setShortcutState({
|
if (shortcut.name === shortcutName) {
|
||||||
...shortcutState,
|
return { ...shortcut, enabled: isOn };
|
||||||
[key]: isOn,
|
}
|
||||||
|
return shortcut;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setSettingsState({ ...settingsState, shortcuts: updatedShortcuts });
|
||||||
};
|
};
|
||||||
|
|
||||||
const DefaultShortcuts = [
|
const [newTitle, setNewTitle] = useState<string>("");
|
||||||
{
|
const [newURL, setNewURL] = useState<string>("");
|
||||||
title: "YouTube",
|
|
||||||
link: "https://youtube.com",
|
const isValidTitle = (title: string): boolean => title.trim() !== "";
|
||||||
modifyElement: <Switch state={shortcutState.youtube} onChange={(isOn: boolean) => switchChange('youtube', isOn)} />
|
|
||||||
},
|
const isValidURL = (url: string): boolean => {
|
||||||
{
|
const pattern = new RegExp("^(https?:\\/\\/)?[\\w.-]+[\\w.-]+$", "i");
|
||||||
title: "Outlook",
|
return pattern.test(url);
|
||||||
link: "https://outlook.office.com/mail/inbox",
|
};
|
||||||
modifyElement: <Switch state={shortcutState.outlook} onChange={(isOn: boolean) => switchChange('outlook', isOn)} />
|
|
||||||
},
|
const addNewCustomShortcut = (): void => {
|
||||||
{
|
if (isValidTitle(newTitle) && isValidURL(newURL)) {
|
||||||
title: "Office",
|
const newShortcut: Shortcut = { name: newTitle.trim(), url: newURL.trim() };
|
||||||
link: "https://www.office.com/",
|
const updatedCustomShortcuts = [...settingsState.customshortcuts, newShortcut];
|
||||||
modifyElement: <Switch state={shortcutState.office} onChange={(isOn: boolean) => switchChange('office', isOn)} />
|
setSettingsState({ ...settingsState, customshortcuts: updatedCustomShortcuts });
|
||||||
},
|
setNewTitle("");
|
||||||
{
|
setNewURL("");
|
||||||
title: "Spotify",
|
|
||||||
link: "https://www.spotify.com/",
|
setFormVisible(false);
|
||||||
modifyElement: <Switch state={shortcutState.spotify} onChange={(isOn: boolean) => switchChange('spotify', isOn)} />
|
} else {
|
||||||
},
|
// Replace with a more user-friendly way to display errors
|
||||||
{
|
console.error("Please enter a valid title and URL.");
|
||||||
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)} />
|
|
||||||
}
|
}
|
||||||
];
|
};
|
||||||
|
|
||||||
|
const deleteCustomShortcut = (index: number): void => {
|
||||||
|
const updatedCustomShortcuts = settingsState.customshortcuts.filter((_, i) => i !== index);
|
||||||
|
setSettingsState({ ...settingsState, customshortcuts: updatedCustomShortcuts });
|
||||||
|
};
|
||||||
|
|
||||||
|
const [isFormVisible, setFormVisible] = useState(false);
|
||||||
|
|
||||||
|
const toggleForm = () => {
|
||||||
|
setFormVisible(!isFormVisible);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col divide-y divide-zinc-100">
|
<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}>
|
<AnimatePresence>
|
||||||
{shortcut.title}
|
{isFormVisible ? (
|
||||||
{shortcut.modifyElement}
|
<motion.div
|
||||||
</div>
|
initial={{ opacity: 0, height: 0 }}
|
||||||
))}
|
animate={{ opacity: 1, height: "auto" }}
|
||||||
|
exit={{ opacity: 0, height: 0 }}
|
||||||
|
transition={{ type: "spring", damping: 20 }}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center mb-4">
|
||||||
|
<motion.input
|
||||||
|
initial={{ opacity: 0, y: -10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.2 }}
|
||||||
|
className="w-full p-2 rounded-md bg-zinc-100 dark:bg-zinc-700 focus:outline-none"
|
||||||
|
type="text"
|
||||||
|
placeholder="Shortcut Name"
|
||||||
|
value={newTitle}
|
||||||
|
onChange={(e) => setNewTitle(e.target.value)}
|
||||||
|
/>
|
||||||
|
<motion.input
|
||||||
|
initial={{ opacity: 0, y: -10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.3 }}
|
||||||
|
className="w-full p-2 my-2 rounded-md bg-zinc-100 dark:bg-zinc-700 focus:outline-none"
|
||||||
|
type="text"
|
||||||
|
placeholder="URL eg. https://google.com"
|
||||||
|
value={newURL}
|
||||||
|
onChange={(e) => setNewURL(e.target.value)}
|
||||||
|
/>
|
||||||
|
<motion.button
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ delay: 0.4 }}
|
||||||
|
className="w-full px-4 py-2 text-white bg-blue-500 rounded-md"
|
||||||
|
onClick={ addNewCustomShortcut }
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</motion.button>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
) : (
|
||||||
|
<motion.button
|
||||||
|
initial={{ backgroundColor: "rgba(29, 161, 242, 1)", height: "auto" }}
|
||||||
|
animate={{ backgroundColor: "rgba(29, 161, 242, 1)", height: "auto" }}
|
||||||
|
exit={{ backgroundColor: "rgba(29, 161, 242, 1)", height: "auto" }}
|
||||||
|
transition={{ type: 'tween', ease: "easeOut" }}
|
||||||
|
className="px-4 py-2 mb-4 text-white bg-blue-500 rounded"
|
||||||
|
onClick={toggleForm}
|
||||||
|
>
|
||||||
|
Add Custom Shortcut
|
||||||
|
</motion.button>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* Shortcuts Section */}
|
||||||
|
{settingsState.shortcuts ? (
|
||||||
|
settingsState.shortcuts.map((shortcut) => shortcut.name && (
|
||||||
|
<div className="flex items-center justify-between px-4 py-3" key={shortcut.name}>
|
||||||
|
{shortcut.name}
|
||||||
|
<Switch state={shortcut.enabled} onChange={(isOn) => switchChange(shortcut.name, isOn)} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p>Loading shortcuts...</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Custom Shortcuts Section */}
|
||||||
|
{settingsState.customshortcuts ? (
|
||||||
|
settingsState.customshortcuts.map((shortcut, index) => (
|
||||||
|
<div className="flex items-center justify-between px-4 py-3" key={shortcut.name}>
|
||||||
|
{shortcut.name}
|
||||||
|
<button onClick={() => deleteCustomShortcut(index)}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="w-4 h-4 text-red-500"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
d="M15.707 4.293a1 1 0 010 1.414L11.414 10l4.293 4.293a1 1 0 11-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 11-1.414-1.414L8.586 10 4.293 5.707a1 1 0 111.414-1.414L10 8.586l4.293-4.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p>Loading custom shortcuts...</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
export interface SettingsState {
|
||||||
|
notificationCollector: boolean;
|
||||||
|
lessonAlerts: boolean;
|
||||||
|
animatedBackground: boolean;
|
||||||
|
animatedBackgroundSpeed: string;
|
||||||
|
customThemeColor: string;
|
||||||
|
betterSEQTAPlus: boolean;
|
||||||
|
shortcuts: Shortcut[];
|
||||||
|
customshortcuts: CustomShortcut[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToggleItem {
|
||||||
|
toggle: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Shortcut {
|
||||||
|
enabled: boolean;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CustomShortcut {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MainConfig {
|
||||||
|
DarkMode: boolean;
|
||||||
|
animatedbk: boolean;
|
||||||
|
bksliderinput: string;
|
||||||
|
customshortcuts: CustomShortcut[];
|
||||||
|
defaultmenuorder: any[];
|
||||||
|
lessonalert: boolean;
|
||||||
|
menuitems: {
|
||||||
|
assessments: ToggleItem;
|
||||||
|
courses: ToggleItem;
|
||||||
|
dashboard: ToggleItem;
|
||||||
|
documents: ToggleItem;
|
||||||
|
forums: ToggleItem;
|
||||||
|
goals: ToggleItem;
|
||||||
|
home: ToggleItem;
|
||||||
|
messages: ToggleItem;
|
||||||
|
myed: ToggleItem;
|
||||||
|
news: ToggleItem;
|
||||||
|
notices: ToggleItem;
|
||||||
|
portals: ToggleItem;
|
||||||
|
reports: ToggleItem;
|
||||||
|
settings: ToggleItem;
|
||||||
|
timetable: ToggleItem;
|
||||||
|
welcome: ToggleItem;
|
||||||
|
};
|
||||||
|
menuorder: any[];
|
||||||
|
notificationcollector: boolean;
|
||||||
|
onoff: boolean;
|
||||||
|
selectedColor: string;
|
||||||
|
shortcuts: Shortcut[];
|
||||||
|
subjectfilters: Record<string, any>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface ColorPickerProps {
|
||||||
|
color: string;
|
||||||
|
onChange: (color: string) => void;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import type { SettingsState } from './AppProps';
|
||||||
|
|
||||||
|
export interface SettingsList {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
modifyElement: JSX.Element;
|
||||||
|
}
|
||||||
|
export interface SettingsProps {
|
||||||
|
settingsState: SettingsState;
|
||||||
|
setSettingsState: React.Dispatch<React.SetStateAction<SettingsState>>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import "./Slider.css";
|
||||||
|
export interface Slider {
|
||||||
|
onValueChange: (value: number) => void;
|
||||||
|
}
|
||||||
|
declare const Slider: React.FC<Slider>;
|
||||||
|
export default Slider;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import "./Switch.css";
|
||||||
|
|
||||||
|
export interface SwitchProps {
|
||||||
|
onChange: (isOn: boolean) => void;
|
||||||
|
state: boolean;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React, { JSX } from 'react';
|
||||||
|
export interface Tab {
|
||||||
|
title: string;
|
||||||
|
content: JSX.Element;
|
||||||
|
}
|
||||||
|
export interface TabbedContainerProps {
|
||||||
|
tabs: Tab[];
|
||||||
|
}
|
||||||
|
declare const TabbedContainer: React.FC<TabbedContainerProps>;
|
||||||
|
export default TabbedContainer;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
declare const _default: import("vite").UserConfig;
|
||||||
|
export default _default;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
build: {
|
||||||
|
//outDir: '../../public/popup-dist',
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
assetFileNames: 'client/rsc/[ext]/[name][extname]',
|
||||||
|
chunkFileNames: 'client/rsc/[chunks]/[name].[hash].js',
|
||||||
|
entryFileNames: 'client/public/client.js'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -27,5 +27,17 @@
|
|||||||
"webextension-polyfill": "^0.10.0",
|
"webextension-polyfill": "^0.10.0",
|
||||||
"webpack": "^5.88.2",
|
"webpack": "^5.88.2",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/color": "^3.0.4",
|
||||||
|
"@types/react": "^18.2.21",
|
||||||
|
"autoprefixer": "^10.4.15",
|
||||||
|
"color": "^4.2.3",
|
||||||
|
"install": "^0.13.0",
|
||||||
|
"npm": "^10.1.0",
|
||||||
|
"postcss": "^8.4.29",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require("tailwindcss"),
|
||||||
|
require("autoprefixer"),
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require("tailwindcss"),
|
||||||
|
require("autoprefixer"),
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -50,6 +50,22 @@
|
|||||||
{
|
{
|
||||||
"resources": ["popup/*"],
|
"resources": ["popup/*"],
|
||||||
"matches": ["*://*/*"]
|
"matches": ["*://*/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resources": ["client.js"],
|
||||||
|
"matches": ["*://*/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resources": ["index.css"],
|
||||||
|
"matches": ["*://*/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resources": ["interface/*"],
|
||||||
|
"matches": ["*://*/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resources": ["client/*"],
|
||||||
|
"matches": ["*://*/*"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,12 @@
|
|||||||
@import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,600");
|
@import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,600");
|
||||||
|
|
||||||
.outside-container {
|
.outside-container {
|
||||||
width: 350px;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: #131313;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
top: 80px;
|
top: 80px;
|
||||||
|
height: 590px;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+183
-133
@@ -1,4 +1,6 @@
|
|||||||
/*global chrome*/
|
/*global chrome*/
|
||||||
|
import Color from "color";
|
||||||
|
|
||||||
import ShortcutLinks from "./seqta/content/links.json";
|
import ShortcutLinks from "./seqta/content/links.json";
|
||||||
import MenuitemSVGKey from "./seqta/content/MenuItemSVGKey.json";
|
import MenuitemSVGKey from "./seqta/content/MenuItemSVGKey.json";
|
||||||
import stringToHTML from "./seqta/utils/stringToHTML.js";
|
import stringToHTML from "./seqta/utils/stringToHTML.js";
|
||||||
@@ -13,7 +15,6 @@ let SettingsClicked = false;
|
|||||||
let MenuOptionsOpen = false;
|
let MenuOptionsOpen = false;
|
||||||
let UserInitalCode = "";
|
let UserInitalCode = "";
|
||||||
let currentSelectedDate = new Date();
|
let currentSelectedDate = new Date();
|
||||||
let WhatsNewOpen = false;
|
|
||||||
let LessonInterval;
|
let LessonInterval;
|
||||||
let DarkMode;
|
let DarkMode;
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ function SetDisplayNone(ElementName) {
|
|||||||
return `li[data-key=${ElementName}]{display:var(--menuHidden) !important; transition: 1s;}`;
|
return `li[data-key=${ElementName}]{display:var(--menuHidden) !important; transition: 1s;}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function animbkEnable (item) {
|
function animbkEnable(item) {
|
||||||
if (item.animatedbk) {
|
if (item.animatedbk) {
|
||||||
CreateBackground();
|
CreateBackground();
|
||||||
} else {
|
} else {
|
||||||
@@ -38,7 +39,9 @@ function bkValues (item) {
|
|||||||
const bg = document.getElementsByClassName("bg");
|
const bg = document.getElementsByClassName("bg");
|
||||||
const bg2 = document.getElementsByClassName("bg2");
|
const bg2 = document.getElementsByClassName("bg2");
|
||||||
const bg3 = document.getElementsByClassName("bg3");
|
const bg3 = document.getElementsByClassName("bg3");
|
||||||
const value = 200 - item.bksliderinput;
|
const value = 200 - item.bksliderinput; // reverse the slider direction to match the animation direction
|
||||||
|
|
||||||
|
if (bg.length == 0 || bg2.length == 0 || bg3.length == 0) return;
|
||||||
|
|
||||||
const minDuration = 1; // minimum duration in seconds
|
const minDuration = 1; // minimum duration in seconds
|
||||||
const maxDuration = 10; // maximum duration in seconds
|
const maxDuration = 10; // maximum duration in seconds
|
||||||
@@ -172,12 +175,12 @@ function OpenWhatsNewPopup() {
|
|||||||
var bkelement = document.getElementById("whatsnewbk");
|
var bkelement = document.getElementById("whatsnewbk");
|
||||||
bkelement.addEventListener("click", function () {
|
bkelement.addEventListener("click", function () {
|
||||||
DeleteWhatsNew();
|
DeleteWhatsNew();
|
||||||
WhatsNewOpen = false;
|
//WhatsNewOpen = false;
|
||||||
});
|
});
|
||||||
var closeelement = document.getElementById("whatsnewclosebutton");
|
var closeelement = document.getElementById("whatsnewclosebutton");
|
||||||
closeelement.addEventListener("click", function () {
|
closeelement.addEventListener("click", function () {
|
||||||
DeleteWhatsNew();
|
DeleteWhatsNew();
|
||||||
WhatsNewOpen = false;
|
//WhatsNewOpen = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +196,7 @@ async function finishLoad() {
|
|||||||
|
|
||||||
chrome.storage.local.get(["justupdated"], function (result) {
|
chrome.storage.local.get(["justupdated"], function (result) {
|
||||||
if (result.justupdated) {
|
if (result.justupdated) {
|
||||||
WhatsNewOpen = true;
|
//WhatsNewOpen = true;
|
||||||
OpenWhatsNewPopup();
|
OpenWhatsNewPopup();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -233,6 +236,9 @@ function RemoveBackground() {
|
|||||||
var bk = document.getElementsByClassName("bg");
|
var bk = document.getElementsByClassName("bg");
|
||||||
var bk2 = document.getElementsByClassName("bg2");
|
var bk2 = document.getElementsByClassName("bg2");
|
||||||
var bk3 = document.getElementsByClassName("bg3");
|
var bk3 = document.getElementsByClassName("bg3");
|
||||||
|
|
||||||
|
if (bk.length == 0 || bk2.length == 0 || bk3.length == 0) return;
|
||||||
|
|
||||||
bk[0].remove();
|
bk[0].remove();
|
||||||
bk2[0].remove();
|
bk2[0].remove();
|
||||||
bk3[0].remove();
|
bk3[0].remove();
|
||||||
@@ -621,116 +627,116 @@ function AppendElementsToDisabledPage() {
|
|||||||
document.head.append(settingsStyle);
|
document.head.append(settingsStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
function lightenAndPaleColor(
|
function lightenAndPaleColor(inputColor, lightenFactor = 0.75, paleFactor = 0.55) {
|
||||||
hexColor,
|
// Step 1: Convert RGBA to separate R, G and B values
|
||||||
lightenFactor = 0.75,
|
const [r, g, b] = inputColor.match(/\d+/g).map(Number);
|
||||||
paleFactor = 0.55,
|
|
||||||
) {
|
// Step 2: Convert RGB to HSL
|
||||||
// Convert a RGB value to HSL
|
let r1 = r / 255, g1 = g / 255, b1 = b / 255;
|
||||||
function rgbToHsl(r, g, b) {
|
const max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1);
|
||||||
(r /= 255), (g /= 255), (b /= 255);
|
let h, s, l = (max + min) / 2;
|
||||||
let max = Math.max(r, g, b),
|
|
||||||
min = Math.min(r, g, b);
|
if (max === min) {
|
||||||
let h,
|
h = s = 0;
|
||||||
s,
|
} else {
|
||||||
l = (max + min) / 2;
|
const d = max - min;
|
||||||
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||||
|
switch (max) {
|
||||||
|
|
||||||
|
case r1: h = (g1 - b1) / d + (g1 < b1 ? 6 : 0); break;
|
||||||
|
case g1: h = (b1 - r1) / d + 2; break;
|
||||||
|
case b1: h = (r1 - g1) / d + 4; break;
|
||||||
|
|
||||||
if (max === min) {
|
|
||||||
h = s = 0;
|
|
||||||
} else {
|
|
||||||
let d = max - min;
|
|
||||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
||||||
switch (max) {
|
|
||||||
case r:
|
|
||||||
h = (g - b) / d + (g < b ? 6 : 0);
|
|
||||||
break;
|
|
||||||
case g:
|
|
||||||
h = (b - r) / d + 2;
|
|
||||||
break;
|
|
||||||
case b:
|
|
||||||
h = (r - g) / d + 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
h /= 6;
|
|
||||||
}
|
}
|
||||||
|
h /= 6;
|
||||||
return [h, s, l];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert an HSL value to RGB
|
// Step 3: Adjust saturation and lightness
|
||||||
function hslToRgb(h, s, l) {
|
|
||||||
function hue2rgb(p, q, t) {
|
|
||||||
if (t < 0) t += 1;
|
|
||||||
if (t > 1) t -= 1;
|
|
||||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
||||||
if (t < 1 / 2) return q;
|
|
||||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
let r, g, b;
|
|
||||||
if (s === 0) {
|
|
||||||
r = g = b = l;
|
|
||||||
} else {
|
|
||||||
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
||||||
let p = 2 * l - q;
|
|
||||||
r = hue2rgb(p, q, h + 1 / 3);
|
|
||||||
g = hue2rgb(p, q, h);
|
|
||||||
b = hue2rgb(p, q, h - 1 / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [r * 255, g * 255, b * 255];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the red, green, and blue components from hex
|
|
||||||
let r = parseInt(hexColor.substr(1, 2), 16);
|
|
||||||
let g = parseInt(hexColor.substr(3, 2), 16);
|
|
||||||
let b = parseInt(hexColor.substr(5, 2), 16);
|
|
||||||
|
|
||||||
// Convert RGB to HSL
|
|
||||||
let [h, s, l] = rgbToHsl(r, g, b);
|
|
||||||
|
|
||||||
// Adjust saturation and lightness
|
|
||||||
s -= s * paleFactor;
|
s -= s * paleFactor;
|
||||||
l += (1 - l) * lightenFactor;
|
l += (1 - l) * lightenFactor;
|
||||||
|
|
||||||
// Convert HSL back to RGB
|
// Step 4: Convert HSL back to RGB
|
||||||
[r, g, b] = hslToRgb(h, s, l);
|
const hue2rgb = (p, q, t) => {
|
||||||
|
if(t < 0) t += 1;
|
||||||
|
if(t > 1) t -= 1;
|
||||||
|
if(t < 1/6) return p + (q - p) * 6 * t;
|
||||||
|
if(t < 1/2) return q;
|
||||||
|
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
// Convert RGB to hex
|
let r2, g2, b2;
|
||||||
r = Math.round(r).toString(16).padStart(2, "0");
|
if (s === 0) {
|
||||||
g = Math.round(g).toString(16).padStart(2, "0");
|
r2 = g2 = b2 = l;
|
||||||
b = Math.round(b).toString(16).padStart(2, "0");
|
} else {
|
||||||
|
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||||
return "#" + r + g + b;
|
const p = 2 * l - q;
|
||||||
}
|
r2 = hue2rgb(p, q, h + 1/3);
|
||||||
|
g2 = hue2rgb(p, q, h);
|
||||||
function ColorLuminance(hex, lum) {
|
b2 = hue2rgb(p, q, h - 1/3);
|
||||||
// validate hex string
|
|
||||||
hex = String(hex).replace(/[^0-9a-f]/gi, "");
|
|
||||||
if (hex.length < 6) {
|
|
||||||
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
||||||
}
|
|
||||||
lum = lum || 0;
|
|
||||||
|
|
||||||
// convert to decimal and change luminosity
|
|
||||||
var rgb = "#",
|
|
||||||
c,
|
|
||||||
i;
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
c = parseInt(hex.substr(i * 2, 2), 16);
|
|
||||||
c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16);
|
|
||||||
rgb += ("00" + c).substring(c.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rgb;
|
// Step 5: Format Output
|
||||||
|
const result = `rgb(${Math.round(r2 * 255)}, ${Math.round(g2 * 255)}, ${Math.round(b2 * 255)})`;
|
||||||
|
|
||||||
|
return `${result}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ColorLuminance(color, lum = 0) {
|
||||||
|
// Regular expression to match RGBA colors
|
||||||
|
const rgbaRegex = /rgba?\(([^)]+)\)/g;
|
||||||
|
|
||||||
|
// Check if the input color is a gradient (linear or radial)
|
||||||
|
if (color.includes("gradient")) {
|
||||||
|
let gradient = color;
|
||||||
|
|
||||||
|
// Find and replace all instances of RGBA in the gradient
|
||||||
|
let match;
|
||||||
|
while ((match = rgbaRegex.exec(color)) !== null) {
|
||||||
|
const rgbaString = match[1];
|
||||||
|
const [r, g, b, a] = rgbaString.split(",").map(str => str.trim());
|
||||||
|
|
||||||
|
// Apply the original luminance adjustment logic
|
||||||
|
let adjustedRgba = [];
|
||||||
|
for (let c of [r, g, b]) {
|
||||||
|
c = Math.round(Math.min(Math.max(0, c + c * lum), 255));
|
||||||
|
adjustedRgba.push(c);
|
||||||
|
}
|
||||||
|
adjustedRgba.push(a); // Add the alpha component back
|
||||||
|
|
||||||
|
// Replace the original RGBA string with the adjusted one
|
||||||
|
gradient = gradient.replace(`rgba(${rgbaString})`, `rgba(${adjustedRgba.join(", ")})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gradient;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Handle as a simple color (could be HEX, RGBA, etc., as supported by your Color library)
|
||||||
|
const hexColor = Color(color).hex();
|
||||||
|
let adjustedHex = String(hexColor).replace(/[^0-9a-f]/gi, "");
|
||||||
|
if (adjustedHex.length < 6) {
|
||||||
|
adjustedHex = adjustedHex[0] + adjustedHex[0] + adjustedHex[1] + adjustedHex[1] + adjustedHex[2] + adjustedHex[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
let rgb = "#",
|
||||||
|
c;
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
c = parseInt(adjustedHex.substr(i * 2, 2), 16);
|
||||||
|
c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16);
|
||||||
|
rgb += ("00" + c).substring(c.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Color(rgb).hex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
chrome.storage.onChanged.addListener(function (changes) {
|
chrome.storage.onChanged.addListener(function (changes) {
|
||||||
if (changes.selectedColor) {
|
if (changes.selectedColor) {
|
||||||
try {
|
try {
|
||||||
chrome.storage.local.get(["DarkMode"], function (result) {
|
chrome.storage.local.get(["DarkMode"], function (result) {
|
||||||
if (!result.DarkMode) {
|
if (!result.DarkMode) {
|
||||||
|
console.log(changes.selectedColor.newValue);
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
"--better-pale",
|
"--better-pale",
|
||||||
lightenAndPaleColor(changes.selectedColor.newValue),
|
lightenAndPaleColor(changes.selectedColor.newValue),
|
||||||
@@ -742,6 +748,7 @@ chrome.storage.onChanged.addListener(function (changes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let rbg = GetThresholdofHex(changes.selectedColor.newValue);
|
let rbg = GetThresholdofHex(changes.selectedColor.newValue);
|
||||||
|
|
||||||
if (rbg > 210) {
|
if (rbg > 210) {
|
||||||
document.documentElement.style.setProperty("--text-color", "black");
|
document.documentElement.style.setProperty("--text-color", "black");
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
@@ -760,7 +767,6 @@ chrome.storage.onChanged.addListener(function (changes) {
|
|||||||
"--better-main",
|
"--better-main",
|
||||||
changes.selectedColor.newValue,
|
changes.selectedColor.newValue,
|
||||||
);
|
);
|
||||||
// document.documentElement.style.setProperty('--better-sub', ColorLuminance(changes.selectedColor.newValue, -0.15));
|
|
||||||
|
|
||||||
if (changes.selectedColor.newValue == "#ffffff") {
|
if (changes.selectedColor.newValue == "#ffffff") {
|
||||||
document.documentElement.style.setProperty("--better-light", "#b7b7b7");
|
document.documentElement.style.setProperty("--better-light", "#b7b7b7");
|
||||||
@@ -801,7 +807,7 @@ async function CheckLoadOnPeriods() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function RunFunctionOnTrue(storedSetting) {
|
function main(storedSetting) {
|
||||||
DarkMode = storedSetting.DarkMode;
|
DarkMode = storedSetting.DarkMode;
|
||||||
// If the option is 'on', open BetterSEQTA
|
// If the option is 'on', open BetterSEQTA
|
||||||
if (typeof storedSetting.onoff == "undefined") {
|
if (typeof storedSetting.onoff == "undefined") {
|
||||||
@@ -891,7 +897,6 @@ function RunFunctionOnTrue(storedSetting) {
|
|||||||
"--better-main",
|
"--better-main",
|
||||||
storedSetting.selectedColor,
|
storedSetting.selectedColor,
|
||||||
);
|
);
|
||||||
// document.documentElement.style.setProperty('--better-sub', ColorLuminance(storedSetting.selectedColor, -0.15));
|
|
||||||
|
|
||||||
if (storedSetting.selectedColor == "#ffffff") {
|
if (storedSetting.selectedColor == "#ffffff") {
|
||||||
document.documentElement.style.setProperty("--better-light", "#b7b7b7");
|
document.documentElement.style.setProperty("--better-light", "#b7b7b7");
|
||||||
@@ -966,7 +971,7 @@ document.addEventListener(
|
|||||||
document.getElementsByTagName("html")[0].appendChild(link);
|
document.getElementsByTagName("html")[0].appendChild(link);
|
||||||
|
|
||||||
chrome.storage.local.get(null, function (items) {
|
chrome.storage.local.get(null, function (items) {
|
||||||
RunFunctionOnTrue(items);
|
main(items);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -978,7 +983,7 @@ document.addEventListener(
|
|||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
/*
|
||||||
function RunExtensionSettingsJS() {
|
function RunExtensionSettingsJS() {
|
||||||
const whatsnewsettings = document.getElementById("whatsnewsettings");
|
const whatsnewsettings = document.getElementById("whatsnewsettings");
|
||||||
whatsnewsettings.addEventListener("click", function () {
|
whatsnewsettings.addEventListener("click", function () {
|
||||||
@@ -1043,9 +1048,9 @@ function RunExtensionSettingsJS() {
|
|||||||
function FindSEQTATab() {
|
function FindSEQTATab() {
|
||||||
chrome.runtime.sendMessage({ type: "reloadTabs" });
|
chrome.runtime.sendMessage({ type: "reloadTabs" });
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
Store the currently selected settings using chrome.storage.local.
|
// Store the currently selected settings using chrome.storage.local.
|
||||||
*/
|
|
||||||
function storeSettings() {
|
function storeSettings() {
|
||||||
chrome.storage.local.set({ onoff: onoffselection.checked }, function () {
|
chrome.storage.local.set({ onoff: onoffselection.checked }, function () {
|
||||||
FindSEQTATab();
|
FindSEQTATab();
|
||||||
@@ -1072,10 +1077,10 @@ function RunExtensionSettingsJS() {
|
|||||||
|
|
||||||
FindSEQTATab();
|
FindSEQTATab();
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
Update the options UI with the settings values retrieved from storage,
|
// Update the options UI with the settings values retrieved from storage,
|
||||||
or the default settings if the stored settings are empty.
|
// or the default settings if the stored settings are empty.
|
||||||
*/
|
|
||||||
function updateUI(restoredSettings) {
|
function updateUI(restoredSettings) {
|
||||||
if (typeof restoredSettings.onoff == "undefined") {
|
if (typeof restoredSettings.onoff == "undefined") {
|
||||||
chrome.runtime.sendMessage({ type: "setDefaultStorage" });
|
chrome.runtime.sendMessage({ type: "setDefaultStorage" });
|
||||||
@@ -1310,7 +1315,7 @@ function RunExtensionSettingsJS() {
|
|||||||
chrome.storage.local.set({ selectedColor: b });
|
chrome.storage.local.set({ selectedColor: b });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
|
|
||||||
function CallExtensionSettings() {
|
function CallExtensionSettings() {
|
||||||
// Injecting CSS File to the webpage to overwrite iFrame default CSS
|
// Injecting CSS File to the webpage to overwrite iFrame default CSS
|
||||||
@@ -1333,7 +1338,7 @@ function CallExtensionSettings() {
|
|||||||
fileref.setAttribute("href", cssFile);
|
fileref.setAttribute("href", cssFile);
|
||||||
document.head.append(fileref);
|
document.head.append(fileref);
|
||||||
|
|
||||||
let Settings =
|
/*let Settings =
|
||||||
stringToHTML(
|
stringToHTML(
|
||||||
String.raw`
|
String.raw`
|
||||||
<div class="outside-container hidden" id="ExtensionPopup"><div class="logo-container"><img src=${chrome.runtime.getURL(
|
<div class="outside-container hidden" id="ExtensionPopup"><div class="logo-container"><img src=${chrome.runtime.getURL(
|
||||||
@@ -1580,14 +1585,31 @@ function CallExtensionSettings() {
|
|||||||
<p style="margin: 0; flex: 1; padding-left: 24px; margin-right: 5px; color: white;">By SethBurkart123</p>
|
<p style="margin: 0; flex: 1; padding-left: 24px; margin-right: 5px; color: white;">By SethBurkart123</p>
|
||||||
<button style="margin: 0; margin-right: 20px; cursor:pointer; padding: 8px 10px; background: #ff5f5f; color:#1a1a1a;font-weight: 500; border-radius: 10px;" id="whatsnewsettings">Changelog v${chrome.runtime.getManifest().version}</button>
|
<button style="margin: 0; margin-right: 20px; cursor:pointer; padding: 8px 10px; background: #ff5f5f; color:#1a1a1a;font-weight: 500; border-radius: 10px;" id="whatsnewsettings">Changelog v${chrome.runtime.getManifest().version}</button>
|
||||||
</div>
|
</div>
|
||||||
</div></div>`);
|
</div></div>`);*/
|
||||||
document.body.append(Settings.firstChild);
|
let Settings2 =
|
||||||
|
stringToHTML(
|
||||||
|
String.raw`
|
||||||
|
<div class="outside-container hide" id="ExtensionPopup">
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
document.body.append(Settings2.firstChild);
|
||||||
|
|
||||||
|
// add an iframe to the div: <iframe src="interface/index.html"></iframe>
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
iframe.src = chrome.runtime.getURL("interface/index.html");
|
||||||
|
iframe.allowTransparency = "true";
|
||||||
|
iframe.style.width = "384px";
|
||||||
|
iframe.style.height = "590px";
|
||||||
|
iframe.style.border = "none";
|
||||||
|
iframe.setAttribute("excludeDarkCheck", "true");
|
||||||
|
|
||||||
|
document.getElementById("ExtensionPopup").append(iframe);
|
||||||
|
|
||||||
var container = document.getElementById("container");
|
var container = document.getElementById("container");
|
||||||
var extensionsettings = document.getElementById("ExtensionPopup");
|
var extensionsettings = document.getElementById("ExtensionPopup");
|
||||||
container.onclick = function () {
|
container.onclick = function () {
|
||||||
if (!SettingsClicked) {
|
if (!SettingsClicked) {
|
||||||
extensionsettings.classList.add("hidden");
|
extensionsettings.classList.add("hide");
|
||||||
}
|
}
|
||||||
SettingsClicked = false;
|
SettingsClicked = false;
|
||||||
};
|
};
|
||||||
@@ -1981,10 +2003,12 @@ function AddBetterSEQTAElements(toggle) {
|
|||||||
students[index]?.house_colour,
|
students[index]?.house_colour,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (colorresult > 300) {
|
if (colorresult && colorresult > 300) {
|
||||||
houseelement.style.color = "black";
|
houseelement.style.color = "black";
|
||||||
} else {
|
} else if (colorresult < 300) {
|
||||||
houseelement.style.color = "white";
|
houseelement.style.color = "white";
|
||||||
|
} else {
|
||||||
|
houseelement.style.color = "black";
|
||||||
}
|
}
|
||||||
houseelement.innerText =
|
houseelement.innerText =
|
||||||
students[index].year + students[index].house;
|
students[index].year + students[index].house;
|
||||||
@@ -2050,8 +2074,9 @@ function AddBetterSEQTAElements(toggle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CallExtensionSettings();
|
CallExtensionSettings();
|
||||||
RunExtensionSettingsJS();
|
//RunExtensionSettingsJS();
|
||||||
|
|
||||||
|
// If betterSEQTA+ is enabled, run the code
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
// Creates settings and dashboard buttons next to alerts
|
// Creates settings and dashboard buttons next to alerts
|
||||||
var SettingsButton = stringToHTML(
|
var SettingsButton = stringToHTML(
|
||||||
@@ -2112,6 +2137,11 @@ function AddBetterSEQTAElements(toggle) {
|
|||||||
|
|
||||||
for (let i = 0; i < alliframes.length; i++) {
|
for (let i = 0; i < alliframes.length; i++) {
|
||||||
const element = alliframes[i];
|
const element = alliframes[i];
|
||||||
|
|
||||||
|
if (element.getAttribute("excludeDarkCheck") == "true") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
element.contentDocument.documentElement.childNodes[1].style.color =
|
element.contentDocument.documentElement.childNodes[1].style.color =
|
||||||
"white";
|
"white";
|
||||||
element.contentDocument.documentElement.firstChild.appendChild(
|
element.contentDocument.documentElement.firstChild.appendChild(
|
||||||
@@ -2145,6 +2175,11 @@ function AddBetterSEQTAElements(toggle) {
|
|||||||
|
|
||||||
for (let i = 0; i < alliframes.length; i++) {
|
for (let i = 0; i < alliframes.length; i++) {
|
||||||
const element = alliframes[i];
|
const element = alliframes[i];
|
||||||
|
|
||||||
|
if (element.getAttribute("excludeDarkCheck") == "true") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
element.contentDocument.documentElement.childNodes[1].style.color =
|
element.contentDocument.documentElement.childNodes[1].style.color =
|
||||||
"black";
|
"black";
|
||||||
element.contentDocument.documentElement.firstChild.lastChild.remove();
|
element.contentDocument.documentElement.firstChild.lastChild.remove();
|
||||||
@@ -2168,7 +2203,7 @@ function AddBetterSEQTAElements(toggle) {
|
|||||||
var AddedSettings = document.getElementById("AddedSettings");
|
var AddedSettings = document.getElementById("AddedSettings");
|
||||||
var extensionsettings = document.getElementById("ExtensionPopup");
|
var extensionsettings = document.getElementById("ExtensionPopup");
|
||||||
AddedSettings.addEventListener("click", function () {
|
AddedSettings.addEventListener("click", function () {
|
||||||
extensionsettings.classList.toggle("hidden");
|
extensionsettings.classList.toggle("hide");
|
||||||
SettingsClicked = true;
|
SettingsClicked = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2275,23 +2310,38 @@ function CheckCurrentLesson(lesson, num) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hexToRGB(hex) {
|
function GetThresholdofHex(color) {
|
||||||
try {
|
// Regular expression for matching RGBA colors
|
||||||
var r = parseInt(hex.slice(1, 3), 16),
|
const rgbaRegex = /rgba?\(([^)]+)\)/g;
|
||||||
g = parseInt(hex.slice(3, 5), 16),
|
|
||||||
b = parseInt(hex.slice(5, 7), 16);
|
|
||||||
|
|
||||||
return { r: r, g: g, b: b };
|
// Check if the color string is a gradient (linear or radial)
|
||||||
} catch {
|
if (color.includes("gradient")) {
|
||||||
// do nothing becuase this functoin is a bit broken right now (feel free to fix it!)
|
let gradient = color;
|
||||||
|
|
||||||
|
// Find and replace all instances of RGBA in the gradient
|
||||||
|
let match;
|
||||||
|
while ((match = rgbaRegex.exec(color)) !== null) {
|
||||||
|
// Extract the individual components (r, g, b, a)
|
||||||
|
const rgbaString = match[1];
|
||||||
|
const [r, g, b, a] = rgbaString.split(",").map(str => str.trim());
|
||||||
|
|
||||||
|
// Compute the threshold using your existing algorithm
|
||||||
|
const threshold = Math.sqrt(r ** 2 + g ** 2 + b ** 2);
|
||||||
|
|
||||||
|
// Replace the original RGBA string with the computed threshold
|
||||||
|
// Note: You can modify this part based on what you actually want to do with the threshold
|
||||||
|
gradient = gradient.replace(`rgba(${rgbaString})`, `rgba(${threshold}, ${threshold}, ${threshold}, ${a})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gradient;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Handle the color as a simple RGBA (or hex, or whatever the Color library supports)
|
||||||
|
const rgb = Color.rgb(color).string();
|
||||||
|
return Math.sqrt(rgb.r ** 2 + rgb.g ** 2 + rgb.b ** 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetThresholdofHex(hex) {
|
|
||||||
var rgb = hexToRGB(hex);
|
|
||||||
return Math.sqrt(rgb.r ** 2 + rgb.g ** 2 + rgb.b ** 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CheckCurrentLessonAll(lessons) {
|
function CheckCurrentLessonAll(lessons) {
|
||||||
// Checks each lesson and sets an interval to run every 60 seconds to continue updating
|
// Checks each lesson and sets an interval to run every 60 seconds to continue updating
|
||||||
LessonInterval = setInterval(
|
LessonInterval = setInterval(
|
||||||
|
|||||||
+38
-7
@@ -15,7 +15,7 @@ chrome.runtime.onMessage.addListener(function (request) {
|
|||||||
ReloadSEQTAPages();
|
ReloadSEQTAPages();
|
||||||
} else if (request.type == "githubTab") {
|
} else if (request.type == "githubTab") {
|
||||||
chrome.tabs.create({
|
chrome.tabs.create({
|
||||||
url: "github.com/SethBurkart123/BetterThanBetterSeqta",
|
url: "github.com/SethBurkart123/EvenBetterSEQTA",
|
||||||
});
|
});
|
||||||
} else if (request.type == "setDefaultStorage") {
|
} else if (request.type == "setDefaultStorage") {
|
||||||
console.log("setting default values");
|
console.log("setting default values");
|
||||||
@@ -102,7 +102,6 @@ chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
|
|||||||
console.log(TodayFormatted);
|
console.log(TodayFormatted);
|
||||||
console.log(from);
|
console.log(from);
|
||||||
|
|
||||||
// var url = `https://newsapi.org/v2/everything?sources=abc-news&from=${TodayFormatted}&sortBy=popularity&apiKey=17c0da766ba347c89d094449504e3080`;
|
|
||||||
var url = `https://newsapi.org/v2/everything?domains=abc.net.au&from=${from}&apiKey=17c0da766ba347c89d094449504e3080`;
|
var url = `https://newsapi.org/v2/everything?domains=abc.net.au&from=${from}&apiKey=17c0da766ba347c89d094449504e3080`;
|
||||||
|
|
||||||
GetNews(url, sendResponse);
|
GetNews(url, sendResponse);
|
||||||
@@ -168,7 +167,7 @@ const DefaultValues = {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "educationperfect",
|
name: "educationperfect",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -217,13 +216,45 @@ function UpdateCurrentValues(details) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function migrateOldStorage() {
|
||||||
|
chrome.storage.local.get(null, function (items) {
|
||||||
|
let shouldUpdate = false; // Flag to check if there is anything to update
|
||||||
|
|
||||||
|
// Check for the old "Name" field and convert it to "name"
|
||||||
|
if (items.shortcuts && items.shortcuts.length > 0 && "Name" in items.shortcuts[0]) {
|
||||||
|
shouldUpdate = true;
|
||||||
|
items.shortcuts = items.shortcuts.map((shortcut) => {
|
||||||
|
return {
|
||||||
|
name: shortcut.Name, // Convert "Name" to "name"
|
||||||
|
enabled: shortcut.enabled // Keep the "enabled" field as is
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for "educationperfect" and convert it to "Education Perfect"
|
||||||
|
if (items.shortcuts && items.shortcuts.length > 0) {
|
||||||
|
for (let shortcut of items.shortcuts) {
|
||||||
|
if (shortcut.name === "educationperfect") {
|
||||||
|
shouldUpdate = true;
|
||||||
|
shortcut.name = "Education Perfect"; // Convert to "Education Perfect"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's something to update, set the new values in storage
|
||||||
|
if (shouldUpdate) {
|
||||||
|
chrome.storage.local.set({ shortcuts: items.shortcuts }, function() {
|
||||||
|
console.log("Migration completed.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
chrome.runtime.onInstalled.addListener(function (event) {
|
chrome.runtime.onInstalled.addListener(function (event) {
|
||||||
chrome.storage.local.remove(["justupdated"]);
|
chrome.storage.local.remove(["justupdated"]);
|
||||||
UpdateCurrentValues();
|
UpdateCurrentValues();
|
||||||
if (
|
if ( event.reason == "install" ) {
|
||||||
/*chrome.runtime.getManifest().version > event.previousVersion || */ event.reason ==
|
|
||||||
"install"
|
|
||||||
) {
|
|
||||||
chrome.storage.local.set({ justupdated: true });
|
chrome.storage.local.set({ justupdated: true });
|
||||||
|
migrateOldStorage();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+47
-54
@@ -2,7 +2,6 @@
|
|||||||
@import "./injected/popup.css";
|
@import "./injected/popup.css";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
background-color: var(--better-main) !important;
|
|
||||||
background: var(--better-main) !important;
|
background: var(--better-main) !important;
|
||||||
--navy: #1a1a1a !important;
|
--navy: #1a1a1a !important;
|
||||||
--auto-background: var(--better-pale, var(--background-secondary)) !important;
|
--auto-background: var(--better-pale, var(--background-secondary)) !important;
|
||||||
@@ -15,7 +14,7 @@ html {
|
|||||||
|
|
||||||
#container {
|
#container {
|
||||||
transition: 200ms;
|
transition: 200ms;
|
||||||
background-color: var(--auto-background) !important;
|
background: var(--auto-background) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -23,11 +22,6 @@ html {
|
|||||||
--theme-fg-parts: white;
|
--theme-fg-parts: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
#title {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 500 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 900px) {
|
@media (min-width: 900px) {
|
||||||
#title > span {
|
#title > span {
|
||||||
transform: translateY(2px);
|
transform: translateY(2px);
|
||||||
@@ -220,7 +214,7 @@ li.item.draggable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background-color: var(--better-main) !important;
|
background: var(--better-main) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Messages */
|
/* Messages */
|
||||||
@@ -270,29 +264,29 @@ ol:has(.MessageList__avatar___2wxyb svg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content [autocomplete="off"] {
|
.content [autocomplete="off"] {
|
||||||
background-color: var(--background-primary) !important;
|
background: var(--background-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MessageList__MessageList___3DxoC .footer {
|
.MessageList__MessageList___3DxoC .footer {
|
||||||
background-color: var(--background-secondary) !important;
|
background: var(--background-secondary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content [placeholder="Subject…"] {
|
.content [placeholder="Subject…"] {
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding-left: 12px !important;
|
padding-left: 12px !important;
|
||||||
background-color: var(--background-primary) !important;
|
background: var(--background-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listWrapper {
|
.listWrapper {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-top-left-radius: 16px;
|
border-top-left-radius: 16px;
|
||||||
border-top-right-radius: 16px;
|
border-top-right-radius: 16px;
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
margin-top: 26%;
|
margin-top: 26%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.functions {
|
.functions {
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
border-bottom-left-radius: 16px;
|
border-bottom-left-radius: 16px;
|
||||||
border-bottom-right-radius: 16px;
|
border-bottom-right-radius: 16px;
|
||||||
@@ -468,7 +462,7 @@ ol > [data-label] {
|
|||||||
.Viewer__newMessage___3ToUb {
|
.Viewer__newMessage___3ToUb {
|
||||||
border-radius: 0.5rem !important;
|
border-radius: 0.5rem !important;
|
||||||
font-size: 0.8rem !important;
|
font-size: 0.8rem !important;
|
||||||
background-color: var(--background-primary) !important;
|
background: var(--background-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MessageList__sender___32riy :last-child {
|
.MessageList__sender___32riy :last-child {
|
||||||
@@ -509,7 +503,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#main > .timetablepage > .container {
|
#main > .timetablepage > .container {
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
@@ -557,20 +551,19 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
background-position: 10% 10%;
|
|
||||||
color: var(--text-primary) !important;
|
color: var(--text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Module__wrapper___2sbOo {
|
.Module__wrapper___2sbOo {
|
||||||
|
overflow: clip;
|
||||||
background: var(--background-primary) !important;
|
background: var(--background-primary) !important;
|
||||||
|
border-radius: 1rem !important;
|
||||||
color: var(--text-primary) !important;
|
color: var(--text-primary) !important;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.course .composer {
|
.course .composer {
|
||||||
background: var(--background-primary) !important;
|
background: transparent !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,7 +572,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
> .Container__container___33GlY
|
> .Container__container___33GlY
|
||||||
> .Document__document___1KJCG
|
> .Document__document___1KJCG
|
||||||
> .Canvas__canvas___OBdCZ {
|
> .Canvas__canvas___OBdCZ {
|
||||||
background-color: unset !important;
|
background-color: transparent !important;
|
||||||
background-image: unset !important;
|
background-image: unset !important;
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
@@ -607,20 +600,18 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
|
|
||||||
#title {
|
#title {
|
||||||
background: var(--background-primary);
|
background: var(--background-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
box-shadow: rgb(0 0 0 / 35%) 0px 0px 10px;
|
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
|
box-shadow: rgb(0 0 0 / 35%) 0px 0px 10px;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
font-weight: 500 !important;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg {
|
.bg {
|
||||||
animation: slide 3s ease-in-out infinite alternate;
|
animation: slide 3s ease-in-out infinite alternate;
|
||||||
background-image: linear-gradient(
|
background: var(--better-main);
|
||||||
-60deg,
|
|
||||||
var(--better-main) 50%,
|
|
||||||
var(--auto-background) 50%
|
|
||||||
);
|
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: -50%;
|
left: -50%;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
@@ -629,6 +620,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
top: 0;
|
top: 0;
|
||||||
z-index: 0 !important;
|
z-index: 0 !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
scale: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg2 {
|
.bg2 {
|
||||||
@@ -642,11 +634,11 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
|
|
||||||
@keyframes slide {
|
@keyframes slide {
|
||||||
0% {
|
0% {
|
||||||
transform: translateX(-25%);
|
transform: translate(50%) rotate(-60deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: translateX(25%);
|
transform: translateX(5%) rotate(-60deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,7 +686,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
font-size: 3em !important;
|
font-size: 3em !important;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
margin: 30px auto 60px;
|
margin: 30px auto 60px;
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
height: 3em;
|
height: 3em;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -708,7 +700,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
width: 94%;
|
width: 94%;
|
||||||
margin: 50px auto;
|
margin: 50px auto;
|
||||||
height: 19em;
|
height: 19em;
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
-webkit-box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
|
-webkit-box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
|
||||||
@@ -716,7 +708,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.day-container {
|
.day-container {
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
transition: 200ms;
|
transition: 200ms;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 15em;
|
height: 15em;
|
||||||
@@ -729,7 +721,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
width: 94%;
|
width: 94%;
|
||||||
margin: 50px auto;
|
margin: 50px auto;
|
||||||
max-height: 60em;
|
max-height: 60em;
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
-webkit-box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
|
-webkit-box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
|
||||||
@@ -737,7 +729,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notice-container {
|
.notice-container {
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 55em;
|
max-height: 55em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -789,7 +781,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
border: 2px solid var(--better-main);
|
border: 2px solid var(--better-main);
|
||||||
width: 94%;
|
width: 94%;
|
||||||
margin: 10px auto 50px;
|
margin: 10px auto 50px;
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
-webkit-box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3);
|
-webkit-box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3);
|
||||||
@@ -807,7 +799,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.shortcuts {
|
.shortcuts {
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
@@ -991,13 +983,13 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modaliser {
|
.modaliser {
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-container {
|
.alert-container {
|
||||||
height: 35em;
|
height: 35em;
|
||||||
width: 22em;
|
width: 22em;
|
||||||
background-color: var(--better-sub);
|
background: var(--better-sub);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -1081,7 +1073,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
|
|
||||||
#ExtensionPopup {
|
#ExtensionPopup {
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
box-shadow: 0px 10px 15px -3px rgba(0, 0, 0, 0.4);
|
box-shadow: 0px 0px 20px -2px rgba(0,0,0,0.6)
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu li.active {
|
#menu li.active {
|
||||||
@@ -1171,7 +1163,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
|
|
||||||
.Input__Input___3RSTI {
|
.Input__Input___3RSTI {
|
||||||
transition: background-color 0.5s,border-color 0.5s;
|
transition: background-color 0.5s,border-color 0.5s;
|
||||||
background-color: var(--auto-background);
|
background: var(--auto-background);
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
@@ -1245,7 +1237,7 @@ iframe.userHTML {
|
|||||||
|
|
||||||
.Thermoscore__Thermoscore___2tWMi {
|
.Thermoscore__Thermoscore___2tWMi {
|
||||||
background-image: unset;
|
background-image: unset;
|
||||||
background-color: var(--auto-background);
|
background: var(--auto-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
#toolbar {
|
#toolbar {
|
||||||
@@ -1275,6 +1267,7 @@ iframe.userHTML {
|
|||||||
|
|
||||||
#main > .course > .content > h1 {
|
#main > .course > .content > h1 {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main > .course > .content > .outline > h2,
|
#main > .course > .content > .outline > h2,
|
||||||
@@ -1291,7 +1284,7 @@ iframe.userHTML {
|
|||||||
|
|
||||||
::-webkit-scrollbar-thumb:vertical:hover,
|
::-webkit-scrollbar-thumb:vertical:hover,
|
||||||
::-webkit-scrollbar-thumb:horizontal:hover {
|
::-webkit-scrollbar-thumb:horizontal:hover {
|
||||||
background-color: var(--better-light);
|
background: var(--better-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
@@ -1381,16 +1374,16 @@ ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.legacy-root .uiFileHandler {
|
.legacy-root .uiFileHandler {
|
||||||
background-color: var(--auto-background);
|
background: var(--auto-background);
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ResourceList__ResourceList___2z-c1 .legacy-root .uiFileHandler {
|
.ResourceList__ResourceList___2z-c1 .legacy-root .uiFileHandler {
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.legacy-root .uiFileHandler.dragTarget {
|
.legacy-root .uiFileHandler.dragTarget {
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.MenuButton__MenuPanel___2q42B {
|
.MenuButton__MenuPanel___2q42B {
|
||||||
@@ -1497,13 +1490,13 @@ blurred {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.uiSlidePane > .pane > .header {
|
.uiSlidePane > .pane > .header {
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content [placeholder="Subject…"] {
|
.content [placeholder="Subject…"] {
|
||||||
padding-left: 12px !important;
|
padding-left: 12px !important;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
background-color: var(--background-primary) !important;
|
background: var(--background-primary) !important;
|
||||||
color: var(--text-primary) !important;
|
color: var(--text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1523,7 +1516,7 @@ blurred {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.formattedText > .footer {
|
.formattedText > .footer {
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -1544,7 +1537,7 @@ blurred {
|
|||||||
|
|
||||||
.uiSlidePane > .pane {
|
.uiSlidePane > .pane {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background-color: var(--auto-background);
|
background: var(--auto-background);
|
||||||
transform: translateY(100%);
|
transform: translateY(100%);
|
||||||
transition:
|
transition:
|
||||||
transform 0.5s ease-in-out,
|
transform 0.5s ease-in-out,
|
||||||
@@ -1672,7 +1665,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.MessageList__MessageList___3DxoC > ol > li.MessageList__unread___3imtO {
|
.MessageList__MessageList___3DxoC > ol > li.MessageList__unread___3imtO {
|
||||||
box-shadow: inset 3px 0 rgb(255, 255, 255);
|
box-shadow: inset 3px 0 var(--better-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.connectedNotificationsWrapper > div > button {
|
.connectedNotificationsWrapper > div > button {
|
||||||
@@ -2054,7 +2047,7 @@ body {
|
|||||||
width: 94%;
|
width: 94%;
|
||||||
margin: 50px auto;
|
margin: 50px auto;
|
||||||
max-height: 60em;
|
max-height: 60em;
|
||||||
background-color: var(--better-main);
|
background: var(--better-main);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
-webkit-box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3);
|
-webkit-box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3);
|
||||||
@@ -2226,7 +2219,7 @@ body {
|
|||||||
|
|
||||||
/* When the checkbox is checked, add a blue background */
|
/* When the checkbox is checked, add a blue background */
|
||||||
.upcoming-checkbox-container input:checked ~ .upcoming-checkmark {
|
.upcoming-checkbox-container input:checked ~ .upcoming-checkmark {
|
||||||
background-color: var(--item-colour);
|
background: var(--item-colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the checkmark/indicator (hidden when not checked) */
|
/* Create the checkmark/indicator (hidden when not checked) */
|
||||||
@@ -2378,7 +2371,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.upcoming-items {
|
.upcoming-items {
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
transition: 200ms;
|
transition: 200ms;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 55em;
|
max-height: 55em;
|
||||||
@@ -2532,7 +2525,7 @@ body {
|
|||||||
|
|
||||||
/* When the checkbox is checked, add a blue background */
|
/* When the checkbox is checked, add a blue background */
|
||||||
.upcoming-checkbox-container input:checked ~ .upcoming-checkmark {
|
.upcoming-checkbox-container input:checked ~ .upcoming-checkmark {
|
||||||
background-color: var(--item-colour);
|
background: var(--item-colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the checkmark/indicator (hidden when not checked) */
|
/* Create the checkmark/indicator (hidden when not checked) */
|
||||||
@@ -2668,7 +2661,7 @@ body {
|
|||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s;
|
||||||
transform-origin: top;
|
transform-origin: top;
|
||||||
background-color: var(--background-primary);
|
background: var(--background-primary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|||||||
@@ -5,3 +5,8 @@
|
|||||||
.topmenu {
|
.topmenu {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export function StorageListner() {
|
||||||
|
chrome.storage.onChanged.addListener(function (changes) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
important: true,
|
||||||
|
darkMode: "class",
|
||||||
|
theme: {
|
||||||
|
fontSize: {
|
||||||
|
"xs": ".65rem",
|
||||||
|
"sm": ".775rem",
|
||||||
|
"base": "0.65rem",
|
||||||
|
"md": "0.65rem",
|
||||||
|
"lg": "1rem",
|
||||||
|
"xl": "1.25rem",
|
||||||
|
"2xl": "1.5rem",
|
||||||
|
"3xl": "1.875rem",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2016",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2016", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ES2015",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": false,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -64,6 +64,7 @@ export default {
|
|||||||
{ from: "public", to: "." },
|
{ from: "public", to: "." },
|
||||||
{ from: "src/inject/preview", to: "inject/preview" },
|
{ from: "src/inject/preview", to: "inject/preview" },
|
||||||
{ from: "node_modules/webextension-polyfill/dist/browser-polyfill.js", to: "."},
|
{ from: "node_modules/webextension-polyfill/dist/browser-polyfill.js", to: "."},
|
||||||
|
{ from: "interface/dist/client", to: "client" }
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user