mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 11:44:40 +00:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f996e4bf19 | |||
| 30c5a823d8 | |||
| 10977247cc | |||
| cd4dc73897 | |||
| d748eece8a | |||
| 37dab0f5a7 | |||
| 842a132c7f | |||
| b36426a94b | |||
| 9659f9aae2 | |||
| b205c0f832 | |||
| f99e76c723 | |||
| 4680e9879d | |||
| cfdea6a116 | |||
| 31954dcbce | |||
| 444cb14e8a | |||
| 856ef62306 | |||
| 5335bba04c | |||
| 611fcef12b | |||
| 6e4fe64789 | |||
| 545a999c46 | |||
| b6dbcfeb69 | |||
| 5bc3d22214 | |||
| be44e86290 | |||
| a78993fffc | |||
| a8ff2213bd | |||
| 7519312282 | |||
| 8b9ad39e8e | |||
| 182597efce | |||
| 4d38af402f | |||
| e19020066a | |||
| ee76b4d9d2 | |||
| 47014bc77f | |||
| 3dc7396f7a | |||
| f70c032f06 | |||
| 5d97ab3da6 | |||
| 5549de571d | |||
| 8bad8d5b34 | |||
| f97276082e | |||
| 4a9048ac62 | |||
| a2dac4d84d | |||
| ddd1bbe847 | |||
| 0873a33da2 | |||
| f9c2f5876f | |||
| dfef312ddc | |||
| 629c98ce0e | |||
| 3279775a99 | |||
| 97089e5134 | |||
| fed198108a | |||
| 76ed27e82d | |||
| cc88d8c984 | |||
| c6b4bdcbc9 |
@@ -11,30 +11,36 @@
|
||||
<a target="_blank" href="https://discord.gg/YzmbnCDkat"><img src="https://github.com/SethBurkart123/EvenBetterSEQTA/assets/108050083/23055730-b16e-44c0-9bef-221d8545af92" width="240" style="border-radius:10%;" /></a>
|
||||
</p>
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Getting Started](#getting-started)
|
||||
|
||||
## Release Videos
|
||||
<video autoplay loop muted controls="false" width="33%" src="https://github.com/SethBurkart123/EvenBetterSEQTA/assets/108050083/3084644a-edbc-40e5-b1ad-1fdea4f0ca18"></video>
|
||||
|
||||
<div>
|
||||
<img src="https://img.shields.io/chrome-web-store/users/afdgaoaclhkhemfkkkonemoapeinchel" />
|
||||
<img src="https://img.shields.io/chrome-web-store/rating/afdgaoaclhkhemfkkkonemoapeinchel" />
|
||||
</div>
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Creating Custom Themes](#creating-custom-themes)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Running Development](#running-development)
|
||||
- [Building for production](#building-for-production)
|
||||
- [Folder Structure](#folder-structure)
|
||||
- [Contributors](#contributors)
|
||||
- [Credits](#credits)
|
||||
- [Star History](#star-history)
|
||||
|
||||
## Features
|
||||
|
||||
- Dark mode
|
||||
- Custom Background
|
||||
- Custom Background/Themes
|
||||
- Improved Styling/CSS
|
||||
- Improved look for SEQTA Learn
|
||||
- Custom Home Page including:
|
||||
- Daily Lessons
|
||||
- Shortcuts
|
||||
- Easier Access Notices
|
||||
- Assessments
|
||||
- Options to remove certain items from the side menu
|
||||
- Fully customisable themes and an offical theme store
|
||||
- Notification for next lesson (sent 5 minutes before end of the lesson)
|
||||
- Browser Support
|
||||
- Chrome Supported
|
||||
@@ -42,9 +48,21 @@
|
||||
- Brave Supported
|
||||
- Opera Supported
|
||||
- Vivaldi Supported
|
||||
- Firefox (Experimental - available [here](https://addons.mozilla.org/en-US/firefox/addon/betterseqta-plus/))
|
||||
- Firefox (Experimental - available [here](https://addons.mozilla.org/en-US/firefox/addon/betterseqta-plus/)
|
||||
- Safari (Experimental - only available via compilation)
|
||||
|
||||
## Creating Custom Themes
|
||||
|
||||
If you are looking to create custom themes, I would recommend you start at the official documentation [here](https://betterseqta.gitbook.io/betterseqta-docs). You can see some premade examples along with a compilation script that can be used to allow for CSS frameworks and libraries such as SCSS to be used [here](https://github.com/SethBurkart123/BetterSEQTA-theme-generator).
|
||||
|
||||
Don't worry- if you get stuck feel free to ask around in the discord. We're open and happy to help out! Happy creating :)
|
||||
|
||||
## Creating Custom Themes
|
||||
|
||||
If you are looking to create custom themes, I would recommend you start at the official documentation [here](https://betterseqta.gitbook.io/betterseqta-docs). You can see some premade examples along with a compilation script that can be used to allow for CSS frameworks and libraries such as SCSS to be used [here](https://github.com/SethBurkart123/BetterSEQTA-theme-generator).
|
||||
|
||||
Don't worry- if you get stuck feel free to ask around in the discord. We're open and happy to help out! Happy creating :)
|
||||
|
||||
## Getting started
|
||||
|
||||
1. Clone the repository
|
||||
@@ -93,7 +111,7 @@ npm run build
|
||||
3. Package it up (optional)
|
||||
|
||||
```
|
||||
npm run package # this requires 7zip to be installed in order to work
|
||||
npm run package # This requires 7-Zip to be installed in order to work
|
||||
```
|
||||
|
||||
## Folder Structure
|
||||
@@ -106,15 +124,13 @@ The folder structure is as follows:
|
||||
|
||||
- The `dist` folder is where the compiled code ends up, this is the folder what you need to load into chrome as an unpacked extension for development.
|
||||
|
||||
- The `safari` folder is an Xcode project, building it for MacOS does work, IOS needs a few modifications to the manifest to work, but I have managed to get it working. It will give an error, to fix this you need to regenerate it, you can delete the safari folder and then run the command `xcrun safari-web-extension-converter <extension-folder>/dist` and it will automatically generate the xcode project where you are.
|
||||
|
||||
## Contributors
|
||||
|
||||
<a href="https://github.com/betterseqta/betterseqta-plus/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=betterseqta/betterseqta-plus" />
|
||||
</a>
|
||||
|
||||
Want to contribute? [Click Here!](https://github.com/BetterSEQTA/BetterSEQTA-Plus/contribute.md)
|
||||
Want to contribute? [Click Here!](https://github.com/BetterSEQTA/BetterSEQTA-Plus/blob/main/contribute.md)
|
||||
## Credits
|
||||
|
||||
This extension was initially developed by [Nulkem](https://github.com/Nulkem/betterseqta), was ported to manifest V3 by [MEGA-Dawg68](https://github.com/MEGA-Dawg68) and is currently under active development by [SethBurkart123](https://github.com/SethBurkart123) and [Crazypersonalph](https://github.com/Crazypersonalph)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"64": "src/resources/icons/icon-64.png"
|
||||
}
|
||||
},
|
||||
"permissions": ["tabs", "notifications", "storage", "activeTab", "scripting"],
|
||||
"permissions": ["tabs", "notifications", "storage", "activeTab"],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"background": {
|
||||
"scripts": ["src/background.ts"]
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "BetterSEQTA+",
|
||||
"version": "3.3.0",
|
||||
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development, and incorporate a plethora of new features!",
|
||||
"version": "3.3.1",
|
||||
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development add add heaps more features!",
|
||||
"icons": {
|
||||
"32": "src/resources/icons/icon-32.png",
|
||||
"48": "src/resources/icons/icon-48.png",
|
||||
@@ -17,7 +17,7 @@
|
||||
"64": "src/resources/icons/icon-64.png"
|
||||
}
|
||||
},
|
||||
"permissions": ["tabs", "notifications", "storage", "scripting"],
|
||||
"permissions": ["tabs", "notifications", "storage"],
|
||||
"host_permissions": ["https://newsapi.org/", "*://*/*"],
|
||||
"background": {
|
||||
"service_worker": "src/background.ts"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "betterseqtaplus",
|
||||
"version": "3.2.6",
|
||||
"version": "3.3.0",
|
||||
"type": "module",
|
||||
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development, and incorporate a plethora of new features!",
|
||||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||
|
||||
+85
-36
@@ -1,32 +1,40 @@
|
||||
// Third-party libraries
|
||||
import Color from 'color'
|
||||
import Sortable from 'sortablejs'
|
||||
import browser from 'webextension-polyfill'
|
||||
import { animate, spring, stagger } from 'motion'
|
||||
import loading, { AppendLoadingSymbol } from './seqta/ui/Loading'
|
||||
|
||||
// Internal utilities and functions
|
||||
import { delay } from './seqta/utils/delay'
|
||||
import stringToHTML from './seqta/utils/stringToHTML'
|
||||
import { MessageHandler } from './seqta/utils/listeners/MessageListener'
|
||||
import { initializeSettingsState, settingsState } from './seqta/utils/listeners/SettingsState'
|
||||
import { StorageChangeHandler } from './seqta/utils/listeners/StorageChanges'
|
||||
import { eventManager } from './seqta/utils/listeners/EventManager'
|
||||
|
||||
// UI and theme management
|
||||
import loading, { AppendLoadingSymbol } from './seqta/ui/Loading'
|
||||
import { enableCurrentTheme } from './seqta/ui/themes/enableCurrent'
|
||||
import { updateAllColors } from './seqta/ui/colors/Manager'
|
||||
import { SettingsResizer } from './seqta/ui/SettingsResizer'
|
||||
import { AddBetterSEQTAElements } from './seqta/ui/AddBetterSEQTAElements'
|
||||
|
||||
// JSON content
|
||||
import MenuitemSVGKey from './seqta/content/MenuItemSVGKey.json'
|
||||
import ShortcutLinks from './seqta/content/links.json'
|
||||
|
||||
// Icons and fonts
|
||||
import IconFamily from './resources/fonts/IconFamily.woff'
|
||||
import LogoLight from './resources/icons/betterseqta-light-icon.png'
|
||||
import LogoLightOutline from './resources/icons/betterseqta-light-outline.png'
|
||||
import icon48 from './resources/icons/icon-48.png?base64'
|
||||
|
||||
import Color from 'color'
|
||||
import MenuitemSVGKey from './seqta/content/MenuItemSVGKey.json'
|
||||
import { MessageHandler } from './seqta/utils/listeners/MessageListener'
|
||||
import ShortcutLinks from './seqta/content/links.json'
|
||||
import Sortable from 'sortablejs'
|
||||
import assessmentsicon from './seqta/icons/assessmentsIcon'
|
||||
import browser from 'webextension-polyfill'
|
||||
import coursesicon from './seqta/icons/coursesIcon'
|
||||
import { delay } from "./seqta/utils/delay"
|
||||
import { enableCurrentTheme } from "./seqta/ui/themes/enableCurrent";
|
||||
import iframeCSS from "./css/iframe.scss?raw"
|
||||
|
||||
// Stylesheets
|
||||
import iframeCSS from './css/iframe.scss?raw'
|
||||
import injectedCSS from './css/injected.scss?inline'
|
||||
import stringToHTML from './seqta/utils/stringToHTML'
|
||||
import { updateAllColors } from './seqta/ui/colors/Manager'
|
||||
import { SettingsResizer } from "./seqta/ui/SettingsResizer";
|
||||
import documentLoadCSS from './css/documentload.scss?inline'
|
||||
import { injectYouTubeVideo } from './seqta/ui/VideoLoader'
|
||||
import { initializeSettingsState, settingsState } from './seqta/utils/listeners/SettingsState'
|
||||
import { StorageChangeHandler } from './seqta/utils/listeners/StorageChanges'
|
||||
import { AddBetterSEQTAElements } from './seqta/ui/AddBetterSEQTAElements'
|
||||
import { eventManager } from './seqta/utils/listeners/EventManager'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -133,9 +141,16 @@ export function OpenWhatsNewPopup() {
|
||||
let imagecont = document.createElement('div')
|
||||
imagecont.classList.add('whatsnewImgContainer')
|
||||
|
||||
let div = document.createElement('div')
|
||||
div.classList.add('whatsnewImg')
|
||||
imagecont.appendChild(div)
|
||||
let video = document.createElement('video')
|
||||
let source = document.createElement('source')
|
||||
// Perhaps we host this on a server and then grab it instead of having it locally?
|
||||
source.setAttribute('src', 'https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-video.webm')
|
||||
video.autoplay = true
|
||||
video.muted = true
|
||||
video.loop = true
|
||||
video.appendChild(source)
|
||||
video.classList.add('whatsnewImg')
|
||||
imagecont.appendChild(video)
|
||||
|
||||
let textcontainer = document.createElement('div')
|
||||
textcontainer.classList.add('whatsnewTextContainer')
|
||||
@@ -143,9 +158,23 @@ export function OpenWhatsNewPopup() {
|
||||
let text = stringToHTML(
|
||||
/* html */ `
|
||||
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: scroll;">
|
||||
<h1>3.3.1 - Hot Fix</h1>
|
||||
<li>Fixed assessments not loading when no notices are available</li>
|
||||
|
||||
<h1>3.3.0 - Overhauled Theming System</h1>
|
||||
<li>Find and create awesome themes! <span style="background: var(--background-secondary); color: var(--text-primary); padding: 2px 4px; border-radius: 4px; font-size: 12px; font-weight: 600;">Beta</span></li>
|
||||
<li>Added a theme store!</li>
|
||||
<li>Added the new theme creator!</li>
|
||||
<li>Fixed Notices not working on home page</li>
|
||||
<li>Fixed dark/light button labels inverted</li>
|
||||
<li>Switched to GitHub for hosting the update video</li>
|
||||
<li>Fixed an issue where the settings menu wouldn't change theme</li>
|
||||
<li>Fixed custom shortcuts not allowing ports to be used</li>
|
||||
<li>Fixed occasional flashing when using animations</li>
|
||||
<li>Fixed loading of the tab icon</li>
|
||||
<li>Made animations toggle apply to settings</li>
|
||||
<li>Small styling improvements</li>
|
||||
<li>Other minor bug fixes</li>
|
||||
|
||||
|
||||
<h1>3.2.7 - Minor Improvements</h1>
|
||||
<li>Improved performance!</li>
|
||||
@@ -285,10 +314,6 @@ export function OpenWhatsNewPopup() {
|
||||
let bkelement = document.getElementById('whatsnewbk')
|
||||
let popup = document.getElementsByClassName('whatsnewContainer')[0]
|
||||
|
||||
injectYouTubeVideo(
|
||||
'JdDA45GYEUc', 'PLSlFV-9e6dvyvZJFPCtBMb3LSp-LGbrbI', document.querySelector('.whatsnewImg')!, true, true, '100%', '100%'
|
||||
)
|
||||
|
||||
if (settingsState.animations) {
|
||||
animate(
|
||||
[popup, bkelement as HTMLElement],
|
||||
@@ -484,7 +509,7 @@ async function updateIframesWithDarkMode(): Promise<void> {
|
||||
}, (element) => {
|
||||
const iframe = element as HTMLIFrameElement;
|
||||
try {
|
||||
applyDarkModeToIframe(iframe, cssLink, settingsState.DarkMode);
|
||||
applyDarkModeToIframe(iframe, cssLink);
|
||||
|
||||
if (element.classList.contains('cke_wysiwyg_frame')) {
|
||||
(async () => {
|
||||
@@ -498,18 +523,20 @@ async function updateIframesWithDarkMode(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function applyDarkModeToIframe(iframe: HTMLIFrameElement, cssLink: HTMLStyleElement, DarkMode: boolean): void {
|
||||
function applyDarkModeToIframe(iframe: HTMLIFrameElement, cssLink: HTMLStyleElement): void {
|
||||
const iframeDocument = iframe.contentDocument;
|
||||
if (!iframeDocument) return;
|
||||
|
||||
if (iframeDocument.readyState !== 'complete') {
|
||||
iframe.onload = () => {
|
||||
applyDarkModeToIframe(iframe, cssLink, DarkMode);
|
||||
applyDarkModeToIframe(iframe, cssLink);
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (DarkMode) iframeDocument.documentElement.classList.add('dark');
|
||||
if (settingsState.DarkMode) {
|
||||
iframeDocument.documentElement.classList.add('dark')
|
||||
}
|
||||
|
||||
const head = iframeDocument.head;
|
||||
if (head && !head.innerHTML.includes('iframecss')) {
|
||||
@@ -603,8 +630,13 @@ async function handleSublink(sublink: string | undefined): Promise<void> {
|
||||
handleMessages(document.querySelector('.messages')!)
|
||||
await handleDefault()
|
||||
break;
|
||||
case 'dashboard':
|
||||
handleDashboard(document.querySelector('.dashboard')!)
|
||||
await handleDefault()
|
||||
break;
|
||||
|
||||
default:
|
||||
await handleDefault();
|
||||
await handleDefault()
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -649,6 +681,12 @@ async function handleMessages(node: Element): Promise<void> {
|
||||
|
||||
if (!settingsState.animations) return;
|
||||
|
||||
// Hides messages on page load
|
||||
const style = document.createElement('style')
|
||||
style.classList.add('messageHider')
|
||||
style.innerHTML = '[data-message]{opacity: 0 !important;}'
|
||||
document.head.append(style)
|
||||
|
||||
await waitForElm('[data-message]', true, 10);
|
||||
const messages = Array.from(document.querySelectorAll('[data-message]')).slice(0, 35);
|
||||
animate(
|
||||
@@ -660,12 +698,19 @@ async function handleMessages(node: Element): Promise<void> {
|
||||
easing: [.22, .03, .26, 1]
|
||||
}
|
||||
);
|
||||
|
||||
document.head.querySelector('style.messageHider')?.remove()
|
||||
}
|
||||
|
||||
async function handleDashboard(node: Element): Promise<void> {
|
||||
if (!(node instanceof HTMLElement)) return;
|
||||
if (!settingsState.animations) return;
|
||||
|
||||
const style = document.createElement('style')
|
||||
style.classList.add('dashboardHider')
|
||||
style.innerHTML = '.dashboard{opacity: 0 !important;}'
|
||||
document.head.append(style)
|
||||
|
||||
await waitForElm('.dashlet', true, 10);
|
||||
animate(
|
||||
'.dashboard > *',
|
||||
@@ -676,6 +721,8 @@ async function handleDashboard(node: Element): Promise<void> {
|
||||
easing: [.22, .03, .26, 1]
|
||||
}
|
||||
);
|
||||
|
||||
document.head.querySelector('style.dashboardHider')?.remove()
|
||||
}
|
||||
|
||||
async function handleDocuments(node: Element): Promise<void> {
|
||||
@@ -2150,7 +2197,7 @@ export async function loadHomePage() {
|
||||
|
||||
var Notices = stringToHTML(NoticesStr)
|
||||
// Appends the shortcut container into the home container
|
||||
document.getElementById('home-container')!.append(Notices.firstChild!)
|
||||
document.getElementById('home-container')!.append(Notices.firstChild!) // HERE!!!
|
||||
|
||||
if (settingsState.animations) {
|
||||
animate(
|
||||
@@ -2174,8 +2221,10 @@ export async function loadHomePage() {
|
||||
|
||||
const response = await GetPrefs.json()
|
||||
|
||||
const labelArray = response.payload.filter((item: any) => item.name === 'notices.filters').map((item: any) => item.value)[0].split(' ')
|
||||
const labelArray = response.payload.filter((item: any) => item.name === 'notices.filters').map((item: any) => item.value)
|
||||
|
||||
if (labelArray.length !== 0) {
|
||||
const labelArray = response.payload.filter((item: any) => item.name === 'notices.filters').map((item: any) => item.value)[0].split(' ')
|
||||
const xhr2 = new XMLHttpRequest()
|
||||
xhr2.open(
|
||||
'POST',
|
||||
@@ -2206,6 +2255,7 @@ export async function loadHomePage() {
|
||||
}
|
||||
|
||||
dateControl.addEventListener('input', onInputChange);
|
||||
}
|
||||
|
||||
if (settingsState.notificationcollector) {
|
||||
enableNotificationCollector()
|
||||
@@ -2280,8 +2330,7 @@ function processNotices(responseText: any, labelArray: any) {
|
||||
let colour = notice.colour;
|
||||
if (typeof colour === 'string') {
|
||||
const rgb = GetThresholdOfColor(colour);
|
||||
const DarkModeResult = settingsState.DarkMode;
|
||||
if (rgb < 100 && DarkModeResult) {
|
||||
if (rgb < 100 && settingsState.DarkMode) {
|
||||
colour = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-11
@@ -1,6 +1,5 @@
|
||||
import browser from 'webextension-polyfill'
|
||||
import { SettingsState } from "./types/storage";
|
||||
import { applyYoutubeStyles } from './seqta/ui/VideoLoader';
|
||||
|
||||
export const openDB = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -118,16 +117,6 @@ browser.runtime.onMessage.addListener((request: any, _sender: any, sendResponse:
|
||||
GetNews(sendResponse, url);
|
||||
return true;
|
||||
|
||||
case 'youtubeIframe':
|
||||
const { hideControls } = request;
|
||||
|
||||
browser.scripting.executeScript({
|
||||
target: { tabId: _sender.tab.id, allFrames: true },
|
||||
func: applyYoutubeStyles,
|
||||
args: [hideControls]
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Unknown request type');
|
||||
}
|
||||
@@ -283,6 +272,7 @@ async function UpdateCurrentValues() {
|
||||
browser.runtime.onInstalled.addListener(function (event) {
|
||||
browser.storage.local.remove(['justupdated']);
|
||||
browser.storage.local.remove(['data']);
|
||||
|
||||
UpdateCurrentValues();
|
||||
if ( event.reason == 'install', event.reason == 'update' ) {
|
||||
browser.storage.local.set({ justupdated: true });
|
||||
|
||||
+36
-37
@@ -159,7 +159,13 @@ html {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.legacy-root button:active, .legacy-root a:active {
|
||||
.cke_panel {
|
||||
border-radius: 16px !important;
|
||||
margin-top: 8px !important;
|
||||
background: unset;
|
||||
}
|
||||
|
||||
.legacy-root button:active, .legacy-root a:active:not(.cke_combo_button) {
|
||||
background-image: unset !important;
|
||||
}
|
||||
|
||||
@@ -543,13 +549,13 @@ td.colourBar {
|
||||
}
|
||||
#toolbar .search {
|
||||
padding-left: 30px;
|
||||
/* Provides space for the icon */
|
||||
}
|
||||
#toolbar span:has(.search)::before {
|
||||
content: "\eca5";
|
||||
/* Unicode for the search icon */
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
z-index: 10;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: currentColor;
|
||||
@@ -616,10 +622,6 @@ ul.buttonChecklist {
|
||||
margin-right: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
ol > [data-message] {
|
||||
padding-left: 8px !important;
|
||||
padding-right: 4px !important;
|
||||
}
|
||||
ol > [data-label] {
|
||||
margin-left: 4px;
|
||||
margin-bottom: 4px;
|
||||
@@ -1035,6 +1037,7 @@ div > ol:has(.uiFileHandlerWrapper) {
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 16px;
|
||||
border-bottom-left-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
}
|
||||
@@ -1050,11 +1053,9 @@ div > ol:has(.uiFileHandlerWrapper) {
|
||||
}
|
||||
.notice {
|
||||
position: relative;
|
||||
width: 95%;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0px auto 7px;
|
||||
background: var(--background-primary);
|
||||
transition: 200ms;
|
||||
box-shadow: inset 0px 5px 20px 1px rgba(0, 0, 0, 0.3);
|
||||
@@ -1454,11 +1455,6 @@ div > ol:has(.uiFileHandlerWrapper) {
|
||||
height: 180px;
|
||||
background: var(--background-primary);
|
||||
}
|
||||
.cke_panel {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: unset;
|
||||
}
|
||||
.Avatar__Avatar___gE5kx.Avatar__staff___4gVLs {
|
||||
--person-colour: var(--better-light);
|
||||
background: var(--person-colour, var(--navy));
|
||||
@@ -1571,7 +1567,7 @@ div,
|
||||
ol,
|
||||
ul {
|
||||
scrollbar-width: thin !important;
|
||||
scrollbar-color: var(--better-light) var(--better-sub) !important;
|
||||
scrollbar-color: var(--better-light) var(--better-sub);
|
||||
}
|
||||
.connectedNotificationsWrapper > div > button {
|
||||
color: var(--text-primary) !important;
|
||||
@@ -1656,6 +1652,22 @@ ul {
|
||||
.MenuButton__MenuPanel___2q42B {
|
||||
background: var(--background-primary);
|
||||
color: var(--text-primary);
|
||||
border-radius: 16px;
|
||||
overflow: clip;
|
||||
|
||||
> div {
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
> div {
|
||||
> h2 {
|
||||
padding-bottom: 1px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dailycal > .header {
|
||||
color: var(--text-primary);
|
||||
@@ -1718,6 +1730,11 @@ div.entry.class[style*="width: 46.5%"] {
|
||||
margin-top: 4px !important;
|
||||
}
|
||||
|
||||
.uiFile {
|
||||
border-radius: 8px !important;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.dark .title a.uiFile {
|
||||
color: #06b4fc !important;
|
||||
}
|
||||
@@ -1869,13 +1886,15 @@ div.bar.flat {
|
||||
padding: 0;
|
||||
}
|
||||
.cke_toolbox {
|
||||
padding: 0 !important;
|
||||
padding-left: 8px !important;
|
||||
background: unset !important;
|
||||
gap: 4px;
|
||||
gap: 0 8px;
|
||||
}
|
||||
.cke_toolbox > .cke_toolbar > .cke_toolgroup {
|
||||
margin: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
#cke_1_top a:hover {
|
||||
#cke_1_top a:hover:not(.cke_combo_button) {
|
||||
background: #5a5a5a;
|
||||
}
|
||||
.legacy-root button.depressed,
|
||||
@@ -2884,32 +2903,12 @@ li.MessageList__unread___3imtO {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.whatsnewImg {
|
||||
background-color: black;
|
||||
pointer-events: none !important;
|
||||
margin: 8px auto;
|
||||
width: 90%;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
|
||||
|
||||
}
|
||||
.whatsnewImg > iframe {
|
||||
aspect-ratio: 16/9.823;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 16px;
|
||||
opacity: 0;
|
||||
|
||||
animation: fade-in 0.5s forwards;
|
||||
animation-delay: 0.8s !important;
|
||||
}
|
||||
@keyframes fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.whatsnewTextContainer {
|
||||
display: flex;
|
||||
overflow-x: hidden;
|
||||
|
||||
@@ -25,7 +25,8 @@ export const SettingsContextProvider: React.FC<{ children: ReactNode }> = ({ chi
|
||||
transparencyEffects: false,
|
||||
selectedTheme: '',
|
||||
animations: true,
|
||||
defaultPage: 'home'
|
||||
defaultPage: 'home',
|
||||
devMode: false
|
||||
});
|
||||
|
||||
const [showPicker, setShowPicker] = useState<boolean>(false);
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React, { memo, useEffect, useRef, useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { TabbedContainerProps } from '../types/TabbedContainerProps';
|
||||
import { useSettingsContext } from '../SettingsContext';
|
||||
|
||||
const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs }) => {
|
||||
const { settingsState } = useSettingsContext();
|
||||
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);
|
||||
|
||||
|
||||
// Function to handle message
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data === "popupClosed") {
|
||||
@@ -27,7 +28,6 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs }) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const newPosition = -activeTab * 100;
|
||||
setPosition(newPosition);
|
||||
@@ -36,7 +36,7 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs }) => {
|
||||
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const springTransition = { type: 'spring', stiffness: 250, damping: 25 };
|
||||
const springTransition = settingsState.animations ? { type: 'spring', stiffness: 250, damping: 25 } : { duration: 0 };
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
@@ -85,7 +85,7 @@ const TabbedContainer: React.FC<TabbedContainerProps> = ({ tabs }) => {
|
||||
className='flex'
|
||||
>
|
||||
{tabs.map((tab, index) => (
|
||||
<div key={index} className={`absolute h-[100vh] focus-visible:outline-none overflow-y-scroll w-full pb-40 transition-opacity duration-300 ${activeTab === index ? 'opacity-100' : 'opacity-0'}`}
|
||||
<div key={index} className={`absolute h-[100vh] focus-visible:outline-none overflow-y-scroll w-full pb-40 ${ settingsState.animations ? 'transition-opacity duration-300' : ''} ${activeTab === index ? 'opacity-100' : 'opacity-0'}`}
|
||||
style={{left: `${index * 100}%`}}>
|
||||
{tab.content}
|
||||
</div>
|
||||
|
||||
@@ -53,9 +53,9 @@ const useSettingsState = ({ settingsState, setSettingsState }: SettingsProps) =>
|
||||
for (const [key, { newValue }] of Object.entries(changes)) {
|
||||
if (key === "DarkMode") {
|
||||
if (key === "DarkMode" && newValue) {
|
||||
document.body.classList.add('dark');
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,11 +35,11 @@ root.render(
|
||||
<div className="grid w-full h-screen text-center place-content-center dark:text-white">
|
||||
<h1 className="text-2xl font-bold">An error occurred 😭😭😭</h1>
|
||||
<p className="text-lg">Try clicking this button and see if it helps...</p>
|
||||
<button className='flex gap-2 p-2 px-4 mx-auto mt-4 text-white rounded-lg bg-zinc-100 dark:bg-zinc-800/20 outline outline-white/20 w-fit' onClick={() => window.location.reload()}>
|
||||
<button className='flex gap-2 p-2 px-4 mx-auto mt-4 rounded-lg dark:text-white bg-zinc-100 dark:bg-zinc-800/20 outline outline-white/20 w-fit' onClick={() => window.location.reload()}>
|
||||
<svg height="18" width="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#F7F7F7">
|
||||
<path d="M9.03,12.22c-.293-.293-.768-.293-1.061,0s-.293,.768,0,1.061l1.208,1.208c-.059,.002-.118,.012-.178,.012-3.032,0-5.5-2.467-5.5-5.5,0-1.616,.706-3.143,1.938-4.191,.315-.269,.354-.742,.085-1.057s-.74-.353-1.058-.085c-1.567,1.333-2.466,3.277-2.466,5.333,0,3.76,2.983,6.829,6.704,6.985l-.735,.735c-.293,.293-.293,.768,0,1.061,.146,.146,.338,.22,.53,.22s.384-.073,.53-.22l2.25-2.25c.293-.293,.293-.768,0-1.061l-2.25-2.25Z" fill="#F7F7F7"/>
|
||||
<path d="M9.296,2.015l.735-.735c.293-.293,.293-.768,0-1.061s-.768-.293-1.061,0l-2.25,2.25c-.293,.293-.293,.768,0,1.061l2.25,2.25c.146,.146,.338,.22,.53,.22s.384-.073,.53-.22c.293-.293,.293-.768,0-1.061l-1.208-1.208c.059-.002,.118-.012,.177-.012,3.032,0,5.5,2.467,5.5,5.5,0,1.616-.706,3.143-1.938,4.191-.315,.269-.354,.742-.085,1.057,.148,.174,.359,.264,.571,.264,.172,0,.345-.059,.486-.179,1.567-1.333,2.466-3.277,2.466-5.333,0-3.76-2.983-6.829-6.704-6.985Z" fill="#F7F7F7"/>
|
||||
<g fill="currentColor">
|
||||
<path d="M9.03,12.22c-.293-.293-.768-.293-1.061,0s-.293,.768,0,1.061l1.208,1.208c-.059,.002-.118,.012-.178,.012-3.032,0-5.5-2.467-5.5-5.5,0-1.616,.706-3.143,1.938-4.191,.315-.269,.354-.742,.085-1.057s-.74-.353-1.058-.085c-1.567,1.333-2.466,3.277-2.466,5.333,0,3.76,2.983,6.829,6.704,6.985l-.735,.735c-.293,.293-.293,.768,0,1.061,.146,.146,.338,.22,.53,.22s.384-.073,.53-.22l2.25-2.25c.293-.293,.293-.768,0-1.061l-2.25-2.25Z" fill="currentColor"/>
|
||||
<path d="M9.296,2.015l.735-.735c.293-.293,.293-.768,0-1.061s-.768-.293-1.061,0l-2.25,2.25c-.293,.293-.293,.768,0,1.061l2.25,2.25c.146,.146,.338,.22,.53,.22s.384-.073,.53-.22c.293-.293,.293-.768,0-1.061l-1.208-1.208c.059-.002,.118-.012,.177-.012,3.032,0,5.5,2.467,5.5,5.5,0,1.616-.706,3.143-1.938,4.191-.315,.269-.354,.742-.085,1.057,.148,.174,.359,.264,.571,.264,.172,0,.345-.059,.486-.179,1.567-1.333,2.466-3.277,2.466-5.333,0-3.76-2.983-6.829-6.704-6.985Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
||||
Reload
|
||||
|
||||
@@ -41,7 +41,7 @@ const SettingsPage = ({ standalone }: SettingsPage) => {
|
||||
<button onClick={() => browser.runtime.sendMessage({ type: 'currentTab', info: 'OpenChangelog' })} className="absolute w-8 h-8 text-lg rounded-xl font-IconFamily top-1 right-1 bg-zinc-100 dark:bg-zinc-700"></button>
|
||||
</div>
|
||||
<Picker />
|
||||
<TabbedContainer tabs={tabs} />
|
||||
<TabbedContainer tabs={tabs} animations={false} />
|
||||
</div>
|
||||
</SettingsContextProvider>
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ const Shortcuts = memo(() => {
|
||||
const isValidTitle = useCallback((title: string) => title.trim() !== "", []);
|
||||
|
||||
const isValidURL = useCallback((url: string) => {
|
||||
const pattern = new RegExp("^(https?:\\/\\/)?[\\w.-]+[\\w.-]+(/[\\w.-]*)*$", "i");
|
||||
const pattern = new RegExp("^(https?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\-]+)*(?::\\d+)?(/[\\w\\-./]*)*$", "i");
|
||||
return pattern.test(url);
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ const Store = () => {
|
||||
>
|
||||
<motion.div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="w-full max-w-xl h-[95%] p-8 pt-5 bg-white rounded-t-2xl dark:bg-zinc-800 overflow-scroll"
|
||||
className="w-full max-w-xl h-[95%] p-4 bg-white rounded-t-2xl dark:bg-zinc-800 overflow-scroll"
|
||||
exit={{ y: "100vh" }}
|
||||
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
|
||||
variants={containerVariants}
|
||||
@@ -219,7 +219,7 @@ const Store = () => {
|
||||
<motion.h2 className="mb-4 text-2xl font-bold" variants={textVariants}>
|
||||
{displayTheme.name}
|
||||
</motion.h2>
|
||||
<motion.img src={displayTheme.coverImage} alt="Theme Cover" className="object-cover w-full mb-4 rounded-md" variants={textVariants} />
|
||||
<motion.img src={displayTheme.marqueeImage} alt="Theme Cover" className="object-cover w-full mb-4 rounded-md" variants={textVariants} />
|
||||
<motion.p className="mb-4 text-gray-700 dark:text-gray-300" variants={textVariants}>
|
||||
{displayTheme.description}
|
||||
</motion.p>
|
||||
@@ -259,16 +259,9 @@ const Store = () => {
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
{gridThemes.filter(theme => theme.id !== displayTheme.id).sort((a, b) => a.name.localeCompare(displayTheme.name) - b.name.localeCompare(displayTheme.name)).map((theme, index) => (
|
||||
<motion.div key={index} onClick={() => { setDisplayTheme(null); setDisplayTheme(theme); }} className='w-full cursor-pointer' variants={textVariants}>
|
||||
<div
|
||||
className="w-full overflow-clip rounded-xl transition-all duration-300 relative group/card flex flex-col hover:shadow-xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] h-auto"
|
||||
>
|
||||
<div className='absolute bottom-0 left-0 z-10 p-2'>
|
||||
<h6 className="text-xl font-bold text-neutral-600 dark:text-white">
|
||||
<div className="bg-gray-50 w-full transition-all hover:scale-105 duration-500 relative group group/card flex flex-col hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] h-auto rounded-xl overflow-clip border">
|
||||
<div className="absolute z-10 mb-1 text-xl font-bold text-white transition-all duration-500 group-hover:-translate-y-0.5 bottom-1 left-3">
|
||||
{theme.name}
|
||||
</h6>
|
||||
<p className="max-w-sm text-sm text-neutral-500 dark:text-neutral-200">
|
||||
{theme.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className='absolute bottom-0 z-0 w-full h-3/4 bg-gradient-to-t from-black/80 to-transparent' />
|
||||
<img src={theme.coverImage} alt="Theme Preview" className="object-cover w-full h-48" />
|
||||
@@ -304,52 +297,28 @@ const Store = () => {
|
||||
<div className="grid grid-cols-1 gap-4 py-12 mx-auto sm:grid-cols-2 lg:grid-cols-3">
|
||||
{filteredThemes.map((theme, index) => (
|
||||
<div onClick={() => setDisplayTheme(theme)} key={index} className='w-full cursor-pointer'>
|
||||
<div className="bg-gray-50 w-full transition-all duration-300 relative group/card flex flex-col hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] h-auto rounded-xl p-6 border">
|
||||
<div>
|
||||
|
||||
<div className="mb-1 text-xl font-bold text-neutral-600 dark:text-white">
|
||||
<div className="bg-gray-50 w-full transition-all hover:scale-105 duration-500 relative group group/card flex flex-col hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] h-auto rounded-xl overflow-clip border">
|
||||
<div className="absolute z-10 mb-1 text-xl font-bold text-white transition-all duration-500 group-hover:-translate-y-0.5 bottom-1 left-3">
|
||||
{theme.name}
|
||||
</div>
|
||||
<p className="max-w-sm mb-4 text-sm text-neutral-500 dark:text-neutral-300">
|
||||
{theme.description}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div className='absolute bottom-0 z-0 w-full h-3/4 bg-gradient-to-t from-black/80 to-transparent' />
|
||||
<div
|
||||
className='w-full'>
|
||||
<img src={theme.coverImage} alt="Theme Preview" className="object-cover w-full h-48 rounded-md" />
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
currentThemes.includes(theme.id) ?
|
||||
<button
|
||||
onClick={() => removeTheme(theme.id)}
|
||||
className="flex px-4 py-2 mt-4 ml-auto transition rounded-full dark:text-white bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600/50 hover:bg-zinc-200 focus:outline-none focus:ring-2 focus:ring-zinc-800 focus:ring-offset-2">
|
||||
{ installingThemes.includes(theme.id) ?
|
||||
<>
|
||||
<SpinnerIcon className="w-4 h-4 mr-2" />
|
||||
Removing...
|
||||
</> :
|
||||
<> Remove </>
|
||||
}
|
||||
</button> :
|
||||
<button
|
||||
onClick={() => downloadTheme(theme.id)}
|
||||
className="flex px-4 py-2 mt-4 ml-auto transition rounded-full dark:text-white bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600/50 hover:bg-zinc-200 focus:outline-none focus:ring-2 focus:ring-zinc-800 focus:ring-offset-2">
|
||||
{ installingThemes.includes(theme.id) ?
|
||||
<>
|
||||
<SpinnerIcon className="w-4 h-4 mr-2" />
|
||||
Installing...
|
||||
</> :
|
||||
<> Install </>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<a href="https://betterseqta.gitbook.io/betterseqta-docs" className='w-full cursor-pointer'>
|
||||
<div className="bg-zinc-50 h-48 w-full transition-all hover:scale-105 duration-500 relative justify-center items-center group group/card flex flex-col hover:shadow-2xl dark:hover:shadow-white/[0.1] hover:shadow-white/[0.8] dark:bg-zinc-800 dark:border-white/[0.1] rounded-xl overflow-clip border">
|
||||
<div className="text-2xl font-IconFamily">{'\uecb3'}</div>
|
||||
<div className="text-xl font-bold text-center transition-all duration-500 dark:text-white">
|
||||
Got a Theme Idea?
|
||||
<p className="text-lg font-light subtitle">Transform it into a stunning theme!</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{filteredThemes.length == 0 && !loading && (
|
||||
<div className="flex flex-col items-center justify-center w-full text-center h-96">
|
||||
|
||||
@@ -4,7 +4,7 @@ import ColorPicker from 'react-best-gradient-color-picker';
|
||||
import Accordion from '../components/Accordian';
|
||||
import Switch from '../components/Switch';
|
||||
import { sendThemeUpdate } from '../hooks/ThemeManagment';
|
||||
import { PlusIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import { MoonIcon, PlusIcon, SunIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { CustomTheme, CustomThemeBase64 } from '../types/CustomThemes';
|
||||
import browser from 'webextension-polyfill';
|
||||
@@ -23,6 +23,7 @@ function ThemeCreator() {
|
||||
coverImage: null,
|
||||
isEditable: true,
|
||||
hideThemeName: false,
|
||||
forceDark: undefined,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -159,6 +160,12 @@ function ThemeCreator() {
|
||||
<div className='w-full min-h-[100vh] bg-zinc-100 dark:bg-zinc-800 dark:text-white transition duration-30'>
|
||||
<div className='flex flex-col p-2'>
|
||||
<h1 className='text-xl font-semibold'>Theme Creator</h1>
|
||||
<a href="https://betterseqta.gitbook.io/betterseqta-docs" target="_blank" className='text-sm font-light text-zinc-500 dark:text-zinc-400'>
|
||||
<span className="no-underline font-IconFamily pr-0.5">{'\ueb44'}</span>
|
||||
<span className="underline">
|
||||
Need help? Check out the docs!
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<Divider />
|
||||
|
||||
@@ -244,6 +251,29 @@ function ThemeCreator() {
|
||||
|
||||
<Divider /> */}
|
||||
|
||||
<div className='flex items-center justify-between'>
|
||||
<div>
|
||||
<div className='pr-2 text-sm font-semibold'>Force Theme</div>
|
||||
<div className='pr-2 text-[11px]'>Force users to use either dark or light mode</div>
|
||||
</div>
|
||||
|
||||
<Switch state={theme.forceDark == undefined ? false : true} onChange={value => setTheme({ ...theme, forceDark: value ? false : undefined })} />
|
||||
</div>
|
||||
|
||||
{ theme.forceDark != undefined &&
|
||||
<div className='flex items-center justify-between pt-4'>
|
||||
<div>
|
||||
<div className='pr-2 text-sm font-semibold'>Force {theme.forceDark ? 'Dark' : 'Light'} Mode</div>
|
||||
<div className='pr-2 text-[11px]'>Force users to use {theme.forceDark ? 'dark' : 'light'} mode</div>
|
||||
</div>
|
||||
<button className='flex items-center justify-center p-2 transition rounded-lg bg-zinc-100 dark:bg-zinc-700' onClick={() => setTheme({ ...theme, forceDark: !theme.forceDark })}>
|
||||
{theme.forceDark ? <MoonIcon className='w-6 h-6' /> : <SunIcon className='w-6 h-6' />}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<Divider />
|
||||
|
||||
<Accordion defaultOpened title='Default Theme Colour'>
|
||||
<div className='p-2 mt-2 bg-white rounded-lg w-fit dark:bg-zinc-900'>
|
||||
<ColorPicker
|
||||
|
||||
@@ -12,6 +12,7 @@ export type CustomTheme = {
|
||||
hideThemeName: boolean;
|
||||
webURL?: string;
|
||||
selectedColor?: string;
|
||||
forceDark?: boolean;
|
||||
}
|
||||
|
||||
export type DownloadedTheme = CustomTheme & {
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface Tab {
|
||||
}
|
||||
export interface TabbedContainerProps {
|
||||
tabs: Tab[];
|
||||
animations?: boolean;
|
||||
}
|
||||
declare const TabbedContainer: React.FC<TabbedContainerProps>;
|
||||
export default TabbedContainer;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -4,6 +4,7 @@ import { appendBackgroundToUI } from "./ImageBackgrounds";
|
||||
import stringToHTML from "../utils/stringToHTML";
|
||||
import { settingsState } from "../utils/listeners/SettingsState";
|
||||
import { updateAllColors } from "./colors/Manager";
|
||||
import { delay } from "../utils/delay";
|
||||
|
||||
export async function AddBetterSEQTAElements() {
|
||||
if (settingsState.onoff) {
|
||||
@@ -220,8 +221,8 @@ async function createSettingsButton() {
|
||||
ContentDiv!.append(SettingsButton.firstChild!);
|
||||
}
|
||||
|
||||
function GetLightDarkModeString(darkMode: boolean) {
|
||||
if (darkMode) {
|
||||
function GetLightDarkModeString() {
|
||||
if (settingsState.DarkMode) {
|
||||
return 'Switch to light theme'
|
||||
} else {
|
||||
return 'Switch to dark theme'
|
||||
@@ -229,7 +230,7 @@ function GetLightDarkModeString(darkMode: boolean) {
|
||||
}
|
||||
|
||||
async function addDarkLightToggle() {
|
||||
const tooltipString = GetLightDarkModeString(settingsState.DarkMode);
|
||||
const tooltipString = GetLightDarkModeString();
|
||||
const svgContent = settingsState.DarkMode ?
|
||||
/* html */`<defs><clipPath id="__lottie_element_80"><rect width="24" height="24" x="0" y="0"></rect></clipPath></defs><g clip-path="url(#__lottie_element_80)"><g style="display: block;" transform="matrix(1,0,0,1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,-4 C-2.2100000381469727,-4 -4,-2.2100000381469727 -4,0 C-4,2.2100000381469727 -2.2100000381469727,4 0,4 C2.2100000381469727,4 4,2.2100000381469727 4,0 C4,-2.2100000381469727 2.2100000381469727,-4 0,-4z"></path></g></g><g style="display: block;" transform="matrix(1,0,0,1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,6 C-3.309999942779541,6 -6,3.309999942779541 -6,0 C-6,-3.309999942779541 -3.309999942779541,-6 0,-6 C3.309999942779541,-6 6,-3.309999942779541 6,0 C6,3.309999942779541 3.309999942779541,6 0,6z M8,-3.309999942779541 C8,-3.309999942779541 8,-8 8,-8 C8,-8 3.309999942779541,-8 3.309999942779541,-8 C3.309999942779541,-8 0,-11.3100004196167 0,-11.3100004196167 C0,-11.3100004196167 -3.309999942779541,-8 -3.309999942779541,-8 C-3.309999942779541,-8 -8,-8 -8,-8 C-8,-8 -8,-3.309999942779541 -8,-3.309999942779541 C-8,-3.309999942779541 -11.3100004196167,0 -11.3100004196167,0 C-11.3100004196167,0 -8,3.309999942779541 -8,3.309999942779541 C-8,3.309999942779541 -8,8 -8,8 C-8,8 -3.309999942779541,8 -3.309999942779541,8 C-3.309999942779541,8 0,11.3100004196167 0,11.3100004196167 C0,11.3100004196167 3.309999942779541,8 3.309999942779541,8 C3.309999942779541,8 8,8 8,8 C8,8 8,3.309999942779541 8,3.309999942779541 C8,3.309999942779541 11.3100004196167,0 11.3100004196167,0 C11.3100004196167,0 8,-3.309999942779541 8,-3.309999942779541z"></path></g></g></g>` :
|
||||
/* html */`<defs><clipPath id="__lottie_element_263"><rect width="24" height="24" x="0" y="0"></rect></clipPath></defs><g clip-path="url(#__lottie_element_263)"><g style="display: block;" transform="matrix(1.5,0,0,1.5,7,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,-4 C-2.2100000381469727,-4 -1.2920000553131104,-2.2100000381469727 -1.2920000553131104,0 C-1.2920000553131104,2.2100000381469727 -2.2100000381469727,4 0,4 C2.2100000381469727,4 4,2.2100000381469727 4,0 C4,-2.2100000381469727 2.2100000381469727,-4 0,-4z"></path></g></g><g style="display: block;" transform="matrix(-1,0,0,-1,12,12)" opacity="1"><g opacity="1" transform="matrix(1,0,0,1,0,0)"><path fill-opacity="1" d=" M0,6 C-3.309999942779541,6 -6,3.309999942779541 -6,0 C-6,-3.309999942779541 -3.309999942779541,-6 0,-6 C3.309999942779541,-6 6,-3.309999942779541 6,0 C6,3.309999942779541 3.309999942779541,6 0,6z M8,-3.309999942779541 C8,-3.309999942779541 8,-8 8,-8 C8,-8 3.309999942779541,-8 3.309999942779541,-8 C3.309999942779541,-8 0,-11.3100004196167 0,-11.3100004196167 C0,-11.3100004196167 -3.309999942779541,-8 -3.309999942779541,-8 C-3.309999942779541,-8 -8,-8 -8,-8 C-8,-8 -8,-3.309999942779541 -8,-3.309999942779541 C-8,-3.309999942779541 -11.3100004196167,0 -11.3100004196167,0 C-11.3100004196167,0 -8,3.309999942779541 -8,3.309999942779541 C-8,3.309999942779541 -8,8 -8,8 C-8,8 -3.309999942779541,8 -3.309999942779541,8 C-3.309999942779541,8 0,11.3100004196167 0,11.3100004196167 C0,11.3100004196167 3.309999942779541,8 3.309999942779541,8 C3.309999942779541,8 8,8 8,8 C8,8 8,3.309999942779541 8,3.309999942779541 C8,3.309999942779541 11.3100004196167,0 11.3100004196167,0 C11.3100004196167,0 8,-3.309999942779541 8,-3.309999942779541z"></path></g></g></g>`;
|
||||
@@ -247,12 +248,23 @@ async function addDarkLightToggle() {
|
||||
updateAllColors();
|
||||
|
||||
document.getElementById('LightDarkModeButton')!.addEventListener('click', async () => {
|
||||
const darklightText = document.getElementById('darklighttooliptext');
|
||||
|
||||
if (settingsState.originalDarkMode != undefined && settingsState.selectedTheme) {
|
||||
darklightText!.innerText = 'Locked by current theme';
|
||||
|
||||
await delay(1000)
|
||||
|
||||
darklightText!.innerText = GetLightDarkModeString();
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
settingsState.DarkMode = !settingsState.DarkMode;
|
||||
|
||||
updateAllColors();
|
||||
|
||||
const darklightText = document.getElementById('darklighttooliptext');
|
||||
darklightText!.innerText = GetLightDarkModeString(!settingsState.DarkMode);
|
||||
darklightText!.innerText = GetLightDarkModeString();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import Browser from "webextension-polyfill";
|
||||
|
||||
/**
|
||||
* Injects a YouTube iframe into the specified element.
|
||||
*
|
||||
* @param videoId - The YouTube video ID to embed.
|
||||
* @param playlistId - The YouTube playlist ID to allow embed to loop.
|
||||
* @param mountElement - The element to mount the iframe to.
|
||||
* @param hideControls - Whether to hide the YouTube player controls.
|
||||
* @param mute - Whether to mute the video.
|
||||
* @param width - The width of the iframe.
|
||||
* @param height - The height of the iframe.
|
||||
*/
|
||||
export function injectYouTubeVideo(videoId: string, playlistId: string, mountElement: HTMLElement, hideControls: boolean, mute: boolean, width: string, height: string): void {
|
||||
const controlsParam = hideControls ? 'controls=0' : 'controls=1';
|
||||
const autoplayParam = 'autoplay=1';
|
||||
const muteParam = mute ? 'mute=1' : 'mute=0';
|
||||
const listParams = playlistId ? `list=${playlistId}&` : '';
|
||||
|
||||
const iframeSrc = `https://www.youtube.com/embed/${videoId}?${listParams}${autoplayParam}&${controlsParam}&${muteParam}&loop=1`;
|
||||
const iframe = document.createElement('iframe');
|
||||
|
||||
iframe.width = width;
|
||||
iframe.height = height;
|
||||
iframe.src = iframeSrc;
|
||||
iframe.frameBorder = '0';
|
||||
iframe.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';
|
||||
iframe.allowFullscreen = true;
|
||||
|
||||
iframe.onload = () => {
|
||||
Browser.runtime.sendMessage({ type: 'youtubeIframe', hideControls });
|
||||
};
|
||||
|
||||
mountElement.innerHTML = ''; // Clear any existing content
|
||||
mountElement.appendChild(iframe);
|
||||
|
||||
/* if (hideControls) {
|
||||
applyCustomStylesToIframe(iframe);
|
||||
} */
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to inject CSS styles into the iframe.
|
||||
*
|
||||
* @param hideControls - Whether to hide the YouTube player controls.
|
||||
*/
|
||||
export function applyYoutubeStyles(hideControls: boolean) {
|
||||
if (window.location == window.parent.location) return;
|
||||
if (!window.location.href.includes('youtube.com/embed/')) return;
|
||||
|
||||
if (hideControls) {
|
||||
const hideControlsCss = `
|
||||
.ytp-gradient-top,
|
||||
.ytp-chrome-bottom,
|
||||
.ytp-chrome-top,
|
||||
.ytp-chrome-top-buttons,
|
||||
.ytp-pause-overlay,
|
||||
.ytp-watermark {
|
||||
display: none !important;
|
||||
}
|
||||
`;
|
||||
const hideControlsStyle = document.createElement('style');
|
||||
hideControlsStyle.textContent = hideControlsCss;
|
||||
document.head.appendChild(hideControlsStyle);
|
||||
|
||||
const f =() => {
|
||||
const btn = document.querySelector('.ytp-ad-skip-button') as HTMLButtonElement | null;
|
||||
const adText = document.querySelector('.ytp-ad-text');
|
||||
const v = document.querySelector('video')!;
|
||||
if(adText){
|
||||
v.currentTime = v.duration
|
||||
}
|
||||
if(btn){
|
||||
v.currentTime = v.duration
|
||||
btn.click();
|
||||
}
|
||||
}
|
||||
setInterval(f, 100);
|
||||
}
|
||||
}
|
||||
@@ -64,9 +64,9 @@ export function updateAllColors() {
|
||||
}
|
||||
|
||||
if (settingsState.DarkMode) {
|
||||
element.contentDocument?.body.classList.add('dark');
|
||||
element.contentDocument?.documentElement.classList.add('dark');
|
||||
} else {
|
||||
element.contentDocument?.body.classList.remove('dark');
|
||||
element.contentDocument?.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,14 @@ import { settingsState } from '../../utils/listeners/SettingsState';
|
||||
export const UpdateThemePreview = async (updatedTheme: CustomThemeBase64 /* Omit<CustomTheme, 'CustomImages'> & { CustomImages: Omit<CustomImage, 'blob'>[] } */) => {
|
||||
const { CustomCSS, CustomImages, defaultColour } = updatedTheme;
|
||||
|
||||
if (updatedTheme.forceDark != undefined) {
|
||||
if (updatedTheme.forceDark) {
|
||||
settingsState.DarkMode = true;
|
||||
} else {
|
||||
settingsState.DarkMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update image data
|
||||
const currentImageIds = Object.keys(imageData);
|
||||
const updatedImageIds = CustomImages.map((image) => image.id);
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { CustomImage, CustomTheme } from '../../../interface/types/CustomThemes';
|
||||
import { settingsState } from '../../utils/listeners/SettingsState';
|
||||
import { applyCustomCSS } from './Themes';
|
||||
|
||||
|
||||
export const applyTheme = async (theme: CustomTheme) => {
|
||||
export const applyTheme = async (theme: CustomTheme, reEnable?: boolean) => {
|
||||
let CustomCSS = '';
|
||||
let CustomImages: CustomImage[] = [];
|
||||
|
||||
if (theme?.CustomCSS) CustomCSS = theme.CustomCSS;
|
||||
if (theme?.CustomImages) CustomImages = theme.CustomImages;
|
||||
if (theme?.forceDark != undefined) {
|
||||
if (!reEnable) settingsState.originalDarkMode = settingsState.DarkMode
|
||||
|
||||
settingsState.DarkMode = theme.forceDark
|
||||
}
|
||||
|
||||
// Apply custom CSS
|
||||
applyCustomCSS(CustomCSS);
|
||||
|
||||
@@ -8,7 +8,7 @@ export const enableCurrentTheme = async () => {
|
||||
if (settingsState.selectedTheme) {
|
||||
const theme = await localforage.getItem(settingsState.selectedTheme) as CustomTheme;
|
||||
if (theme) {
|
||||
await applyTheme(theme);
|
||||
await applyTheme(theme, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,6 +20,11 @@ export const removeTheme = async (theme: CustomTheme) => {
|
||||
settingsState.selectedColor = settingsState.originalSelectedColor
|
||||
}
|
||||
|
||||
if (settingsState.originalDarkMode !== undefined) {
|
||||
settingsState.DarkMode = settingsState.originalDarkMode
|
||||
settingsState.originalDarkMode = undefined
|
||||
}
|
||||
|
||||
// Remove custom images
|
||||
const customImageVariables = theme.CustomImages.map((image) => image.variableName);
|
||||
customImageVariables.forEach((variableName) => {
|
||||
|
||||
@@ -21,7 +21,6 @@ class StorageManager {
|
||||
return Reflect.get(target.data, prop);
|
||||
},
|
||||
set: (target, prop: keyof SettingsState, value) => {
|
||||
console.log(target)
|
||||
Reflect.set(target.data, prop, value);
|
||||
target.saveToStorage();
|
||||
return true;
|
||||
|
||||
@@ -37,6 +37,7 @@ export interface SettingsState {
|
||||
animations: boolean;
|
||||
defaultPage: string;
|
||||
devMode?: boolean;
|
||||
originalDarkMode?: boolean;
|
||||
}
|
||||
|
||||
interface ToggleItem {
|
||||
|
||||
Reference in New Issue
Block a user