Compare commits

..

79 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
SethBurkart123 0ef0078fb7 bump(version): 3.4.3 + changelog 2025-02-03 13:00:09 +11:00
SethBurkart123 834b8b41af chore: clean up source files 2024-12-05 18:10:32 +11:00
SethBurkart123 f1512ba6e1 chore: clean up code 2024-12-05 14:43:12 +11:00
SethBurkart123 13fc077686 bump(version): 3.4.2 + changelog 2024-12-05 14:38:02 +11:00
SethBurkart123 7cf765121c fix(style): z-index of panels increased 2024-12-05 14:36:04 +11:00
SethBurkart123 4e393f14bb fix: enable assessments average by default 2024-12-05 14:35:23 +11:00
Seth Burkart 98347e038d Merge pull request #192 from ar-cyber/patch-16
Add mention for grades calc in readme
2024-12-03 17:23:35 +11:00
Andrew R f2bdb22ea8 fix(doc): make the readme mention the grade calculator 2024-12-03 13:52:16 +10:30
SethBurkart123 4afab2c52a perf: refactor AddBetterSEQTAElements for improved performance 2024-12-03 07:15:50 +11:00
SethBurkart123 4c6b43d7c7 fix(initial): make assessments average enabled by default 2024-12-03 06:59:06 +11:00
SethBurkart123 9e26d2c192 fix(changelog): versions and changelog fixed 2024-12-02 17:34:39 +11:00
SethBurkart123 7445e8be78 feat(changelog): update changelog + bump version 2024-12-02 12:30:25 +11:00
SethBurkart123 d1a876ff22 fix(home): sidebar button sometimes not selecting 2024-12-02 12:25:14 +11:00
SethBurkart123 e2176ea2fa fix(assessments): placing subject average trice 2024-12-02 12:23:47 +11:00
SethBurkart123 a999e4384b feat(notices): add animations to notices 2024-12-02 12:17:39 +11:00
SethBurkart123 4bf5420140 fix(eventManager): not triggering for already created elements' 2024-12-02 12:17:28 +11:00
SethBurkart123 8fb29f7f21 feat(settings): add subject average setting 2024-12-02 12:01:03 +11:00
SethBurkart123 44e3ed34d0 feat(assessments): add subject averages 2024-12-02 11:58:42 +11:00
SethBurkart123 32228ee4db style(changelog): reduce video padding for better screen realestate 2024-12-02 11:11:49 +11:00
SethBurkart123 1692bd3e92 fix(home): notices date picker not working 2024-12-02 11:11:03 +11:00
SethBurkart123 71cf9dbca8 feat(messages): improvements to direct message animations 2024-12-02 11:01:54 +11:00
SethBurkart123 372b591b16 feat(home): async home page loading 2024-12-02 10:58:24 +11:00
SethBurkart123 7578ecee74 chore(logging): improve logging 2024-12-02 09:27:33 +11:00
SethBurkart123 a2b4f81b86 feat(home): add skeleton loaders to homepage 2024-12-02 09:26:49 +11:00
SethBurkart123 fcd95f6823 perf(homePage): add fragmentation and refactor code 2024-11-30 21:55:10 +11:00
SethBurkart123 f2ea7c8104 fix: builds failing 2024-11-29 17:53:47 +11:00
SethBurkart123 379a3ebda0 chore: code cleanup 2024-11-29 17:34:16 +11:00
SethBurkart123 d1850e8ddb chore: fix CI/CD pipeline 2024-11-29 17:24:02 +11:00
SethBurkart123 88a87692cd fix(icon): discord icon scaled incorrectly 2024-11-29 17:18:42 +11:00
Seth Burkart 4e6e4870b0 Merge pull request #189 from MEGA-Dawg68/main
Added Discord icon
2024-11-29 17:15:53 +11:00
SethBurkart123 18f215fa5f chore: code cleanup 2024-11-29 17:14:32 +11:00
SethBurkart123 2ea8ada439 fix(popups): backdrop filter failing to appear during animation 2024-11-29 17:13:59 +11:00
SethBurkart123 34b2501617 chore(changelog): update changelog 2024-11-29 14:57:41 +11:00
SethBurkart123 88d4d3aa11 feat(news): add animations to news page 2024-11-29 14:57:17 +11:00
SethBurkart123 5ed3a05f6a perf(news): improved news page performance 2024-11-29 14:52:26 +11:00
SethBurkart123 430f158957 style(news): improved news styles 2024-11-29 14:38:41 +11:00
SethBurkart123 547caabc45 bump(version): 3.4.1 + changelog 2024-11-29 14:25:58 +11:00
SethBurkart123 f6e549c5da feat(animations): refine home page animations 2024-11-29 11:40:37 +11:00
SethBurkart123 dc1ae9c0a1 feat(animations): migrate to motion-one@11 2024-11-29 11:32:43 +11:00
SethBurkart123 34306e77cf fix(studentInfo): some cases year may be undefined 2024-11-29 10:29:35 +11:00
SethBurkart123 00c9f03827 bump: version to 3.4.0.4 2024-11-27 09:23:40 +11:00
SethBurkart123 6c93477998 fix: background migration not working properly 2024-11-27 09:22:34 +11:00
MEGA_Dawg68 9784b6162f Added Discord icon 2024-11-25 19:57:45 +08:00
SethBurkart123 96cf8e3eac feat: clean up CI/CD piplelines 2024-11-15 17:24:26 +11:00
SethBurkart123 0e23ea0cc3 fix: build running twice in CI/CD 2024-11-15 17:23:34 +11:00
SethBurkart123 7dfe347562 chore: move install to build section 2024-11-15 17:14:15 +11:00
SethBurkart123 21a8472c94 fix: themes failing to install from coverImage decoding error 2024-11-15 16:35:28 +11:00
34 changed files with 1356 additions and 720 deletions
+24 -59
View File
@@ -1,73 +1,38 @@
name: MVP - make, version & publish name: NodeJS Build
on: on:
push: push:
branches: branches: [ "main" ]
- main pull_request:
workflow_dispatch: # This line adds manual triggering from the GitHub UI branches: [ "main" ]
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs: jobs:
make_version_publish: build:
name: Make, Version & Publish
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup Node 20.x strategy:
matrix:
node-version: [20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20.x node-version: ${{ matrix.node-version }}
- name: Install bun & Deps - name: Build
run: | run: |
npm install bun -g npm install --legacy-peer-deps
bun install npm run build
- name: 'Build - all browsers' - name: Zip dist folder
id: buildProject run: |
run: MODE=chrome vite build && MODE=firefox vite build zip -r dist.zip dist
- name: '[ V E R S I O N ] : Create or Update Release Pull Request - Version Changes' - name: Upload artifact
id: changesets uses: actions/upload-artifact@v4
uses: changesets/action@v1
with: with:
version: bun run version name: dist-zip
env: path: dist.zip
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 'Get current version info from package.json'
if: steps.changesets.outputs.hasChangesets == 'false'
id: package
run: |
echo "::set-output name=PACKAGE_NAME::$(jq -r .name package.json)"
echo "::set-output name=PACKAGE_VERSION::$(jq -r .version package.json)"
working-directory: ${{ github.workspace }}
- name: 'Check if a git release already exists for current version'
if: steps.changesets.outputs.hasChangesets == 'false'
id: checkRelease
run: |
TAG_NAME=${{ steps.package.outputs.PACKAGE_NAME }}@${{ steps.package.outputs.PACKAGE_VERSION }}
if gh release view $TAG_NAME &>/dev/null; then
echo "Release $TAG_NAME already exists."
echo "RELEASE_EXISTS=true" >> $GITHUB_ENV
else
echo "RELEASE_EXISTS=false" >> $GITHUB_ENV
fi
- name: 'Create Release Archive(s) - zip 🫰 it 🫰 up 🫰 !'
id: zip
if: steps.changesets.outputs.hasChangesets == 'false'
run: bun run zip
- name: 'Create a git release w/ notes & release archive(s)'
id: gitRelease
if: steps.changesets.outputs.hasChangesets == 'false' && env.RELEASE_EXISTS != 'true'
run: bun run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PACKAGE_NAME: ${{ steps.package.outputs.PACKAGE_NAME }}
PACKAGE_VERSION: ${{ steps.package.outputs.PACKAGE_VERSION }}
-38
View File
@@ -1,38 +0,0 @@
name: NodeJS Build
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Build
run: |
npm install
npm run build
- name: Zip dist folder
run: |
zip -r dist.zip dist
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: dist-zip
path: dist.zip
+1
View File
@@ -43,6 +43,7 @@
- Easier Access Notices - Easier Access Notices
- Assessments - Assessments
- Options to remove certain items from the side menu - Options to remove certain items from the side menu
- Grades calculator
- Fully customisable themes and an offical theme store - Fully customisable themes and an offical theme store
- Notification for next lesson (sent 5 minutes before end of the lesson) - Notification for next lesson (sent 5 minutes before end of the lesson)
- Browser Support - Browser Support
+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();
}); });
}, },
+21 -24
View File
@@ -1,8 +1,8 @@
{ {
"name": "betterseqtaplus", "name": "betterseqtaplus",
"version": "3.4.0", "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",
@@ -31,43 +31,42 @@
}, },
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@babel/plugin-transform-runtime": "^7.25.9",
"@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",
"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",
@@ -75,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": "^10.18.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",
@@ -90,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"
} }
} }
+754 -342
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -167,6 +167,7 @@ const DefaultValues: SettingsState = {
originalSelectedColor: '', originalSelectedColor: '',
DarkMode: true, DarkMode: true,
animations: true, animations: true,
assessmentsAverage: true,
defaultPage: 'home', defaultPage: 'home',
shortcuts: [ shortcuts: [
{ {
+1 -1
View File
@@ -15,7 +15,7 @@
* along with EvenBetterSEQTA. If not, see <https://www.gnu.org/licenses/>. * along with EvenBetterSEQTA. If not, see <https://www.gnu.org/licenses/>.
*/ */
@import './injected/popup.scss'; @use 'injected/popup.scss';
html { html {
background: #161616 !important; background: #161616 !important;
+175 -47
View File
@@ -1,9 +1,9 @@
@charset "UTF-8"; @use "sass:meta";
@import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,600"); @import url("https://fonts.googleapis.com/css?family=Rubik:300,400,500,600");
@import "./injected/sidebar-animation.scss"; @include meta.load-css("injected/sidebar-animation.scss");
@import "./injected/theme.scss"; @include meta.load-css("injected/theme.scss");
@import "./injected/transparency.scss"; @include meta.load-css("injected/transparency.scss");
:root { :root {
background: var(--better-main) !important; background: var(--better-main) !important;
@@ -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%;
} }
@@ -1110,9 +1183,9 @@ div > ol:has(.uiFileHandlerWrapper) {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: var(--background-primary); background: var(--background-primary);
transition: 200ms;
box-shadow: inset 0px 5px 20px 1px rgba(0, 0, 0, 0.3); box-shadow: inset 0px 5px 20px 1px rgba(0, 0, 0, 0.3);
padding-bottom: 25px; padding-bottom: 25px;
transition: none;
color: var(--text-primary); color: var(--text-primary);
} }
.dummynotice { .dummynotice {
@@ -1551,6 +1624,10 @@ iframe.userHTML {
.Collapsible__Collapsible___3O8P3 > .Collapsible__header___-Afvq { .Collapsible__Collapsible___3O8P3 > .Collapsible__header___-Afvq {
background: none; background: none;
} }
.Collapsible__Collapsible___3O8P3 > .Collapsible__content___2c6of.Collapsible__enterActive___3b2ow,
.Collapsible__Collapsible___3O8P3 > .Collapsible__content___2c6of.Collapsible__exitActive___3rFL1 {
animation-timing-function: ease-out !important;
}
.AssessmentList__AssessmentList___1GdCl .AssessmentList__AssessmentList___1GdCl
> .AssessmentList__searchFilter___3N70o > .AssessmentList__searchFilter___3N70o
+ .AssessmentList__items___3LcmQ { + .AssessmentList__items___3LcmQ {
@@ -1752,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;
@@ -1928,6 +2004,7 @@ div.bar.flat {
transition: background-color 0.5s ease-in-out; transition: background-color 0.5s ease-in-out;
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
transition-duration: 500ms !important; transition-duration: 500ms !important;
z-index: 22 !important;
} }
.uiSlidePane.shown > .pane { .uiSlidePane.shown > .pane {
transform: translatey(0%) !important; transform: translatey(0%) !important;
@@ -1961,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;
} }
@@ -1977,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,
@@ -2188,44 +2276,55 @@ li.MessageList__unread___3imtO {
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
padding: 0 !important; padding: 0 !important;
margin: 0px auto 30px !important;
background: var(--background-primary) !important; background: var(--background-primary) !important;
color: var(--text-primary); color: var(--text-primary);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
-webkit-box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3); -webkit-box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3);
box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3); box-shadow: 0px 5px 16px 6px rgba(0, 0, 0, 0.3);
}
.articleimage { &:hover {
width: 35%; .ArticleText a {
background-position: center; text-decoration: underline;
background-size: cover; }
min-height: 18em; }
background-repeat: no-repeat;
} .ArticleText a {
.NewsArticle img {
width: 35%;
}
.ArticleText a {
padding: 10px 20px; padding: 10px 20px;
margin: 0; margin: 0;
font-weight: 800; font-weight: 800;
font-size: 2em; font-size: 2em;
background: none; background: none;
} }
.NewsArticle:hover > .ArticleText a {
text-decoration: underline; .ArticleText p {
}
.ArticleText p {
padding: 10px 20px; padding: 10px 20px;
margin: 0; margin: 0;
font-size: 1.5em; font-size: 1.5em;
} }
.ArticleText {
.ArticleText {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 65%; width: 65%;
height: 100%; height: 100%;
}
.articleimage {
width: 35%;
background-position: center;
background-size: cover;
min-height: 18em;
background-repeat: no-repeat;
}
}
#news-container {
gap: 16px;
> h1 {
margin-bottom: 12px;
}
} }
.editmenu { .editmenu {
position: absolute; position: absolute;
@@ -2632,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;
} }
@@ -2712,7 +2815,7 @@ li.MessageList__unread___3imtO {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
color: var(--text-primary); color: var(--text-primary);
transition: 200ms; transition: 200ms, background-color 0s;
border-radius: 16px; border-radius: 16px;
} }
.dark .upcoming-items { .dark .upcoming-items {
@@ -2989,7 +3092,6 @@ li.MessageList__unread___3imtO {
width: 96%; width: 96%;
display: flex; display: flex;
margin: 0 auto; margin: 0 auto;
padding-bottom: 16px;
} }
.whatsnewImg { .whatsnewImg {
margin: 8px auto; margin: 8px auto;
@@ -3082,3 +3184,29 @@ li.MessageList__unread___3imtO {
object-fit: cover; object-fit: cover;
margin-bottom: 12px; margin-bottom: 12px;
} }
@keyframes shimmer {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}
.loading {
&.upcoming-items,
&.day-container {
background: linear-gradient(90deg,
var(--background-primary) 0%,
var(--background-secondary) 50%,
var(--background-primary) 100%
);
background-size: 1000px 100%;
animation: shimmer 2s infinite linear;
}
&.upcoming-items {
height: 35em;
}
}
+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;
+13 -5
View File
@@ -2,7 +2,7 @@
import { onMount } from 'svelte' import { onMount } from 'svelte'
import ColourPicker from './ColourPicker.tsx'; import ColourPicker from './ColourPicker.tsx';
import ReactAdapter from './utils/ReactAdapter.svelte'; import ReactAdapter from './utils/ReactAdapter.svelte';
import { animate, spring } from 'motion'; import { animate } from 'motion';
import { delay } from '@/seqta/utils/delay.ts' import { delay } from '@/seqta/utils/delay.ts'
const { hidePicker, standalone = false, savePresets = true, customOnChange = null, customState = null } = $props<{ const { hidePicker, standalone = false, savePresets = true, customOnChange = null, customState = null } = $props<{
@@ -23,13 +23,17 @@
animate( animate(
content, content,
{ scale: [1, 0.4], opacity: [1, 0] }, { scale: [1, 0.4], opacity: [1, 0] },
{ easing: spring({ stiffness: 400, damping: 30 }) } {
type: 'spring',
stiffness: 400,
damping: 30
}
); );
animate( animate(
background, background,
{ opacity: [1, 0] }, { opacity: [1, 0] },
{ easing: [0.4, 0, 0.2, 1] } { ease: [0.4, 0, 0.2, 1] }
); );
await delay(400); await delay(400);
@@ -43,13 +47,17 @@
animate( animate(
background, background,
{ opacity: [0, 1] }, { opacity: [0, 1] },
{ duration: 0.3, easing: [0.4, 0, 0.2, 1] } { duration: 0.3, ease: [0.4, 0, 0.2, 1] }
); );
animate( animate(
content, content,
{ scale: [0.4, 1], opacity: [0, 1] }, { scale: [0.4, 1], opacity: [0, 1] },
{ easing: spring({ stiffness: 400, damping: 30 }) } {
type: 'spring',
stiffness: 400,
damping: 30
}
); );
const handleEscapeKey = (e: KeyboardEvent) => { const handleEscapeKey = (e: KeyboardEvent) => {
+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) {
+13 -17
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy, createEventDispatcher } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import { animate as motionAnimate, spring } from 'motion'; import { animate as motionAnimate } from 'motion';
let { initial, animate, exit, transition, children, class: className } = $props<{ let { initial, animate, exit, transition, children, class: className } = $props<{
initial?: any, initial?: any,
@@ -12,11 +12,9 @@
}>(); }>();
let divElement: HTMLElement; let divElement: HTMLElement;
const dispatch = createEventDispatcher();
const playAnimation = (keyframe: any) => { const playAnimation = (keyframe: any) => {
if (divElement && keyframe) { if (divElement && keyframe) {
let animationOptions = transition;
let finalKeyframe = { ...keyframe }; let finalKeyframe = { ...keyframe };
if (finalKeyframe.height === 'auto') { if (finalKeyframe.height === 'auto') {
@@ -36,16 +34,18 @@
finalKeyframe.height = `${autoHeight}px`; finalKeyframe.height = `${autoHeight}px`;
} }
if (!transition || transition.type === 'spring') { const defaultSpringConfig = { stiffness: 250, damping: 25 };
const springConfig = transition?.config || { stiffness: 250, damping: 25 };
animationOptions = {
...transition,
easing: spring(springConfig)
};
}
const animation = motionAnimate(divElement, finalKeyframe, animationOptions); const animation = motionAnimate(
return animation.finished; [divElement],
finalKeyframe,
{
type: 'spring',
stiffness: transition?.stiffness || defaultSpringConfig.stiffness,
damping: transition?.damping || defaultSpringConfig.damping
}
);
return animation;
} }
return Promise.resolve(); return Promise.resolve();
}; };
@@ -57,16 +57,12 @@
} else if (animate) { } else if (animate) {
await playAnimation(animate); await playAnimation(animate);
} }
dispatch('animationend');
}); });
$effect(() => { $effect(() => {
if (animate) { if (animate) {
playAnimation(animate); playAnimation(animate);
} }
dispatch('animationend');
}); });
onDestroy(async () => { onDestroy(async () => {
+4 -2
View File
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { animate, spring } from 'motion'; import { animate } from 'motion';
import { standalone } from '../utils/standalone.svelte' import { standalone } from '../utils/standalone.svelte'
let { state, onChange } = $props<{ state: boolean, onChange: (newState: boolean) => void }>(); let { state, onChange } = $props<{ state: boolean, onChange: (newState: boolean) => void }>();
@@ -18,7 +18,9 @@
x: enabled ? (standalone.standalone ? 24 : 20) : 0, x: enabled ? (standalone.standalone ? 24 : 20) : 0,
}, },
{ {
easing: spring(springParams), type: 'spring',
stiffness: springParams.stiffness,
damping: springParams.damping,
} }
); );
}; };
@@ -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>
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { Theme } from '@/interface/types/Theme' import type { Theme } from '@/interface/types/Theme'
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { animate, spring } from 'motion'; import { animate } from 'motion';
let { theme, currentThemes, setDisplayTheme, onInstall, onRemove, allThemes, displayTheme } = $props<{ let { theme, currentThemes, setDisplayTheme, onInstall, onRemove, allThemes, displayTheme } = $props<{
theme: Theme | null; theme: Theme | null;
@@ -28,7 +28,11 @@
animate( animate(
modalElement, modalElement,
{ y: [500, 0], opacity: [0, 1] }, { y: [500, 0], opacity: [0, 1] },
{ easing: spring({ stiffness: 150, damping: 20 }) } {
type: 'spring',
stiffness: 150,
damping: 20
}
); );
} }
}); });
@@ -37,7 +41,11 @@
animate( animate(
modalElement, modalElement,
{ y: [10, 500], opacity: [1, 0] }, { y: [10, 500], opacity: [1, 0] },
{ easing: spring({ stiffness: 150, damping: 20 }) } {
type: 'spring',
stiffness: 150,
damping: 20
}
); );
setTimeout(() => { setTimeout(() => {
setDisplayTheme(relatedTheme ?? null); setDisplayTheme(relatedTheme ?? null);
@@ -46,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();
}} }}
@@ -71,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"/>
@@ -85,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"/>
@@ -104,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}
@@ -97,6 +97,16 @@
onChange: (isOn: boolean) => settingsState.notificationcollector = isOn onChange: (isOn: boolean) => settingsState.notificationcollector = isOn
} }
}, },
{
title: "Assessment Average",
description: "Shows your subject average for assessments.",
id: 8,
Component: Switch,
props: {
state: $settingsState.assessmentsAverage,
onChange: (isOn: boolean) => settingsState.assessmentsAverage = isOn
}
},
{ {
title: "Lesson Alerts", title: "Lesson Alerts",
description: "Sends a native browser notification ~5 minutes prior to lessons.", description: "Sends a native browser notification ~5 minutes prior to lessons.",
+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],
}, },
+4 -2
View File
@@ -1,8 +1,6 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "BetterSEQTA+", "name": "BetterSEQTA+",
"version": "3.4.0",
"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",
@@ -40,6 +38,10 @@
{ {
"resources": ["resources/icons/*"], "resources": ["resources/icons/*"],
"matches": ["*://*/*"] "matches": ["*://*/*"]
},
{
"resources": ["seqta/utils/migration/migrate.html"],
"matches": ["*://*/*"]
} }
] ]
} }
+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',
+74 -53
View File
@@ -6,17 +6,59 @@ import { settingsState } from "@/seqta/utils/listeners/SettingsState";
import { updateAllColors } from "./colors/Manager"; import { updateAllColors } from "./colors/Manager";
import { delay } from "@/seqta/utils/delay"; import { delay } from "@/seqta/utils/delay";
let cachedUserInfo: any = null;
async function getUserInfo() {
if (cachedUserInfo) return cachedUserInfo;
try {
const response = await fetch(`${location.origin}/seqta/student/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify({
mode: 'normal',
query: null,
redirect_url: location.origin,
}),
});
const responseData = await response.json();
cachedUserInfo = responseData.payload;
return cachedUserInfo;
} catch (error) {
console.error('Error fetching user info:', error);
throw error;
}
}
export async function AddBetterSEQTAElements() { export async function AddBetterSEQTAElements() {
if (settingsState.onoff) { if (settingsState.onoff) {
initializeSettings(); initializeSettings();
if (settingsState.DarkMode) { if (settingsState.DarkMode) {
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
} }
createHomeButton();
await appendBackgroundToUI(); const fragment = document.createDocumentFragment();
await handleUserInfo(); const menu = document.getElementById('menu')!;
handleStudentData(); const menuList = menu.firstChild as HTMLElement;
createNewsButton();
createHomeButton(fragment, menuList);
createNewsButton(fragment, menu);
menuList.insertBefore(fragment, menuList.firstChild);
try {
await Promise.all([
appendBackgroundToUI(),
handleUserInfo(),
handleStudentData()
]);
} catch (error) {
console.error('Error initializing UI elements:', error);
}
setupEventListeners(); setupEventListeners();
await addDarkLightToggle(); await addDarkLightToggle();
customizeMenuToggle(); customizeMenuToggle();
@@ -24,7 +66,6 @@ export async function AddBetterSEQTAElements() {
addExtensionSettings(); addExtensionSettings();
await createSettingsButton(); await createSettingsButton();
setupSettingsButton(); setupSettingsButton();
} }
@@ -33,18 +74,15 @@ function initializeSettings() {
updateBgDurations(); updateBgDurations();
} }
function createHomeButton() { function createHomeButton(fragment: DocumentFragment, menuList: HTMLElement) {
const container = document.getElementById('content')!; const container = document.getElementById('content')!;
const div = document.createElement('div'); const div = document.createElement('div');
div.classList.add('titlebar'); div.classList.add('titlebar');
container.append(div); container.append(div);
const NewButton = stringToHTML('<li class="item" data-key="home" id="homebutton" data-path="/home" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /></svg><span>Home</span></label></li>'); const NewButton = stringToHTML('<li class="item" data-key="home" id="homebutton" data-path="/home" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /></svg><span>Home</span></label></li>');
const menu = document.getElementById('menu')!;
const List = menu.firstChild! as HTMLElement;
if (NewButton.firstChild) { if (NewButton.firstChild) {
List.insertBefore(NewButton.firstChild, List.firstChild); fragment.appendChild(NewButton.firstChild);
} }
} }
@@ -121,30 +159,8 @@ async function handleStudentData() {
} }
} }
async function getUserInfo() {
try {
const response = await fetch(`${location.origin}/seqta/student/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify({
mode: 'normal',
query: null,
redirect_url: location.origin,
}),
});
const responseData = await response.json();
return responseData.payload;
} catch (error) {
console.error('Error fetching user info:', error);
throw error; // Rethrow the error after logging it
}
}
async function updateStudentInfo(students: any) { async function updateStudentInfo(students: any) {
const info = await getUserInfo(); // You would need to implement this to fetch or pass the user info const info = await getUserInfo();
var index = students.findIndex(function (person: any) { var index = students.findIndex(function (person: any) {
return ( return (
person.firstname == info.userDesc.split(' ')[0] && person.firstname == info.userDesc.split(' ')[0] &&
@@ -167,45 +183,50 @@ async function updateStudentInfo(students: any) {
} }
} }
} else { } else {
try {
houseelement.innerText = students[index].year; houseelement.innerText = students[index].year;
} catch(err) {
houseelement.innerText = 'N/A';
}
} }
} }
function createNewsButton() { function createNewsButton(fragment: DocumentFragment, menu: HTMLElement) {
const NewsButtonStr = '<li class="item" data-key="news" id="newsbutton" data-path="/news" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M20 3H4C2.89 3 2 3.89 2 5V19C2 20.11 2.89 21 4 21H20C21.11 21 22 20.11 22 19V5C22 3.89 21.11 3 20 3M5 7H10V13H5V7M19 17H5V15H19V17M19 13H12V11H19V13M19 9H12V7H19V9Z" /></svg><span>News</span></label></li>'; const NewsButtonStr = '<li class="item" data-key="news" id="newsbutton" data-path="/news" data-betterseqta="true"><label><svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M20 3H4C2.89 3 2 3.89 2 5V19C2 20.11 2.89 21 4 21H20C21.11 21 22 20.11 22 19V5C22 3.89 21.11 3 20 3M5 7H10V13H5V7M19 17H5V15H19V17M19 13H12V11H19V13M19 9H12V7H19V9Z" /></svg><span>News</span></label></li>';
const NewsButton = stringToHTML(NewsButtonStr); const NewsButton = stringToHTML(NewsButtonStr);
const menu = document.getElementById('menu')!;
const List = menu.firstChild! as HTMLElement;
List!.appendChild(NewsButton.firstChild!); if (NewsButton.firstChild) {
fragment.appendChild(NewsButton.firstChild);
}
let a = document.createElement('div'); let iconCover = document.createElement('div');
a.classList.add('icon-cover'); iconCover.classList.add('icon-cover');
a.id = 'icon-cover'; iconCover.id = 'icon-cover';
menu!.appendChild(a); menu.appendChild(iconCover);
} }
function setupEventListeners() { function setupEventListeners() {
const menuCover = document.querySelector('#icon-cover'); const menuCover = document.querySelector('#icon-cover');
menuCover!.addEventListener('click', function () {
location.href = '../#?page=/home';
loadHomePage();
(document!.getElementById('menu')!.firstChild! as HTMLElement).classList.remove('noscroll');
});
const homebutton = document.getElementById('homebutton'); const homebutton = document.getElementById('homebutton');
homebutton!.addEventListener('click', function () { const newsbutton = document.getElementById('newsbutton');
if (!homebutton?.classList.contains('draggable') && !homebutton?.classList.contains('active')) {
homebutton?.addEventListener('click', function() {
if (!homebutton.classList.contains('draggable') && !homebutton.classList.contains('active')) {
loadHomePage(); loadHomePage();
} }
}); });
const newsbutton = document.getElementById('newsbutton'); newsbutton?.addEventListener('click', function() {
newsbutton!.addEventListener('click', function () { if (!newsbutton.classList.contains('draggable') && !newsbutton.classList.contains('active')) {
if (!newsbutton?.classList.contains('draggable') && !newsbutton?.classList.contains('active')) {
SendNewsPage(); SendNewsPage();
} }
}); });
menuCover?.addEventListener('click', function() {
location.href = '../#?page=/home';
loadHomePage();
(document.getElementById('menu')!.firstChild! as HTMLElement).classList.remove('noscroll');
});
} }
async function createSettingsButton() { async function createSettingsButton() {
+15 -2
View File
@@ -1,7 +1,14 @@
import localforage from 'localforage'; import localforage from 'localforage';
import type { Theme } from '@/old-interface/pages/Store';
import base64ToBlob from '@/seqta/utils/base64ToBlob'; import base64ToBlob from '@/seqta/utils/base64ToBlob';
type Theme = {
name: string;
description: string;
coverImage: string;
marqueeImage: string;
id: string;
}
type ThemeContent = { type ThemeContent = {
id: string; id: string;
name: string; name: string;
@@ -14,6 +21,11 @@ type ThemeContent = {
images: { id: string, variableName: string, data: string }[]; // data: base64 images: { id: string, variableName: string, data: string }[]; // data: base64
}; };
function stripBase64Prefix(base64String: string): string {
const prefixRegex = /^data:image\/\w+;base64,/;
return base64String.replace(prefixRegex, '');
}
export const StoreDownloadTheme = async (theme: { themeContent: Theme }) => { export const StoreDownloadTheme = async (theme: { themeContent: Theme }) => {
if (!theme.themeContent.id) return; if (!theme.themeContent.id) return;
@@ -24,7 +36,8 @@ export const StoreDownloadTheme = async (theme: { themeContent: Theme }) => {
}; };
export const InstallTheme = async (themeData: ThemeContent) => { export const InstallTheme = async (themeData: ThemeContent) => {
const coverImageBlob = base64ToBlob(themeData.coverImage); const strippedCoverImage = stripBase64Prefix(themeData.coverImage);
const coverImageBlob = base64ToBlob(strippedCoverImage);
const images = themeData.images.map((image) => ({ const images = themeData.images.map((image) => ({
...image, ...image,
+18 -2
View File
@@ -46,10 +46,27 @@ class EventManager {
} }
const unregister = () => this.unregisterById(event, id); const unregister = () => this.unregisterById(event, id);
this.listeners.get(event)!.push({ id, options, callback, unregister }); this.listeners.get(event)!.push({ id, options, callback, unregister });
this.scanExistingElements(options, callback);
this.startObserving(options.parentElement); this.startObserving(options.parentElement);
return { unregister }; return { unregister };
} }
private async scanExistingElements(options: EventListenerOptions, callback: (element: Element) => void): Promise<void> {
const root = options.parentElement || document.documentElement;
const elements = Array.from(root.getElementsByTagName('*'));
elements.unshift(root);
for (let i = 0; i < elements.length; i += this.chunkSize) {
const chunk = elements.slice(i, i + this.chunkSize);
const filteredChunk = chunk.filter(element => this.matchesOptions(element, options));
for (const element of filteredChunk) {
callback(element);
}
}
}
public unregister(event: string): void { public unregister(event: string): void {
if (this.listeners.has(event)) { if (this.listeners.has(event)) {
this.listeners.delete(event); this.listeners.delete(event);
@@ -119,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);
} }
+17 -1
View File
@@ -44,7 +44,23 @@ const getSelectedBackground = (): string | null => {
const startMigration = async () => { const startMigration = async () => {
try { try {
console.info('Starting background extraction...'); console.info('Starting background extraction...');
const backgrounds = await getAllBackgrounds(); let backgrounds: Data[];
try {
backgrounds = await getAllBackgrounds();
if (!backgrounds || backgrounds.length === 0) {
console.info('No backgrounds to migrate');
window.parent.postMessage({ type: 'MIGRATION_COMPLETE' }, '*');
return;
}
} catch (error: any) {
if (error.name === 'NotFoundError' && error.message.includes('object stores was not found')) {
console.info('No backgrounds to migrate: object store not found');
window.parent.postMessage({ type: 'MIGRATION_COMPLETE' }, '*');
return;
}
console.error('Error fetching backgrounds:', error);
throw new Error('Failed to fetch backgrounds');
}
const selectedBackground = getSelectedBackground(); const selectedBackground = getSelectedBackground();
console.info(`Found ${backgrounds.length} backgrounds`); console.info(`Found ${backgrounds.length} backgrounds`);
+1
View File
@@ -38,6 +38,7 @@ export interface SettingsState {
defaultPage: string; defaultPage: string;
devMode?: boolean; devMode?: boolean;
originalDarkMode?: boolean; originalDarkMode?: boolean;
assessmentsAverage?: boolean;
} }
interface ToggleItem { interface ToggleItem {
+1 -8
View File
@@ -1,17 +1,10 @@
const { import flattenColorPalette from "tailwindcss/lib/util/flattenColorPalette";
default: flattenColorPalette,
} = require("tailwindcss/lib/util/flattenColorPalette");
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
content: [ content: [
"./src/**/*.{js,ts,jsx,tsx,html,svelte}", "./src/**/*.{js,ts,jsx,tsx,html,svelte}",
], ],
//safelist: [
//{
// pattern: / */,
//}
//],
darkMode: "class", darkMode: "class",
theme: { theme: {
fontSize: { fontSize: {
+18 -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: {
@@ -54,6 +56,19 @@ export default defineConfig({
port: 5173 port: 5173
} }
}, },
css: {
preprocessorOptions: {
scss: {
api: 'modern'
}
}
},
optimizeDeps: {
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,
@@ -65,4 +80,4 @@ export default defineConfig({
} }
} }
} }
}); }));