Compare commits

...

32 Commits

Author SHA1 Message Date
SethBurkart123 5f561f516c feat: rely on package.json for version and description 2025-02-14 17:11:33 +11:00
SethBurkart123 395ec3291e fix: custom sidebar layouts not applying on page load #205 2025-02-14 17:07:45 +11:00
SethBurkart123 96b17c7eeb feat: update changelog 2025-02-14 16:37:04 +11:00
SethBurkart123 fad50e6eba feat: change order of zoom timetable buttons 2025-02-14 16:36:17 +11:00
Alphons Joseph f74ad97c0a Merge branch 'main' of https://github.com/BetterSEQTA/BetterSEQTA-Plus 2025-02-11 19:42:46 +08:00
Alphons Joseph 7f4e6cf5ec create function to collapse sidebar 2025-02-11 19:42:41 +08:00
SethBurkart123 677f17c418 fix: colour of timetable buttons in dark mode 2025-02-11 22:06:01 +11:00
Alphons Joseph e58584a55a Merge branch 'main' of https://github.com/BetterSEQTA/BetterSEQTA-Plus 2025-02-11 19:04:04 +08:00
Alphons Joseph 59444dc904 patch timetable not being centred upon zoom 2025-02-11 19:03:47 +08:00
SethBurkart123 178c4fdef4 feat: update changelog 2025-02-11 22:02:01 +11:00
SethBurkart123 cdaaceade7 fix: vite hanging after completing builds 2025-02-11 21:53:37 +11:00
SethBurkart123 d65bfa8c46 feat: add zoom scaling to timetable page #202 2025-02-11 21:40:57 +11:00
SethBurkart123 694d11477d fix: timetable quickbar arrow when placed above recieving incorrect colour 2025-02-11 19:23:11 +11:00
SethBurkart123 61e1bcdae9 fix: timetable clipped at 4pm 2025-02-11 19:05:25 +11:00
SethBurkart123 23a09004d8 feat: keep theme enabled after editing 2025-02-11 19:03:19 +11:00
SethBurkart123 3ce075cd47 fix: theme disabling when opening editor #204 2025-02-11 18:59:57 +11:00
SethBurkart123 92a51daf36 fix: codemirror failing to run 2025-02-11 18:55:57 +11:00
SethBurkart123 479b2878a9 fix: builds failing in some cases + extension failing to load due to vite legacy api 2025-02-11 18:19:07 +11:00
SethBurkart123 18ffa1b47d feat: clean up eventmanager class 2025-02-11 18:03:48 +11:00
Alphons Joseph 6098cf9608 FR #203 2025-02-10 19:49:43 +08:00
Alphons Joseph 5fde2a3660 fix broken build process 2025-02-06 21:17:26 +08:00
SethBurkart123 e4d5f7fd3f chore: remove unused dependencies 2025-02-06 17:45:44 +11:00
Alphons Joseph 31b069056d General updates and 2025-02-05 19:43:51 +08:00
SethBurkart123 3e5ebe8ef4 feat: change theme store to use marquee image for everything 2025-02-05 17:13:36 +11:00
SethBurkart123 338292ac15 style: remove gradient colours on toolbar buttons 2025-02-05 11:20:54 +11:00
SethBurkart123 187c484901 feat: add parsing for letter grades #191 2025-02-05 11:08:26 +11:00
SethBurkart123 24d0616110 feat: clean up compose buttons 2025-02-04 09:58:46 +11:00
SethBurkart123 260ac4aaea style: improved compose UI 2025-02-04 09:48:06 +11:00
SethBurkart123 4311a8fe76 chore: minor css cleanup 2025-02-04 09:09:20 +11:00
SethBurkart123 251e09941b chore: remove unused notices network request and add Svelte module declaration 2025-02-04 09:05:54 +11:00
Seth Burkart bb1541ab2d Merge pull request #195 from ar-cyber/patch-18
fix: anything before this update is now unusable
2025-02-03 16:41:31 +11:00
Andrew R 1c6ec3ee91 fix: anything before this update is now unusable
As a major bug was found impacting some functionality, it is reasonable to push this to make every other version deprecated.
2025-02-03 12:35:19 +10:30
20 changed files with 473 additions and 133 deletions
+2 -3
View File
@@ -6,11 +6,10 @@ Below here is the supported versions of BetterSEQTA+. Anything older than this i
| Version | Supported | | Version | Supported |
| ------- | ------------------ | | ------- | ------------------ |
| 3.4.0 | :white_check_mark: | | 3.4.3 | |
| <= 3.3 | :x: | | < 3.4.3 | :x: |
`*` May not work on other devices. `*` May not work on other devices.
## Reporting a Vulnerability ## Reporting a Vulnerability
If you find vulnerabilities, REPORT IT IMMEDIATELY. Make an issue and use the template provided for vulnerabilities. If you find vulnerabilities, REPORT IT IMMEDIATELY. Make an issue and use the template provided for vulnerabilities.
+25
View File
@@ -0,0 +1,25 @@
// ref: https://stackoverflow.com/a/76920975
import type { Plugin } from 'vite';
export default function ClosePlugin(): Plugin {
return {
name: 'ClosePlugin', // required, will show up in warnings and errors
// use this to catch errors when building
buildEnd(error) {
if(error) {
console.error('Error bundling')
console.error(error)
process.exit(1)
} else {
console.log('Build ended')
}
},
// use this to catch the end of a build without errors
closeBundle() {
console.log('Bundle closed')
process.exit(0)
},
}
}
+17 -2
View File
@@ -25,17 +25,32 @@ export function updateManifestPlugin(): PluginOption {
console.log('** updated **'); console.log('** updated **');
} }
// Implement retry mechanism for file watching
const watchWithRetry = () => {
if (!fs.existsSync(manifestPath)) {
console.log('Manifest not found, retrying in 1 second...');
setTimeout(watchWithRetry, 1000);
return;
}
fs.watchFile(manifestPath, () => { fs.watchFile(manifestPath, () => {
console.log('** watchFile ** '); console.log('** watchFile **');
try {
const manifestContents = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); const manifestContents = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
if (manifestContents.web_accessible_resources.some((resource: any) => resource.use_dynamic_url)) { if (manifestContents.web_accessible_resources?.some((resource: any) => resource.use_dynamic_url)) {
const updated = forceDisableUseDynamicUrl(); const updated = forceDisableUseDynamicUrl();
if (updated) { if (updated) {
server.ws.send({ type: 'full-reload' }); server.ws.send({ type: 'full-reload' });
console.log('** updated **'); console.log('** updated **');
} }
} }
} catch (error) {
console.log('Error reading manifest, will retry on next change:', error.message);
}
}); });
};
watchWithRetry();
}); });
}, },
+19 -25
View File
@@ -1,8 +1,8 @@
{ {
"name": "betterseqtaplus", "name": "betterseqtaplus",
"version": "3.4.3", "version": "3.4.4",
"type": "module", "type": "module",
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development, while incorporating a plethora of new and improved features!", "description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development add add heaps more features!",
"browserslist": "> 0.5%, last 2 versions, not dead", "browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": { "scripts": {
"dev": "cross-env MODE=chrome vite dev", "dev": "cross-env MODE=chrome vite dev",
@@ -32,45 +32,41 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@babel/plugin-transform-runtime": "^7.25.9", "@babel/plugin-transform-runtime": "^7.25.9",
"@babel/runtime": "^7.26.0", "@babel/runtime": "^7.26.7",
"@bedframe/cli": "^0.0.85",
"@crxjs/vite-plugin": "2.0.0-beta.25", "@crxjs/vite-plugin": "2.0.0-beta.25",
"@types/mime-types": "^2.1.4", "@types/mime-types": "^2.1.4",
"@vitejs/plugin-react-swc": "^3.7.0", "@vitejs/plugin-react-swc": "^3.7.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.57.0", "eslint": "^8.57.1",
"glob": "^11.0.0", "glob": "^11.0.1",
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"prettier": "^3.3.3", "prettier": "^3.4.2",
"process": "^0.11.10", "process": "^0.11.10",
"sass": "^1.78.0", "sass": "^1.83.4",
"sass-loader": "^13.3.3", "sass-loader": "^13.3.3",
"semver": "^7.6.3", "semver": "^7.7.1",
"url": "^0.11.4" "url": "^0.11.4"
}, },
"dependencies": { "dependencies": {
"@bedframe/cli": "^0.0.85",
"@codemirror/lang-css": "^6.3.0", "@codemirror/lang-css": "^6.3.0",
"@codemirror/lang-less": "^6.0.2",
"@codemirror/theme-one-dark": "^6.1.2",
"@sveltejs/vite-plugin-svelte": "^4.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0",
"@tailwindcss/forms": "^0.5.9", "@tailwindcss/forms": "^0.5.9",
"@tsconfig/svelte": "^5.0.4", "@tsconfig/svelte": "^5.0.4",
"@types/chrome": "^0.0.270", "@types/chrome": "^0.0.270",
"@types/color": "^3.0.6", "@types/color": "^3.0.6",
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.2.0",
"@types/lodash": "^4.17.7", "@types/lodash": "^4.17.15",
"@types/node": "^20.16.5", "@types/node": "^20.17.17",
"@types/react": "17", "@types/react": "^17.0.83",
"@types/react-dom": "17", "@types/react-dom": "^17.0.26",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@types/webextension-polyfill": "^0.10.7", "@types/webextension-polyfill": "^0.10.7",
"@uiw/codemirror-extensions-color": "^4.23.3", "@uiw/codemirror-extensions-color": "^4.23.8",
"@uiw/codemirror-theme-github": "^4.23.3", "@uiw/codemirror-theme-github": "^4.23.8",
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"caniuse-lite": "^1.0.30001696",
"classnames": "^2.5.1",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"color": "^4.2.3", "color": "^4.2.3",
"dompurify": "^3.1.6", "dompurify": "^3.1.6",
@@ -78,13 +74,11 @@
"embla-carousel-svelte": "^8.3.1", "embla-carousel-svelte": "^8.3.1",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"idb": "^8.0.0", "idb": "^8.0.0",
"kolorist": "^1.8.0",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"million": "^3.1.11", "million": "^3.1.11",
"motion": "^11.12.0", "motion": "^11.12.0",
"postcss": "^8.4.45", "postcss": "^8.4.45",
"publish-browser-extension": "^2.2.1",
"react": "17", "react": "17",
"react-best-gradient-color-picker": "^3.0.10", "react-best-gradient-color-picker": "^3.0.10",
"react-dom": "17", "react-dom": "17",
@@ -93,7 +87,7 @@
"tailwindcss": "^3.4.11", "tailwindcss": "^3.4.11",
"typescript": "^5.6.2", "typescript": "^5.6.2",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"vite": "^5.4.4", "vite": "^5.4.14",
"webextension-polyfill": "^0.10.0" "webextension-polyfill": "^0.10.0"
} }
} }
+211 -20
View File
@@ -165,6 +165,19 @@ export function OpenWhatsNewPopup() {
/* html */ ` /* html */ `
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: scroll;"> <div class="whatsnewTextContainer" style="height: 50%;overflow-y: scroll;">
<h1>3.4.4 - Bug Fixes and Improvements</h1>
<li>Added vertical zoom to the timetable</li>
<li>Removed broken gradients on the backgrounds of certain buttons</li>
<li>Fixed timetable quickbar arrow receiving the wrong colour</li>
<li>Auto-applied selected theme after saving in theme creator</li>
<li>Fixed a bug where timetable was clipped at certain times</li>
<li>Fixed custom sidebar layouts not applying on page load</li>
<li>Improved spacing of the message editor buttons</li>
<li>Added HEX colour input to the theme creator</li>
<li>Fixed theme application in the creator</li>
<li>Performance improvements</li>
<li>Other minor bug fixes</li>
<h1>3.4.3 - Minor Bug Fixes</h1> <h1>3.4.3 - Minor Bug Fixes</h1>
<li>Fixed a bug where timetable colours couldn't be changed</li> <li>Fixed a bug where timetable colours couldn't be changed</li>
<li>Other minor bug fixes</li> <li>Other minor bug fixes</li>
@@ -389,6 +402,30 @@ export function OpenWhatsNewPopup() {
}) })
} }
export function hideSideBar() {
const sidebar = document.getElementById('menu') // The sidebar element to be closed
const main = document.getElementById('main') // The main content element that must be resized to fill the page
const currentMenuWidth = window.getComputedStyle(sidebar!).width // Get the styles of the different elements
const currentContentPosition = window.getComputedStyle(main!).position
if (currentMenuWidth != "0") { // Actually modify it to collapse the sidebar
sidebar!.style.width = "0";
} else {
sidebar!.style.width = "100%";
}
if (currentContentPosition != "relative") {
main!.style.position = 'relative';
} else {
main!.style.position = 'absolute';
}
}
export function OpenAboutPage() { export function OpenAboutPage() {
const background = document.createElement('div') const background = document.createElement('div')
background.id = 'whatsnewbk' background.id = 'whatsnewbk'
@@ -742,6 +779,7 @@ async function LoadPageElements(): Promise<void> {
className: 'notice', className: 'notice',
}, handleNotices); }, handleNotices);
if (settingsState.assessmentsAverage) { if (settingsState.assessmentsAverage) {
eventManager.register('assessmentsAdded', { eventManager.register('assessmentsAdded', {
elementType: 'div', elementType: 'div',
@@ -752,6 +790,120 @@ async function LoadPageElements(): Promise<void> {
await handleSublink(sublink); await handleSublink(sublink);
} }
function handleTimetableZoom(): void {
console.log('Initializing timetable zoom controls');
// Lazy initialize state variables only when function is first called
let timetableZoomLevel = 1;
let baseContainerHeight: number | null = null;
const originalEntryPositions = new Map<Element, { topRatio: number; heightRatio: number }>();
// Create zoom controls
const zoomControls = document.createElement('div');
zoomControls.className = 'timetable-zoom-controls';
const zoomIn = document.createElement('button');
zoomIn.className = 'uiButton timetable-zoom iconFamily';
zoomIn.innerHTML = '&#xed93;'; // Using unicode for zoom in icon
const zoomOut = document.createElement('button');
zoomOut.className = 'uiButton timetable-zoom iconFamily';
zoomOut.innerHTML = '&#xed94;'; // Using unicode for zoom out icon
zoomControls.appendChild(zoomOut);
zoomControls.appendChild(zoomIn);
const toolbar = document.getElementById('toolbar');
toolbar?.appendChild(zoomControls);
const initializePositions = () => {
// Get the base container height from the first TD
const firstDayColumn = document.querySelector('.dailycal .content .days td') as HTMLElement;
if (!firstDayColumn) return false;
baseContainerHeight = parseInt(firstDayColumn.style.height) || firstDayColumn.offsetHeight;
// Store original ratios
const entries = document.querySelectorAll('.entriesWrapper .entry');
entries.forEach((entry: Element) => {
const entryEl = entry as HTMLElement;
// Calculate ratios relative to detected base height
if (baseContainerHeight === null) return;
const topRatio = parseInt(entryEl.style.top) / baseContainerHeight;
const heightRatio = parseInt(entryEl.style.height) / baseContainerHeight;
originalEntryPositions.set(entry, { topRatio, heightRatio });
});
return true;
};
const updateZoom = () => {
// Initialize positions if not already done
if (baseContainerHeight === null && !initializePositions()) {
console.error('Failed to initialize positions');
return;
}
console.debug(`Updating zoom level to: ${timetableZoomLevel}`);
// Calculate new container height
if (baseContainerHeight === null) return;
const newContainerHeight = baseContainerHeight * timetableZoomLevel;
// Update all day columns (TDs)
const dayColumns = document.querySelectorAll('.dailycal .content .days td');
dayColumns.forEach((td: Element) => {
(td as HTMLElement).style.height = `${newContainerHeight}px`;
});
// Update all entries using stored ratios
const entries = document.querySelectorAll('.entriesWrapper .entry');
entries.forEach((entry: Element) => {
const entryEl = entry as HTMLElement;
const originalRatios = originalEntryPositions.get(entry);
if (originalRatios) {
// Calculate new positions from original ratios
const newTop = originalRatios.topRatio * newContainerHeight;
const newHeight = originalRatios.heightRatio * newContainerHeight;
// Apply new values
entryEl.style.top = `${Math.round(newTop)}px`;
entryEl.style.height = `${Math.round(newHeight)}px`;
}
});
// Update time column to match
const timeColumn = document.querySelector('.times');
if (timeColumn) {
const times = timeColumn.querySelectorAll('.time');
const timeHeight = newContainerHeight / times.length;
times.forEach((time: Element) => {
(time as HTMLElement).style.height = `${timeHeight}px`;
});
}
entries[Math.round((entries.length - 1) / 2)].scrollIntoView({ behavior: 'instant', block: 'center' });
};
zoomIn.addEventListener('click', () => {
if (timetableZoomLevel < 2) {
timetableZoomLevel += 0.2;
updateZoom();
}
});
zoomOut.addEventListener('click', () => {
if (timetableZoomLevel > 0.6) {
timetableZoomLevel -= 0.2;
updateZoom();
}
});
}
async function handleNotices(node: Element): Promise<void> { async function handleNotices(node: Element): Promise<void> {
if (!(node instanceof HTMLElement)) return; if (!(node instanceof HTMLElement)) return;
if (!settingsState.animations) return; if (!settingsState.animations) return;
@@ -802,15 +954,25 @@ async function handleSublink(sublink: string | undefined): Promise<void> {
} }
async function handleTimetable(): Promise<void> { async function handleTimetable(): Promise<void> {
await waitForElm('.time', true, 10) await waitForElm('.time', true, 10);
// Store original heights when timetable loads
const lessons = document.querySelectorAll('.dailycal .lesson');
lessons.forEach((lesson: Element) => {
const lessonEl = lesson as HTMLElement;
lessonEl.setAttribute('data-original-height', lessonEl.offsetHeight.toString());
});
// Existing time format code
if (settingsState.timeFormat == '12') { if (settingsState.timeFormat == '12') {
const times = document.querySelectorAll('.timetablepage .times .time') const times = document.querySelectorAll('.timetablepage .times .time');
for (const time of times) { for (const time of times) {
if (!time.textContent) continue if (!time.textContent) continue;
time.textContent = convertTo12HourFormat(time.textContent, true) time.textContent = convertTo12HourFormat(time.textContent, true);
} }
} }
handleTimetableZoom();
} }
async function handleNewsPage(): Promise<void> { async function handleNewsPage(): Promise<void> {
@@ -2272,7 +2434,6 @@ export async function loadHomePage() {
assessmentsPromise, assessmentsPromise,
classesPromise, classesPromise,
prefsPromise, prefsPromise,
noticesPromise
] = [ ] = [
// Timetable data // Timetable data
fetch(`${location.origin}/seqta/student/load/timetable?`, { fetch(`${location.origin}/seqta/student/load/timetable?`, {
@@ -2296,23 +2457,15 @@ export async function loadHomePage() {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ asArray: true, request: 'userPrefs' }) body: JSON.stringify({ asArray: true, request: 'userPrefs' })
}).then(res => res.json()),
// Notices data
fetch(`${location.origin}/seqta/student/load/notices?`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ date: TodayFormatted })
}).then(res => res.json()) }).then(res => res.json())
] ]
// Process all data in parallel // Process all data in parallel
const [timetableData, assessments, classes, prefs, notices] = await Promise.all([ const [timetableData, assessments, classes, prefs] = await Promise.all([
timetablePromise, timetablePromise,
assessmentsPromise, assessmentsPromise,
classesPromise, classesPromise,
prefsPromise, prefsPromise
noticesPromise
]) ])
// Process timetable data // Process timetable data
@@ -2640,9 +2793,8 @@ export async function SendNewsPage() {
async function CheckForMenuList() { async function CheckForMenuList() {
try { try {
if (document.getElementById('menu')?.firstChild) { await waitForElm('#menu > ul');
ObserveMenuItemPosition() ObserveMenuItemPosition();
}
} catch (error) { } catch (error) {
return; return;
} }
@@ -2746,6 +2898,44 @@ async function handleAssessments(node: Element): Promise<void> {
const assessmentsWrapper = await waitForElm('#main > .assessmentsWrapper .assessments .AssessmentItem__AssessmentItem___2EZ95', true, 50); const assessmentsWrapper = await waitForElm('#main > .assessmentsWrapper .assessments .AssessmentItem__AssessmentItem___2EZ95', true, 50);
if (!assessmentsWrapper) return; if (!assessmentsWrapper) return;
// Grade conversion map for letter grades
const letterGradeMap: Record<string, number> = {
'A+': 100,
'A': 95,
'A-': 90,
'B+': 85,
'B': 80,
'B-': 75,
'C+': 70,
'C': 65,
'C-': 60,
'D+': 55,
'D': 50,
'D-': 45,
'E+': 40,
'E': 35,
'E-': 30,
'F': 0
};
// Function to parse grade text into a number
function parseGrade(gradeText: string): number {
// Remove any whitespace
const trimmedGrade = gradeText.trim().toUpperCase();
// Check if it's a percentage
if (trimmedGrade.includes('%')) {
return parseFloat(trimmedGrade.replace('%', '')) || 0;
}
// Check if it's a letter grade
if (letterGradeMap.hasOwnProperty(trimmedGrade)) {
return letterGradeMap[trimmedGrade];
}
return 0;
}
// Function to calculate average of grades // Function to calculate average of grades
function calculateAverageGrade(): number { function calculateAverageGrade(): number {
const gradeElements = document.querySelectorAll('.Thermoscore__text___1NdvB'); const gradeElements = document.querySelectorAll('.Thermoscore__text___1NdvB');
@@ -2753,8 +2943,9 @@ async function handleAssessments(node: Element): Promise<void> {
let count = 0; let count = 0;
gradeElements.forEach(element => { gradeElements.forEach(element => {
const grade = parseFloat(element.textContent?.replace('%', '') || '0'); const gradeText = element.textContent || '';
if (!isNaN(grade)) { const grade = parseGrade(gradeText);
if (grade > 0) {
total += grade; total += grade;
count++; count++;
} }
+107 -23
View File
@@ -11,10 +11,16 @@
--auto-background: var(--better-pale, var(--background-secondary)) !important; --auto-background: var(--better-pale, var(--background-secondary)) !important;
font-family: Rubik, sans-serif !important; font-family: Rubik, sans-serif !important;
} }
.hidden { .hidden {
display: none; display: none;
} }
button.uiButton.timetable-zoom.iconFamily,
.iconFamily {
font-family: "IconFamily" !important;
}
body, body,
.legacy-root input, .legacy-root input,
.legacy-root textarea, .legacy-root textarea,
@@ -120,6 +126,7 @@ html {
.modaliser-container { .modaliser-container {
backdrop-filter: none !important; backdrop-filter: none !important;
pointer-events: none !important;
} }
.connectedNotificationsWrapper > div > button > svg > g { .connectedNotificationsWrapper > div > button > svg > g {
@@ -202,7 +209,13 @@ html {
.cke_panel { .cke_panel {
border-radius: 16px !important; border-radius: 16px !important;
margin-top: 8px !important; margin-top: 8px !important;
background: unset; background: var(--background-primary) !important;
border: var(--background-secondary) !important;
overflow: clip;
iframe {
background: transparent !important;
}
} }
.legacy-root button:active, .legacy-root button:active,
@@ -222,6 +235,10 @@ html {
} }
} }
.timetable-zoom {
font-size: 14px !important;
}
#main > .dashboard { #main > .dashboard {
grid-template-columns: repeat(autofit, minmax(200px, 400px)) !important; grid-template-columns: repeat(autofit, minmax(200px, 400px)) !important;
background: unset; background: unset;
@@ -243,8 +260,23 @@ html {
color: var(--text-primary); color: var(--text-primary);
} }
.ais-btnSearch .material-icons { .ais-btnSearch {
transition: background 200ms, color 200ms, box-shadow 200ms;
&:hover {
background: rgba(0, 0, 0, 0.2) !important;
color: var(--text-primary) !important;
box-shadow: unset !important;
}
.material-icons {
font-size: 0px !important;
font-family: Rubik, sans-serif !important;
&::before {
font-size: 18px !important; font-size: 18px !important;
content: 'Search' !important;
}
}
} }
} }
@@ -484,9 +516,24 @@ ol:has(.MessageList__avatar___2wxyb svg) {
} }
.singleSelect { .singleSelect {
border-radius: 16px !important; border-radius: 16px !important;
padding: 4px !important;
padding-left: 12px !important; &[style*="absolute"] {
box-shadow: 0px 10px 15px -3px rgba(0, 0, 0, 0.2) !important; box-shadow: 0px 10px 15px -3px rgba(0, 0, 0, 0.2) !important;
padding: 0 2px !important;
outline: 2px solid rgba(0, 0, 0, 0.01) !important;
}
> li {
border-radius: 12px !important;
transition: background 150ms;
margin-bottom: 2px !important;
margin-top: 2px !important;
border-bottom: unset !important;
&:hover {
background: rgba(0, 0, 0, 0.1) !important;
}
}
} }
.quickbar .actions a > svg { .quickbar .actions a > svg {
scale: 0.95; scale: 0.95;
@@ -539,29 +586,42 @@ ol:has(.MessageList__avatar___2wxyb svg) {
clip-path: polygon(50% 40%, 0 100%, 100% 100%); clip-path: polygon(50% 40%, 0 100%, 100% 100%);
border-bottom-color: transparent !important; border-bottom-color: transparent !important;
} }
#main > .timetablepage > .quickbar.below::before { #main > .timetablepage > .quickbar {
&.below::before {
top: -23px; top: -23px;
background-color: inherit; background-color: inherit;
clip-path: polygon(50% 40%, 0 100%, 100% 100%); clip-path: polygon(50% 40%, 0 100%, 100% 100%);
border-bottom-color: transparent !important; border-bottom-color: transparent !important;
} }
#main > .timetablepage > .quickbar.above::after {
&.above[data-yiq="light"]::after {
background-color: rgba(0, 0, 0, 0.2);
}
&.above[data-yiq="dark"]::after {
background-color: rgba(255, 255, 255, 0.2);
}
&.above::after {
content: ""; content: "";
position: absolute; position: absolute;
bottom: -23px; bottom: -24px;
z-index: 2; z-index: 0;
left: 50%; left: 50%;
margin: 0 0 0 -12px; margin: 0 0 0 -12px;
background-color: rgba(255, 255, 255, 0.2);
clip-path: polygon(50% 40%, 0 0, 100% 0); clip-path: polygon(50% 40%, 0 0, 100% 0);
border: 12px solid rgba(255, 255, 255, 0); border: 12px solid rgba(255, 255, 255, 0);
border-top-color: transparent; border-top-color: transparent;
} }
#main > .timetablepage > .quickbar.above::before {
&.above::before {
border-bottom-color: transparent !important; border-bottom-color: transparent !important;
bottom: -23px !important; border-top-color: transparent !important;
bottom: -24px !important;
z-index: -1 !important;
background-color: inherit; background-color: inherit;
clip-path: polygon(50% 40%, 0 0, 100% 0); clip-path: polygon(50% 40%, 0 0, 100% 0);
}
} }
#main .timetablepage .actions a, #main .timetablepage .actions a,
#main .timetablepage .actions button { #main .timetablepage .actions button {
@@ -624,9 +684,17 @@ td.colourBar {
#container #content .uiButton { #container #content .uiButton {
border-radius: 16px; border-radius: 16px;
} }
.dark {
#toolbar button.toggled,
#toolbar button.depressed {
background: #333333;
color: white;
}
}
#toolbar button.toggled, #toolbar button.toggled,
#toolbar button.depressed { #toolbar button.depressed {
background: var(--better-main); background: #f3f3f3;
color: black;
} }
ul.buttonChecklist { ul.buttonChecklist {
border-radius: 16px; border-radius: 16px;
@@ -648,14 +716,19 @@ ul.buttonChecklist {
border-radius: 8px !important; border-radius: 8px !important;
} }
&:has(.item.checked) button:nth-child(2) { &:has(.item.checked) button:nth-child(1) {
background: var(--background-secondary) !important; background: var(--background-secondary) !important;
} }
&:has(.item.unchecked) button:nth-child(1) { &:has(.item.unchecked) button:nth-child(2) {
background: var(--background-secondary) !important; background: var(--background-secondary) !important;
} }
} }
.dark ul.buttonChecklist {
li.item.checked {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="white" viewBox="0 0 24 24"><path d="M9 16.172l10.594-10.594 1.406 1.406-12 12-5.578-5.578 1.406-1.406z"/></svg>');
}
}
#toolbar > span:has(input) { #toolbar > span:has(input) {
flex: 1 1 0%; flex: 1 1 0%;
} }
@@ -1756,7 +1829,6 @@ ul {
} }
.content > .wrapper .days tbody tr > td { .content > .wrapper .days tbody tr > td {
overflow: hidden; overflow: hidden;
height: 1440px !important;
} }
.title { .title {
color: var(--text-primary) !important; color: var(--text-primary) !important;
@@ -1878,9 +1950,6 @@ blurred {
.uiSlidePane > .pane > .header { .uiSlidePane > .pane > .header {
background: var(--better-main); background: var(--better-main);
} }
.modaliser-container {
pointer-events: none;
}
.content [placeholder="Subject…"] { .content [placeholder="Subject…"] {
padding-left: 12px !important; padding-left: 12px !important;
border-radius: 16px; border-radius: 16px;
@@ -1969,6 +2038,11 @@ div.bar.flat {
background: unset !important; background: unset !important;
gap: 0 8px; gap: 0 8px;
} }
.cke_toolbar:has(.cke_toolgroup) {
.cke_combo {
margin-right: 8px !important;
}
}
.cke_toolbox > .cke_toolbar > .cke_toolgroup { .cke_toolbox > .cke_toolbar > .cke_toolgroup {
margin: 0 !important; margin: 0 !important;
} }
@@ -1985,7 +2059,13 @@ div.bar.flat {
} }
.cke_toolbox > .cke_toolbar .cke_combo_on > .cke_combo_button, .cke_toolbox > .cke_toolbar .cke_combo_on > .cke_combo_button,
.cke_toolbox > .cke_toolbar .cke_button_on { .cke_toolbox > .cke_toolbar .cke_button_on {
background-color: #797979 !important; background-color: #d5d5d6 !important;
&::after {
background: black !important;
}
}
.quicktable {
border-radius: 12px;
} }
.dark { .dark {
.cke_toolbox > .cke_toolbar .cke_combo_on > .cke_combo_button, .cke_toolbox > .cke_toolbar .cke_combo_on > .cke_combo_button,
@@ -2651,11 +2731,15 @@ li.MessageList__unread___3imtO {
} }
.calendar { .calendar {
background: var(--better-main) !important; background: var(--background-primary) !important;
color: var(--text-color) !important; color: var(--text-primary) !important;
border-radius: 16px !important; border-radius: 16px !important;
margin-top: 4px; margin-top: 4px;
&.container {
box-shadow: -2px 2px 30px 0px rgba(0,0,0,0.3) !important;
}
table { table {
background: transparent !important; background: transparent !important;
} }
+1
View File
@@ -3,6 +3,7 @@ declare module '*.woff';
declare module '*.scss'; declare module '*.scss';
declare module '*.png'; declare module '*.png';
declare module '*.html'; declare module '*.html';
declare module '*.svelte';
declare module "*.png?base64" { declare module "*.png?base64" {
const value: string; const value: string;
+1 -1
View File
@@ -93,7 +93,7 @@ export default function Picker({
<ColorPicker <ColorPicker
disableDarkMode={true} disableDarkMode={true}
presets={presets} presets={presets}
hideInputs={true} hideInputs={false}
value={customThemeColor ?? ""} value={customThemeColor ?? ""}
onChange={(color: string) => { onChange={(color: string) => {
if (customOnChange) { if (customOnChange) {
@@ -1,4 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { Theme } from '@/interface/types/Theme'
let { theme, onClick } = $props<{ theme: Theme; onClick: () => void }>(); let { theme, onClick } = $props<{ theme: Theme; onClick: () => void }>();
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
@@ -6,12 +8,12 @@
<div class="w-full cursor-pointer" role="button" tabindex="-1" onkeydown={onClick} onclick={onClick}> <div class="w-full cursor-pointer" role="button" tabindex="-1" onkeydown={onClick} onclick={onClick}>
<div class="bg-gray-50 w-full transition-all hover:scale-105 duration-500 relative group 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" transition:fade> <div class="bg-gray-50 w-full transition-all hover:scale-105 duration-500 relative group 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" transition:fade>
<div class="absolute z-10 mb-1 text-xl font-bold text-white bottom-1 left-3"> <div class="absolute bottom-1 left-3 z-10 mb-1 text-xl font-bold text-white">
{theme.name} {theme.name}
</div> </div>
<div class='absolute bottom-0 z-0 w-full h-3/4 bg-gradient-to-t from-black/80 to-transparent'></div> <div class='absolute bottom-0 z-0 w-full h-3/4 bg-gradient-to-t to-transparent from-black/80'></div>
<div class='w-full'> <div class='w-full'>
<img src={theme.coverImage} alt="Theme Preview" class="object-cover w-full h-48 rounded-md" /> <img src={theme.marqueeImage} alt="Theme Preview" class="object-cover w-full h-48 rounded-md" />
</div> </div>
</div> </div>
</div> </div>
@@ -54,7 +54,7 @@
</script> </script>
<div <div
class="fixed inset-0 z-50 flex items-end justify-center bg-black bg-opacity-70" class="flex fixed inset-0 z-50 justify-center items-end bg-black bg-opacity-70"
onclick={(e) => { onclick={(e) => {
if (e.target === e.currentTarget) hideModal(); if (e.target === e.currentTarget) hideModal();
}} }}
@@ -79,12 +79,12 @@
<h2 class="mb-4 text-2xl font-bold"> <h2 class="mb-4 text-2xl font-bold">
{theme.name} {theme.name}
</h2> </h2>
<img src={theme.marqueeImage} alt="Theme Cover" class="object-cover w-full mb-4 rounded-md" /> <img src={theme.marqueeImage} alt="Theme Cover" class="object-cover mb-4 w-full rounded-md" />
<p class="mb-4 text-gray-700 dark:text-gray-300"> <p class="mb-4 text-gray-700 dark:text-gray-300">
{theme.description} {theme.description}
</p> </p>
{#if currentThemes.includes(theme.id)} {#if currentThemes.includes(theme.id)}
<button onclick={async () => {installing = true; await onRemove(theme.id); installing = false}} class="relative flex items-center justify-center w-32 px-4 py-2 mt-4 ml-auto text-black rounded-full dark:text-white bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600/50 hover:bg-zinc-200"> <button onclick={async () => {installing = true; await onRemove(theme.id); installing = false}} class="flex relative justify-center items-center px-4 py-2 mt-4 ml-auto w-32 text-black rounded-full dark:text-white bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600/50 hover:bg-zinc-200">
{#if installing} {#if installing}
<svg class="absolute w-4 h-4 { installing ? 'opacity-100' : 'opacity-0' }" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="absolute w-4 h-4 { installing ? 'opacity-100' : 'opacity-0' }" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke="currentColor" fill="currentColor" class="origin-center animate-spin-fast" d="M2,12A11.2,11.2,0,0,1,13,1.05C12.67,1,12.34,1,12,1a11,11,0,0,0,0,22c.34,0,.67,0,1-.05C6,23,2,17.74,2,12Z"/> <path stroke="currentColor" fill="currentColor" class="origin-center animate-spin-fast" d="M2,12A11.2,11.2,0,0,1,13,1.05C12.67,1,12.34,1,12,1a11,11,0,0,0,0,22c.34,0,.67,0,1-.05C6,23,2,17.74,2,12Z"/>
@@ -93,7 +93,7 @@
<span class="{ installing ? 'opacity-0' : 'opacity-100' }">Remove</span> <span class="{ installing ? 'opacity-0' : 'opacity-100' }">Remove</span>
</button> </button>
{:else} {:else}
<button onclick={async () => {installing = true; await onInstall(theme.id); installing = false}} class="relative flex items-center justify-center w-32 px-4 py-2 mt-4 ml-auto text-black rounded-full dark:text-white bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600/50 hover:bg-zinc-200"> <button onclick={async () => {installing = true; await onInstall(theme.id); installing = false}} class="flex relative justify-center items-center px-4 py-2 mt-4 ml-auto w-32 text-black rounded-full dark:text-white bg-zinc-300 dark:bg-zinc-700 dark:hover:bg-zinc-600/50 hover:bg-zinc-200">
{#if installing} {#if installing}
<svg class="absolute w-4 h-4 { installing ? 'opacity-100' : 'opacity-0' }" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="absolute w-4 h-4 { installing ? 'opacity-100' : 'opacity-0' }" width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke="currentColor" fill="currentColor" class="origin-center animate-spin-fast" d="M2,12A11.2,11.2,0,0,1,13,1.05C12.67,1,12.34,1,12,1a11,11,0,0,0,0,22c.34,0,.67,0,1-.05C6,23,2,17.74,2,12Z"/> <path stroke="currentColor" fill="currentColor" class="origin-center animate-spin-fast" d="M2,12A11.2,11.2,0,0,1,13,1.05C12.67,1,12.34,1,12,1a11,11,0,0,0,0,22c.34,0,.67,0,1-.05C6,23,2,17.74,2,12Z"/>
@@ -112,11 +112,11 @@
{#each getRelatedThemes() as relatedTheme (relatedTheme.id)} {#each getRelatedThemes() as relatedTheme (relatedTheme.id)}
<button onclick={() => { hideModal(relatedTheme) }} class="w-full cursor-pointer"> <button onclick={() => { hideModal(relatedTheme) }} class="w-full cursor-pointer">
<div class="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 class="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 class="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"> <div class="absolute bottom-1 left-3 z-10 mb-1 text-xl font-bold text-white transition-all duration-500 group-hover:-translate-y-0.5">
{relatedTheme.name} {relatedTheme.name}
</div> </div>
<div class="absolute bottom-0 z-0 w-full h-3/4 bg-gradient-to-t from-black/80 to-transparent"></div> <div class="absolute bottom-0 z-0 w-full h-3/4 bg-gradient-to-t to-transparent from-black/80"></div>
<img src={relatedTheme.coverImage} alt="Theme Preview" class="object-cover w-full h-48" /> <img src={relatedTheme.marqueeImage} alt="Theme Preview" class="object-cover w-full h-48" />
</div> </div>
</button> </button>
{/each} {/each}
+16 -14
View File
@@ -27,6 +27,7 @@
import { CloseThemeCreator } from '@/seqta/ui/ThemeCreator' import { CloseThemeCreator } from '@/seqta/ui/ThemeCreator'
import { themeUpdates } from '../hooks/ThemeUpdates' import { themeUpdates } from '../hooks/ThemeUpdates'
import { disableTheme } from '@/seqta/ui/themes/disableTheme' import { disableTheme } from '@/seqta/ui/themes/disableTheme'
import { setTheme } from '@/seqta/ui/themes/setTheme'
const { themeID } = $props<{ themeID: string }>() const { themeID } = $props<{ themeID: string }>()
let theme = $state<LoadedCustomTheme>({ let theme = $state<LoadedCustomTheme>({
@@ -55,7 +56,7 @@
} }
onMount(async () => { onMount(async () => {
disableTheme(); await disableTheme();
if (themeID) { if (themeID) {
const tempTheme = await getTheme(themeID) const tempTheme = await getTheme(themeID)
@@ -111,6 +112,7 @@
ClearThemePreview(); ClearThemePreview();
saveTheme(themeClone); saveTheme(themeClone);
setTheme(themeClone.id);
themeUpdates.triggerUpdate(); themeUpdates.triggerUpdate();
CloseThemeCreator(); CloseThemeCreator();
} }
@@ -166,7 +168,7 @@
</div> </div>
{#if item.direction === 'vertical'} {#if item.direction === 'vertical'}
<div class="flex items-center justify-center h-full text-xl font-light text-zinc-500 dark:text-zinc-300"> <div class="flex justify-center items-center h-full text-xl font-light text-zinc-500 dark:text-zinc-300">
<span class='font-IconFamily transition-transform duration-300 {closedAccordions.includes(item.title) ? 'rotate-180' : ''}'>{'\ue9e6'}</span> <span class='font-IconFamily transition-transform duration-300 {closedAccordions.includes(item.title) ? 'rotate-180' : ''}'>{'\ue9e6'}</span>
</div> </div>
{/if} {/if}
@@ -190,8 +192,8 @@
{/key} {/key}
{:else if item.type === 'imageUpload'} {:else if item.type === 'imageUpload'}
{#each theme.CustomImages as image (image.id)} {#each theme.CustomImages as image (image.id)}
<div class="flex items-center h-16 gap-2 px-2 py-2 mb-4 bg-white rounded-lg shadow-lg dark:bg-zinc-700"> <div class="flex gap-2 items-center px-2 py-2 mb-4 h-16 bg-white rounded-lg shadow-lg dark:bg-zinc-700">
<div class="h-full "> <div class="h-full">
<img src={image.url} alt={image.variableName} class="object-contain h-full rounded" /> <img src={image.url} alt={image.variableName} class="object-contain h-full rounded" />
</div> </div>
<input <input
@@ -207,14 +209,14 @@
</div> </div>
{/each} {/each}
<div class="relative flex justify-center w-full h-8 gap-1 overflow-hidden transition rounded-lg place-items-center bg-zinc-200 dark:bg-zinc-700"> <div class="flex overflow-hidden relative gap-1 justify-center place-items-center w-full h-8 rounded-lg transition bg-zinc-200 dark:bg-zinc-700">
<span class='font-IconFamily'>{'\uec60'}</span> <span class='font-IconFamily'>{'\uec60'}</span>
<span class='dark:text-white'>Add image</span> <span class='dark:text-white'>Add image</span>
<input type="file" accept='image/*' onchange={onImageUpload} class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" /> <input type="file" accept='image/*' onchange={onImageUpload} class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" />
</div> </div>
{:else if item.type === 'lightDarkToggle'} {:else if item.type === 'lightDarkToggle'}
<button <button
class="relative px-4 py-1 overflow-hidden text-xl font-medium transition rounded-lg bg-zinc-200 dark:bg-zinc-700 hover:bg-zinc-300 dark:hover:bg-zinc-600 font-IconFamily" class="overflow-hidden relative px-4 py-1 text-xl font-medium rounded-lg transition bg-zinc-200 dark:bg-zinc-700 hover:bg-zinc-300 dark:hover:bg-zinc-600 font-IconFamily"
onclick={() => (item.props as LightDarkToggleProps).onChange(!(item.props as LightDarkToggleProps).state)} onclick={() => (item.props as LightDarkToggleProps).onChange(!(item.props as LightDarkToggleProps).state)}
> >
{#key (item.props as LightDarkToggleProps).state} {#key (item.props as LightDarkToggleProps).state}
@@ -236,10 +238,10 @@
{/snippet} {/snippet}
<div class='h-screen overflow-y-scroll {$settingsState.DarkMode && "dark"} no-scrollbar'> <div class='h-screen overflow-y-scroll {$settingsState.DarkMode && "dark"} no-scrollbar'>
<div class='flex flex-col w-full min-h-screen p-2 bg-zinc-100 dark:bg-zinc-800 dark:text-white'> <div class='flex flex-col p-2 w-full min-h-screen bg-zinc-100 dark:bg-zinc-800 dark:text-white'>
<h1 class='text-xl font-semibold'>Theme Creator</h1> <h1 class='text-xl font-semibold'>Theme Creator</h1>
<a href='https://betterseqta.gitbook.io/betterseqta-docs' target='_blank' class='text-sm font-light text-zinc-500 dark:text-zinc-400'> <a href='https://betterseqta.gitbook.io/betterseqta-docs' target='_blank' class='text-sm font-light text-zinc-500 dark:text-zinc-400'>
<span class='no-underline font-IconFamily pr-0.5'>{'\ueb44'}</span> <span class='pr-0.5 no-underline font-IconFamily'>{'\ueb44'}</span>
<span class='underline'> <span class='underline'>
Need help? Check out the docs! Need help? Check out the docs!
</span> </span>
@@ -254,7 +256,7 @@
type='text' type='text'
placeholder='What is your theme called?' placeholder='What is your theme called?'
bind:value={theme.name} bind:value={theme.name}
class='w-full p-2 mb-4 transition border-0 rounded-lg dark:placeholder-zinc-300 bg-zinc-200 dark:bg-zinc-700 focus:bg-zinc-300/50 dark:focus:bg-zinc-600' /> class='p-2 mb-4 w-full rounded-lg border-0 transition dark:placeholder-zinc-300 bg-zinc-200 dark:bg-zinc-700 focus:bg-zinc-300/50 dark:focus:bg-zinc-600' />
</div> </div>
<div> <div>
@@ -263,23 +265,23 @@
id='themeDescription' id='themeDescription'
placeholder="Don't worry, this one's optional!" placeholder="Don't worry, this one's optional!"
bind:value={theme.description} bind:value={theme.description}
class='w-full p-2 transition border-0 rounded-lg dark:placeholder-zinc-300 bg-zinc-200 dark:bg-zinc-700 focus:outline-none focus:ring-1 focus:ring-zinc-100 dark:focus:ring-zinc-700 focus:bg-zinc-300/50 dark:focus:bg-zinc-600'></textarea> class='p-2 w-full rounded-lg border-0 transition dark:placeholder-zinc-300 bg-zinc-200 dark:bg-zinc-700 focus:outline-none focus:ring-1 focus:ring-zinc-100 dark:focus:ring-zinc-700 focus:bg-zinc-300/50 dark:focus:bg-zinc-600'></textarea>
</div> </div>
<Divider /> <Divider />
<div class="relative flex justify-center w-full gap-1 overflow-hidden transition rounded-lg aspect-theme group place-items-center bg-zinc-200 dark:bg-zinc-700"> <div class="flex overflow-hidden relative gap-1 justify-center place-items-center w-full rounded-lg transition aspect-theme group bg-zinc-200 dark:bg-zinc-700">
<div class={`transition pointer-events-none z-30 font-IconFamily ${ theme.coverImage ? 'opacity-0 group-hover:opacity-100' : ''}`}> <div class={`transition pointer-events-none z-30 font-IconFamily ${ theme.coverImage ? 'opacity-0 group-hover:opacity-100' : ''}`}>
{'\uec60'} {'\uec60'}
</div> </div>
<span class={`dark:text-white pointer-events-none z-30 transition ${ theme.coverImage ? 'opacity-0 group-hover:opacity-100' : ''}`}>{theme.coverImage ? 'Change' : 'Add'} cover image</span> <span class={`dark:text-white pointer-events-none z-30 transition ${ theme.coverImage ? 'opacity-0 group-hover:opacity-100' : ''}`}>{theme.coverImage ? 'Change' : 'Add'} cover image</span>
<input type="file" accept='image/*' onchange={onCoverImageUpload} class="absolute inset-0 z-10 w-full h-full opacity-0 cursor-pointer" /> <input type="file" accept='image/*' onchange={onCoverImageUpload} class="absolute inset-0 z-10 w-full h-full opacity-0 cursor-pointer" />
{#if !theme.hideThemeName && theme.coverImage} {#if !theme.hideThemeName && theme.coverImage}
<div class="absolute z-30 transition-opacity opacity-100 pointer-events-none group-hover:opacity-0">{theme.name}</div> <div class="absolute z-30 opacity-100 transition-opacity pointer-events-none group-hover:opacity-0">{theme.name}</div>
{/if} {/if}
{#if theme.coverImage} {#if theme.coverImage}
<div class="absolute z-20 w-full h-full transition-opacity opacity-0 pointer-events-none group-hover:opacity-100 bg-black/20"></div> <div class="absolute z-20 w-full h-full opacity-0 transition-opacity pointer-events-none group-hover:opacity-100 bg-black/20"></div>
<img src={theme.coverImageUrl} alt='Cover' class="absolute z-0 object-cover w-full h-full rounded" /> <img src={theme.coverImageUrl} alt='Cover' class="object-cover absolute z-0 w-full h-full rounded" />
{/if} {/if}
</div> </div>
+6 -1
View File
@@ -1,4 +1,9 @@
import { createManifest } from '../../lib/createManifest' import { createManifest } from '../../lib/createManifest'
import baseManifest from './manifest.json' import baseManifest from './manifest.json'
import pkg from '../../package.json'
export const brave = createManifest(baseManifest, 'brave') export const brave = createManifest({
...baseManifest,
version: pkg.version,
description: pkg.description,
}, 'brave')
+6 -1
View File
@@ -1,4 +1,9 @@
import { createManifest } from '../../lib/createManifest' import { createManifest } from '../../lib/createManifest'
import baseManifest from './manifest.json' import baseManifest from './manifest.json'
import pkg from '../../package.json'
export const chrome = createManifest(baseManifest, 'chrome') export const chrome = createManifest({
...baseManifest,
version: pkg.version,
description: pkg.description,
}, 'chrome')
+6 -1
View File
@@ -1,4 +1,9 @@
import { createManifest } from '../../lib/createManifest' import { createManifest } from '../../lib/createManifest'
import baseManifest from './manifest.json' import baseManifest from './manifest.json'
import pkg from '../../package.json'
export const edge = createManifest(baseManifest, 'edge') export const edge = createManifest({
...baseManifest,
version: pkg.version,
description: pkg.description,
}, 'edge')
+2
View File
@@ -4,6 +4,8 @@ import pkg from '../../package.json'
const updatedFirefoxManifest = { const updatedFirefoxManifest = {
...baseManifest, ...baseManifest,
version: pkg.version,
description: pkg.description,
background: { background: {
scripts: [baseManifest.background.service_worker], scripts: [baseManifest.background.service_worker],
}, },
-2
View File
@@ -1,8 +1,6 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "BetterSEQTA+", "name": "BetterSEQTA+",
"version": "3.4.3",
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development add add heaps more features!",
"icons": { "icons": {
"32": "resources/icons/icon-32.png", "32": "resources/icons/icon-32.png",
"48": "resources/icons/icon-48.png", "48": "resources/icons/icon-48.png",
+6 -1
View File
@@ -1,4 +1,9 @@
import { createManifest } from '../../lib/createManifest' import { createManifest } from '../../lib/createManifest'
import baseManifest from './manifest.json' import baseManifest from './manifest.json'
import pkg from '../../package.json'
export const opera = createManifest(baseManifest, 'opera') export const opera = createManifest({
...baseManifest,
version: pkg.version,
description: pkg.description,
}, 'opera')
+3
View File
@@ -1,8 +1,11 @@
import { createManifest } from '../../lib/createManifest' import { createManifest } from '../../lib/createManifest'
import baseManifest from './manifest.json' import baseManifest from './manifest.json'
import pkg from '../../package.json'
const updatedSafariManifest = { const updatedSafariManifest = {
...baseManifest, ...baseManifest,
version: pkg.version,
description: pkg.description,
browser_specific_settings: { browser_specific_settings: {
safari: { safari: {
strict_min_version: '15.4', strict_min_version: '15.4',
+1 -2
View File
@@ -136,11 +136,10 @@ class EventManager {
} }
private async checkElement(element: Element): Promise<void> { private async checkElement(element: Element): Promise<void> {
if (element.classList.contains('code')) console.log('Code Detected!');
for (const [event, listeners] of this.listeners.entries()) { for (const [event, listeners] of this.listeners.entries()) {
for (const { id, options, callback } of listeners) { for (const { id, options, callback } of listeners) {
if (this.matchesOptions(element, options)) { if (this.matchesOptions(element, options)) {
await callback(element); callback(element);
if (options.once) { if (options.once) {
this.unregisterById(event, id); this.unregisterById(event, id);
} }
+8 -3
View File
@@ -4,6 +4,7 @@ import { join, resolve } from 'path';
import { updateManifestPlugin } from './lib/patchPackage'; import { updateManifestPlugin } from './lib/patchPackage';
import { base64Loader } from './lib/base64loader'; import { base64Loader } from './lib/base64loader';
import type { BuildTarget } from './lib/types'; import type { BuildTarget } from './lib/types';
import ClosePlugin from './lib/closePlugin';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import million from "million/compiler"; import million from "million/compiler";
@@ -25,7 +26,7 @@ const targets: BuildTarget[] = [
const mode = process.env.MODE || 'chrome'; const mode = process.env.MODE || 'chrome';
export default defineConfig({ export default defineConfig(({ command }) => ({
plugins: [ plugins: [
base64Loader, base64Loader,
react(), react(),
@@ -38,7 +39,8 @@ export default defineConfig({
manifest: targets.find(t => t.browser === mode.toLowerCase())?.manifest ?? chrome.manifest, manifest: targets.find(t => t.browser === mode.toLowerCase())?.manifest ?? chrome.manifest,
browser: mode.toLowerCase() === "firefox" ? "firefox" : "chrome" browser: mode.toLowerCase() === "firefox" ? "firefox" : "chrome"
}), }),
updateManifestPlugin() updateManifestPlugin(),
...(command === 'build' ? [ClosePlugin()] : [])
], ],
root: resolve(__dirname, './src'), root: resolve(__dirname, './src'),
resolve: { resolve: {
@@ -64,6 +66,9 @@ export default defineConfig({
optimizeDeps: { optimizeDeps: {
include: ['@babel/runtime/helpers/extends', '@babel/runtime/helpers/interopRequireDefault'], include: ['@babel/runtime/helpers/extends', '@babel/runtime/helpers/interopRequireDefault'],
}, },
legacy: {
skipWebSocketTokenCheck: true,
},
build: { build: {
outDir: resolve(__dirname, 'dist', mode), outDir: resolve(__dirname, 'dist', mode),
emptyOutDir: false, emptyOutDir: false,
@@ -75,4 +80,4 @@ export default defineConfig({
} }
} }
} }
}); }));