Merge pull request #6 from SethBurkart123/dev

Merge DEV (new popup) into main
This commit is contained in:
Seth Burkart
2023-09-25 11:44:39 +10:00
committed by GitHub
45 changed files with 1061 additions and 400 deletions
+1
View File
@@ -1,6 +1,7 @@
# npm
node_modules/
package-lock.json
bun.lockb
# Build
package.zip
+3
View File
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
BIN
View File
Binary file not shown.
+26
View File
@@ -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',
},
},
],
}
File diff suppressed because one or more lines are too long
+1
View File
@@ -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

+15
View File
@@ -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>
+13
View File
@@ -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>
+34
View File
@@ -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"
}
}
+7 -38
View File
@@ -1,51 +1,18 @@
// App.tsx
import { useState } from 'react';
import React from 'react';
import TabbedContainer from './components/TabbedContainer';
import Settings from './pages/Settings';
import logo from './assets/betterseqta-dark-full.png';
import logoDark from './assets/betterseqta-light-full.png';
import Shortcuts from './pages/Shortcuts';
import About from './pages/About';
export interface SettingsState {
notificationCollector: boolean;
lessonAlerts: boolean;
animatedBackground: boolean;
animatedBackgroundSpeed: boolean;
customThemeColor: string;
betterSEQTAPlus: boolean;
}
import { SettingsContextProvider } from './SettingsContext';
const App: React.FC = () => {
const [settingsState, setSettingsState] = useState<SettingsState>({
notificationCollector: false,
lessonAlerts: false,
animatedBackground: false,
animatedBackgroundSpeed: false,
customThemeColor: "#db6969",
betterSEQTAPlus: true
});
// Handler for Switches
const switchChange = (key: string, isOn: boolean) => {
setSettingsState({
...settingsState,
[key]: isOn,
});
};
// Handler for ColorPicker
const colorChange = (color: string) => {
setSettingsState({
...settingsState,
customThemeColor: color,
});
};
const tabs = [
{
title: 'Settings',
content: <Settings settingsState={settingsState} switchChange={switchChange} colorChange={colorChange} />
content: <Settings />
},
{
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 }}> */}
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="flex flex-col w-[384px] shadow-2xl gap-2 bg-white rounded-xl h-[590px] dark:bg-zinc-800 dark:text-white">
<div className="grid border-b border-b-zinc-200/40 place-items-center">
<img src={logo} className="w-4/5 dark:hidden" />
<img src={logoDark} className="hidden w-4/5 dark:block" />
</div>
<TabbedContainer themeColor={settingsState.customThemeColor} tabs={tabs} />
<TabbedContainer tabs={tabs} />
</div>
</SettingsContextProvider>
);
};
+39
View File
@@ -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;
};
+6 -9
View File
@@ -1,12 +1,7 @@
// TODO: Create types for ColorPicker
// @ts-expect-error No typescript declarations available
// @ts-expect-error There aren't any types for the below library
import ColorPicker from 'react-best-gradient-color-picker';
import { useState, useRef, useEffect } from 'react';
interface ColorPickerProps {
color: string;
onChange: (color: string) => void;
}
import type { ColorPickerProps } from '../types/ColorPickerProps';
const Picker = ({ color, onChange }: ColorPickerProps) => {
const [showPicker, setShowPicker] = useState<boolean>(false);
@@ -27,18 +22,20 @@ const Picker = ({ color, onChange }: ColorPickerProps) => {
}, [showPicker]);
return (
<div className="">
<>
<button
onClick={() => setShowPicker(!showPicker)}
style={{ background: color }}
className="w-16 h-8 rounded-md"
></button>
{showPicker && (
<div className="absolute top-0 left-0 w-full h-full bg-black/20">
<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>
)}
</>
);
};
+1 -4
View File
@@ -1,9 +1,6 @@
import React, { useState } from 'react';
import "./Slider.css";
interface Slider {
onValueChange: (value: number) => void;
}
import type { Slider } from '../types/SliderProps';
const Slider: React.FC<Slider> = ({ onValueChange }) => {
const [sliderValue, setSliderValue] = useState(0);
+1
View File
@@ -1,3 +1,4 @@
.dark .switch[data-ison="true"],
.switch[data-ison="true"] {
background-color: #30D259;
}
+1 -5
View File
@@ -1,10 +1,6 @@
import { motion } from "framer-motion";
import "./Switch.css";
interface SwitchProps {
onChange: (isOn: boolean) => void;
state: boolean;
}
import type { SwitchProps } from "../types/SwitchProps";
export default function Switch(props: SwitchProps) {
const toggleSwitch = () => {
+28 -19
View File
@@ -1,22 +1,15 @@
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 {
title: string;
content: JSX.Element;
}
interface TabbedContainerProps {
tabs: Tab[];
themeColor: string;
}
const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) => {
const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs }) => {
const [activeTab, setActiveTab] = useState(0);
const [hoveredTab, setHoveredTab] = useState<number | null>(null);
const [tabWidth, setTabWidth] = useState(0);
const [position, setPosition] = useState(0);
const positionRef = useRef(position);
const themeColor = useSettingsContext().settingsState.customThemeColor;
useEffect(() => {
const newPosition = -activeTab * 100;
@@ -28,6 +21,13 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) =
const springTransition = { type: 'spring', stiffness: 250, damping: 25 };
const contentVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
const fastOpacityTransition = { duration: 0.2 };
useEffect(() => {
if (containerRef.current) {
// @ts-expect-error for some reason its giving an error in TS but it works...
@@ -44,8 +44,8 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) =
};
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">
<motion.div
className="absolute top-0 left-0 z-0 h-full rounded-full opacity-40"
@@ -67,25 +67,34 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs, themeColor }) =
))}
</div>
</div>
<div className="relative">
<div className="h-full px-4 overflow-y-scroll overflow-x-clip">
<motion.div
initial={false}
animate={{ x: `${position}%` }}
transition={springTransition}
>
<div className="absolute flex w-full" style={{ left: `${-position}%` }}>
<AnimatePresence>
{tabs.map((tab, index) => (
<div
activeTab === index && (
<motion.div
key={index}
className={`w-full ${activeTab === index ? '' : 'hidden'}`}
className="absolute w-full"
initial="hidden"
animate="visible"
exit="hidden"
transition={fastOpacityTransition}
variants={contentVariants}
>
{tab.content}
</div>
</motion.div>
)
))}
</AnimatePresence>
</div>
</motion.div>
</div>
</div>
</>
);
};
+94
View File
@@ -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;
+6
View File
@@ -0,0 +1,6 @@
import './index.css';
declare module "*.png";
declare module "*.svg";
declare module "*.jpeg";
declare module "*.jpg";
+2 -2
View File
@@ -1,6 +1,6 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import App from './App.js'
import './index.css'
const root = ReactDOM.createRoot(document.getElementById('ExtensionPopup')!);
@@ -9,4 +9,4 @@ root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
);
+21 -16
View File
@@ -1,21 +1,26 @@
import Switch from '../components/Switch';
import ColorPicker from '../components/ColorPicker';
import { SettingsState } from '../App';
import { SettingsList } from '../types/SettingsProps';
import { useSettingsContext } from '../SettingsContext';
interface ISetting {
title: string;
description: string;
modifyElement: JSX.Element;
}
const Settings: React.FC = () => {
const { settingsState, setSettingsState } = useSettingsContext();
interface SettingsProps {
settingsState: SettingsState;
switchChange: (key: string, isOn: boolean) => void;
colorChange: (color: string) => void;
}
const switchChange = (key: string, isOn: boolean) => {
setSettingsState({
...settingsState,
[key]: isOn,
});
};
const Settings: React.FC<SettingsProps> = ({ settingsState, switchChange, colorChange }) => {
const settings: ISetting[] = [
const colorChange = (color: string) => {
setSettingsState({
...settingsState,
customThemeColor: color,
});
};
const settings: SettingsList[] = [
{
title: "Notification Collector",
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",
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",
@@ -43,13 +48,13 @@ const Settings: React.FC<SettingsProps> = ({ settingsState, switchChange, colorC
},
{
title: "BetterSEQTA+",
description: "Unlocks premium features.",
description: "Enables BetterSEQTA+ features",
modifyElement: <Switch state={settingsState.betterSEQTAPlus} onChange={(isOn: boolean) => switchChange('betterSEQTAPlus', isOn)} />
}
];
return (
<div className="flex flex-col overflow-y-scroll divide-y divide-zinc-100">
<div className="flex flex-col -mt-4 overflow-y-scroll divide-y divide-zinc-100">
{settings.map((setting, index) => (
<div className="flex items-center justify-between px-4 py-3" key={index}>
<div className="pr-4">
+142 -79
View File
@@ -1,95 +1,158 @@
import { useState } from "react";
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() {
const [shortcutState, setShortcutState] = useState({
youtube: false,
outlook: false,
office: false,
spotify: false,
google: false,
duckduckgo: false,
coolmathgames: false,
sace: false,
googlescholar: false,
gmail: false,
netflix: false
const { settingsState, setSettingsState } = useSettingsContext();
const switchChange = (shortcutName: string, isOn: boolean): void => {
const updatedShortcuts = settingsState.shortcuts.map((shortcut) => {
if (shortcut.name === shortcutName) {
return { ...shortcut, enabled: isOn };
}
return shortcut;
});
// Handler for Switches
const switchChange = (key: string, isOn: boolean) => {
setShortcutState({
...shortcutState,
[key]: isOn,
});
setSettingsState({ ...settingsState, shortcuts: updatedShortcuts });
};
const DefaultShortcuts = [
{
title: "YouTube",
link: "https://youtube.com",
modifyElement: <Switch state={shortcutState.youtube} onChange={(isOn: boolean) => switchChange('youtube', isOn)} />
},
{
title: "Outlook",
link: "https://outlook.office.com/mail/inbox",
modifyElement: <Switch state={shortcutState.outlook} onChange={(isOn: boolean) => switchChange('outlook', isOn)} />
},
{
title: "Office",
link: "https://www.office.com/",
modifyElement: <Switch state={shortcutState.office} onChange={(isOn: boolean) => switchChange('office', isOn)} />
},
{
title: "Spotify",
link: "https://www.spotify.com/",
modifyElement: <Switch state={shortcutState.spotify} onChange={(isOn: boolean) => switchChange('spotify', isOn)} />
},
{
title: "Google",
link: "https://www.google.com/",
modifyElement: <Switch state={shortcutState.google} onChange={(isOn: boolean) => switchChange('google', isOn)} />
},
{
title: "DuckDuckGo",
link: "https://duckduckgo.com/",
modifyElement: <Switch state={shortcutState.duckduckgo} onChange={(isOn: boolean) => switchChange('duckduckgo', isOn)} />
},
{
title: "Cool Math Games",
link: "https://www.coolmathgames.com/",
modifyElement: <Switch state={shortcutState.coolmathgames} onChange={(isOn: boolean) => switchChange('coolmathgames', isOn)} />
},
{
title: "SACE",
link: "https://www.sace.sa.edu.au/",
modifyElement: <Switch state={shortcutState.sace} onChange={(isOn: boolean) => switchChange('sace', isOn)} />
},
{
title: "Google Scholar",
link: "https://scholar.google.com/",
modifyElement: <Switch state={shortcutState.googlescholar} onChange={(isOn: boolean) => switchChange('googlescholar', isOn)} />
},
{
title: "Gmail",
link: "https://mail.google.com/",
modifyElement: <Switch state={shortcutState.gmail} onChange={(isOn: boolean) => switchChange('gmail', isOn)} />
},
{
title: "Netflix",
link: "https://www.netflix.com/",
modifyElement: <Switch state={shortcutState.netflix} onChange={(isOn: boolean) => switchChange('netflix', isOn)} />
const [newTitle, setNewTitle] = useState<string>("");
const [newURL, setNewURL] = useState<string>("");
const isValidTitle = (title: string): boolean => title.trim() !== "";
const isValidURL = (url: string): boolean => {
const pattern = new RegExp("^(https?:\\/\\/)?[\\w.-]+[\\w.-]+$", "i");
return pattern.test(url);
};
const addNewCustomShortcut = (): void => {
if (isValidTitle(newTitle) && isValidURL(newURL)) {
const newShortcut: Shortcut = { name: newTitle.trim(), url: newURL.trim() };
const updatedCustomShortcuts = [...settingsState.customshortcuts, newShortcut];
setSettingsState({ ...settingsState, customshortcuts: updatedCustomShortcuts });
setNewTitle("");
setNewURL("");
setFormVisible(false);
} else {
// Replace with a more user-friendly way to display errors
console.error("Please enter a valid title and URL.");
}
];
};
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 (
<div className="flex flex-col divide-y divide-zinc-100">
{DefaultShortcuts.map((shortcut, index) => (
<div className="flex items-center justify-between px-4 py-3" key={index}>
{shortcut.title}
{shortcut.modifyElement}
<AnimatePresence>
{isFormVisible ? (
<motion.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>
);
}
+57
View File
@@ -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>;
}
+4
View File
@@ -0,0 +1,4 @@
export interface ColorPickerProps {
color: string;
onChange: (color: string) => void;
}
+11
View File
@@ -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>>;
}
+7
View File
@@ -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;
+6
View File
@@ -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;
+24
View File
@@ -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"]
}
+2
View File
@@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfig;
export default _default;
+17
View File
@@ -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'
}
}
}
})
+12
View File
@@ -27,5 +27,17 @@
"webextension-polyfill": "^0.10.0",
"webpack": "^5.88.2",
"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"
}
}
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
plugins: [
require("tailwindcss"),
require("autoprefixer"),
],
};
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
plugins: [
require("tailwindcss"),
require("autoprefixer"),
],
};
+16
View File
@@ -50,6 +50,22 @@
{
"resources": ["popup/*"],
"matches": ["*://*/*"]
},
{
"resources": ["client.js"],
"matches": ["*://*/*"]
},
{
"resources": ["index.css"],
"matches": ["*://*/*"]
},
{
"resources": ["interface/*"],
"matches": ["*://*/*"]
},
{
"resources": ["client/*"],
"matches": ["*://*/*"]
}
]
}
+1 -2
View File
@@ -14,13 +14,12 @@
@import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,600");
.outside-container {
width: 350px;
margin: 0;
background-color: #131313;
overflow: hidden;
position: absolute;
right: 10px;
top: 80px;
height: 590px;
z-index: 20;
}
+155 -105
View File
@@ -1,4 +1,6 @@
/*global chrome*/
import Color from "color";
import ShortcutLinks from "./seqta/content/links.json";
import MenuitemSVGKey from "./seqta/content/MenuItemSVGKey.json";
import stringToHTML from "./seqta/utils/stringToHTML.js";
@@ -13,7 +15,6 @@ let SettingsClicked = false;
let MenuOptionsOpen = false;
let UserInitalCode = "";
let currentSelectedDate = new Date();
let WhatsNewOpen = false;
let LessonInterval;
let DarkMode;
@@ -38,7 +39,9 @@ function bkValues (item) {
const bg = document.getElementsByClassName("bg");
const bg2 = document.getElementsByClassName("bg2");
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 maxDuration = 10; // maximum duration in seconds
@@ -172,12 +175,12 @@ function OpenWhatsNewPopup() {
var bkelement = document.getElementById("whatsnewbk");
bkelement.addEventListener("click", function () {
DeleteWhatsNew();
WhatsNewOpen = false;
//WhatsNewOpen = false;
});
var closeelement = document.getElementById("whatsnewclosebutton");
closeelement.addEventListener("click", function () {
DeleteWhatsNew();
WhatsNewOpen = false;
//WhatsNewOpen = false;
});
}
@@ -193,7 +196,7 @@ async function finishLoad() {
chrome.storage.local.get(["justupdated"], function (result) {
if (result.justupdated) {
WhatsNewOpen = true;
//WhatsNewOpen = true;
OpenWhatsNewPopup();
}
});
@@ -233,6 +236,9 @@ function RemoveBackground() {
var bk = document.getElementsByClassName("bg");
var bk2 = document.getElementsByClassName("bg2");
var bk3 = document.getElementsByClassName("bg3");
if (bk.length == 0 || bk2.length == 0 || bk3.length == 0) return;
bk[0].remove();
bk2[0].remove();
bk3[0].remove();
@@ -621,116 +627,116 @@ function AppendElementsToDisabledPage() {
document.head.append(settingsStyle);
}
function lightenAndPaleColor(
hexColor,
lightenFactor = 0.75,
paleFactor = 0.55,
) {
// Convert a RGB value to HSL
function rgbToHsl(r, g, b) {
(r /= 255), (g /= 255), (b /= 255);
let max = Math.max(r, g, b),
min = Math.min(r, g, b);
let h,
s,
l = (max + min) / 2;
function lightenAndPaleColor(inputColor, lightenFactor = 0.75, paleFactor = 0.55) {
// Step 1: Convert RGBA to separate R, G and B values
const [r, g, b] = inputColor.match(/\d+/g).map(Number);
// Step 2: Convert RGB to HSL
let r1 = r / 255, g1 = g / 255, b1 = b / 255;
const max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
let d = max - min;
const 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;
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;
}
h /= 6;
}
return [h, s, l];
}
// Step 3: Adjust saturation and lightness
s -= s * paleFactor;
l += (1 - l) * lightenFactor;
// Convert an HSL value to RGB
function hslToRgb(h, s, l) {
function hue2rgb(p, q, t) {
// Step 4: Convert HSL back to RGB
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;
}
};
let r, g, b;
let r2, g2, b2;
if (s === 0) {
r = g = b = l;
r2 = g2 = b2 = 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);
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r2 = hue2rgb(p, q, h + 1/3);
g2 = hue2rgb(p, q, h);
b2 = hue2rgb(p, q, h - 1/3);
}
return [r * 255, g * 255, b * 255];
// Step 5: Format Output
const result = `rgb(${Math.round(r2 * 255)}, ${Math.round(g2 * 255)}, ${Math.round(b2 * 255)})`;
return `${result}`;
}
// 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);
function ColorLuminance(color, lum = 0) {
// Regular expression to match RGBA colors
const rgbaRegex = /rgba?\(([^)]+)\)/g;
// Convert RGB to HSL
let [h, s, l] = rgbToHsl(r, g, b);
// Check if the input color is a gradient (linear or radial)
if (color.includes("gradient")) {
let gradient = color;
// Adjust saturation and lightness
s -= s * paleFactor;
l += (1 - l) * lightenFactor;
// 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());
// Convert HSL back to RGB
[r, g, b] = hslToRgb(h, s, l);
// 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
// Convert RGB to hex
r = Math.round(r).toString(16).padStart(2, "0");
g = Math.round(g).toString(16).padStart(2, "0");
b = Math.round(b).toString(16).padStart(2, "0");
return "#" + r + g + b;
// Replace the original RGBA string with the adjusted one
gradient = gradient.replace(`rgba(${rgbaString})`, `rgba(${adjustedRgba.join(", ")})`);
}
function ColorLuminance(hex, lum) {
// 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;
return gradient;
// convert to decimal and change luminosity
var rgb = "#",
c,
i;
for (i = 0; i < 3; i++) {
c = parseInt(hex.substr(i * 2, 2), 16);
} 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 rgb;
return Color(rgb).hex();
}
}
chrome.storage.onChanged.addListener(function (changes) {
if (changes.selectedColor) {
try {
chrome.storage.local.get(["DarkMode"], function (result) {
if (!result.DarkMode) {
console.log(changes.selectedColor.newValue);
document.documentElement.style.setProperty(
"--better-pale",
lightenAndPaleColor(changes.selectedColor.newValue),
@@ -742,6 +748,7 @@ chrome.storage.onChanged.addListener(function (changes) {
}
let rbg = GetThresholdofHex(changes.selectedColor.newValue);
if (rbg > 210) {
document.documentElement.style.setProperty("--text-color", "black");
document.documentElement.style.setProperty(
@@ -760,7 +767,6 @@ chrome.storage.onChanged.addListener(function (changes) {
"--better-main",
changes.selectedColor.newValue,
);
// document.documentElement.style.setProperty('--better-sub', ColorLuminance(changes.selectedColor.newValue, -0.15));
if (changes.selectedColor.newValue == "#ffffff") {
document.documentElement.style.setProperty("--better-light", "#b7b7b7");
@@ -801,7 +807,7 @@ async function CheckLoadOnPeriods() {
}
}
function RunFunctionOnTrue(storedSetting) {
function main(storedSetting) {
DarkMode = storedSetting.DarkMode;
// If the option is 'on', open BetterSEQTA
if (typeof storedSetting.onoff == "undefined") {
@@ -891,7 +897,6 @@ function RunFunctionOnTrue(storedSetting) {
"--better-main",
storedSetting.selectedColor,
);
// document.documentElement.style.setProperty('--better-sub', ColorLuminance(storedSetting.selectedColor, -0.15));
if (storedSetting.selectedColor == "#ffffff") {
document.documentElement.style.setProperty("--better-light", "#b7b7b7");
@@ -966,7 +971,7 @@ document.addEventListener(
document.getElementsByTagName("html")[0].appendChild(link);
chrome.storage.local.get(null, function (items) {
RunFunctionOnTrue(items);
main(items);
});
}
if (
@@ -978,7 +983,7 @@ document.addEventListener(
},
true,
);
/*
function RunExtensionSettingsJS() {
const whatsnewsettings = document.getElementById("whatsnewsettings");
whatsnewsettings.addEventListener("click", function () {
@@ -1043,9 +1048,9 @@ function RunExtensionSettingsJS() {
function FindSEQTATab() {
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() {
chrome.storage.local.set({ onoff: onoffselection.checked }, function () {
FindSEQTATab();
@@ -1072,10 +1077,10 @@ function RunExtensionSettingsJS() {
FindSEQTATab();
}
/*
Update the options UI with the settings values retrieved from storage,
or the default settings if the stored settings are empty.
*/
// Update the options UI with the settings values retrieved from storage,
// or the default settings if the stored settings are empty.
function updateUI(restoredSettings) {
if (typeof restoredSettings.onoff == "undefined") {
chrome.runtime.sendMessage({ type: "setDefaultStorage" });
@@ -1310,7 +1315,7 @@ function RunExtensionSettingsJS() {
chrome.storage.local.set({ selectedColor: b });
}
});
}
}*/
function CallExtensionSettings() {
// Injecting CSS File to the webpage to overwrite iFrame default CSS
@@ -1333,7 +1338,7 @@ function CallExtensionSettings() {
fileref.setAttribute("href", cssFile);
document.head.append(fileref);
let Settings =
/*let Settings =
stringToHTML(
String.raw`
<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>
<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>`);
document.body.append(Settings.firstChild);
</div></div>`);*/
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 extensionsettings = document.getElementById("ExtensionPopup");
container.onclick = function () {
if (!SettingsClicked) {
extensionsettings.classList.add("hidden");
extensionsettings.classList.add("hide");
}
SettingsClicked = false;
};
@@ -1981,10 +2003,12 @@ function AddBetterSEQTAElements(toggle) {
students[index]?.house_colour,
);
if (colorresult > 300) {
if (colorresult && colorresult > 300) {
houseelement.style.color = "black";
} else {
} else if (colorresult < 300) {
houseelement.style.color = "white";
} else {
houseelement.style.color = "black";
}
houseelement.innerText =
students[index].year + students[index].house;
@@ -2050,8 +2074,9 @@ function AddBetterSEQTAElements(toggle) {
}
CallExtensionSettings();
RunExtensionSettingsJS();
//RunExtensionSettingsJS();
// If betterSEQTA+ is enabled, run the code
if (toggle) {
// Creates settings and dashboard buttons next to alerts
var SettingsButton = stringToHTML(
@@ -2112,6 +2137,11 @@ function AddBetterSEQTAElements(toggle) {
for (let i = 0; i < alliframes.length; i++) {
const element = alliframes[i];
if (element.getAttribute("excludeDarkCheck") == "true") {
continue;
}
element.contentDocument.documentElement.childNodes[1].style.color =
"white";
element.contentDocument.documentElement.firstChild.appendChild(
@@ -2145,6 +2175,11 @@ function AddBetterSEQTAElements(toggle) {
for (let i = 0; i < alliframes.length; i++) {
const element = alliframes[i];
if (element.getAttribute("excludeDarkCheck") == "true") {
continue;
}
element.contentDocument.documentElement.childNodes[1].style.color =
"black";
element.contentDocument.documentElement.firstChild.lastChild.remove();
@@ -2168,7 +2203,7 @@ function AddBetterSEQTAElements(toggle) {
var AddedSettings = document.getElementById("AddedSettings");
var extensionsettings = document.getElementById("ExtensionPopup");
AddedSettings.addEventListener("click", function () {
extensionsettings.classList.toggle("hidden");
extensionsettings.classList.toggle("hide");
SettingsClicked = true;
});
}
@@ -2275,22 +2310,37 @@ function CheckCurrentLesson(lesson, num) {
}
}
function hexToRGB(hex) {
try {
var r = parseInt(hex.slice(1, 3), 16),
g = parseInt(hex.slice(3, 5), 16),
b = parseInt(hex.slice(5, 7), 16);
function GetThresholdofHex(color) {
// Regular expression for matching RGBA colors
const rgbaRegex = /rgba?\(([^)]+)\)/g;
return { r: r, g: g, b: b };
} catch {
// do nothing becuase this functoin is a bit broken right now (feel free to fix it!)
}
// Check if the color string 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) {
// 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})`);
}
function GetThresholdofHex(hex) {
var rgb = hexToRGB(hex);
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 CheckCurrentLessonAll(lessons) {
// Checks each lesson and sets an interval to run every 60 seconds to continue updating
+38 -7
View File
@@ -15,7 +15,7 @@ chrome.runtime.onMessage.addListener(function (request) {
ReloadSEQTAPages();
} else if (request.type == "githubTab") {
chrome.tabs.create({
url: "github.com/SethBurkart123/BetterThanBetterSeqta",
url: "github.com/SethBurkart123/EvenBetterSEQTA",
});
} else if (request.type == "setDefaultStorage") {
console.log("setting default values");
@@ -102,7 +102,6 @@ chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log(TodayFormatted);
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`;
GetNews(url, sendResponse);
@@ -168,7 +167,7 @@ const DefaultValues = {
enabled: false,
},
{
Name: "educationperfect",
name: "educationperfect",
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.storage.local.remove(["justupdated"]);
UpdateCurrentValues();
if (
/*chrome.runtime.getManifest().version > event.previousVersion || */ event.reason ==
"install"
) {
if ( event.reason == "install" ) {
chrome.storage.local.set({ justupdated: true });
migrateOldStorage();
}
});
+47 -54
View File
@@ -2,7 +2,6 @@
@import "./injected/popup.css";
:root {
background-color: var(--better-main) !important;
background: var(--better-main) !important;
--navy: #1a1a1a !important;
--auto-background: var(--better-pale, var(--background-secondary)) !important;
@@ -15,7 +14,7 @@ html {
#container {
transition: 200ms;
background-color: var(--auto-background) !important;
background: var(--auto-background) !important;
}
* {
@@ -23,11 +22,6 @@ html {
--theme-fg-parts: white;
}
#title {
color: var(--text-primary);
font-weight: 500 !important;
}
@media (min-width: 900px) {
#title > span {
transform: translateY(2px);
@@ -220,7 +214,7 @@ li.item.draggable {
}
html {
background-color: var(--better-main) !important;
background: var(--better-main) !important;
}
/* Messages */
@@ -270,29 +264,29 @@ ol:has(.MessageList__avatar___2wxyb svg) {
}
.content [autocomplete="off"] {
background-color: var(--background-primary) !important;
background: var(--background-primary) !important;
}
.MessageList__MessageList___3DxoC .footer {
background-color: var(--background-secondary) !important;
background: var(--background-secondary) !important;
}
.content [placeholder="Subject…"] {
border-radius: 16px;
padding-left: 12px !important;
background-color: var(--background-primary) !important;
background: var(--background-primary) !important;
}
.listWrapper {
padding: 8px;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
background-color: var(--background-primary);
background: var(--background-primary);
margin-top: 26%;
}
.functions {
background-color: var(--background-primary);
background: var(--background-primary);
margin: 0 !important;
border-bottom-left-radius: 16px;
border-bottom-right-radius: 16px;
@@ -468,7 +462,7 @@ ol > [data-label] {
.Viewer__newMessage___3ToUb {
border-radius: 0.5rem !important;
font-size: 0.8rem !important;
background-color: var(--background-primary) !important;
background: var(--background-primary) !important;
}
.MessageList__sender___32riy :last-child {
@@ -509,7 +503,7 @@ div > ol:has(.uiFileHandlerWrapper) {
}
#main > .timetablepage > .container {
background-color: var(--background-primary);
background: var(--background-primary);
}
#content {
@@ -557,20 +551,19 @@ div > ol:has(.uiFileHandlerWrapper) {
top: 0;
width: 100%;
height: 100vh;
background-repeat: no-repeat;
background-position: center;
background-position: 10% 10%;
color: var(--text-primary) !important;
}
.Module__wrapper___2sbOo {
overflow: clip;
background: var(--background-primary) !important;
border-radius: 1rem !important;
color: var(--text-primary) !important;
box-shadow: none;
}
.course .composer {
background: var(--background-primary) !important;
background: transparent !important;
overflow: hidden;
}
@@ -579,7 +572,7 @@ div > ol:has(.uiFileHandlerWrapper) {
> .Container__container___33GlY
> .Document__document___1KJCG
> .Canvas__canvas___OBdCZ {
background-color: unset !important;
background-color: transparent !important;
background-image: unset !important;
color: white !important;
}
@@ -607,20 +600,18 @@ div > ol:has(.uiFileHandlerWrapper) {
#title {
background: var(--background-primary);
color: var(--text-primary);
height: 4rem;
box-shadow: rgb(0 0 0 / 35%) 0px 0px 10px;
min-height: 48px;
box-shadow: rgb(0 0 0 / 35%) 0px 0px 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
font-weight: 500 !important;
z-index: 1;
}
.bg {
animation: slide 3s ease-in-out infinite alternate;
background-image: linear-gradient(
-60deg,
var(--better-main) 50%,
var(--auto-background) 50%
);
background: var(--better-main);
bottom: 0;
left: -50%;
opacity: 0.5;
@@ -629,6 +620,7 @@ div > ol:has(.uiFileHandlerWrapper) {
top: 0;
z-index: 0 !important;
overflow: hidden;
scale: 1.5;
}
.bg2 {
@@ -642,11 +634,11 @@ div > ol:has(.uiFileHandlerWrapper) {
@keyframes slide {
0% {
transform: translateX(-25%);
transform: translate(50%) rotate(-60deg);
}
100% {
transform: translateX(25%);
transform: translateX(5%) rotate(-60deg);
}
}
@@ -694,7 +686,7 @@ div > ol:has(.uiFileHandlerWrapper) {
font-size: 3em !important;
font-weight: 300;
margin: 30px auto 60px;
background-color: var(--background-primary);
background: var(--background-primary);
height: 3em;
align-items: center;
justify-content: center;
@@ -708,7 +700,7 @@ div > ol:has(.uiFileHandlerWrapper) {
width: 94%;
margin: 50px auto;
height: 19em;
background-color: var(--better-main);
background: var(--better-main);
display: flex;
flex-direction: column;
-webkit-box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
@@ -716,7 +708,7 @@ div > ol:has(.uiFileHandlerWrapper) {
}
.day-container {
background-color: var(--background-primary);
background: var(--background-primary);
transition: 200ms;
width: 100%;
height: 15em;
@@ -729,7 +721,7 @@ div > ol:has(.uiFileHandlerWrapper) {
width: 94%;
margin: 50px auto;
max-height: 60em;
background-color: var(--better-main);
background: var(--better-main);
display: flex;
flex-direction: column;
-webkit-box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
@@ -737,7 +729,7 @@ div > ol:has(.uiFileHandlerWrapper) {
}
.notice-container {
background-color: var(--better-main);
background: var(--better-main);
width: 100%;
max-height: 55em;
overflow-y: auto;
@@ -789,7 +781,7 @@ div > ol:has(.uiFileHandlerWrapper) {
border: 2px solid var(--better-main);
width: 94%;
margin: 10px auto 50px;
background-color: var(--better-main);
background: var(--better-main);
display: flex;
flex-direction: column;
-webkit-box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3);
@@ -807,7 +799,7 @@ div > ol:has(.uiFileHandlerWrapper) {
}
.shortcuts {
background-color: var(--better-main);
background: var(--better-main);
width: 100%;
padding-top: 10px;
padding-bottom: 10px;
@@ -991,13 +983,13 @@ div > ol:has(.uiFileHandlerWrapper) {
}
.modaliser {
background-color: var(--better-main);
background: var(--better-main);
}
.alert-container {
height: 35em;
width: 22em;
background-color: var(--better-sub);
background: var(--better-sub);
position: absolute;
right: 0;
top: 0;
@@ -1081,7 +1073,7 @@ div > ol:has(.uiFileHandlerWrapper) {
#ExtensionPopup {
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 {
@@ -1171,7 +1163,7 @@ div > ol:has(.uiFileHandlerWrapper) {
.Input__Input___3RSTI {
transition: background-color 0.5s,border-color 0.5s;
background-color: var(--auto-background);
background: var(--auto-background);
position: relative;
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
@@ -1245,7 +1237,7 @@ iframe.userHTML {
.Thermoscore__Thermoscore___2tWMi {
background-image: unset;
background-color: var(--auto-background);
background: var(--auto-background);
}
#toolbar {
@@ -1275,6 +1267,7 @@ iframe.userHTML {
#main > .course > .content > h1 {
color: var(--text-primary);
border-bottom: none;
}
#main > .course > .content > .outline > h2,
@@ -1291,7 +1284,7 @@ iframe.userHTML {
::-webkit-scrollbar-thumb:vertical:hover,
::-webkit-scrollbar-thumb:horizontal:hover {
background-color: var(--better-light);
background: var(--better-light);
}
::-webkit-scrollbar-track {
@@ -1381,16 +1374,16 @@ ul {
}
.legacy-root .uiFileHandler {
background-color: var(--auto-background);
background: var(--auto-background);
border-radius: 1rem;
}
.ResourceList__ResourceList___2z-c1 .legacy-root .uiFileHandler {
background-color: var(--background-primary);
background: var(--background-primary);
}
.legacy-root .uiFileHandler.dragTarget {
background-color: var(--better-main);
background: var(--better-main);
}
.MenuButton__MenuPanel___2q42B {
@@ -1497,13 +1490,13 @@ blurred {
}
.uiSlidePane > .pane > .header {
background-color: var(--better-main);
background: var(--better-main);
}
.content [placeholder="Subject…"] {
padding-left: 12px !important;
border-radius: 1rem;
background-color: var(--background-primary) !important;
background: var(--background-primary) !important;
color: var(--text-primary) !important;
}
@@ -1523,7 +1516,7 @@ blurred {
}
.formattedText > .footer {
background-color: var(--background-primary);
background: var(--background-primary);
border-radius: 1rem;
margin-top: 0.5rem;
}
@@ -1544,7 +1537,7 @@ blurred {
.uiSlidePane > .pane {
color: var(--text-primary);
background-color: var(--auto-background);
background: var(--auto-background);
transform: translateY(100%);
transition:
transform 0.5s ease-in-out,
@@ -1672,7 +1665,7 @@ body {
}
.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 {
@@ -2054,7 +2047,7 @@ body {
width: 94%;
margin: 50px auto;
max-height: 60em;
background-color: var(--better-main);
background: var(--better-main);
display: flex;
flex-direction: column;
-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 */
.upcoming-checkbox-container input:checked ~ .upcoming-checkmark {
background-color: var(--item-colour);
background: var(--item-colour);
}
/* Create the checkmark/indicator (hidden when not checked) */
@@ -2378,7 +2371,7 @@ body {
}
.upcoming-items {
background-color: var(--background-primary);
background: var(--background-primary);
transition: 200ms;
width: 100%;
max-height: 55em;
@@ -2532,7 +2525,7 @@ body {
/* When the checkbox is checked, add a blue background */
.upcoming-checkbox-container input:checked ~ .upcoming-checkmark {
background-color: var(--item-colour);
background: var(--item-colour);
}
/* Create the checkmark/indicator (hidden when not checked) */
@@ -2668,7 +2661,7 @@ body {
transform: scale(0);
transition: transform 0.2s;
transform-origin: top;
background-color: var(--background-primary);
background: var(--background-primary);
color: var(--text-primary);
text-align: center;
border-radius: 6px;
+5
View File
@@ -5,3 +5,8 @@
.topmenu {
margin-top: 0;
}
.hide {
opacity: 0;
pointer-events: none;
}
+5
View File
@@ -0,0 +1,5 @@
export function StorageListner() {
chrome.storage.onChanged.addListener(function (changes) {
}
}
+22
View File
@@ -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: [],
};
+23
View File
@@ -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
},
}
+1
View File
@@ -64,6 +64,7 @@ export default {
{ from: "public", to: "." },
{ from: "src/inject/preview", to: "inject/preview" },
{ from: "node_modules/webextension-polyfill/dist/browser-polyfill.js", to: "."},
{ from: "interface/dist/client", to: "client" }
],
}),
],