Compare commits
168 Commits
DereC4-der
...
v2.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
234f3d627d | ||
|
|
be1dccfcb9 | ||
|
|
454e5e807a | ||
|
|
29d20d5c5a | ||
|
|
e29546c727 | ||
|
|
5a89be6238 | ||
|
|
cfb5faa09b | ||
| 37471efb74 | |||
|
|
7a4f40a765 | ||
|
|
d11d55db66 | ||
|
|
76b6aa7c15 | ||
|
|
70d4fecad6 | ||
|
|
c3fa91752c | ||
|
|
7c2beef193 | ||
|
|
630d0d80d2 | ||
|
|
695743104c | ||
|
|
d014244b28 | ||
|
|
5cd56259f7 | ||
|
|
fa9f78b46e | ||
|
|
4a5f67f0fd | ||
|
|
3bed9cc27f | ||
|
|
0dcae25b93 | ||
|
|
ca734dcd39 | ||
|
|
9448072112 | ||
|
|
b1e98ca9d7 | ||
|
|
f036d409e6 | ||
|
|
5493c63f18 | ||
|
|
6c3139bf0f | ||
|
|
28ebb69612 | ||
|
|
008cb40cb8 | ||
|
|
195d3a219a | ||
|
|
c6452c4f2b | ||
|
|
3dbacf0d65 | ||
|
|
56a82baf40 | ||
|
|
20d210245b | ||
|
|
21cbcf677a | ||
|
|
8fb355d03b | ||
|
|
218477404f | ||
|
|
766c0bc1b4 | ||
|
|
4a8d0666c2 | ||
|
|
8fac10a743 | ||
|
|
1aa4e8c5fb | ||
|
|
ee4c6ce699 | ||
|
|
b171f01d01 | ||
|
|
846070ebb5 | ||
|
|
9c766c2695 | ||
|
|
4752f5860a | ||
|
|
c2328e461e | ||
|
|
aa29bcf9fe | ||
|
|
f0b257aa12 | ||
|
|
93733e3721 | ||
|
|
501f506677 | ||
|
|
009de62828 | ||
|
|
b74c698866 | ||
| cd05e5e7fc | |||
|
|
f425510e11 | ||
|
|
1f635d2515 | ||
|
|
a61bddf0e8 | ||
|
|
843cb5b443 | ||
|
|
52347fd56d | ||
|
|
a20332e53d | ||
|
|
0aa469af81 | ||
|
|
0d73b13b28 | ||
|
|
24256798ba | ||
|
|
e61ab565c3 | ||
|
|
9cbcfa4720 | ||
|
|
c712fc741e | ||
|
|
ca5e4c13d3 | ||
|
|
37bd7e79d9 | ||
|
|
918f4e419c | ||
| 3abb11734a | |||
|
|
a5e921fd75 | ||
|
|
7dd93690d6 | ||
| 7dbffc6eef | |||
|
|
8b922082a7 | ||
|
|
19e3838df2 | ||
| 25e3ba1ed4 | |||
|
|
598bafe67f | ||
|
|
ebcc0aa76a | ||
|
|
e987fbbe8e | ||
| c41467c617 | |||
|
|
9ad32390d1 | ||
|
|
0d51cae4c8 | ||
|
|
91fa78e2d0 | ||
|
|
b13e217a5b | ||
|
|
b732a80eaa | ||
|
|
46c76b1703 | ||
|
|
db04bbb52e | ||
|
|
e425daf041 | ||
|
|
93548627a6 | ||
|
|
638ee88c96 | ||
|
|
50e88fa015 | ||
|
|
b3ae91d8f3 | ||
|
|
0077ae70d2 | ||
|
|
94744e01b9 | ||
|
|
8de88d6ad7 | ||
|
|
2d0804f90e | ||
|
|
d3577358d0 | ||
|
|
7346720894 | ||
|
|
b00bf6c180 | ||
|
|
eb306787ae | ||
|
|
643ea13207 | ||
|
|
6f1afc5b25 | ||
|
|
83d76f72da | ||
|
|
768ac776ed | ||
|
|
9995b93d2a | ||
|
|
4f609aeec7 | ||
|
|
b6eccaca6a | ||
|
|
cef99c2d72 | ||
|
|
86792eb56f | ||
|
|
a715bbd933 | ||
|
|
c2007ef090 | ||
|
|
b967240f8f | ||
|
|
839f9c6d6a | ||
|
|
f29e3ef97d | ||
|
|
79dd29cfc9 | ||
|
|
e082158592 | ||
|
|
e261641e59 | ||
|
|
5634fbed8a | ||
|
|
ceba38b1ac | ||
|
|
05f00b23d2 | ||
|
|
59f173c4e7 | ||
|
|
aeff5e09a2 | ||
|
|
4629626a31 | ||
|
|
4cf8c3f964 | ||
|
|
58eed49196 | ||
|
|
2983a0c0fd | ||
|
|
d22237d561 | ||
| e774f316e3 | |||
|
|
187a4c8555 | ||
|
|
65ff6bfbbf | ||
|
|
bd17e33537 | ||
| aede681d4b | |||
|
|
db1eac33a2 | ||
|
|
90861d7ee7 | ||
|
|
7a5c3a2e62 | ||
|
|
d73615e281 | ||
|
|
dcc1d81a74 | ||
|
|
b3632c0196 | ||
| 668c8d0075 | |||
| da6d86c785 | |||
|
|
575e92ead1 | ||
|
|
5f3d25fd75 | ||
|
|
952e14a5e7 | ||
|
|
895cd31d8e | ||
|
|
0da27e2c46 | ||
|
|
4936e9ca41 | ||
|
|
290b841517 | ||
|
|
99c982d6e1 | ||
|
|
15fc3699cf | ||
|
|
9ec05ef764 | ||
|
|
88eeb620ae | ||
|
|
6a363aeb5c | ||
|
|
c2cab407f3 | ||
|
|
08652c4ac1 | ||
|
|
1942508d8d | ||
| a4d2856bfc | |||
|
|
e8d2c2e142 | ||
|
|
4c8f03ca47 | ||
| 9971435716 | |||
|
|
b4dd91ad25 | ||
|
|
8bb6f901dc | ||
| 5ca24dab82 | |||
|
|
3ff06e043b | ||
|
|
e2cbfa3f48 | ||
|
|
857007c13c | ||
|
|
d16dfc1107 | ||
| abae7a5c22 |
242
.dockerignore
Normal file
242
.dockerignore
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node,react,storybookjs
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,node,react,storybookjs
|
||||||
|
|
||||||
|
### macOS ###
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### macOS Patch ###
|
||||||
|
# iCloud generated files
|
||||||
|
*.icloud
|
||||||
|
|
||||||
|
### Node ###
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-te
|
||||||
|
node_modulesst
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
### Node Patch ###
|
||||||
|
# Serverless Webpack directories
|
||||||
|
.webpack/
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
# SvelteKit build / generate output
|
||||||
|
.svelte-kit
|
||||||
|
|
||||||
|
### react ###
|
||||||
|
.DS_*
|
||||||
|
**/*.backup.*
|
||||||
|
**/*.back.*
|
||||||
|
|
||||||
|
*.sublime*
|
||||||
|
|
||||||
|
psd
|
||||||
|
thumb
|
||||||
|
sketch
|
||||||
|
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
.ionide
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node,react,storybookjs
|
||||||
|
|
||||||
|
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
|
||||||
|
package-lock.json
|
||||||
|
storybook-static/
|
||||||
|
package/
|
||||||
|
|
||||||
|
# Version control
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
.pnpm-store
|
||||||
|
!pnpm-lock.yaml
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
docker-compose*
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
README.md
|
||||||
|
CHANGELOG.md
|
||||||
|
DOCKER_DEV_SETUP.md
|
||||||
|
docs/
|
||||||
@@ -23,6 +23,7 @@ module.exports = {
|
|||||||
'import',
|
'import',
|
||||||
'import-essentials',
|
'import-essentials',
|
||||||
'jsdoc',
|
'jsdoc',
|
||||||
|
'eslint-plugin-tsdoc',
|
||||||
'react-prefer-function-component',
|
'react-prefer-function-component',
|
||||||
'@typescript-eslint',
|
'@typescript-eslint',
|
||||||
'simple-import-sort',
|
'simple-import-sort',
|
||||||
@@ -100,6 +101,12 @@ module.exports = {
|
|||||||
'import/no-cycle': 'off',
|
'import/no-cycle': 'off',
|
||||||
'import/no-extraneous-dependencies': 'off',
|
'import/no-extraneous-dependencies': 'off',
|
||||||
'react/jsx-props-no-spreading': 'off',
|
'react/jsx-props-no-spreading': 'off',
|
||||||
|
'react/jsx-no-useless-fragment': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allowExpressions: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
'keyword-spacing': [
|
'keyword-spacing': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
@@ -137,7 +144,7 @@ module.exports = {
|
|||||||
'jsdoc/newline-after-description': 'off',
|
'jsdoc/newline-after-description': 'off',
|
||||||
'react/require-default-props': 'off',
|
'react/require-default-props': 'off',
|
||||||
'jsdoc/require-jsdoc': [
|
'jsdoc/require-jsdoc': [
|
||||||
'warn',
|
'error',
|
||||||
{
|
{
|
||||||
enableFixer: false,
|
enableFixer: false,
|
||||||
publicOnly: true,
|
publicOnly: true,
|
||||||
@@ -165,8 +172,16 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'tsdoc/syntax': 'error',
|
||||||
'@typescript-eslint/no-explicit-any': 'error',
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrorsIgnorePattern: '^_',
|
||||||
|
},
|
||||||
|
],
|
||||||
'@typescript-eslint/naming-convention': 'off',
|
'@typescript-eslint/naming-convention': 'off',
|
||||||
'@typescript-eslint/space-before-function-paren': 'off',
|
'@typescript-eslint/space-before-function-paren': 'off',
|
||||||
'@typescript-eslint/ban-ts-comment': 'off',
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
|||||||
59
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
59
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: "[BUG] "
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Pre-submission Checklist**
|
||||||
|
|
||||||
|
<!-- Please check all applicable boxes before submitting your issue -->
|
||||||
|
|
||||||
|
- [ ] I have searched existing issues to make sure this is not a duplicate
|
||||||
|
- [ ] I have cleared my browser cache and confirmed the issue persists
|
||||||
|
- [ ] I have checked this issue affects the latest version of the extension
|
||||||
|
- [ ] I have disabled other extensions to ensure this isn't a conflict issue
|
||||||
|
- [ ] I have included all the information requested below
|
||||||
|
|
||||||
|
**Bug Description**
|
||||||
|
|
||||||
|
<!-- A clear and concise description of what the bug is -->
|
||||||
|
|
||||||
|
**Steps To Reproduce**
|
||||||
|
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected Behavior**
|
||||||
|
|
||||||
|
<!-- A clear and concise description of what you expected to happen -->
|
||||||
|
|
||||||
|
**Current Behavior**
|
||||||
|
|
||||||
|
<!-- A clear and concise description of what actually happened -->
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
|
||||||
|
<!-- If applicable, add screenshots to help explain your problem -->
|
||||||
|
|
||||||
|
**Debug Information**
|
||||||
|
|
||||||
|
- UTRP Extension Version: [e.g. 1.2.0]
|
||||||
|
- Browser Info: [e.g. Chrome 120.0.0]
|
||||||
|
- OS: [e.g. Windows 11, macOS Sonoma]
|
||||||
|
|
||||||
|
**Console Logs**
|
||||||
|
|
||||||
|
<!-- Please check the browser console (F12) and paste any relevant error messages -->
|
||||||
|
|
||||||
|
```
|
||||||
|
// Paste console logs here
|
||||||
|
```
|
||||||
|
|
||||||
|
**Additional Context**
|
||||||
|
|
||||||
|
<!-- Add any other context about the problem here -->
|
||||||
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: Feature Request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: "[FEATURE] "
|
||||||
|
labels: feature
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Pre-submission Checklist**
|
||||||
|
|
||||||
|
<!-- Please check all applicable boxes before submitting your feature request -->
|
||||||
|
|
||||||
|
- [ ] I have searched existing issues to ensure this feature hasn't been requested
|
||||||
|
- [ ] I have searched closed issues to check if this was previously rejected/implemented
|
||||||
|
- [ ] I have checked the project roadmap (if available) for planned similar features
|
||||||
|
- [ ] I have reviewed the documentation to confirm this feature doesn't exist
|
||||||
|
- [ ] I have completed all sections below with detailed information
|
||||||
|
|
||||||
|
**Feature Description**
|
||||||
|
|
||||||
|
<!-- A clear and concise description of the feature you'd like to see -->
|
||||||
|
|
||||||
|
**Proposed Solution**
|
||||||
|
|
||||||
|
<!-- A clear and concise description of what you want to happen -->
|
||||||
|
|
||||||
|
**UI/UX Considerations**
|
||||||
|
|
||||||
|
<!-- If this feature involves UI changes, please describe the visual aspects -->
|
||||||
|
|
||||||
|
**Technical Implementation Details**
|
||||||
|
|
||||||
|
<!-- If you have specific technical suggestions, list them here -->
|
||||||
12
.github/ISSUE_TEMPLATE/updating-build-dependencies.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/updating-build-dependencies.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: Updating Build Dependencies
|
||||||
|
about: Updating Build Dependencies
|
||||||
|
title: ''
|
||||||
|
labels: build, dependencies
|
||||||
|
assignees: doprz, Razboy20
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] Updated Nix Flake
|
||||||
|
- [ ] Update Dockerfile
|
||||||
|
- [ ] Update Docs
|
||||||
21
.github/dependabot.yml
vendored
Normal file
21
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: 'npm'
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: 'weekly'
|
||||||
|
day: 'monday'
|
||||||
|
time: '09:00'
|
||||||
|
timezone: 'America/Chicago'
|
||||||
|
groups:
|
||||||
|
minor-and-patch-updates:
|
||||||
|
update-types:
|
||||||
|
- 'minor'
|
||||||
|
- 'patch'
|
||||||
|
major-updates:
|
||||||
|
update-types:
|
||||||
|
- 'major'
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- dependency-name: '@crxjs/vite-plugin'
|
||||||
|
- dependency-name: '@unocss/vite'
|
||||||
8
.github/workflows/best-practices.yml
vendored
8
.github/workflows/best-practices.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v3
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 9
|
version: 10
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
@@ -32,9 +32,9 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v3
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 9
|
version: 10
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|||||||
4
.github/workflows/check-types.yml
vendored
4
.github/workflows/check-types.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v3
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 9
|
version: 10
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|||||||
4
.github/workflows/chromatic.yml
vendored
4
.github/workflows/chromatic.yml
vendored
@@ -11,9 +11,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v3
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 9
|
version: 10
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|||||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -13,9 +13,9 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v3
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 9
|
version: 10
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -210,3 +210,4 @@ sketch
|
|||||||
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
|
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
|
||||||
package-lock.json
|
package-lock.json
|
||||||
storybook-static/
|
storybook-static/
|
||||||
|
package/
|
||||||
|
|||||||
1
@types/vite-env.d.ts
vendored
1
@types/vite-env.d.ts
vendored
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly VITE_PACKAGE_VERSION: string;
|
readonly VITE_PACKAGE_VERSION: string;
|
||||||
|
readonly VITE_SENTRY_ENVIRONMENT: string;
|
||||||
readonly VITE_BETA_BUILD?: 'true';
|
readonly VITE_BETA_BUILD?: 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
372
CHANGELOG.md
Normal file
372
CHANGELOG.md
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
## [2.2.1](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.2.0...v2.2.1) (2025-06-04)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add dining app promo ([#598](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/598)) ([be1dccf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/be1dccfcb9d052c6b291b50cc53418d6bb645beb))
|
||||||
|
* inside jokes005 ([#590](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/590)) ([37471ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/37471efb740c7a5828cf3b54bac70954694359d7))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* course columns on calendar ([#587](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/587)) ([cfb5faa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cfb5faa09bb0788e270d100f1f36536a53bcff75))
|
||||||
|
* hide sentry instrumentation on debug builds ([#604](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/604)) ([454e5e8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/454e5e807af29ae0384cc3a3b8b691df5edc69d1))
|
||||||
|
## [2.2.0](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.1.1...v2.2.0) (2025-04-06)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* auto create empty schedule when deleted all schedules ([#552](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/552)) ([7c2beef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7c2beef1930fbc887e8ec1aea789016b3150cd21))
|
||||||
|
* ensure unique splash text on schedule change ([#554](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/554)) ([9448072](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/94480721124e052426c1f3236e8605c7088df79c))
|
||||||
|
* implement a What's New prompt ([#539](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/539)) ([f036d40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f036d409e60a39fd1d3cb2f0db53a6056615f336))
|
||||||
|
* persist sidebar toggle state ([#569](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/569)) ([6957431](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/695743104c57951ba1957258c60c843f8fae793f))
|
||||||
|
* recruitment banner for designer ([#578](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/578)) ([70d4fec](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/70d4fecad61ec3cd3ba839de302fd851e075d073))
|
||||||
|
* **release:** v2.2.0 ([7a4f40a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a4f40a765d704bf32a3b515d695916ed84f9397))
|
||||||
|
* rework start time to checkboxes ([#553](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/553)) ([ca734dc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ca734dcd39a433cfd2e930ea04adeba959b32c36))
|
||||||
|
* sticky calendar header and days ([#568](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/568)) ([fa9f78b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fa9f78b46e3a2270a44d4cc0691195a7c695cb93))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ics calendar export dates ([#535](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/535)) ([4a5f67f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4a5f67f0fda9f0ef57f821e4b7a55d63f099f579))
|
||||||
|
* include logo in screenshot, fix screenshots on small/zoomed windows ([#579](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/579)) ([76b6aa7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/76b6aa7c150299dfcfa4b3dc00ce2de32f90f75c))
|
||||||
|
* merge course labels across pages ([#541](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/541)) ([6c3139b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6c3139bf0f324c9a7be826b6c24e8bf142fc53b1))
|
||||||
|
* **schedule:** truncate long schedule names in popup ([#564](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/564)) ([3bed9cc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3bed9cc27febfe795af0766a913c4845e74cc2da))
|
||||||
|
## [2.1.1](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.1.0...v2.1.1) (2025-03-03)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add isDeveloper ([c6452c4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c6452c4f2b174487e6f51ad546ca1c3f8b4dbc1f))
|
||||||
|
* map page ([#390](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/390)) ([2184774](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/218477404fdeacda7b39cd233e4e1e65995935d4))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* import schedule file upload button ([#515](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/515)) ([766c0bc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/766c0bc1b4a75f8146a922cb2eca8871032c1dc9))
|
||||||
|
## [2.1.0](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.2...v2.1.0) (2025-02-20)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add 'new search' link to the course catalog page ([#456](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/456)) ([ca5e4c1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ca5e4c13d31aeb603660972536712ea161c6f870))
|
||||||
|
* add 404 page ([#426](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/426)) ([46c76b1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/46c76b1703ea2344c3330c4cfa82560663be1c4c))
|
||||||
|
* add CacheStore for GitHub stats and use names instead of usernames ([#405](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/405)) ([b732a80](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b732a80eaa6f6cbf5b627ffc31ab321de3a8e8f4))
|
||||||
|
* add eslint-plugin-tsdoc ([#430](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/430)) ([e987fbb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e987fbbe8e733de6767d62811c2d4d6eaccf2d24))
|
||||||
|
* add explanation to grade distribution ([#325](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/325)) ([9ad3239](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9ad32390d13e1d413ca01b1bc45d02c695bc23dd))
|
||||||
|
* add open calendar button ([#457](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/457)) ([93733e3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93733e37214f00543479e77209ca03864776a1a6))
|
||||||
|
* add spacing system ([#474](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/474)) ([e61ab56](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e61ab565c35a197609d205adcaac9c7ffe0fc6da))
|
||||||
|
* **build:** add Docker support ([#322](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/322)) ([a5e921f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a5e921fd75109a50f17d05c9682e73ba246f3dd1))
|
||||||
|
* **build:** add vite-build-logger ([#507](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/507)) ([1aa4e8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1aa4e8c5fb2d1aea308e4cccea3e2818a9c946ec))
|
||||||
|
* **build:** refactor gulpfile to use gulp-execa ([#323](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/323)) ([db04bbb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/db04bbb52e4ab19730dd66f417e68ab013ce2f79))
|
||||||
|
* export/import functionality (backup/restore/share with friends) + a new input component ([#433](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/433)) ([7dbffc6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7dbffc6eef346747042f1596da627ad0a2fcae1a))
|
||||||
|
* injected button - add all courses from MyUT AND passing URL to handler ([#291](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/291)) ([c41467c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c41467c6176d31f70cd3ecdcf56eeb73696c6c23))
|
||||||
|
* modify Course Block text style and time and location text ([#409](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/409)) ([0d51cae](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0d51cae4c8b27201b09c0898f4d7a6e7abb3c100))
|
||||||
|
* **settings:** add option to always open calendar in new tab ([#488](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/488)) ([009de62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/009de628285ce9c6571e492f8e3f52cdeeed4459))
|
||||||
|
* **settings:** allow disabling of auto-loading courses ([#489](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/489)) ([b74c698](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b74c698866c1074ce2236ede549c94555667e6a5))
|
||||||
|
* **ui:** add schedule list icons ([#500](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/500)) ([f0b257a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f0b257aa124a8fff0bb31e3396715aecb09948d5))
|
||||||
|
* **ui:** added shadows to popup buttons and course blocks ([#378](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/378)) ([a20332e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a20332e53da9bda873fabb8395afe6ff6303799c))
|
||||||
|
* **ui:** calendar header redesign ([#479](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/479)) ([9c766c2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9c766c26959673389e372c3a440c1a73d5887b1d))
|
||||||
|
* **ui:** calendar sidebar redesign ([#464](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/464)) ([843cb5b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/843cb5b4430885527592feee33656dfae50c95c2))
|
||||||
|
* **ui:** change icons to phosphor-icons [#467](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/467) ([#469](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/469)) ([37bd7e7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/37bd7e79d9202d55c7d0f566518526b0aed53c68))
|
||||||
|
* **ui:** change red text when instructor not found ([#483](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/483)) ([52347fd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/52347fd56dff95190915d9f375dbebe1383e76c8))
|
||||||
|
* **ui:** color picker final touches ([#491](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/491)) ([c2328e4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c2328e461ea0a846f399a22bc525540da2796dcd))
|
||||||
|
* **ui:** course color picker ([#382](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/382)) ([1f635d2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1f635d2515fc403ad7f08fc3a244a17d262e3f7b))
|
||||||
|
* **ui:** course unique number copy button ([#490](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/490)) ([501f506](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/501f50667725aa1515391869b133908b8383d868))
|
||||||
|
* **ui:** Modify Calendar Footer design and Unscheduled courses ([#503](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/503)) ([b171f01](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b171f01d01f47c75e7df5e5c8e4e7b15b7397c52))
|
||||||
|
* **ui:** redesign grade distribution tooltip ([#485](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/485)) ([a61bddf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a61bddf0e8a76c89d4963d9e580fb063309bee92))
|
||||||
|
* **ui:** update button variants following figma ([#482](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/482)) ([0aa469a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0aa469af81e7912a6b26ee1b80c5920f677b5fbd))
|
||||||
|
* **ui:** update popup and course blocks ([#506](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/506)) ([ee4c6ce](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ee4c6ce6999ad35821b9be5d657790f2dee017b3))
|
||||||
|
* **ui:** update theme colors [#466](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/466) ([#473](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/473)) ([0d73b13](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0d73b13b288bf8b18444f26455da24c2f4acedf6))
|
||||||
|
* update SWE list ([aa29bcf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aa29bcf9fe4bad7812336d55d7575a6032aea91c))
|
||||||
|
* update text styles ([#468](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/468)) ([918f4e4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/918f4e419cf5fa71bda1112597e9a373daca69ea)), closes [#465](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/465)
|
||||||
|
* validate login passed to background and implemented into add all injected button ([#443](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/443)) ([cd05e5e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cd05e5e7fcaa02dab30f230c178f189d8052a7c9))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 4th attempt for: now able to delete schedule even if active ([#435](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/435)) ([2425679](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/24256798ba7b877dd170d77ce4970b13b8a69a68))
|
||||||
|
* instructor formatting errors ([#425](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/425)) ([8b92208](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b922082a79ebdbf50a09f37ecb9dfea1be6e1f3))
|
||||||
|
* place hours and courses under schedule name ([#388](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/388)) ([7dd9369](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7dd93690d6dbd206cb50442f42ec85d6bbfc1da8))
|
||||||
|
* transition added ([#381](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/381)) ([598bafe](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/598bafe67f47ad57b3801a425e61f86093ba2be8))
|
||||||
|
* **ui:** fix longstanding drag-and-drop duplication issue ([#502](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/502)) ([4752f58](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4752f5860a96e08a1177c0ddf57a0fa269d89072))
|
||||||
|
* **ui:** reduce left side grade distribution margin/padding ([#427](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/427)) ([91fa78e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/91fa78e2d0532a73e24ecc013d2ba6c0f62c1fcd))
|
||||||
|
* **ui:** stop import button dropdown from squishing ([#504](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/504)) ([846070e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/846070ebb5e13e0fc0df94666dbce75194100049))
|
||||||
|
* updated text when time/location not provided ([#289](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/289)) ([ebcc0aa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ebcc0aa76a89f0d1a9e90cfc50d70f017c9bed42))
|
||||||
|
## [2.0.2](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.1...v2.0.2) (2024-11-05)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add core curriculum chips to injected popup ([#372](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/372)) ([6f1afc5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6f1afc5b25441c6a1fbfdf57b3c8b5b74e36f5a0))
|
||||||
|
* Add linkedin social to calendar ([#368](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/368)) ([b6eccac](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b6eccaca6a2cdba9b57d2f49f064ae8504bbd5cb))
|
||||||
|
* add more relevant links to the From the Team section ([#380](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/380)) ([643ea13](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/643ea1320798aabb7783d267f5e6fd7c00fc2e3f))
|
||||||
|
* bold course number in grade distribution chart, change text to ut-black ([#406](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/406)) ([638ee88](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/638ee88c96510a779c157b524903caaeffc9ef19))
|
||||||
|
* disable/some actions when no instructor ([#319](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/319)) ([839f9c6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/839f9c6d6afd4a1eae1a0bdf8893ab2e19b9fdff))
|
||||||
|
* **ui:** changed popup close icon to ut-black ([#394](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/394)) ([0077ae7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0077ae70d22f24549c4c3b243188d19adbfbac14)), closes [#333F48](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/333F48)
|
||||||
|
* update senior swe admins ([#326](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/326)) ([b967240](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b967240f8fbb7a790a78f4aa256f0a77a491abb8))
|
||||||
|
* update useful links ([#367](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/367)) ([cef99c2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cef99c2d72d3a2800f8a918d01cb116f8795d0c8))
|
||||||
|
* use "copy of" for duplicated schedules and place them under the original schedule [#358](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/358) ([#397](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/397)) ([94744e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/94744e01b94819fb4f5d64616ea56857b906c2dd))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* added descending sort for commits on contributor section in settings page ([#365](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/365)) ([a715bbd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a715bbd933a87742e7bce3a44e8ba1bd419ad5eb)), closes [#363](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/363) [#363](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/363) [#363](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/363)
|
||||||
|
* change schedule total courses text color to UTRP black ([#369](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/369)) ([b00bf6c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b00bf6c180f1c6c3a61c5ef855e160ddf4af3ea4))
|
||||||
|
* changed the font-weight of h1-course ([#370](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/370)) ([4f609ae](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4f609aeec797c1f99f0a57e5aeef7b82756ea4bc)), closes [#347](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/347)
|
||||||
|
* ensure input elements take full width of parent ([#364](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/364)) ([c2007ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c2007ef090aab3bbfcb8bca1ebc476255d09cb90))
|
||||||
|
* remove screenshot padding class for png download for [#344](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/344) ([#376](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/376)) ([768ac77](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/768ac776ed4d5ca2113a032a93c2dc7432915aa1)), closes [#334](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/334)
|
||||||
|
* sentry issues ([#389](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/389)) ([2d0804f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2d0804f90e5d7a9ff83f7fd5c5acfdc7c1b1cc84))
|
||||||
|
* typo in settings page ([#386](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/386)) ([d357735](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d3577358d0d1fb60f2c776ae4b01e255fcf9109e))
|
||||||
|
* **ui:** add space before/after forward slash in "ASYNC/OTHER" text ([#366](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/366)) ([86792eb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/86792eb56f04b615f7d52b2f417b88f4cb9a82ec))
|
||||||
|
* **ui:** duplicate schedule warning ([#295](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/295)) ([7346720](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/73467208947e0116ce8538052ee75dea1d8038f9))
|
||||||
|
* **ui:** main popup now shows 0 for empty schedule ([#395](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/395)) ([8de88d6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8de88d6ad7d4c2b5c3aa08e1efc59f7226b40c6b))
|
||||||
|
* **ui:** multiple instructors are formatted properly, displays last name only, and are capitalized in all course blocks ([#342](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/342)) ([#403](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/403)) ([50e88fa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/50e88fa015e0290fbe0dab8a19f8fcdbc4dd02b0))
|
||||||
|
* **ui:** placeholder text for no instructor course [#400](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/400) ([#402](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/402)) ([b3ae91d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b3ae91d8f3cebb89e5e5cea7f1200d28326afb4d))
|
||||||
|
## [2.0.1](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.0...v2.0.1) (2024-10-17)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* spring 2024 instructors db ([#317](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/317)) ([79dd29c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/79dd29cfc9e849b09e7d91bd0eed51c1c93b3352))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add a little error checking to settings ([#315](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/315)) ([e261641](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e261641e5985d8bd5047d8a0be5d1caae844e40f))
|
||||||
|
* gulp zip ([#314](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/314)) ([05f00b2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/05f00b23d26b90f564710db4364426e90c8d6831))
|
||||||
|
* migration loop ([aeff5e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aeff5e09a238503293c3882d97d40270da1e4883))
|
||||||
|
* show calendar in active window ([#312](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/312)) ([ceba38b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ceba38b1ac74ec9e6630222183bd466a8d12c27d))
|
||||||
|
## [2.0.0](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/b4e8c7589e53f1064d70703459cc6d66fae1b04c...v2.0.0) (2024-10-15)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* abhinavchadaga/course-catalog-popup ([#128](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/128)) ([745f9dd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/745f9dd6fb96ccc0eace7189b87abf9a38f03828))
|
||||||
|
* abhinavchadaga/reusable-popup-prompt ([#148](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/148)) ([44af9e1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/44af9e16e4bf3b92c9a4fa0e779197f4f9ecb237))
|
||||||
|
* actually sum for duplicate semesters (different uniques) ([#202](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/202)) ([d3f64ec](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d3f64ec79eb64ebe72ae28991127dbe521823842))
|
||||||
|
* add button to the rows, use new ConflictsWithWarning component ([bec2649](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bec2649fc1244bffb5cb03809db62cc0ea477260))
|
||||||
|
* add buttons with icons in tailwind ([93b65ac](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93b65ac2edef3fc728305cc51d1a32ecf5073274))
|
||||||
|
* add cal save buttons (no functionality) ([53e7c7f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/53e7c7fedaf1f268d2d7d06ff1a5df9e2af2306d))
|
||||||
|
* add Calendar Component ([e0212d5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e0212d510928e5e95d3ba75e391c34714f1bb035))
|
||||||
|
* add Calendar schedules component (clicking on storybook not working) ([d1a336e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d1a336e903f09b04b89f11ba7ede2f7d26e0ac3c))
|
||||||
|
* add CalendarHeader and its Storybook, need to resize ([23276e5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/23276e5c7c907261b1d02e9006d8021d99b43984))
|
||||||
|
* add check-path-alias custom ESLint rule ([#123](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/123)) ([208103d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/208103d7081abcf5b31bf36335320aaf7d213464))
|
||||||
|
* add chrome.storage api mocks for storybook use ([#141](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/141)) ([3839bff](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3839bff29ef6f0ecfad1e975e685df9a64d96c92))
|
||||||
|
* add CSS for calendarCell div ([6cdcf49](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6cdcf4930d9868c0ed45820f66102d89a644cfb6))
|
||||||
|
* add CSS for hourLine div ([0ba6153](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0ba61534cbfc83408cb64eb1145ecacef1ee8f04))
|
||||||
|
* add CSS for timeLabelContainer div ([56f6456](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/56f6456ce85f77edb9d6fa64ecb82a8388feec41))
|
||||||
|
* add custom ESLint rule restrict-import-depth ([#110](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/110)) ([8c069b7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8c069b7ad339ed8db97bb5e3e6e88ab8d332489b))
|
||||||
|
* add downloadBlob util ([2af351e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2af351efa85264da5abeee27fbd091d4ce637041))
|
||||||
|
* add empty settings component - waiting on design ([9d0f210](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9d0f2105481cc3d5affdb4bd69d428379ea92de3))
|
||||||
|
* add ImportantLinks Component ([5dbee6f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5dbee6f0c39e620c3b65a5e0156fd59ec8217ca7))
|
||||||
|
* add List component ([e6b4049](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e6b40494031ca88894b2f918bb18448b70124481))
|
||||||
|
* add MIMEType ([0c76052](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c760524781556cf089f1885dc4ff3fd567bfc2b))
|
||||||
|
* add new db powered by UT_Grade_Parser ([#163](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/163)) ([60d1f48](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/60d1f48bd95132f0517258f017a7644d8aff5101))
|
||||||
|
* add react-loading-skeleton package ([#244](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/244)) ([b4dd91a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4dd91ad2595d9583a60530c2c2ff6d829d2d5e5))
|
||||||
|
* add skeleton loader for course description + distribution ([#267](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/267)) ([c2cab40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c2cab407f3a07c1d3073e936c9d23cb1cecc9cb9))
|
||||||
|
* add story for CalendarGridCell ([fd91c3b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fd91c3b12ef5670563e689d190be75ef1f695d1c))
|
||||||
|
* add Storybook story ([cb3cb5d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cb3cb5d5fc18a68ca5a825f97b808b535aa1fe29))
|
||||||
|
* add tailwind version of Button component ([28d93b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/28d93b3c25e1cbca3baea992a299d626ca823570))
|
||||||
|
* add tickmarks to day div ([8b9cb06](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b9cb065c28b5dd38b7e1681839a81f55691a3c6))
|
||||||
|
* add time column to Calendar Component ([9d68211](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9d6821127e7ff9bf63cef659932ee7408de4c969))
|
||||||
|
* add timeAndGrid div ([95e0544](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/95e0544b7301d382439b74038fb1b3e25dfe6d30))
|
||||||
|
* added flag ;-; ([#195](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/195)) ([9b4d61c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9b4d61c2b0d8806b5d4a10296c1cc1266ac4229f))
|
||||||
|
* added scrapedAt property ([#149](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/149)) ([8e181b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e181b3010dfe25ea1e4b4ba181cbe9944d6e92e))
|
||||||
|
* additional changes to [#201](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/201) ([#224](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/224)) ([bc354f3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bc354f3798a02e6ebc3c66a8b5eee553c25a19d4))
|
||||||
|
* aesthetically pleasing squishier course blocks when compressed ([#232](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/232)) ([438c82b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/438c82bfb5db0f570b261d15368a05447939b0fb))
|
||||||
|
* align day labels and add to grid ([1bb6191](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1bb6191244f40220a9af66cced4fe8f0c1e546a0))
|
||||||
|
* alignment on calendar and header ([#109](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/109)) ([07ec5ab](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/07ec5abc3e1b40b801b7c977d21399b31222a4d3))
|
||||||
|
* async course adding and async/other course block adjustments ([#273](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/273)) ([668c8d0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/668c8d00756a5e3e3daaade46380967c74eec1ae))
|
||||||
|
* async text hiding on Calendar's Bottom Bar when there are no async courses ([#152](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/152)) ([0dff122](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0dff12232c29415fa7bc5ac3771393a406ab738d))
|
||||||
|
* basic CalendarCourseMeeting component laid out - missing Text and Right Icon ([da9e7aa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/da9e7aac4135afa5184e4949c36a288ef6ab62a7))
|
||||||
|
* Best Practices ([#102](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/102)) ([5eb7be2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5eb7be246cd2a1db27c7ca777d81074ae7bb3b82))
|
||||||
|
* beta builds ([#187](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/187)) ([8a5e12e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8a5e12ec63539ae7199f0f1561f642cb9cf19302))
|
||||||
|
* better discord icon ([#205](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/205)) ([a3f5e0f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a3f5e0f27ffdfcb4e3ddd47140979aca379f9f42))
|
||||||
|
* bold red refresh error ([#220](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/220)) ([229a8a2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/229a8a29e8d04dfbe15de200ceffc53b4f758413))
|
||||||
|
* bottom bar for the calendar page ([#91](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/91)) ([0f730d6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0f730d6c50462c9f7d0428ebb379742305212ef6))
|
||||||
|
* build without errors ([babc925](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/babc925967b01f1064b8d8e067e7bc792dd57b0a))
|
||||||
|
* calendar components 3rd attempt at merging ([#60](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/60)) ([4faca8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4faca8c43befe19c9b05ff4f3afba420e016bb59))
|
||||||
|
* Calendar Components 3rd Attempt at Merging ([#60](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/60)) ([ab2cd68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ab2cd688fae616742b39c0637291a65e508e23bd))
|
||||||
|
* calendar course block component ([#75](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/75)) ([00e0019](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/00e00197efb807d420196f2b030fc4dc71e9cd42))
|
||||||
|
* calendar course block component ([#75](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/75)) ([a41cb3e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a41cb3ed8711eff9a2fbbe435e867c073904f452))
|
||||||
|
* calendar grid and grid cells ([#81](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/81)) ([bfeb239](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bfeb2398aacf9a1fba892737b6ef0286acbc326f))
|
||||||
|
* Calendar Grid and Grid Cells ([#81](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/81)) ([dd2f696](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dd2f696f8de2c0b291e917ef4d4cb2171b76712c))
|
||||||
|
* calendar header formatting and data displaying ([#160](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/160)) ([5cce1c7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5cce1c79fc2b50843426ed290a1b53849375e4eb))
|
||||||
|
* calendar matchings ([#173](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/173)) ([791a42b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/791a42bcd49478024b6f1a237a2d2a9f6211749e))
|
||||||
|
* Calendar Schedule component finished, fix: list didn't allow updates when adding a new schedule ([#115](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/115)) ([a99a557](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a99a55788a0b9791ff048c8bbbaad5bada47c41f))
|
||||||
|
* calendar-course-cell-color-picker ([#157](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/157)) ([df18491](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/df1849180de2c793c10e3a49d2c862d236456ec2))
|
||||||
|
* can open tabs, updated injected popup heading. basically done ([35fab34](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/35fab3444582b436f63c7b16a26ebd5989c4bf3e))
|
||||||
|
* change Chip to tailwind css. Fixed eslint for ConflictsWithWarning ([3568b8e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3568b8eb5ee9ce3fe17f77f5641c0583bacc17a4))
|
||||||
|
* check-path-alias autofix ([#124](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/124)) ([265652c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/265652c4200a56b1ae62e6e94e0609fb4604c188))
|
||||||
|
* chrome extension works ([35f3c72](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/35f3c7225049d760c6061ed827aa796514291e62))
|
||||||
|
* color palette for calendar ([#118](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/118)) ([471e55d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/471e55dcea1ae439658d00ed41570e2f218f0c3d))
|
||||||
|
* Conventional Commits ([#103](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/103)) ([fe599df](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fe599dfe7514ead880596d3b75c8370bde796e09))
|
||||||
|
* convert all LabelsAndDetails Components to Tailwind ([9e0f9df](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9e0f9df9de1a5c99657ddcef3b2eb101dd825b84))
|
||||||
|
* course color generation ([#179](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/179)) ([5ed81e4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5ed81e4be99a8b7f3c68a3ba70358e4cbe5cc613))
|
||||||
|
* course colors ([#175](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/175)) ([dc77cc2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dc77cc27da7ab521e290a8c9ba810573e677b6b8))
|
||||||
|
* Course Flag Tooltips ([#178](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/178)) ([ba2bc60](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ba2bc60add2b87ccd4e9894c454d7a8d8382f0dd))
|
||||||
|
* course-catalog-injected-popup ([#98](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/98)) ([89d03f4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/89d03f4244d324915cadadaf75a827221525d43f))
|
||||||
|
* CourseStatus Component implemented ([#83](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/83)) ([58d7df4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/58d7df499ca9a8dfd4dc3aa47bc9d76837f21bf7))
|
||||||
|
* CourseStatus Component implemented ([#83](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/83)) ([fa1d737](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fa1d7374bcafbe19673dc31f33b3f8075e45f37a))
|
||||||
|
* create empty Popup story ([ad83ba4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ad83ba4cdc67f86096d0cbc2cb6659aa9be98a0b))
|
||||||
|
* Create icon helper ([#77](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/77)) ([ccea0f4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ccea0f4bd1e8d66dc0717d92c9e970040d9fbe9e))
|
||||||
|
* Create icon helper ([#77](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/77)) ([1b51d65](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1b51d65c89a4b544f4f2a9c60106e14300f9a3b0))
|
||||||
|
* Derek vinson/calendar header ([#94](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/94)) ([12d09b5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/12d09b54cbf840993d17323b96cf274ec24c67ce))
|
||||||
|
* Derek/disable updating ([#239](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/239)) ([0ab83ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0ab83efd47d75fb6c278994c7dc8a6d22b4b6b83))
|
||||||
|
* Derek/export png ([#95](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/95)) ([58c2b46](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/58c2b4634a9d065ca156d27c4af69a504b7c2d2d))
|
||||||
|
* DialogProvider component ([#198](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/198)) ([d1b921a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d1b921a5b000693d7f3dabaf84d8b9580c361941))
|
||||||
|
* dividers in calendar bottom bar ([#120](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/120)) ([eba5d9f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/eba5d9f508576d2648bd2501e5d5aaff32592566))
|
||||||
|
* **docs:** add extra acknowledgements ([e2cbfa3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e2cbfa3f48cb0b10a43134cf9784a44b13aa542d))
|
||||||
|
* drag only on vertical axis ([34a6449](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/34a644952995d4aedd18d11d327bd8589f5b2610))
|
||||||
|
* early iteration of non-virtual list ([677aa62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/677aa624d7fb2bbb922aaf8806abe979c3399e4e))
|
||||||
|
* enable TS strict mode ([#168](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/168)) ([efed1c0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/efed1c0edbf93987c551952a03c9c4b3c461d819))
|
||||||
|
* experimental toggle for icons, left off for now ([#237](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/237)) ([f34dd95](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f34dd95d7765322b6cecb2e817a46e0549d929a5))
|
||||||
|
* fall 2023 grades ([#226](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/226)) ([863d980](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/863d980b2d1c706b1fc648f1a0de2a31515e339e))
|
||||||
|
* finally fix grid JSX.Element generation ([e49fc29](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e49fc295ba40a37f9bf59686adbd8b4f523cd9c6))
|
||||||
|
* finish ScheduleTotalHoursAndCourses ([12f680d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/12f680d7e9aef826281c8f769825408dc6f9f9de))
|
||||||
|
* fix icons on PopupMain and convert to tailwind ([#108](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/108)) ([87799d8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/87799d8f02aa5986025982f768639afdb1b0267a))
|
||||||
|
* fix save as button dividers ([#153](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/153)) ([7986549](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7986549fdd40834a85bedbfd0862cdb21c9ae657))
|
||||||
|
* grades by professor ([#225](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/225)) ([78d749a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/78d749a8a96f63c32663b464e5b663f932c28ed0))
|
||||||
|
* html2canvas -> htmlToImage and fix derick's bugs ([bda0282](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bda02826b14b96fd65a6f24218950e510c5aa3d8))
|
||||||
|
* icon added successfully ([3b588c2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3b588c20394523ddf90456e1ca58546e5b820c74))
|
||||||
|
* implement Chip component ([23e881f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/23e881f14cc80100d8f8deef6a25daaa72d83f07))
|
||||||
|
* implement flatten course schedule helper function ([e54f488](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e54f488b170d5a9af646c1ac82e8b33579fef61d))
|
||||||
|
* implemented ConflictsWithWarning ([93f3a30](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93f3a307b4acce6cc55fc411f1ea2d7576006b00))
|
||||||
|
* implemented InfoCard ([21b6430](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/21b643000d884442e025534802be6bb1f6ba68e2))
|
||||||
|
* Initial Splash Text Commit ([#208](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/208)) ([0534f60](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0534f607a51f1dd0905f6f45f08519cba65ea05a))
|
||||||
|
* limit schedules to 10 ([#272](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/272)) ([290b841](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/290b8415170bca631e5d71b08d88db80b02163f0))
|
||||||
|
* link to your registered courses ([#228](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/228)) ([f83e012](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f83e012d62e268d95dce19a30e54e34f3779142b))
|
||||||
|
* list reordering ([#154](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/154)) ([038ebaa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/038ebaa2689c14bc9906afb542ef62c21cb13177))
|
||||||
|
* listed versioning for beta builds ([#192](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/192)) ([0c42979](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c429794230a91c4fd949715b7eea210ac18fee3))
|
||||||
|
* lowercase instructor! ([#268](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/268)) ([9ec05ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9ec05ef764b2451c05e2d084d92074fb1e984268))
|
||||||
|
* made List more extensible ([cd34601](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cd34601379d5cd2b53d967afbc954178834823b9))
|
||||||
|
* match calendar designs & add functionality ([#176](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/176)) ([8027c3d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8027c3d1bf228053acbad3e232b030c9ddbaca6a))
|
||||||
|
* migrate styles to TailwindCSS ([7e2f5ea](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7e2f5eaed7d782bc886545f7241e0891d55cfae0))
|
||||||
|
* migration update showing ([#293](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/293)) ([aede681](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aede681d4bb5b01723eb9d68527f4e64991adfab))
|
||||||
|
* missed one chip toggle there ([#245](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/245)) ([5ca24da](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5ca24dab82509d34cf43af4359940476159d052f))
|
||||||
|
* newer grades and parameterized queries ([#238](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/238)) ([75ad416](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/75ad4167b6639bbcf58e2dc6dafc80ed4656a899))
|
||||||
|
* one single exclamation mark did all that ([#235](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/235)) ([be87e41](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/be87e4181430e0df41683be5c9d0a1ce992b2c61))
|
||||||
|
* open an injected course page on course block click in popup main ([#146](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/146)) ([2709484](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/27094846f70feb8d83c8d464ab0fa8dcd99b3e71))
|
||||||
|
* pad unique ids to 5 digits ([#170](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/170)) ([b4ad687](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4ad6870bfc88f68bdd7f87fe1d94c23d0a02b95))
|
||||||
|
* parallelize initializeDB.ts promises ([9f1dcc6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9f1dcc667df9857e3d0469fe7112a9f985aad490))
|
||||||
|
* popout icon for ccip in calendar ([#221](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/221)) ([6812d68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6812d685d08902f7b3d9d2e5293350c8170c6f06))
|
||||||
|
* popup schedule select ([#126](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/126)) ([7f2a589](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7f2a5893d4dd82a0f077dca6dfd28e79f929766f))
|
||||||
|
* PopupCourseBlock Component ([#79](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/79)) ([9accd17](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9accd17bd418f8c9334e80b0f7e38673092ba208))
|
||||||
|
* PopupCourseBlock Component ([#79](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/79)) ([f045b40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f045b400a56569f7353eb00ecd0d081b68660fc5))
|
||||||
|
* proper injected styles ([#164](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/164)) ([e919e96](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e919e96c53bc9bec7db3d09693d1ff0684b56e87))
|
||||||
|
* readme animation ([#281](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/281)) ([da6d86c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/da6d86c785f92f70f52ef0b4cab3dc14e0bd9790))
|
||||||
|
* refactor all components in common ([e544312](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e5443122b422266a126de497b475020e298b2f1c))
|
||||||
|
* refactor all components in injected ([0c44849](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c44849e15333c3a5cbdf8972eb31ada0e830b0a))
|
||||||
|
* refactor calendar ([28f1924](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/28f192472b7f244253b6a0b5339491b9740880f7))
|
||||||
|
* Refactor database initialization code ([5e98f45](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5e98f45210346f0e7bb69af5cf25e336c76416ca))
|
||||||
|
* release notes ([#283](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/283)) ([bd17e33](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bd17e3353792df70ca2e33c8b7a32b5fb9acc7d2))
|
||||||
|
* report issue popup ([#261](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/261)) ([65ff6bf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/65ff6bfbbfc6b696621628e448865155d0405f7f))
|
||||||
|
* rerouted directory to syllabus when click on professor name ([#211](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/211)) ([8959e0d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8959e0d9f7fdd82125f391f1ec51aa390ba9450d))
|
||||||
|
* sam's jokes ([#278](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/278)) ([895cd31](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/895cd31d8eb40e7e99fc0969b543e95bd070f6b7))
|
||||||
|
* save as PNG functionality ([ad18fbd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ad18fbd16235be0c75121b527867fac708670300))
|
||||||
|
* schedule list item action menu ([#230](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/230)) ([15fc369](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/15fc3699cfda3ad56e5a262bd37dca166e2685dd))
|
||||||
|
* screenshot whole page, hide certain elements, screenshot fixed size ([#180](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/180)) ([7d4c5d7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7d4c5d7be8b266b5f3caf2135c4f3fecb96d75be))
|
||||||
|
* settings page ([#260](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/260)) ([7a5c3a2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a5c3a2e62c3a726735f9fe921f9e08d4092d0f9))
|
||||||
|
* show async courses in the bottom bar ([#204](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/204)) ([227de53](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/227de53e8453804f0791f269dae64ec388136390))
|
||||||
|
* some small changes for colors and font ([#201](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/201)) ([bae1da4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bae1da43d3373e12d593682e9fb69e19557b5749))
|
||||||
|
* splash text additions before v2 release ([#296](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/296)) ([e774f31](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e774f316e3f92c03e79274a55f1f729178c9a14a))
|
||||||
|
* splash text has arrived! ([#246](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/246)) ([9971435](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9971435716dec11e954dc26c14bf7d1505563d0d))
|
||||||
|
* Storybook for Vite ([#52](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/52)) ([9cc299c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9cc299ced6132644a5c2375b95a8a16a3482601b))
|
||||||
|
* swe title updates ([#310](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/310)) ([4629626](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4629626a31934c491758ddca874e5018d9c22e57))
|
||||||
|
* switch button ([#229](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/229)) ([abae7a5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/abae7a5c22ab944262d66ae897298b008a03c5f1))
|
||||||
|
* temporary removal of waitlist etc ([#236](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/236)) ([d424ccc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d424ccce49ba05305b24cb61aeb4ffeada5bab33))
|
||||||
|
* UnoCSS (TailwindCSS) (Storybook only) ([#61](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/61)) ([85c7f78](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/85c7f7817c58f13f6a4e8bec13a45f6412ad87db))
|
||||||
|
* UnoCSS (TailwindCSS) (Storybook only) ([#61](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/61)) ([6521a4b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6521a4b2a90af06e182c779f2fc7cb1a3b1e55c1))
|
||||||
|
* unplugin-icons ([#62](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/62)) ([2d67b12](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2d67b1218ff852c59901b77909615c5f54794a67))
|
||||||
|
* unplugin-icons ([#62](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/62)) ([945e09b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/945e09b49e4ffa9bd4b02442f6567bb99831923e))
|
||||||
|
* update admin titles ([4cf8c3f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4cf8c3f9645cd8fda07244413cdc406105b12fec))
|
||||||
|
* update badge count when schedule changes ([#150](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/150)) ([a5e9e3c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a5e9e3c2145d61d8cc5788eb50fa19718e6d13bf))
|
||||||
|
* update Button to v2 design ([863521f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/863521fb3bb268131bd3f369064ae10a442b4fbc))
|
||||||
|
* update dialog component to headlessui ([#159](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/159)) ([442be8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/442be8cbee06ec403467b100d8d5300dd4a7ea72))
|
||||||
|
* update with TailwindCSS ([f3a8a7d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f3a8a7db560a6b28f96a2e976c8ed33af97fe77a))
|
||||||
|
* updated calendar page and recruitment banner links ([#219](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/219)) ([a2303ee](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a2303ee35f10aba5fa7ff5e307d0092a39f18830))
|
||||||
|
* updated divider component ([#99](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/99)) ([8ab60c9](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8ab60c9f018143df594e7bfd0d321666289cc28c))
|
||||||
|
* updated Text component to latest design specification ([#70](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/70)) ([8b8433d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b8433deaf8fe66f917fd66fb56cb0a748bc6e6e))
|
||||||
|
* updated Text component to latest design specification ([#70](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/70)) ([bb727f7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bb727f70bea5a4c2ff5dba7e9b4428c0c84de7b2))
|
||||||
|
* updating joke styling and updating jokes array ([#277](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/277)) ([0da27e2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0da27e2c46e9fc32efb80808715053c1f9f5165a))
|
||||||
|
* use display: grid for calendarGrid ([b535a6e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b535a6eb32ac1091c8b5309ca3cfb6bab0b62526))
|
||||||
|
* use downloadBlob util ([#186](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/186)) ([2dfb10e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2dfb10e57b51a08fcde2dc6638a81b5ec9bbc7ab))
|
||||||
|
* use filter() instead of pop() ([063349d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/063349d96d0ad008fa0dc1f0a8c7a27cf3f108ce))
|
||||||
|
* use React-icons ([8df9ea5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8df9ea55a9f0bcb04c90314cb311bba9d1ebf2e3))
|
||||||
|
* UTRP v2 migration ([#292](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/292)) ([d22237d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d22237d561ddf6a2f8ed699ca4c11088a8b408e8))
|
||||||
|
* working PNG and CAL downloads ([#119](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/119)) ([d9ee23c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d9ee23c5bbe27240a0e2849aae2eae81d7960bb5))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add margin-top: -10px to p ([18406b0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/18406b0c94604b1b524de03bf97e78cffe7a5bbf))
|
||||||
|
* added room number to course popup ([#231](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/231)) ([9eaff24](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9eaff24cbb6d78d7f28521a07c32630a90a96f82))
|
||||||
|
* align timeBlock div ([4dc8957](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4dc8957c459d0ee57e40c67fa61194ace832327e))
|
||||||
|
* async course display size ([#181](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/181)) ([949bbb0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/949bbb0835bfb55a81f0800850aa084b4ed1bfc4))
|
||||||
|
* broken close bracket ([b34aacb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b34aacb06772af6f83d1f7bafe828ba43a10db85))
|
||||||
|
* broken file ([92462cf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/92462cf0dffa222d75965885ee0e63181bce9890))
|
||||||
|
* bugs ([2a01506](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2a0150600f26af5b7394f878e13c4d19611f3f64))
|
||||||
|
* build ([60ab140](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/60ab140c556ec56eca78b1f4e280dfb6e9dbb538))
|
||||||
|
* build errors and merge in Casey's branch (driodiwb) ([39947b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/39947b3694f42f9d5ad2f21d044585aa3d12c407))
|
||||||
|
* build errors and restructure Calendar page ([c6a48dd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c6a48dd3f6025fe531c18e12b8730683d63f67ed))
|
||||||
|
* calendar course cell colors ([74be880](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/74be880f9d2dc340ba48416429711fb28915baa2))
|
||||||
|
* calendar storybook issue ([#125](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/125)) ([0c5bec8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c5bec8002022cb4702f3879f43fcd32b9fa6b5a))
|
||||||
|
* Calendar View/Scaling Issues ([#144](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/144)) ([4c61ebd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4c61ebd3fc93240903f1224f9dd3057a23d30046))
|
||||||
|
* CalendarCourseCell spacing ([13c69ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/13c69ef862f0214dfa6c9368fc3463519aa8f2f0))
|
||||||
|
* change Chromatic action to build current branch, not base branch ([#100](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/100)) ([e73c9fe](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e73c9fe417169e73c395cde1aeb841eee8add579))
|
||||||
|
* change material icons to material symbols ([#71](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/71)) ([52e34cb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/52e34cb830ff1f12659703776bd3d9a24815ee00))
|
||||||
|
* chromatic build ([11303da](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/11303daebcc1e4de340463e6266082dc0920582d))
|
||||||
|
* chromatic builds on PRs ([#140](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/140)) ([78a6939](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/78a69399292767a1f8eac66e4c86cbcb8aad019d))
|
||||||
|
* chromatic storybook - CourseCatalogInjectedPopup ([#106](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/106)) ([ced2997](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ced29975b20a2a5beac51ea875627a2b1042e5c2))
|
||||||
|
* clean up [#173](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/173) ([#174](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/174)) ([afa634f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/afa634f0853dc16cf313c129434d49bf132388d0))
|
||||||
|
* cleanup imports ([#112](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/112)) ([b17c3fa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b17c3fae6d9a8fdb2f6de84dc28d00bc31e7bcc8))
|
||||||
|
* conflict row bug ([#130](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/130)) ([a8ea3bc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a8ea3bc683f88fc33191a8cacf649be83f6758f8))
|
||||||
|
* ConflictsWithWarning ([1599e48](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1599e48d75d4877b3515c943120d83b59d084a55))
|
||||||
|
* correct parsing of noon ([#155](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/155)) ([91f62e1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/91f62e1943633eaef7dc8d437e6a7397456a9123))
|
||||||
|
* db with proper insertion order ([8e79d6a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e79d6a6a87210e0a30584c0c9ceb60bd3a33665))
|
||||||
|
* delete storybook timestamp file ([f93a98e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f93a98e46ae76e9b1474c8143bfc3f5076e8b91f))
|
||||||
|
* dialog movement ([#227](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/227)) ([bc5d68c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bc5d68ce18f818c285ef7f5eda03f499365ee2e8))
|
||||||
|
* disabled [object Object] hover message on dialog popups ([#284](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/284)) ([dcc1d81](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dcc1d81a74922ba27ad2a6859f978f3f20136178))
|
||||||
|
* divider usage in HeaderAndActions ([#113](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/113)) ([84e8320](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/84e8320e8fce5961877a5745e89419a722055ed5))
|
||||||
|
* doesn't autoload on pages that don't have pages to load ([#270](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/270)) ([88eeb62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/88eeb620aec52d6e4172cc908843339412ade90b))
|
||||||
|
* don't crash on cultural diversity flag ([#196](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/196)) ([5b1e451](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5b1e4513e29203a54ed9f4913c227d6c45d2e67a))
|
||||||
|
* eslint and remove React-beautiful-dnd ([6af805b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6af805ba3a7b04b7e5705d3d597b5ace686d61e0))
|
||||||
|
* extra space ([0f43796](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0f43796cd864acd012fc1242cbdf34844df62406))
|
||||||
|
* Fix popup drag hitbox ([#216](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/216)) ([7a40008](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a40008c1e3846f09ac4eeadc4a50f810ffc0d90))
|
||||||
|
* fixed bug where activeSchedule doesn't update correctly ([#158](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/158)) ([a409090](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a409090b904686b76a5db52af980e81108b97082))
|
||||||
|
* fixed bug with course cells after 12 PM extending past midnight ([#122](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/122)) ([f22a3cd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f22a3cd7c0df138ca6ecd4b4a4efaa7ce845c3dd))
|
||||||
|
* fixed issues involving course meeting objects not being recognized as course meeting objects ([#132](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/132)) ([3406e9a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3406e9a0e2644218fc06cbd0b05046df5339264c))
|
||||||
|
* Fixed typescript error ([#161](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/161)) ([df7a7c6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/df7a7c65d695cb5c5a825ac9f5ecc48438735b65))
|
||||||
|
* grade dist when no instructor ([#269](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/269)) ([6a363ae](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6a363aeb5ccb1c11e15db317b34c4f04a3c9aafc))
|
||||||
|
* grid JSX.Element generation ([b691bf3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b691bf3231d5002a631687f26c7409e311237662))
|
||||||
|
* icon library resolution ([#74](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/74)) ([bb3b313](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bb3b313fd272bba12f99270c912e046df5358fed))
|
||||||
|
* idk why that comment was there ([#177](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/177)) ([c5fc621](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c5fc6219e1540e1fb889d8bedc7296190103a56b))
|
||||||
|
* import error ([152bc45](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/152bc4577685605ae95f39d9e670b5449e32e0e9))
|
||||||
|
* improper list data propagation ([#240](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/240)) ([149fda3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/149fda3b721dcc3d8ffa6c2398c28e39dcb62aaa))
|
||||||
|
* improve dialog handling and error management in list items ([#257](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/257)) ([1942508](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1942508d8d11f51b62806894ae13162b6404753b))
|
||||||
|
* injection not working from som/elie commit ([#145](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/145)) ([591687e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/591687eee8731b3649a5f8c1357a416eb2992a93))
|
||||||
|
* list component fixed ([#162](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/162)) ([5714ed1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5714ed16d72c5442f58366ac51f40cc32ea406ab))
|
||||||
|
* made list draggable only by handle ([cbb190b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cbb190bf4b3ad694c87b650489e1fca5aa6b7826))
|
||||||
|
* margins on list component ([cc71389](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cc7138949cf77d1bcf1520633d669e939540575e))
|
||||||
|
* non-determinstic options page generation ([#137](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/137)) ([d700110](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d70011016a45f5de62b06839a31c634637024f4a))
|
||||||
|
* non-virtual dnd ([837fddf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/837fddf8048350b0b25a58bbd16c1957fbad6edb))
|
||||||
|
* old icon removed in .tsx ([4d387e8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4d387e8063314c7c6d7452b746728e2cf85a1787))
|
||||||
|
* only show button hover effects when not disabled ([8e3aa7e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e3aa7ef3380836fd36a12698b1f6982e110c0f7))
|
||||||
|
* openTabFromContentScript TS issue ([3a48859](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3a48859dddc0e85649ff6a949e9afd22c962825d))
|
||||||
|
* options page ([#131](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/131)) ([dc100b5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dc100b5d3ac1db5f1dc011ea2536c286f56fa55b))
|
||||||
|
* reactivity ([#188](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/188)) ([4f4f34e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4f4f34e281e8aabcdbf8b06652d05bca3554b11f))
|
||||||
|
* README.md ([a30fecf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a30fecf8ec76ecae03be9bb4d813803b9ffdb490))
|
||||||
|
* README.md ([b27b21b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b27b21bbfcc8fbdc130c3fa85776479451745b7b))
|
||||||
|
* refactor AST parsing for custom ESLint rule ([62f0851](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/62f0851406bf82d3d0481fed40462bdd30ffcf1b))
|
||||||
|
* remote react-window fully (from List component) ([#114](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/114)) ([5f1c023](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5f1c0231e483bc571ef944bbc0b950ab20d31028))
|
||||||
|
* remove course name from the syllabi lookup ([#200](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/200)) ([bcb5a8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bcb5a8c469c4b5c1909880ab6789d67d04487ed2))
|
||||||
|
* remove extra spacing ([#121](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/121)) ([6ba8b68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6ba8b68654dc182bd2a084f93b0f4f7d5c132862))
|
||||||
|
* remove white space after duplicate schedule regex matching ([#286](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/286)) ([d73615e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d73615e28129ee7d78604c6b295ec572b11be46e))
|
||||||
|
* rename to course block and fix line height for styling ([b602b0b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b602b0b895416a26214e934f9650a8ec0a10711e))
|
||||||
|
* revert "chore: add default story" ([aef8c3d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aef8c3d9873c6df79f6a54ab4c669ee1849b5ea5))
|
||||||
|
* revert "rename to course block and fix line height for styling" ([0273a23](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0273a239139c0f3c0ad773ed8ab1cd4ad8d22fa3))
|
||||||
|
* revert CalendarGrid and CalendarGridCell back to SCSS from Tailwind ([56306ab](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/56306ab9440baf5ca1158f95d788de7ba42d73d8))
|
||||||
|
* revert last commit ([27d945f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/27d945f57c76a1c2f31fee9f98c324fab6183bb6))
|
||||||
|
* Schedule Switching Bugs ([#138](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/138)) ([c51e688](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c51e6881d186e8bef056e483e2bca95585d5eac6))
|
||||||
|
* support classes with no location ([#242](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/242)) ([3ff06e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3ff06e043b112bc232c9c173e72b2ea99609cf7a))
|
||||||
|
* theme colors ([2f537b4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2f537b4f3e3946241f2e287d9ae075e5a2aa7087))
|
||||||
|
* type issues by using correct import ([#111](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/111)) ([19fe070](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/19fe070491be9e2d40f12918561f6f6ac5921237))
|
||||||
|
* **ui:** unhid settings button ([#288](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/288)) ([db1eac3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/db1eac33a2786a3db87f0e6b3679233a85ab923f))
|
||||||
|
* undefined color case ([9aa78a0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9aa78a02a5d4f68daa062e4892875cf5f568306c))
|
||||||
|
* unocss theme color namings ([79d7832](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/79d7832d0987b8a7c9650b984f72ffd702682423))
|
||||||
|
* update alignment ([7eb3113](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7eb3113adaa0917c0275576c3366c387bfe77440))
|
||||||
|
* update daysOfWeek with new DAY_MAP keys ([ecdaadb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ecdaadb83eb21b144d07d573613fd8971f0a5ce2))
|
||||||
|
* visual overflow bug when editing schedule name ([#251](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/251)) ([e8d2c2e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e8d2c2e1429072fcc1173ee770f06b07e9ddda20))
|
||||||
|
* vitest path alias bug ([ee37897](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ee37897df4f65cd2d285e2ad628be43aebb21ea1))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "individual bug fix" ([b4e8c75](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4e8c7589e53f1064d70703459cc6d66fae1b04c))
|
||||||
|
* color palette for calendar ([#118](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/118)) ([51bbd65](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/51bbd6590bf284ce54644b014466c8b2c73b8925))
|
||||||
89
DOCKER_DEV_SETUP.md
Normal file
89
DOCKER_DEV_SETUP.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# Docker Dev Setup
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker installed on your machine
|
||||||
|
- Git (to clone the repository)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/Longhorn-Developers/UT-Registration-Plus.git
|
||||||
|
cd UT-Registration-Plus
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build the Docker image:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t ut-registration-plus .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Docker for Different Modes
|
||||||
|
|
||||||
|
The Docker setup supports three modes of operation: build, zip, and dev. You can choose the mode either by passing it as a command or by setting the `BUILD_MODE` environment variable.
|
||||||
|
|
||||||
|
### Build Mode (Default)
|
||||||
|
|
||||||
|
This mode builds the extension and places the output in the `dist` folder.
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v $(pwd)/dist:/extension/dist ut-registration-plus build
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v $(pwd)/dist:/extension/dist -e BUILD_MODE=build ut-registration-plus
|
||||||
|
```
|
||||||
|
|
||||||
|
### Zip Mode
|
||||||
|
|
||||||
|
This mode builds the extension and creates a zipped package in the `package` folder.
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v $(pwd)/package:/extension/package ut-registration-plus zip
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v $(pwd)/package:/extension/package -e BUILD_MODE=zip ut-registration-plus
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Mode with Hot Module Replacement (HMR)
|
||||||
|
|
||||||
|
This mode runs the extension in development mode with HMR support.
|
||||||
|
|
||||||
|
_Note_: This currently doesn't work.
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v $(pwd)/dist:/extension/dist -v $(pwd)/dist:/extension/dist -p 5173:5173 ut-registration-plus dev
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v $(pwd)/dist:/extension/dist -p 5173:5173 -e BUILD_MODE=dev ut-registration-plus
|
||||||
|
```
|
||||||
|
|
||||||
|
## Accessing the Built Extension
|
||||||
|
|
||||||
|
- For build mode, the extension files will be in the `dist` directory on your host machine.
|
||||||
|
- For zip mode, the zipped extension will be in the `package` directory on your host machine.
|
||||||
|
- For dev mode, the extension will be continuously built in the `dist` directory, and you can load it as an unpacked extension in Chrome.
|
||||||
|
|
||||||
|
## Docker Development Workflow
|
||||||
|
|
||||||
|
When working on the extension:
|
||||||
|
|
||||||
|
1. Run the container in dev mode
|
||||||
|
2. Make changes to your source code
|
||||||
|
3. The extension will automatically rebuild thanks to HMR
|
||||||
|
4. Reload the extension in Chrome to see your changes
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- If you encounter permission issues with the output directories, ensure that the directories exist on your host machine and have the correct permissions.
|
||||||
|
- For any other issues, please check the Docker logs or open an issue in the GitHub repository.
|
||||||
47
Dockerfile
Normal file
47
Dockerfile
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Stage 1: Base image with Node.js and pnpm
|
||||||
|
FROM node:20.9.0-alpine AS base
|
||||||
|
|
||||||
|
# Install pnpm
|
||||||
|
RUN npm install -g pnpm@latest-10
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package.json, pnpm-lock.yaml, and .nvmrc
|
||||||
|
COPY package.json pnpm-lock.yaml .nvmrc ./
|
||||||
|
|
||||||
|
# Copy patches directory if it exists
|
||||||
|
COPY patches ./patches
|
||||||
|
|
||||||
|
# Install dependencies, including applying patches
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Copy the rest of the source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Stage 2: Final stage
|
||||||
|
FROM base AS final
|
||||||
|
|
||||||
|
# Install zip utility and bash
|
||||||
|
RUN apk add --no-cache zip bash
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /extension
|
||||||
|
|
||||||
|
# Copy all files from base
|
||||||
|
COPY --from=base /app ./
|
||||||
|
|
||||||
|
# Copy the entrypoint script
|
||||||
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
|
|
||||||
|
# Make the entrypoint script executable
|
||||||
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
|
# Expose port for HMR
|
||||||
|
EXPOSE 5173
|
||||||
|
|
||||||
|
# Set the entrypoint to our new script
|
||||||
|
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||||
|
|
||||||
|
# Set the default command (which can be overridden)
|
||||||
|
CMD ["build"]
|
||||||
223
README.md
223
README.md
@@ -1,59 +1,216 @@
|
|||||||
# UT Registration Plus
|
# UT Registration Plus
|
||||||
|
|
||||||
We've all been there. 20 tabs of Rate My Professor, Google Spreadsheet, and the UT Course Schedule open and you still don't know what classes to take.
|

|
||||||
This extension, UT Registration Plus (UTRP), tries to streamline most of the unnecessary steps and headaches of registering for classes at UT Austin.
|
|
||||||
|
|
||||||
- For each class in the UT Course Schedule site, UTRP provides a "breakdown" popup, with quick and easy links to the instructor's RateMyProfessor, Course Evaluation Survey (CES) and past syllabi.
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
- Shows the course description with highlighted information on prerequisites, restrictions, etc.
|
**UT Registration Plus (UTRP)** streamlines the process of registering for classes at UT Austin by reducing the chaos of juggling multiple tabs like Rate My Professor, Google Sheets, and the UT Course Schedule. With UTRP, you can simplify class selection and schedule management. We've all been there. 20 tabs of Rate My Professor, Google Spreadsheet, and the UT Course Schedule open and you still don't know what classes to take.
|
||||||
|
|
||||||
- Shows an aggregate and semesterly graph of the grade distributions for each course.
|
## Demo
|
||||||
|
|
||||||
- Gives you the ability to add "Add Course" and view them in the extension popup, a quick list of all the courses you have saved and an easy way to copy unique numbers.
|

|
||||||
|
|
||||||
- Highlights and crosses-out what courses on the UT Course Catalog would conflict with your currently saved courses, making selecting courses that fit with your schedule so much easier.
|
## Features
|
||||||
|
|
||||||
- Display's a weekly schedule based on your saved courses.
|
- **Quick Access to Class Info**: For each class in the UT Course Schedule, UTRP provides a "breakdown" popup with direct links to RateMyProfessor, Course Evaluation Surveys (CES), and past syllabi.
|
||||||
|
- **Prerequisite & Restriction Highlights**: Displays course descriptions with highlighted details on prerequisites, restrictions, and other important info.
|
||||||
- Give you the ability to create multiple schedules to plan for different scenarios.
|
- **Grade Distribution Graphs**: View an aggregate and semester-specific graph of grade distributions for each course.
|
||||||
|
- **Saved Courses List**: Easily add courses to a list and view them in the extension popup. Copy unique numbers with a single click.
|
||||||
- ... and much more!
|
- **Conflict Detection**: Automatically highlights and strikes out courses that conflict with your saved courses in the UT Course Catalog.
|
||||||
|
- **Weekly Schedule View**: Displays your saved courses in a weekly schedule format for easier planning.
|
||||||
|
- **Multiple Schedule Support**: Create multiple schedules to plan for different registration scenarios.
|
||||||
|
- **And much more!**
|
||||||
|
|
||||||
## Toolchain
|
## Toolchain
|
||||||
|
|
||||||
- React 18
|
- React v20.9.0 (LTS)
|
||||||
- TypeScript
|
- TypeScript
|
||||||
- Vite 5
|
- Vite 5
|
||||||
- ESLint
|
- UnoCSS
|
||||||
- Prettier
|
- ESLint
|
||||||
- Storybook
|
- Prettier
|
||||||
- Semantic-Release
|
- Storybook
|
||||||
- Custom Messaging & Storage Wrappers
|
- Figma
|
||||||
|
- Semantic-Release
|
||||||
|
- Custom Messaging & Storage Wrappers
|
||||||
|
|
||||||
## Development: Getting Started
|
## VSCode Extensions
|
||||||
|
|
||||||
1. Clone this repo
|
We recommend using the following VSCode extensions to improve your development experience:
|
||||||
2. This project uses `pnpm` to manage and patch dependencies. Run `pnpm install` to configure the repository for building/development
|
|
||||||
3. Using either of the methods listed below, the extension will build to the `dist/` directory.
|
- **[ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)**: For identifying and fixing linting issues.
|
||||||
|
- **[Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)**: For automatic code formatting.
|
||||||
|
- **[Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)**: For Tailwind CSS class autocomplete and IntelliSense.
|
||||||
|
- **[UnoCSS](https://marketplace.visualstudio.com/items?itemName=antfu.unocss)**: For better support with UnoCSS utilities.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome and encouraged! To get started:
|
||||||
|
|
||||||
|
1. **Fork** the repository.
|
||||||
|
2. **Create a new branch**: `git checkout -b feature/your-feature`.
|
||||||
|
3. **Make changes** and ensure your code passes linting and formatting checks: `pnpm run lint` and `pnpm run prettier`.
|
||||||
|
4. **Commit your changes** following the [Conventional Commits](#conventional-commits--branch-naming-convention) specification.
|
||||||
|
5. **Push** your branch: `git push origin feature/your-feature`.
|
||||||
|
6. **Open a Pull Request**.
|
||||||
|
|
||||||
|
For significant changes, it’s recommended to open an issue first to discuss the proposed updates.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
|
1. Clone this repository: `git clone https://github.com/Longhorn-Developers/UT-Registration-Plus.git`
|
||||||
|
2. **Node Version**: This project requires the Node.js version specified in `.nvmrc`. Use [nvm](https://github.com/nvm-sh/nvm) to install and manage the correct version:
|
||||||
|
```bash
|
||||||
|
nvm install
|
||||||
|
nvm use
|
||||||
|
```
|
||||||
|
_Note: Installing the wrong Node version can lead to errors during setup._
|
||||||
|
3. Install dependencies using `pnpm` (which manages and patches dependencies):
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Once set up, the extension can be built to the `dist/` directory using the following methods:
|
||||||
|
|
||||||
### Development Builds
|
### Development Builds
|
||||||
|
|
||||||
- Run `pnpm dev`
|
- Run the development server:
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> **Note**: Injected content on UT pages may not display correctly in development mode. To develop with accurate styles, use the following command:
|
||||||
> Injected content such as extension content on UT pages is not properly styled, and are missing class stylings. When developing for these pages, use `NODE_ENV='development' pnpm run dev build --mode development -w` to build and watch for changes. This will ensure you are seeing an accurate representation of the extension.
|
>
|
||||||
|
> ```bash
|
||||||
|
> NODE_ENV='development' pnpm run dev build --mode development -w
|
||||||
|
> ```
|
||||||
|
|
||||||
### Production Builds
|
### Production Builds
|
||||||
|
|
||||||
- Run `pnpm build`
|
- To generate production builds:
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Beta builds</summary>
|
<summary>Beta Builds</summary>
|
||||||
Use `BETA=true pnpm build` to build a beta build.
|
|
||||||
|
Use `BETA=true pnpm build` to generate a beta build.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Development: Loading the Extension Manually
|
### Docker
|
||||||
|
|
||||||
Open [chrome://extensions](chrome://extensions), ensure you have 'Developer Mode' enabled, and click 'Load unpacked'.
|
This project includes a Dockerfile that allows you to build, zip, or run the extension in development mode using Docker. Refer to [Docker Dev Setup](./DOCKER_DEV_SETUP.md) to get started.
|
||||||
|
|
||||||
Navigate to the `dist/` folder, and click 'select' to import the extension.
|
## Loading the Extension Manually
|
||||||
|
|
||||||
|
To load the extension manually in Chrome:
|
||||||
|
|
||||||
|
1. Open `chrome://extensions`.
|
||||||
|
2. Enable 'Developer Mode'.
|
||||||
|
3. Click 'Load unpacked'.
|
||||||
|
4. Navigate to the `dist/` directory and select it.
|
||||||
|
|
||||||
|
## Bug Reporting
|
||||||
|
|
||||||
|
If you encounter bugs or issues, please report them in the [Issues](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues) section, including:
|
||||||
|
|
||||||
|
- A clear, descriptive title
|
||||||
|
- Steps to reproduce the issue
|
||||||
|
- Expected behavior
|
||||||
|
- Screenshots or logs (if applicable)
|
||||||
|
|
||||||
|
We aim to address issues promptly.
|
||||||
|
|
||||||
|
## Conventional Commits & Branch Naming Convention
|
||||||
|
|
||||||
|
We follow the **Conventional Commits** specification for commit messages. This ensures a consistent commit history and enables automated versioning and changelog generation.
|
||||||
|
|
||||||
|
### Commit Messages
|
||||||
|
|
||||||
|
Follow this structure for commit messages:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
```
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
- **type**: One of the following:
|
||||||
|
- `feat`: A new feature
|
||||||
|
- `fix`: A bug fix
|
||||||
|
- `docs`: Documentation updates
|
||||||
|
- `style`: Code formatting changes (whitespace, semicolons, etc.)
|
||||||
|
- `refactor`: Code restructuring (without adding features or fixing bugs)
|
||||||
|
- `test`: Adding or modifying tests
|
||||||
|
- `chore`: Maintenance tasks or build process changes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(auth): add login functionality
|
||||||
|
fix(ui): align buttons in navbar
|
||||||
|
```
|
||||||
|
|
||||||
|
### Branch Naming
|
||||||
|
|
||||||
|
Branch names should follow the format:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>/<short-description>
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `feat/user-login`
|
||||||
|
- `fix/navbar-layout`
|
||||||
|
- `docs/update-readme`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License. See the [LICENSE](./LICENSE.md) for more details.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
We maintain a strict code of conduct. By contributing, you agree to adhere to the rules outlined in [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
Special thanks to the developers and contributors behind these amazing tools and libraries:
|
||||||
|
|
||||||
|
- React v20.9.0 (LTS)
|
||||||
|
- TypeScript
|
||||||
|
- Vite 5
|
||||||
|
- UnoCSS
|
||||||
|
- ESLint
|
||||||
|
- Prettier
|
||||||
|
- Storybook
|
||||||
|
- Figma
|
||||||
|
- Semantic-Release
|
||||||
|
- [chrome-extension-toolkit](https://github.com/sghsri/chrome-extension-toolkit)
|
||||||
|
- [UT_Grade_Parser](https://github.com/doprz/UT_Grade_Parser)
|
||||||
|
- [eslint-plugin-import-essentials](https://github.com/doprz/eslint-plugin-import-essentials)
|
||||||
|
- [UT Registration Plus v1.2.2.7](https://github.com/Longhorn-Developers/UT-Registration-Plus/tree/legacy) by @sghsri
|
||||||
|
- [UT Registration Planner](https://github.com/doprz/UT-Registration-Planner) by @doprz
|
||||||
|
- [Figma Designs](https://www.figma.com/design/8tsCay2FRqctrdcZ3r9Ahw/UTRP) by @IsaDavRod
|
||||||
|
- [Longhorn Developers](https://github.com/Longhorn-Developers) - established in 2024
|
||||||
|
- The UTRP devs, beta testers, and all supporters of the project!
|
||||||
|
|
||||||
|
## Activity
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
<a href="https://star-history.com/#Longhorn-Developers/UT-Registration-Plus&Date">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Longhorn-Developers/UT-Registration-Plus&type=Date&theme=dark" />
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Longhorn-Developers/UT-Registration-Plus&type=Date" />
|
||||||
|
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Longhorn-Developers/UT-Registration-Plus&type=Date" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ export default {
|
|||||||
parserPreset: 'conventional-changelog-conventionalcommits',
|
parserPreset: 'conventional-changelog-conventionalcommits',
|
||||||
rules: {
|
rules: {
|
||||||
'body-leading-blank': [RuleConfigSeverity.Warning, 'always'] as const,
|
'body-leading-blank': [RuleConfigSeverity.Warning, 'always'] as const,
|
||||||
'body-max-line-length': [RuleConfigSeverity.Error, 'always', 100] as const,
|
|
||||||
'footer-leading-blank': [RuleConfigSeverity.Warning, 'always'] as const,
|
'footer-leading-blank': [RuleConfigSeverity.Warning, 'always'] as const,
|
||||||
'footer-max-line-length': [RuleConfigSeverity.Error, 'always', 100] as const,
|
'footer-max-line-length': [RuleConfigSeverity.Error, 'always', 100] as const,
|
||||||
'header-max-length': [RuleConfigSeverity.Error, 'always', 100] as const,
|
'header-max-length': [RuleConfigSeverity.Error, 'always', 100] as const,
|
||||||
|
|||||||
46
docker-entrypoint.sh
Normal file
46
docker-entrypoint.sh
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Define supported modes
|
||||||
|
SUPPORTED_MODES=("build" "zip" "dev")
|
||||||
|
|
||||||
|
# Function to display usage information
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [build|zip|dev]"
|
||||||
|
echo " build: Build the extension"
|
||||||
|
echo " zip: Build and zip the extension"
|
||||||
|
echo " dev: Run in development mode with HMR"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if BUILD_MODE is set, otherwise use the first argument
|
||||||
|
if [ -n "${BUILD_MODE:-}" ]; then
|
||||||
|
mode="$BUILD_MODE"
|
||||||
|
elif [ $# -eq 1 ]; then
|
||||||
|
mode="$1"
|
||||||
|
else
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate the mode
|
||||||
|
if [[ ! " ${SUPPORTED_MODES[*]} " =~ " ${mode} " ]]; then
|
||||||
|
echo "Error: Invalid mode '${mode}'" >&2
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute the appropriate command based on the mode
|
||||||
|
case "$mode" in
|
||||||
|
build)
|
||||||
|
echo "Building extension..."
|
||||||
|
exec pnpm run build
|
||||||
|
;;
|
||||||
|
zip)
|
||||||
|
echo "Building and zipping extension..."
|
||||||
|
exec pnpm run zip
|
||||||
|
;;
|
||||||
|
dev)
|
||||||
|
echo "Running in development mode with HMR..."
|
||||||
|
exec pnpm run dev
|
||||||
|
;;
|
||||||
|
esac
|
||||||
97
gulpfile.js
Normal file
97
gulpfile.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { dest, series, src } from 'gulp';
|
||||||
|
import { exec } from 'gulp-execa';
|
||||||
|
import zip from 'gulp-zip';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const DIST_DIR = 'dist';
|
||||||
|
const PACKAGE_DIR = 'package';
|
||||||
|
const DATABASE_DIR = path.join(DIST_DIR, 'database');
|
||||||
|
|
||||||
|
// Custom log functions
|
||||||
|
const log = message => console.log(chalk.blue(`[${new Date().toTimeString().split(' ')[0]}]`), chalk.white(message));
|
||||||
|
const logWarn = message =>
|
||||||
|
console.warn(
|
||||||
|
chalk.blue(`[${new Date().toTimeString().split(' ')[0]}]`),
|
||||||
|
chalk.yellow(' [WARN]'),
|
||||||
|
chalk.white(message)
|
||||||
|
);
|
||||||
|
const logError = message =>
|
||||||
|
console.error(
|
||||||
|
chalk.blue(`[${new Date().toTimeString().split(' ')[0]}]`),
|
||||||
|
chalk.red(' [ERROR]'),
|
||||||
|
chalk.white(message)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove extra database folder
|
||||||
|
function removeExtraDatabaseDir(cb) {
|
||||||
|
fs.rmSync(DATABASE_DIR, { recursive: true, force: true });
|
||||||
|
log('Extra database directory removed.');
|
||||||
|
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instrument with Sentry
|
||||||
|
// Make sure sentry is configured https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/typescript/#2-configure-sentry-cli
|
||||||
|
async function instrumentWithSentry(cb) {
|
||||||
|
if (process.env.SENTRY_ENV && process.env.SENTRY_ENV !== 'development') {
|
||||||
|
await exec(`sentry-cli sourcemaps inject ${DIST_DIR}`);
|
||||||
|
await exec(`sentry-cli sourcemaps upload ${DIST_DIR}`);
|
||||||
|
log('Sentry instrumentation completed.');
|
||||||
|
} else {
|
||||||
|
logWarn('Skipping uploading/creating Sentry source maps. (development build)');
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zip the dist folder
|
||||||
|
function zipDist() {
|
||||||
|
const packageInfo = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
||||||
|
const zipFileName = `${packageInfo.name.replace(/ /g, '-')}-${packageInfo.version}.zip`;
|
||||||
|
|
||||||
|
return src(`${DIST_DIR}/**`, {
|
||||||
|
base: DIST_DIR,
|
||||||
|
encoding: false, // Disable encoding to handle binary files correctly
|
||||||
|
})
|
||||||
|
.pipe(zip(zipFileName))
|
||||||
|
.pipe(dest(PACKAGE_DIR))
|
||||||
|
.on('end', () => log(`Zip file created: ${path.join(PACKAGE_DIR, zipFileName)}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temp fix for CSP on Chrome 130
|
||||||
|
// Manually remove them because there is no option to disable use_dynamic_url on @crxjs/vite-plugin
|
||||||
|
// Force disable use_dynamic_url in manifest.json
|
||||||
|
function forceDisableUseDynamicUrl(cb) {
|
||||||
|
const manifestPath = path.join(DIST_DIR, 'manifest.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(manifestPath)) {
|
||||||
|
logWarn('manifest.json not found. Skipping modification.');
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
||||||
|
let modified = false;
|
||||||
|
|
||||||
|
manifest.web_accessible_resources.forEach(resource => {
|
||||||
|
if (resource.use_dynamic_url) {
|
||||||
|
delete resource.use_dynamic_url;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
||||||
|
log('use_dynamic_url removed from manifest.json');
|
||||||
|
} else {
|
||||||
|
log('No use_dynamic_url found in manifest.json. No changes made.');
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main build task
|
||||||
|
const zipProdBuild = series(removeExtraDatabaseDir, instrumentWithSentry, zipDist);
|
||||||
|
|
||||||
|
export { forceDisableUseDynamicUrl, zipProdBuild };
|
||||||
BIN
images/UTRP-Demo.gif
Normal file
BIN
images/UTRP-Demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 MiB |
BIN
images/UTRP_Social-Preview_Prod.png
Normal file
BIN
images/UTRP_Social-Preview_Prod.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
7
images/UTRP_Social-Preview_Prod.svg
Normal file
7
images/UTRP_Social-Preview_Prod.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 17 KiB |
187
package.json
187
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ut-registration-plus",
|
"name": "ut-registration-plus",
|
||||||
"displayName": "UT Registration Plus",
|
"displayName": "UT Registration Plus",
|
||||||
"version": "2.0.0-beta4",
|
"version": "2.2.1",
|
||||||
"description": "UT Registration Plus is a Chrome extension that allows students to easily register for classes.",
|
"description": "UT Registration Plus is a Chrome extension that allows students to easily register for classes.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "https://github.com/Longhorn-Developers/UT-Registration-Plus",
|
"homepage": "https://github.com/Longhorn-Developers/UT-Registration-Plus",
|
||||||
@@ -9,6 +9,10 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
|
"build:dev": "tsc && NODE_ENV='development' vite build --mode development",
|
||||||
|
"build:watch": "NODE_ENV='development' vite build --mode development -w",
|
||||||
|
"zip": "pnpm build && pnpm gulp zipProdBuild",
|
||||||
|
"zip:to-publish": "SENTRY_ENV='production' pnpm zip",
|
||||||
"prettier": "prettier src --check",
|
"prettier": "prettier src --check",
|
||||||
"prettier:fix": "prettier src --write",
|
"prettier:fix": "prettier src --write",
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives",
|
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives",
|
||||||
@@ -18,106 +22,147 @@
|
|||||||
"test:ui": "vitest --ui",
|
"test:ui": "vitest --ui",
|
||||||
"coverage": "vitest run --coverage",
|
"coverage": "vitest run --coverage",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"generate-changelog": "bun run scripts/generateChangelog.ts",
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"build-storybook": "storybook build",
|
"build-storybook": "storybook build",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.0.3",
|
"@date-fns/tz": "^1.2.0",
|
||||||
"@hello-pangea/dnd": "^16.5.0",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@unocss/vite": "^0.58.6",
|
"@dnd-kit/modifiers": "^9.0.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
|
"@headlessui/react": "^2.2.0",
|
||||||
|
"@octokit/rest": "^21.1.1",
|
||||||
|
"@phosphor-icons/react": "^2.1.7",
|
||||||
|
"@sentry/react": "^8.55.0",
|
||||||
|
"@tanstack/react-query": "^5.69.0",
|
||||||
|
"@unocss/vite": "^0.63.6",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"chrome-extension-toolkit": "^0.0.54",
|
"chrome-extension-toolkit": "^0.0.54",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.1",
|
||||||
"highcharts": "^11.3.0",
|
"conventional-changelog": "^6.0.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"highcharts": "^11.4.8",
|
||||||
"highcharts-react-official": "^3.2.1",
|
"highcharts-react-official": "^3.2.1",
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.13",
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.1.7",
|
||||||
"nanoid": "^5.0.6",
|
"kc-dabr-wasm": "^0.1.2",
|
||||||
|
"nanoid": "^5.1.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"sass": "^1.71.1",
|
"react-loading-skeleton": "^3.5.0",
|
||||||
"sql.js": "1.10.2"
|
"react-markdown": "^9.1.0",
|
||||||
|
"react-syntax-highlighter": "^15.6.1",
|
||||||
|
"remark-gfm": "^4.0.1",
|
||||||
|
"sass": "^1.85.1",
|
||||||
|
"simple-git": "^3.27.0",
|
||||||
|
"sql.js": "1.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chromatic-com/storybook": "^1.4.0",
|
"@chromatic-com/storybook": "^2.0.2",
|
||||||
"@commitlint/cli": "^18.6.1",
|
"@commitlint/cli": "^19.7.1",
|
||||||
"@commitlint/config-conventional": "^18.6.2",
|
"@commitlint/config-conventional": "^19.7.1",
|
||||||
"@commitlint/types": "^19.0.3",
|
"@commitlint/types": "^19.5.0",
|
||||||
"@crxjs/vite-plugin": "2.0.0-beta.21",
|
"@crxjs/vite-plugin": "2.0.0-beta.21",
|
||||||
"@iconify-json/bi": "^1.1.23",
|
"@iconify-json/bi": "^1.2.2",
|
||||||
"@iconify-json/material-symbols": "^1.1.73",
|
"@iconify-json/ic": "^1.2.2",
|
||||||
"@iconify-json/ri": "^1.1.20",
|
"@iconify-json/iconoir": "^1.2.7",
|
||||||
"@storybook/addon-designs": "^8.0.1",
|
"@iconify-json/material-symbols": "^1.2.14",
|
||||||
"@storybook/addon-essentials": "^8.1.1",
|
"@iconify-json/ri": "^1.2.5",
|
||||||
"@storybook/addon-links": "^8.1.1",
|
"@iconify-json/streamline": "^1.2.2",
|
||||||
"@storybook/blocks": "^8.1.1",
|
"@semantic-release/exec": "^6.0.3",
|
||||||
"@storybook/react": "^8.1.1",
|
"@sentry/types": "^8.55.0",
|
||||||
"@storybook/react-vite": "^8.1.1",
|
"@storybook/addon-designs": "^8.2.0",
|
||||||
"@storybook/test": "^8.1.1",
|
"@storybook/addon-essentials": "^8.6.0",
|
||||||
|
"@storybook/addon-links": "^8.6.0",
|
||||||
|
"@storybook/blocks": "^8.6.0",
|
||||||
|
"@storybook/react": "^8.6.0",
|
||||||
|
"@storybook/react-vite": "^8.6.0",
|
||||||
|
"@storybook/test": "^8.6.0",
|
||||||
"@svgr/core": "^8.1.0",
|
"@svgr/core": "^8.1.0",
|
||||||
"@svgr/plugin-jsx": "^8.1.0",
|
"@svgr/plugin-jsx": "^8.1.0",
|
||||||
"@types/chrome": "^0.0.268",
|
"@types/chrome": "^0.0.273",
|
||||||
"@types/node": "^20.12.12",
|
"@types/conventional-changelog": "^3.1.5",
|
||||||
|
"@types/gulp": "^4.0.17",
|
||||||
|
"@types/gulp-zip": "^4.0.4",
|
||||||
|
"@types/node": "^22.13.5",
|
||||||
"@types/prompts": "^2.4.9",
|
"@types/prompts": "^2.4.9",
|
||||||
"@types/react": "^18.3.2",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.5",
|
||||||
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
|
"@types/semantic-release": "^20.0.6",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.5.8",
|
||||||
"@types/sql.js": "^1.4.9",
|
"@types/sql.js": "^1.4.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^7.18.0",
|
||||||
"@unocss/eslint-config": "^0.58.6",
|
"@unocss/eslint-config": "^0.63.6",
|
||||||
"@unocss/postcss": "^0.58.6",
|
"@unocss/postcss": "^0.63.6",
|
||||||
"@unocss/preset-uno": "^0.58.6",
|
"@unocss/preset-uno": "^0.63.6",
|
||||||
"@unocss/preset-web-fonts": "^0.58.6",
|
"@unocss/preset-web-fonts": "^0.63.6",
|
||||||
"@unocss/reset": "^0.58.6",
|
"@unocss/reset": "^0.63.6",
|
||||||
"@unocss/transformer-directives": "^0.58.6",
|
"@unocss/transformer-directives": "^0.63.6",
|
||||||
"@unocss/transformer-variant-group": "^0.58.6",
|
"@unocss/transformer-variant-group": "^0.63.6",
|
||||||
"@vitejs/plugin-react-swc": "^3.6.0",
|
"@vitejs/plugin-react-swc": "^3.8.0",
|
||||||
"@vitest/coverage-v8": "^1.3.1",
|
"@vitest/coverage-v8": "^2.1.9",
|
||||||
"@vitest/ui": "^1.3.1",
|
"@vitest/ui": "^2.1.9",
|
||||||
"chromatic": "^11.3.5",
|
"chalk": "^5.4.1",
|
||||||
"cssnano": "^6.0.5",
|
"chromatic": "^11.26.0",
|
||||||
"cssnano-preset-advanced": "^6.0.5",
|
"cssnano": "^7.0.6",
|
||||||
"dotenv": "^16.4.5",
|
"cssnano-preset-advanced": "^7.0.6",
|
||||||
"es-module-lexer": "^1.4.1",
|
"dotenv": "^16.4.7",
|
||||||
"eslint": "^8.57.0",
|
"es-module-lexer": "^1.6.0",
|
||||||
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "^17.1.0",
|
"eslint-config-airbnb-typescript": "^18.0.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.8.3",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-import-essentials": "^0.2.1",
|
"eslint-plugin-import-essentials": "^0.2.1",
|
||||||
"eslint-plugin-jsdoc": "^48.2.0",
|
"eslint-plugin-jsdoc": "^50.6.3",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-react-prefer-function-component": "^3.3.0",
|
"eslint-plugin-react-prefer-function-component": "^3.4.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
"eslint-plugin-react-refresh": "^0.4.19",
|
||||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"eslint-plugin-storybook": "^0.8.0",
|
"eslint-plugin-storybook": "^0.9.0",
|
||||||
|
"eslint-plugin-tsdoc": "^0.3.0",
|
||||||
|
"gulp": "^5.0.0",
|
||||||
|
"gulp-execa": "^7.0.1",
|
||||||
|
"gulp-zip": "^6.1.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"postcss": "^8.4.35",
|
"postcss": "^8.5.3",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.5.2",
|
||||||
"react-dev-utils": "^12.0.1",
|
"react-dev-utils": "^12.0.1",
|
||||||
"storybook": "^8.1.1",
|
"semantic-release": "^24.2.3",
|
||||||
"typescript": "^5.4.3",
|
"storybook": "^8.6.0",
|
||||||
"unocss": "^0.58.6",
|
"typescript": "^5.7.3",
|
||||||
"unocss-preset-primitives": "0.0.2-beta.0",
|
"unocss": "^0.63.6",
|
||||||
"unplugin-icons": "^0.18.5",
|
"unocss-preset-primitives": "0.0.2-beta.1",
|
||||||
"vite": "^5.1.4",
|
"unplugin-icons": "^0.19.3",
|
||||||
"vite-plugin-inspect": "^0.8.3",
|
"vite": "^5.4.14",
|
||||||
"vitest": "^1.3.1"
|
"vite-plugin-inspect": "^0.8.9",
|
||||||
|
"vitest": "^2.1.9"
|
||||||
|
},
|
||||||
|
"engineStrict": true,
|
||||||
|
"engines": {
|
||||||
|
"pnpm": "^10"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"@crxjs/vite-plugin@2.0.0-beta.21": "patches/@crxjs__vite-plugin@2.0.0-beta.21.patch",
|
"@crxjs/vite-plugin@2.0.0-beta.21": "patches/@crxjs__vite-plugin@2.0.0-beta.21.patch",
|
||||||
"@unocss/vite@0.58.6": "patches/@unocss__vite@0.58.6.patch"
|
"@unocss/vite": "patches/@unocss__vite.patch"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"es-module-lexer": "^1.4.1"
|
"es-module-lexer": "^1.5.4"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"volta": {
|
||||||
|
"node": "20.9.0",
|
||||||
|
"pnpm": "10.6.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
patches/@unocss__vite.patch
Normal file
50
patches/@unocss__vite.patch
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
diff --git a/dist/index.mjs b/dist/index.mjs
|
||||||
|
index 7210f5fd650a0b7bb36b467fff85feb0d8e4ec63..c8f98bd314daec0b91c514ea9d9fc2b79cea8502 100644
|
||||||
|
--- a/dist/index.mjs
|
||||||
|
+++ b/dist/index.mjs
|
||||||
|
@@ -369,15 +369,15 @@ const VIRTUAL_ENTRY_ALIAS = [
|
||||||
|
/^(?:virtual:)?uno(?::(.+))?\.css(\?.*)?$/
|
||||||
|
];
|
||||||
|
const LAYER_MARK_ALL = "__ALL__";
|
||||||
|
-const RESOLVED_ID_WITH_QUERY_RE = /[/\\]__uno(_.*?)?\.css(\?.*)?$/;
|
||||||
|
-const RESOLVED_ID_RE = /[/\\]__uno(?:_(.*?))?\.css$/;
|
||||||
|
+const RESOLVED_ID_WITH_QUERY_RE = /[/\\]uno(_.*?)?\.css(\?.*)?$/;
|
||||||
|
+const RESOLVED_ID_RE = /[/\\]uno(?:_(.*?))?\.css$/;
|
||||||
|
function resolveId(id) {
|
||||||
|
if (id.match(RESOLVED_ID_WITH_QUERY_RE))
|
||||||
|
return id;
|
||||||
|
for (const alias of VIRTUAL_ENTRY_ALIAS) {
|
||||||
|
const match = id.match(alias);
|
||||||
|
if (match) {
|
||||||
|
- return match[1] ? `/__uno_${match[1]}.css` : "/__uno.css";
|
||||||
|
+ return match[1] ? `/uno_${match[1]}.css` : "/uno.css";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -652,7 +652,7 @@ function GlobalModeBuildPlugin(ctx) {
|
||||||
|
css = await applyCssTransform(css, fakeCssId, options.dir, this);
|
||||||
|
const transformHandler = "handler" in cssPost.transform ? cssPost.transform.handler : cssPost.transform;
|
||||||
|
if (isLegacy) {
|
||||||
|
- await transformHandler.call({}, css, "/__uno.css");
|
||||||
|
+ await transformHandler.call({}, css, "/uno.css");
|
||||||
|
} else {
|
||||||
|
const hash = getHash(css);
|
||||||
|
await transformHandler.call({}, getHashPlaceholder(hash), fakeCssId);
|
||||||
|
@@ -914,7 +914,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
|
||||||
|
const { hash, css } = await generateCSS(layer);
|
||||||
|
return {
|
||||||
|
// add hash to the chunk of CSS that it will send back to client to check if there is new CSS generated
|
||||||
|
- code: `${css}__uno_hash_${hash}{--:'';}`,
|
||||||
|
+ code: `${css}uno_hash_${hash}{--:'';}`,
|
||||||
|
map: { mappings: "" }
|
||||||
|
};
|
||||||
|
},
|
||||||
|
@@ -933,7 +933,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
|
||||||
|
if (layer && code.includes("import.meta.hot")) {
|
||||||
|
let hmr = `
|
||||||
|
try {
|
||||||
|
- let hash = __vite__css.match(/__uno_hash_(\\w{${HASH_LENGTH}})/)
|
||||||
|
+ let hash = __vite__css.match(/uno_hash_(\\w{${HASH_LENGTH}})/)
|
||||||
|
hash = hash && hash[1]
|
||||||
|
if (!hash)
|
||||||
|
console.warn('[unocss-hmr]', 'failed to get unocss hash, hmr might not work')
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
diff --git a/dist/index.cjs b/dist/index.cjs
|
|
||||||
index 560f423a07f21b0c47abd494d77654de4c874481..35ae1fdca8bd5546f7e40a23edacb1dbbbd34b58 100644
|
|
||||||
--- a/dist/index.cjs
|
|
||||||
+++ b/dist/index.cjs
|
|
||||||
@@ -35,15 +35,15 @@ const VIRTUAL_ENTRY_ALIAS = [
|
|
||||||
/^(?:virtual:)?uno(?::(.+))?\.css(\?.*)?$/
|
|
||||||
];
|
|
||||||
const LAYER_MARK_ALL = "__ALL__";
|
|
||||||
-const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]__uno(?:(_.*?))?\.css(\?.*)?$/;
|
|
||||||
-const RESOLVED_ID_RE = /[\/\\]__uno(?:_(.*?))?\.css$/;
|
|
||||||
+const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]uno(?:(_.*?))?\.css(\?.*)?$/;
|
|
||||||
+const RESOLVED_ID_RE = /[\/\\]uno(?:_(.*?))?\.css$/;
|
|
||||||
function resolveId(id) {
|
|
||||||
if (id.match(RESOLVED_ID_WITH_QUERY_RE))
|
|
||||||
return id;
|
|
||||||
for (const alias of VIRTUAL_ENTRY_ALIAS) {
|
|
||||||
const match = id.match(alias);
|
|
||||||
if (match) {
|
|
||||||
- return match[1] ? `/__uno_${match[1]}.css` : "/__uno.css";
|
|
||||||
+ return match[1] ? `/uno_${match[1]}.css` : "/uno.css";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -745,7 +745,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
|
|
||||||
const { hash, css } = await generateCSS(layer);
|
|
||||||
return {
|
|
||||||
// add hash to the chunk of CSS that it will send back to client to check if there is new CSS generated
|
|
||||||
- code: `__uno_hash_${hash}{--:'';}${css}`,
|
|
||||||
+ code: `uno_hash_${hash}{--:'';}${css}`,
|
|
||||||
map: { mappings: "" }
|
|
||||||
};
|
|
||||||
},
|
|
||||||
@@ -764,7 +764,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
|
|
||||||
if (layer && code.includes("import.meta.hot")) {
|
|
||||||
let hmr = `
|
|
||||||
try {
|
|
||||||
- let hash = __vite__css.match(/__uno_hash_(\\w{${HASH_LENGTH}})/)
|
|
||||||
+ let hash = __vite__css.match(/uno_hash_(\\w{${HASH_LENGTH}})/)
|
|
||||||
hash = hash && hash[1]
|
|
||||||
if (!hash)
|
|
||||||
console.warn('[unocss-hmr]', 'failed to get unocss hash, hmr might not work')
|
|
||||||
diff --git a/dist/index.mjs b/dist/index.mjs
|
|
||||||
index bbbccb7cad7421cbdb97223a451ec5853c0476cb..4bf6a08d94e562090a530308c0ab8337afdf8243 100644
|
|
||||||
--- a/dist/index.mjs
|
|
||||||
+++ b/dist/index.mjs
|
|
||||||
@@ -1,17 +1,17 @@
|
|
||||||
-import process$1 from 'node:process';
|
|
||||||
+import remapping from '@ampproject/remapping';
|
|
||||||
+import { createFilter } from '@rollup/pluginutils';
|
|
||||||
+import { loadConfig } from '@unocss/config';
|
|
||||||
+import { BetterMap, createGenerator, cssIdRE, notNull, toEscapedSelector } from '@unocss/core';
|
|
||||||
import UnocssInspector from '@unocss/inspector';
|
|
||||||
-import { resolve, isAbsolute, dirname } from 'node:path';
|
|
||||||
-import fs from 'node:fs/promises';
|
|
||||||
import fg from 'fast-glob';
|
|
||||||
import MagicString from 'magic-string';
|
|
||||||
-import remapping from '@ampproject/remapping';
|
|
||||||
-import { createHash } from 'node:crypto';
|
|
||||||
-import { cssIdRE, createGenerator, BetterMap, notNull, toEscapedSelector } from '@unocss/core';
|
|
||||||
import { Buffer } from 'node:buffer';
|
|
||||||
-import { createFilter } from '@rollup/pluginutils';
|
|
||||||
+import { createHash } from 'node:crypto';
|
|
||||||
import fs$1 from 'node:fs';
|
|
||||||
+import fs from 'node:fs/promises';
|
|
||||||
+import { dirname, isAbsolute, resolve } from 'node:path';
|
|
||||||
+import process$1 from 'node:process';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
-import { loadConfig } from '@unocss/config';
|
|
||||||
|
|
||||||
const defaultPipelineExclude = [cssIdRE];
|
|
||||||
const defaultPipelineInclude = [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/];
|
|
||||||
@@ -20,15 +20,15 @@ const VIRTUAL_ENTRY_ALIAS = [
|
|
||||||
/^(?:virtual:)?uno(?::(.+))?\.css(\?.*)?$/
|
|
||||||
];
|
|
||||||
const LAYER_MARK_ALL = "__ALL__";
|
|
||||||
-const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]__uno(?:(_.*?))?\.css(\?.*)?$/;
|
|
||||||
-const RESOLVED_ID_RE = /[\/\\]__uno(?:_(.*?))?\.css$/;
|
|
||||||
+const RESOLVED_ID_WITH_QUERY_RE = /[\/\\]uno(?:(_.*?))?\.css(\?.*)?$/;
|
|
||||||
+const RESOLVED_ID_RE = /[\/\\]uno(?:_(.*?))?\.css$/;
|
|
||||||
function resolveId(id) {
|
|
||||||
if (id.match(RESOLVED_ID_WITH_QUERY_RE))
|
|
||||||
return id;
|
|
||||||
for (const alias of VIRTUAL_ENTRY_ALIAS) {
|
|
||||||
const match = id.match(alias);
|
|
||||||
if (match) {
|
|
||||||
- return match[1] ? `/__uno_${match[1]}.css` : "/__uno.css";
|
|
||||||
+ return match[1] ? `/uno_${match[1]}.css` : "/uno.css";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -730,7 +730,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
|
|
||||||
const { hash, css } = await generateCSS(layer);
|
|
||||||
return {
|
|
||||||
// add hash to the chunk of CSS that it will send back to client to check if there is new CSS generated
|
|
||||||
- code: `__uno_hash_${hash}{--:'';}${css}`,
|
|
||||||
+ code: `uno_hash_${hash}{--:'';}${css}`,
|
|
||||||
map: { mappings: "" }
|
|
||||||
};
|
|
||||||
},
|
|
||||||
@@ -749,7 +749,7 @@ function GlobalModeDevPlugin({ uno, tokens, tasks, flushTasks, affectedModules,
|
|
||||||
if (layer && code.includes("import.meta.hot")) {
|
|
||||||
let hmr = `
|
|
||||||
try {
|
|
||||||
- let hash = __vite__css.match(/__uno_hash_(\\w{${HASH_LENGTH}})/)
|
|
||||||
+ let hash = __vite__css.match(/uno_hash_(\\w{${HASH_LENGTH}})/)
|
|
||||||
hash = hash && hash[1]
|
|
||||||
if (!hash)
|
|
||||||
console.warn('[unocss-hmr]', 'failed to get unocss hash, hmr might not work')
|
|
||||||
15537
pnpm-lock.yaml
generated
15537
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
BIN
public/fonts/roboto-mono.woff2
Normal file
BIN
public/fonts/roboto-mono.woff2
Normal file
Binary file not shown.
54
scripts/generateChangelog.ts
Normal file
54
scripts/generateChangelog.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { exec } from 'child_process';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
|
||||||
|
const execPromise = promisify(exec);
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
preset:
|
||||||
|
| 'angular'
|
||||||
|
| 'atom'
|
||||||
|
| 'codemirror'
|
||||||
|
| 'conventionalcommits'
|
||||||
|
| 'ember'
|
||||||
|
| 'eslint'
|
||||||
|
| 'express'
|
||||||
|
| 'jquery'
|
||||||
|
| 'jshint';
|
||||||
|
|
||||||
|
// The file to write the changelog to
|
||||||
|
outFile?: string;
|
||||||
|
|
||||||
|
// How many releases to be generated from the latest
|
||||||
|
// If 0, the whole changelog will be regenerated and the outfile will be overwritten
|
||||||
|
releaseCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a changelog using the conventional-changelog command.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves when the changelog is generated.
|
||||||
|
* @throws If there is an error generating the changelog.
|
||||||
|
*/
|
||||||
|
async function generateChangelog({ preset, outFile = 'CHANGELOG.md', releaseCount = 1 }: Props): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Run the conventional-changelog command to generate changelog
|
||||||
|
const { stdout, stderr } = await execPromise(
|
||||||
|
`conventional-changelog -p ${preset} -i ${outFile} -s -r ${releaseCount}`,
|
||||||
|
{
|
||||||
|
// Ensures it runs from the project root
|
||||||
|
cwd: resolve(process.cwd()),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Log output and error if any
|
||||||
|
console.log(stdout);
|
||||||
|
if (stderr) {
|
||||||
|
console.error(stderr);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating changelog:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateChangelog({ preset: 'conventionalcommits', releaseCount: 0 });
|
||||||
53
scripts/release.ts
Normal file
53
scripts/release.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import prompts from 'prompts';
|
||||||
|
import { simpleGit } from 'simple-git';
|
||||||
|
|
||||||
|
import { getSourceRef } from '../utils/git/getSourceRef';
|
||||||
|
import { error, info } from '../utils/log';
|
||||||
|
|
||||||
|
const git = simpleGit();
|
||||||
|
const status = await git.status();
|
||||||
|
|
||||||
|
if (status.files.length) {
|
||||||
|
console.log(error('Working directory is not clean, please commit or stash changes before releasing.'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { destinationBranch } = await prompts({
|
||||||
|
type: 'select',
|
||||||
|
name: 'destinationBranch',
|
||||||
|
message: 'What kind of release do you want to create?',
|
||||||
|
choices: ['preview', 'production'].map(releaseType => ({
|
||||||
|
title: releaseType,
|
||||||
|
value: releaseType,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
const sourceRef = await getSourceRef(destinationBranch);
|
||||||
|
|
||||||
|
const { confirm } = await prompts({
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'confirm',
|
||||||
|
message: `Are you sure you want to create a ${destinationBranch} release from ${sourceRef}?`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirm) {
|
||||||
|
console.log(error('Aborting release.'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we fetch the latest changes from the remote
|
||||||
|
await git.fetch();
|
||||||
|
|
||||||
|
// we checkout the source ref, pull the latest changes and then checkout the destination branch
|
||||||
|
console.info(`Checking out and updating ${sourceRef}...`);
|
||||||
|
await git.checkout(sourceRef);
|
||||||
|
await git.pull('origin', sourceRef);
|
||||||
|
console.info(`Checking out and updating ${destinationBranch}...`);
|
||||||
|
await git.checkout(destinationBranch);
|
||||||
|
await git.pull('origin', destinationBranch);
|
||||||
|
|
||||||
|
// we trigger the release github action by merging the source ref into the destination branch
|
||||||
|
console.info(`Merging ${sourceRef} into ${destinationBranch}...`);
|
||||||
|
await git.merge([sourceRef, '--ff-only']);
|
||||||
|
await git.push('origin', destinationBranch);
|
||||||
|
|
||||||
|
console.info(info(`Release to ${destinationBranch} created! Check github for status`));
|
||||||
BIN
src/assets/LD-icon-new.png
Normal file
BIN
src/assets/LD-icon-new.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 188 KiB |
BIN
src/assets/LD-icon.png
Normal file
BIN
src/assets/LD-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
5075
src/assets/UT-Map.svg
Normal file
5075
src/assets/UT-Map.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 347 KiB |
@@ -4,51 +4,116 @@ const splashText: string[] = [
|
|||||||
'Rec Sports fills up fast, even before the sun reaches its peak.',
|
'Rec Sports fills up fast, even before the sun reaches its peak.',
|
||||||
"Ah, Jendy's! A taste ever so refined.",
|
"Ah, Jendy's! A taste ever so refined.",
|
||||||
'Fine dining at Jester City Limits, eh?',
|
'Fine dining at Jester City Limits, eh?',
|
||||||
'Rec Sports fills up fast, even before the sun reaches its peak.',
|
'The road to Kinsolving is long, but their delicacies makes it worth every step.',
|
||||||
|
'The squirrels, they have mastered begging for food better than students. Impressive... but worrying.',
|
||||||
|
"Do you study often? Ha! What am I saying? Of course you don't.",
|
||||||
|
"Hey, you, you're finally awake. You were trying to skip class right?",
|
||||||
|
'Mmm... Brutalist architecture...',
|
||||||
|
'The course syllabus: more than meets the eye',
|
||||||
|
'Pain is temporary, GPA is forever.',
|
||||||
|
"You've Yee'd Your Last Haw.",
|
||||||
|
'lol everything is already waitlisted.',
|
||||||
|
'Could be worse. Could be A&M.',
|
||||||
|
"Should you major in Compsci? well, here's a better question. do you wanna have a bad time?",
|
||||||
|
'A pen and paper is old fashioned, but sometimes old ways are best',
|
||||||
|
'A heart is like bedrock, destroyable only by cheating',
|
||||||
|
'You may not rest now, there are Canvas assignments nearby',
|
||||||
|
'You are filled with DETERMINATION',
|
||||||
|
'60k+ users!',
|
||||||
|
'Almost Turing complete',
|
||||||
|
'#BF5700',
|
||||||
|
'The waitlist is a lie!',
|
||||||
|
'BEVO JOCKEY 🗣️🗣️🗣️',
|
||||||
'RIP Domino, you beloved campus feline.',
|
'RIP Domino, you beloved campus feline.',
|
||||||
"The year is 2055 and Welch still isn't finished.",
|
"The year is 2055 and Welch still isn't finished.",
|
||||||
'Motivation dropping faster than ur GPA',
|
'Motivation dropping faster than ur GPA',
|
||||||
'No Work Happens On PCL 5th Floor.',
|
'No Work Happens On PCL 5th Floor.',
|
||||||
'I may be a sophomore in name, but my credit count screams freshman!',
|
'I may be a sophomore in name, but my credit count screams freshman!',
|
||||||
'Pain is temporary, GPA is forever.',
|
// 'TeXAs iS BaCK GuYZ',
|
||||||
"You've Yee'd Your Last Haw.",
|
|
||||||
'lol everything is already waitlisted.',
|
|
||||||
"At Least You're Not At A&M.",
|
|
||||||
'TeXAs iS BaCK GuYZ',
|
|
||||||
'mAke iT yOuR tExAS',
|
'mAke iT yOuR tExAS',
|
||||||
|
'change yOur slogan',
|
||||||
"'Academically Challenged'",
|
"'Academically Challenged'",
|
||||||
'Does McCombs teach Parseltongue?',
|
'Does McCombs teach Parseltongue?',
|
||||||
'No Cruce Enfrente Del Bus.',
|
'No Cruce Enfrente Del Bus.',
|
||||||
'Omae Wa Mou Shindeiru...',
|
'Omae Wa Mou Shindeiru...',
|
||||||
'Bevo Bucks are the new Bitcoin',
|
"They say each day, another brick disappears from Speedway. No one's sure where to.",
|
||||||
'Every day another brick disappears from Speedway',
|
|
||||||
'The GDC will annex the EER one day',
|
'The GDC will annex the EER one day',
|
||||||
'Just you wait. Our CNS operatives will topple the EER regime',
|
|
||||||
'To hike to Kins or not to hike to Kins...',
|
'To hike to Kins or not to hike to Kins...',
|
||||||
'The road to Kinsolving is long, but their delicacies makes it worth every step.',
|
|
||||||
"C'mon you Longhorns! You want to study forever?",
|
"C'mon you Longhorns! You want to study forever?",
|
||||||
'HOW BOUT A NICE CUP OF LIBER TEA',
|
'HOW BOUT A NICE CUP OF LIBER TEA',
|
||||||
"It's called the quiet floor of the PCL for a reason",
|
"Block of Butter? Now, that's a name I haven't heard in a long time...",
|
||||||
"'Whose car is this and why is it attempting to enter Welch?'",
|
|
||||||
"'I really like one of my TAs and I wanna ask her out after this semester ends'",
|
|
||||||
'CaN YoU aSk OuT a tA aFtEr tHe SeMeStEr Is oVeR AnD gRaDeS ArE DoNe?',
|
'CaN YoU aSk OuT a tA aFtEr tHe SeMeStEr Is oVeR AnD gRaDeS ArE DoNe?',
|
||||||
"The Block of Butter incident of '22",
|
"The Block of Butter incident of '22",
|
||||||
"Arrows of Christ vs Church of Scientology was the crossover we didn't know we needed",
|
'Begun, the midterms have.',
|
||||||
|
'You must construct additional schedules',
|
||||||
'THE WALK SIGN IS ON TO CROSS GUADALUPE AND 21ST',
|
'THE WALK SIGN IS ON TO CROSS GUADALUPE AND 21ST',
|
||||||
'Days since last GDC door alarm: 0',
|
'Pay attention. Might learn something.',
|
||||||
'Finding a parking spot is like winning the lottery... if the lottery required parallel parking skills.',
|
'Roll for Initiative!',
|
||||||
'The squirrels are more ambitious than most freshmen during finals week.',
|
|
||||||
'The line at the on-campus Starbucks is longer than your course waitlist.',
|
'The line at the on-campus Starbucks is longer than your course waitlist.',
|
||||||
'The weather changes more often than your class schedule.',
|
'The weather changes more often than your class schedule.',
|
||||||
"'Sorry, the PCL is full' is the most heartbreaking message you'll ever receive.",
|
|
||||||
'Getting to class on time is like navigating a maze of construction zones.',
|
|
||||||
"'studying' often means refreshing Canvas every five minutes to see if the professor posted lecture slides.",
|
"'studying' often means refreshing Canvas every five minutes to see if the professor posted lecture slides.",
|
||||||
"'I'll just skip this lecture' often turns into a semester-long habit.",
|
"It's over Bevo! I have the high ground!",
|
||||||
|
"I'll just skip this lecture and watch the recording later. What's the worst that could happen?",
|
||||||
|
'I hear the tower bells are ringing, PCL full of students dreaming.',
|
||||||
|
"I don't like sand",
|
||||||
'The libraries are full of students pretending to study but actually napping with their eyes open.',
|
'The libraries are full of students pretending to study but actually napping with their eyes open.',
|
||||||
"The 'campus loop' refers to both the bus route and the endless cycle of trying to find your way around campus.",
|
"The 'campus loop' refers to both the bus route and the endless cycle of trying to find your way around campus.",
|
||||||
'The squirrels have mastered the art of begging for food better than most students during finals week.',
|
'The squirrels have mastered the art of begging for food better than most students during finals week.',
|
||||||
"'going for a run' often means 'running to get food from food trucks'.",
|
"'going for a run' often means 'running to get food from food trucks'.",
|
||||||
'The struggle to fit all your classes into one schedule is as real as the struggle to fit all your groceries into a mini-fridge.',
|
'We got UTRPv2 before GTA 6',
|
||||||
|
'Midterm season: Finish the Fight!',
|
||||||
|
"There's a Bevo on your lawn!",
|
||||||
|
'Veer was here',
|
||||||
|
'follow @sghsri!',
|
||||||
|
'Officially part of the SEC',
|
||||||
|
'Planner is now acquired by Plus',
|
||||||
|
'Longhorn Developers is the best UT Student Org',
|
||||||
|
'The Eiffel Tower is the UT Tower of Paris',
|
||||||
|
"He's a CS Major, but he showers regularly. 🧢",
|
||||||
|
'A CS major walks into a bar. The bar is empty because it is a CS major.',
|
||||||
|
'UT Registration Plus - The only thing that can make registration worse is not having it',
|
||||||
|
'UT Registration Plus - We make registration slightly less painful. Slightly.',
|
||||||
|
'UT Registration Plus - Do you really want to figure out which professors will ruin your GPA by yourself?',
|
||||||
|
"Ayo what's is a memory leak",
|
||||||
|
"lowkey we never thought we'd get this far, how are 60k of you people using this",
|
||||||
|
"dang we're really out here making a splash",
|
||||||
|
"We'd make a joke about A&M, but we're not sure they can read",
|
||||||
|
"We've only caused one or two outages, we swear!",
|
||||||
|
'Reality has a slight CNS bias',
|
||||||
|
'That one time in 2019 we accidentally DDoSed the registration system. (really)',
|
||||||
|
'Best UT financial aid tip? Marry rich.',
|
||||||
|
'Bevo Bucks are legal tender',
|
||||||
|
"'iS iT fInAlS wEeK oR mY fInAl WeEk'",
|
||||||
|
'Do you have an iPhone charger? Wait... Which one?',
|
||||||
|
'Deep in the heart of Texas',
|
||||||
|
'TEXAS!!! FIGHT!!!',
|
||||||
|
'Launder your AP credits through a community college for free',
|
||||||
|
'Also try Minecraft!',
|
||||||
|
'Please register later so the dev team can get the upper divs',
|
||||||
|
'The outdoor warning siren will test around 11:50 a.m.',
|
||||||
|
'You may be seeing this page because you used the Back button while browsing a secure web site or application. Alternatively, you may have mistakenly bookmarked the web sign in form instead of the actual web site you wanted to bookmark or used a link created by somebody else who made the same mistake.',
|
||||||
|
'Flag on the play, procrastinating picking classes, 1 day penalty, still first down',
|
||||||
|
'Pro-fessionals at Pro-crastinating',
|
||||||
|
'Built Texas Tough',
|
||||||
|
'Beware of trucks on Speedway',
|
||||||
|
'Marked as duplicate',
|
||||||
|
'If we ever flood, we can use the canoes on Speedway',
|
||||||
|
'Days since last Jester West fire alarm: 0',
|
||||||
|
'Remember the Alamo!',
|
||||||
|
"Figure out next year's housing by October",
|
||||||
|
'Technoblade never dies!',
|
||||||
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
'Why is my ${major} class in the ${unrelatedMajor} build?', // This is on purpose as a joke
|
||||||
|
'"Will that be cash, card, or id?"',
|
||||||
|
'How much did PepsiCo pay UT to replace all the coke machines?',
|
||||||
|
"Do it... take those 8am's",
|
||||||
|
"I'm polylingual, if you count programming languages",
|
||||||
|
'Just put the classes in the schedule lil bro',
|
||||||
|
'The wait is finally over',
|
||||||
|
"Stop trying to make UTRP happen, it's not going to happen!",
|
||||||
|
'Befriend the raccoons on campus',
|
||||||
|
`It's ${new Date().toLocaleString('en-US', { month: 'long', day: 'numeric' })} and OU still sucks`,
|
||||||
|
'As seen on TV!',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default splashText;
|
export default splashText;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { DevStore } from '@shared/storage/DevStore';
|
import { DevStore } from '@shared/storage/DevStore';
|
||||||
|
import useKC_DABR_WASM from 'kc-dabr-wasm';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
@@ -7,8 +8,8 @@ const manifest = chrome.runtime.getManifest();
|
|||||||
/**
|
/**
|
||||||
* Handles editing the storage for a specific area.
|
* Handles editing the storage for a specific area.
|
||||||
*
|
*
|
||||||
* @param {string} areaName - The name of the storage area.
|
* @param areaName - The name of the storage area.
|
||||||
* @returns {Function} - A function that accepts changes and sets them in the storage.
|
* @returns A function that accepts changes and sets them in the storage.
|
||||||
*/
|
*/
|
||||||
const handleEditStorage = (areaName: 'local' | 'sync' | 'session') => (changes: Record<string, unknown>) => {
|
const handleEditStorage = (areaName: 'local' | 'sync' | 'session') => (changes: Record<string, unknown>) => {
|
||||||
chrome.storage[areaName].set(changes);
|
chrome.storage[areaName].set(changes);
|
||||||
@@ -77,6 +78,7 @@ function DevDashboard() {
|
|||||||
const [localStorage, setLocalStorage] = React.useState<Record<string, unknown>>({});
|
const [localStorage, setLocalStorage] = React.useState<Record<string, unknown>>({});
|
||||||
const [syncStorage, setSyncStorage] = React.useState<Record<string, unknown>>({});
|
const [syncStorage, setSyncStorage] = React.useState<Record<string, unknown>>({});
|
||||||
const [sessionStorage, setSessionStorage] = React.useState<Record<string, unknown>>({});
|
const [sessionStorage, setSessionStorage] = React.useState<Record<string, unknown>>({});
|
||||||
|
useKC_DABR_WASM();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onVisibilityChange = () => {
|
const onVisibilityChange = () => {
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ const nameSuffix = isBeta ? ' (beta)' : mode === 'development' ? ' (dev)' : '';
|
|||||||
|
|
||||||
const HOST_PERMISSIONS: string[] = [
|
const HOST_PERMISSIONS: string[] = [
|
||||||
'*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*',
|
'*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*',
|
||||||
|
'*://*.utdirect.utexas.edu/registration/classlist/*',
|
||||||
'*://*.utexas.collegescheduler.com/*',
|
'*://*.utexas.collegescheduler.com/*',
|
||||||
'*://*.catalog.utexas.edu/ribbit/',
|
'*://*.catalog.utexas.edu/ribbit/',
|
||||||
'*://*.registrar.utexas.edu/schedules/*',
|
'*://*.registrar.utexas.edu/schedules/*',
|
||||||
'*://*.login.utexas.edu/login/*',
|
'*://*.login.utexas.edu/login/*',
|
||||||
'https://utexas.bluera.com/*',
|
'https://utexas.bluera.com/*',
|
||||||
|
'*://my.utexas.edu/student/student/*',
|
||||||
];
|
];
|
||||||
|
|
||||||
const manifest = defineManifest(async () => ({
|
const manifest = defineManifest(async () => ({
|
||||||
|
|||||||
486
src/pages/404/Page404.tsx
Normal file
486
src/pages/404/Page404.tsx
Normal file
@@ -0,0 +1,486 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
/* eslint-disable */
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page404 component
|
||||||
|
*
|
||||||
|
* @returns The Page404 component
|
||||||
|
*/
|
||||||
|
export default function Page404(): JSX.Element {
|
||||||
|
const canvasRef = useRef(null);
|
||||||
|
const ASPECT_RATIO = 848 / 480;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Credit: Notch
|
||||||
|
function _0x58e5() {
|
||||||
|
let _0x33cb15 = [
|
||||||
|
'putImageData',
|
||||||
|
'sin',
|
||||||
|
'getContext',
|
||||||
|
'random',
|
||||||
|
'sqrt',
|
||||||
|
'3498792PzxeQW',
|
||||||
|
'2JZNlgj',
|
||||||
|
'1199520qBJBWq',
|
||||||
|
'8OebCGy',
|
||||||
|
'156395VBdeaj',
|
||||||
|
'79193hyrbkc',
|
||||||
|
'10iqsPJh',
|
||||||
|
'now',
|
||||||
|
'data',
|
||||||
|
'80uLyuyX',
|
||||||
|
'cos',
|
||||||
|
'1843331yKjgfa',
|
||||||
|
'createImageData',
|
||||||
|
'2266182vPDGCM',
|
||||||
|
'current',
|
||||||
|
'49758oxlYFR',
|
||||||
|
];
|
||||||
|
_0x58e5 = function () {
|
||||||
|
return _0x33cb15;
|
||||||
|
};
|
||||||
|
return _0x58e5();
|
||||||
|
}
|
||||||
|
let _0xdd3699 = _0x9cb4;
|
||||||
|
(function (_0x5cc1ea, _0x44f991) {
|
||||||
|
let _0x3b1280 = _0x9cb4;
|
||||||
|
let _0x500290 = _0x5cc1ea();
|
||||||
|
while ([]) {
|
||||||
|
try {
|
||||||
|
let _0xea56e3 =
|
||||||
|
(-parseInt(_0x3b1280(0x8b)) / (0x2344 + 0x63 * 0x59 + -0x45ae)) *
|
||||||
|
(parseInt(_0x3b1280(0x87)) / (0x2ec * -0x3 + -0x9 * 0x3ed + 0x2c1b)) +
|
||||||
|
(parseInt(_0x3b1280(0x95)) / (0x4 * -0x8b7 + -0x16 * -0x109 + 0x1 * 0xc19)) *
|
||||||
|
(-parseInt(_0x3b1280(0x8f)) / (0x17 * -0x8c + 0x1 * 0x1f0f + 0x1d * -0xa3)) +
|
||||||
|
-parseInt(_0x3b1280(0x8a)) / (-0x113 * 0x21 + -0x1 * 0xc20 + 0x2f98) +
|
||||||
|
-parseInt(_0x3b1280(0x88)) / (-0x125c + 0xd * 0x12d + 0x1 * 0x319) +
|
||||||
|
parseInt(_0x3b1280(0x91)) / (0x389 * -0x2 + 0x1 * 0xb9c + -0x483) +
|
||||||
|
(-parseInt(_0x3b1280(0x89)) / (0x4e7 + -0xcf * -0x1 + -0x2d7 * 0x2)) *
|
||||||
|
(-parseInt(_0x3b1280(0x93)) / (-0x1859 + 0x1366 + 0x4fc)) +
|
||||||
|
(parseInt(_0x3b1280(0x8c)) / (-0x1 * -0x22b2 + -0x438 * -0x5 + -0x1be * 0x20)) *
|
||||||
|
(parseInt(_0x3b1280(0x86)) / (0x29 * 0x29 + 0x1d09 * 0x1 + -0x238f));
|
||||||
|
if (_0xea56e3 === _0x44f991) break;
|
||||||
|
else _0x500290.push(_0x500290.shift());
|
||||||
|
} catch (_0x5847bb) {
|
||||||
|
_0x500290.push(_0x500290.shift());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(_0x58e5, 0x3c063 + 0x13c * -0x305 + 0x2e43c);
|
||||||
|
const _0x5dce41 = canvasRef[_0xdd3699(0x94)];
|
||||||
|
const _0x16f39e = _0x5dce41[_0xdd3699(0x83)]('2d');
|
||||||
|
let _0x228047;
|
||||||
|
const _0x124180 = 0x2 * -0x7f7 + -0x70 * 0x1 + -0x602 * -0x3;
|
||||||
|
const _0x591e4f = 0xba7 * 0x1 + 0x1ebc + -0x2973;
|
||||||
|
let _0x39dbea = new Array(
|
||||||
|
(0x94f + -0x215 * -0x1 + -0xb24) *
|
||||||
|
(-0x1 * -0x1dda + -0x23e + -0x1b5c) *
|
||||||
|
(0xb9c + 0x1 * 0x1c57 + -0x27b3 * 0x1)
|
||||||
|
);
|
||||||
|
let _0x391e96 = new Array(
|
||||||
|
(-0x1e8f + -0x1 * -0x20f9 + -0x25a) *
|
||||||
|
(-0xce6 + 0x2a * 0x86 + -0x906) *
|
||||||
|
(0x1d57 * -0x1 + -0x1170 + 0x2eca) *
|
||||||
|
(0x1c2 + -0x11a9 + 0x1 * 0xff7)
|
||||||
|
);
|
||||||
|
function _0x3ebd0f() {
|
||||||
|
let _0x53ab34 = _0xdd3699;
|
||||||
|
for (
|
||||||
|
var _0x599786 = 0x1e5b + 0x7a * -0x3a + -0x2b6;
|
||||||
|
_0x599786 < -0x1 * 0x593 + 0x9 * -0x15 + 0x660;
|
||||||
|
_0x599786++
|
||||||
|
) {
|
||||||
|
let _0x5dd0b8 =
|
||||||
|
0x1043 +
|
||||||
|
0x401 +
|
||||||
|
-0x1345 -
|
||||||
|
((Math[_0x53ab34(0x84)]() * (-0xb10 + -0x173e + 0x22ae)) |
|
||||||
|
(0x1 * -0xf5b + -0x83 * -0xd + -0x8b4 * -0x1));
|
||||||
|
for (
|
||||||
|
var _0xebe028 = -0x1827 * -0x1 + 0x385 + 0x142 * -0x16;
|
||||||
|
_0xebe028 < (-0x77c + 0x11d * 0x1b + -0x1683) * (0x1a * 0x162 + 0x13 * -0xc3 + -0x1578);
|
||||||
|
_0xebe028++
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
var _0x4bc490 = 0x2c * 0x55 + -0xf1d + -0x3 * -0x2b;
|
||||||
|
_0x4bc490 < 0x3 * -0x9a5 + -0x1ee8 + 0x5 * 0xbfb;
|
||||||
|
_0x4bc490++
|
||||||
|
) {
|
||||||
|
let _0x36dcfc = -0xd60e5f * 0x1 + -0x3f232d + 0x1ab9dd6;
|
||||||
|
if (_0x599786 == -0x517 * -0x5 + -0x1b9b + -0x8b * -0x4)
|
||||||
|
_0x36dcfc = -0xcc5387 * 0x1 + -0xa89ad4 + -0xa6 * -0x303bf;
|
||||||
|
(_0x599786 != -0x9 * -0x4f + 0x53 * -0x22 + 0x843 ||
|
||||||
|
((Math[_0x53ab34(0x84)]() * (-0x11de + -0x1 * -0xbf1 + 0x5f0)) |
|
||||||
|
(0xf60 + -0x704 + -0x85c)) ==
|
||||||
|
0xb0b * -0x2 + -0x139b + 0x29b1) &&
|
||||||
|
(_0x5dd0b8 =
|
||||||
|
0x1 * 0x6d4 +
|
||||||
|
-0x9 * -0x1f2 +
|
||||||
|
-0x19 * 0xef -
|
||||||
|
((Math[_0x53ab34(0x84)]() * (-0x5 * 0x79d + -0x233 * -0xb + 0xe40)) |
|
||||||
|
(-0x4e4 + -0x165a + 0x1b3e)));
|
||||||
|
if (
|
||||||
|
_0x599786 == 0xe17 + 0x2a1 + -0x10b7 &&
|
||||||
|
_0xebe028 <
|
||||||
|
(((_0x4bc490 * _0x4bc490 * (-0x25c0 + -0x24 * 0x76 + -0x5 * -0xadf) +
|
||||||
|
_0x4bc490 * (0x2569 + -0x396 + 0x10c1 * -0x2)) >>
|
||||||
|
(-0x6d * 0x5 + 0x3b9 * 0x9 + 0x646 * -0x5)) &
|
||||||
|
(-0x249a + 0xec3 + 0x15da)) +
|
||||||
|
(-0x56 * 0x29 + -0x11 * -0x219 + -0x15d1)
|
||||||
|
)
|
||||||
|
_0x36dcfc = -0x1 * -0x3d6586 + 0x8bbbc5 + -0x5e770b;
|
||||||
|
else
|
||||||
|
_0x599786 == 0x9e + 0x29 * -0xa2 + 0x1955 &&
|
||||||
|
_0xebe028 <
|
||||||
|
(((_0x4bc490 * _0x4bc490 * (0x647 * -0x5 + -0x2359 + 0x42bf) +
|
||||||
|
_0x4bc490 * (-0xf * 0xb8 + -0x220c + 0x2d25)) >>
|
||||||
|
(0x13 * -0xc4 + -0x15fc + -0x3 * -0xc2e)) &
|
||||||
|
(-0x2483 + 0x13a9 + 0x59f * 0x3)) +
|
||||||
|
(0xca3 + -0x480 + -0x810) &&
|
||||||
|
(_0x5dd0b8 =
|
||||||
|
(_0x5dd0b8 * (-0xf2a * 0x1 + 0x4ab + 0xa81 * 0x1)) /
|
||||||
|
(-0x1 * 0x1e5d + -0x1f7a + -0x261 * -0x1a));
|
||||||
|
if (_0x599786 == 0xdad + 0xbe5 + 0xd * -0x1f7) {
|
||||||
|
_0x36dcfc = 0x30e9f6 * -0x2 + -0x3c841f + 0x105aa3c;
|
||||||
|
if (
|
||||||
|
_0x4bc490 > -0x1af + -0x2 * -0xf94 + -0x5 * 0x5e5 &&
|
||||||
|
_0x4bc490 < 0x1ed5 + 0x3c4 * 0x3 + -0x2a12 &&
|
||||||
|
((_0xebe028 > 0x164 + -0x1720 + 0x56f * 0x4 &&
|
||||||
|
_0xebe028 < 0x1514 + -0x127d * 0x1 + -0x288) ||
|
||||||
|
(_0xebe028 > 0x1b * 0x147 + -0x1415 * -0x1 + 0x1b39 * -0x2 &&
|
||||||
|
_0xebe028 < 0x2208 + -0x2408 + 0x22f * 0x1))
|
||||||
|
) {
|
||||||
|
_0x36dcfc = -0x1a7143 + 0x16bbd0e + -0x94b369;
|
||||||
|
let _0xf0bb95 = _0x4bc490 - (-0x20 * -0xce + -0x1222 * -0x1 + -0x2bdb);
|
||||||
|
var _0x436c14 =
|
||||||
|
(_0xebe028 & (0x1f03 + -0x1506 + -0x3e * 0x29)) -
|
||||||
|
(-0x1e7 + 0xef9 * 0x2 + -0x28c * 0xb);
|
||||||
|
if (_0xf0bb95 < -0x10 * 0xe0 + -0x207d + 0x3 * 0xf7f)
|
||||||
|
_0xf0bb95 = -0x8f * 0x2b + -0xd18 + 0x251e * 0x1 - _0xf0bb95;
|
||||||
|
if (_0x436c14 < 0x1f64 + -0x18d * -0x3 + -0x240b * 0x1)
|
||||||
|
_0x436c14 = -0x755 * 0x4 + -0xafb + 0x81 * 0x50 - _0x436c14;
|
||||||
|
if (_0x436c14 > _0xf0bb95) _0xf0bb95 = _0x436c14;
|
||||||
|
_0x5dd0b8 =
|
||||||
|
0x3b5 * 0x5 +
|
||||||
|
-0x1 * 0x1db7 +
|
||||||
|
0xbf2 -
|
||||||
|
((Math.random() * (-0xc45 + 0x257 * -0xb + 0x2622)) |
|
||||||
|
(-0xf29 * -0x1 + -0x948 + 0x1 * -0x5e1)) +
|
||||||
|
(_0xf0bb95 % (-0x1 * -0x13e1 + 0xa4e * -0x3 + 0x1c * 0x65)) *
|
||||||
|
(-0x939 + -0x1d87 + 0x26e0);
|
||||||
|
} else
|
||||||
|
((Math[_0x53ab34(0x84)]() * (-0x405 * 0x6 + -0x122a + 0x2a4a)) |
|
||||||
|
(0x7f7 + -0x1 * 0xc4f + -0x1 * -0x458)) ==
|
||||||
|
0x1 * -0x26b + -0x131 * 0xd + 0x2 * 0x8f4 &&
|
||||||
|
(_0x5dd0b8 =
|
||||||
|
(_0x5dd0b8 *
|
||||||
|
(0x102c +
|
||||||
|
-0x2f9 * 0x4 +
|
||||||
|
-0x3b2 -
|
||||||
|
(_0x4bc490 & (0x18a7 * -0x1 + 0x236e + 0x563 * -0x2)) *
|
||||||
|
(0x89 * 0x45 + 0xb38 + -0x2fc1))) /
|
||||||
|
(-0x1 * 0x156 + -0x4d * 0x65 + 0x201b * 0x1));
|
||||||
|
}
|
||||||
|
_0x599786 == -0x2 * -0x1087 + -0x3 * 0xcb3 + 0x9 * 0x90 &&
|
||||||
|
((_0x36dcfc = -0x100d2 * -0x9f + -0x659425 + 0x7b4bcc),
|
||||||
|
((_0x4bc490 +
|
||||||
|
(_0xebe028 >> (-0x12 * -0x205 + 0x1ca + -0x1 * 0x2622)) *
|
||||||
|
(-0xe67 + -0x1c85 * 0x1 + -0x30 * -0xe5)) %
|
||||||
|
(0x15c1 * -0x1 + 0x73f + 0xe8a * 0x1) ==
|
||||||
|
-0x1729 + -0x1 * 0x247 + 0x1970 ||
|
||||||
|
_0xebe028 % (0x14e9 * -0x1 + 0x21e1 + -0x1 * 0xcf4) ==
|
||||||
|
0x641 * 0x1 + -0x16fd + 0x4 * 0x42f) &&
|
||||||
|
(_0x36dcfc = 0x112d086 + -0x442565 + -0x47edf * 0x4));
|
||||||
|
_0x599786 == 0x6 * -0x2bb + -0x24 + 0x9d * 0x1b &&
|
||||||
|
(_0x36dcfc = -0x197 * 0x4ae9 + -0x22af5 * 0x1 + 0xb98463);
|
||||||
|
let _0xba7812 = _0x5dd0b8;
|
||||||
|
if (_0xebe028 >= -0x4 * -0x52 + 0x25d + -0x385)
|
||||||
|
_0xba7812 /= -0x1 * -0x497 + 0x1a3f + -0x7b5 * 0x4;
|
||||||
|
_0x599786 == -0x1d35 * -0x1 + -0x2445 + 0x718 &&
|
||||||
|
((_0x36dcfc = -0x981b * 0xc4 + -0x6c2f7 * -0x16 + -0x11 * -0x2db19),
|
||||||
|
((Math.random() * (-0x1 * 0x181a + -0x1700 + 0x2f1c)) | (-0x266 + 0x3 * 0x8b4 + -0x17b6)) ==
|
||||||
|
0xcc2 + -0xbc6 + -0xe * 0x12 &&
|
||||||
|
((_0x36dcfc = 0x2589 + -0xd * 0x27f + 0x3e * -0x15),
|
||||||
|
(_0xba7812 = 0x10ef + -0x68a + -0x966)));
|
||||||
|
let _0x1206cd =
|
||||||
|
(((((_0x36dcfc >> (0x2 * 0xfb3 + -0x118a + -0xdcc)) & (-0x8fc + -0x1a19 + 0x1 * 0x2414)) *
|
||||||
|
_0xba7812) /
|
||||||
|
(0xa6 * -0x25 + 0x112c + 0x7d1)) <<
|
||||||
|
(0x140c + -0xac0 + -0x1 * 0x93c)) |
|
||||||
|
(((((_0x36dcfc >> (-0x11 * -0x3e + 0x157d + -0x1993 * 0x1)) &
|
||||||
|
(-0x9 * 0x421 + -0x2102 + 0x472a * 0x1)) *
|
||||||
|
_0xba7812) /
|
||||||
|
(0x107b * -0x1 + -0x16fc + 0x2876)) <<
|
||||||
|
(-0x20e8 + -0x5e * -0x53 + 0x276)) |
|
||||||
|
(((_0x36dcfc & (0x581 * 0x3 + 0x1 * 0x977 + -0x18fb)) * _0xba7812) /
|
||||||
|
(-0xaa0 + 0x211 + 0x2 * 0x4c7));
|
||||||
|
_0x391e96[
|
||||||
|
_0x4bc490 +
|
||||||
|
_0xebe028 * (0x7 * -0x415 + 0x1b6e * -0x1 + 0x1f * 0x1cf) +
|
||||||
|
_0x599786 * (0x504 + -0x2075 + -0x97b * -0x3) * (0x25 * -0xa9 + 0x171d + -0x3 * -0x71)
|
||||||
|
] = _0x1206cd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (
|
||||||
|
var _0x4bc490 = -0x825 * -0x3 + 0x71a + -0x1b * 0x12b;
|
||||||
|
_0x4bc490 < 0x17f0 + -0x15a3 + -0x20d;
|
||||||
|
_0x4bc490++
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
var _0xebe028 = 0x53a + 0x7fe + -0x24 * 0x5e;
|
||||||
|
_0xebe028 < 0x1 * 0x1445 + 0x6d * 0x2 + -0xd * 0x19b;
|
||||||
|
_0xebe028++
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
let _0x2eee05 = 0x69 * 0x39 + -0x202 * 0xb + -0x14b;
|
||||||
|
_0x2eee05 < 0x97 * 0x27 + -0x261 + -0x1 * 0x1460;
|
||||||
|
_0x2eee05++
|
||||||
|
) {
|
||||||
|
var _0x599786 =
|
||||||
|
(_0x2eee05 << (-0x1 * -0x235e + 0x121 + -0x2473)) |
|
||||||
|
(_0xebe028 << (-0x7db + -0x80 * 0x23 + 0x1961)) |
|
||||||
|
_0x4bc490;
|
||||||
|
var _0x436c14 =
|
||||||
|
(_0xebe028 - (0xec6 + 0x6 * 0x199 + -0xbc * 0x21 + 0.5)) *
|
||||||
|
(0x1fca + 0x7 * 0xf9 + 0x2699 * -0x1 + 0.4);
|
||||||
|
let _0x198036 =
|
||||||
|
(_0x2eee05 - (0x1 * 0xecb + -0x24b8 + 0x160d + 0.5)) * (0xc * 0xda + -0x825 + -0x213 + 0.4);
|
||||||
|
_0x39dbea[_0x599786] =
|
||||||
|
(Math.random() * (0x1134 + -0x131 * -0x9 + -0x1bdd)) | (0x160c + -0x7 * 0x6b + -0x131f);
|
||||||
|
if (
|
||||||
|
Math[_0x53ab34(0x84)]() >
|
||||||
|
Math[_0x53ab34(0x85)](Math.sqrt(_0x436c14 * _0x436c14 + _0x198036 * _0x198036)) -
|
||||||
|
(0x15a9 + -0xe8f * -0x2 + 0x32c7 * -0x1 + 0.8)
|
||||||
|
)
|
||||||
|
_0x39dbea[_0x599786] = 0x1a62 + -0xaf * -0x1 + 0x1 * -0x1b11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_0x228047 = _0x16f39e[_0x53ab34(0x92)](_0x124180, _0x591e4f);
|
||||||
|
for (
|
||||||
|
var _0x599786 = 0x26b7 + -0x1b66 * 0x1 + -0x1 * 0xb51;
|
||||||
|
_0x599786 < _0x124180 * _0x591e4f;
|
||||||
|
_0x599786++
|
||||||
|
) {
|
||||||
|
_0x228047[_0x53ab34(0x8e)][
|
||||||
|
_0x599786 * (-0x4 * -0x4c0 + 0x228a + -0x3586) + (-0x856 + -0x22 * 0x37 + 0x1 * 0xfa7)
|
||||||
|
] = -0x4e5 + 0x196a + -0x126 * 0x11;
|
||||||
|
}
|
||||||
|
setInterval(_0x5629d1, (0x26b5 + 0x1 * 0xf9b + 0x1934 * -0x2) / (0x1 * 0xca1 + -0xbb6 + -0x87));
|
||||||
|
}
|
||||||
|
function _0x5629d1() {
|
||||||
|
let _0x13c635 = _0xdd3699;
|
||||||
|
_0x5b7f43(),
|
||||||
|
_0x16f39e[_0x13c635(0x81)](
|
||||||
|
_0x228047,
|
||||||
|
0x9c + -0x1 * 0x23ab + 0x230f * 0x1,
|
||||||
|
-0x1c26 + 0x7bf + -0x6cd * -0x3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let _0x93f360 = 0x64 * 0x49 + 0x1e1e + -0x3aa2;
|
||||||
|
function _0x5b7f43() {
|
||||||
|
let _0x4626de = _0xdd3699;
|
||||||
|
let _0x400eca =
|
||||||
|
Math[_0x4626de(0x82)](
|
||||||
|
((Date.now() % (-0x1f15 * -0x1 + 0x1 * -0x44b9 + -0x265a * -0x2)) /
|
||||||
|
(-0x396f + 0x1 * -0x995 + 0x6a14)) *
|
||||||
|
Math.PI *
|
||||||
|
(0x2429 + 0x2360 + -0x4787)
|
||||||
|
) *
|
||||||
|
(-0x106 * -0x1d + -0x3e * -0xa0 + -0x446e + 0.4) +
|
||||||
|
Math.PI / (0x1b21 + -0x1 * -0xa9 + 0x379 * -0x8);
|
||||||
|
let _0xaac757 =
|
||||||
|
Math[_0x4626de(0x90)](
|
||||||
|
((Date[_0x4626de(0x8d)]() % (0xd * -0x351 + -0x3994 + 0x13f7 * 0x7)) /
|
||||||
|
(0x1ceb + -0x9ef * 0x1 + 0x1414)) *
|
||||||
|
Math.PI *
|
||||||
|
(0x1420 + 0x12b1 + -0x26cf)
|
||||||
|
) *
|
||||||
|
(-0x1 * -0x211a + -0x18ff + -0x81b * 0x1 + 0.4);
|
||||||
|
let _0x159021 = Math[_0x4626de(0x90)](_0xaac757);
|
||||||
|
let _0x11f9a2 = Math[_0x4626de(0x82)](_0xaac757);
|
||||||
|
let _0x1ac9ca = Math[_0x4626de(0x90)](_0x400eca);
|
||||||
|
let _0x49fc16 = Math[_0x4626de(0x82)](_0x400eca);
|
||||||
|
let _0x116a45 =
|
||||||
|
0x1b4f +
|
||||||
|
-0x1167 +
|
||||||
|
-0x9c8 +
|
||||||
|
0.5 +
|
||||||
|
((Date[_0x4626de(0x8d)]() % (-0x1 * -0xfaa + 0x169 * 0x1f + -0x7 * 0x2e7)) /
|
||||||
|
(-0x2831 + 0x1 * 0x171 + 0x4dd0)) *
|
||||||
|
(-0x911 + -0x26e2 + -0x1b * -0x1c9);
|
||||||
|
let _0x46282c = 0x1 * 0x238 + 0x252d + -0x2745 + 0.5;
|
||||||
|
let _0x1680c5 = -0x14ad + -0x187 * 0x15 + -0x30 * -0x11a + 0.5;
|
||||||
|
_0x93f360++;
|
||||||
|
for (let _0x132623 = -0x174b + 0x19 * -0xb7 + 0x292a; _0x132623 < _0x124180; _0x132623++) {
|
||||||
|
let _0x1859c2 = (_0x132623 - _0x124180 / (-0x3e5 * 0x8 + -0x1e7b + -0x3da5 * -0x1)) / _0x591e4f;
|
||||||
|
for (let _0x1a573d = 0x2440 + -0x262f + -0x1ef * -0x1; _0x1a573d < _0x591e4f; _0x1a573d++) {
|
||||||
|
let _0x58642d = (_0x1a573d - _0x591e4f / (0x14f4 + 0x24b * 0x11 + -0x3bed)) / _0x591e4f;
|
||||||
|
let _0x3109fa = -0x7ca + 0x5 * -0x503 + 0x349 * 0xa;
|
||||||
|
let _0x1df399 = _0x3109fa * _0x159021 + _0x58642d * _0x11f9a2;
|
||||||
|
let _0x8e77ec = _0x58642d * _0x159021 - _0x3109fa * _0x11f9a2;
|
||||||
|
let _0x5d0446 = _0x1859c2 * _0x1ac9ca + _0x1df399 * _0x49fc16;
|
||||||
|
let _0x14ed58 = _0x1df399 * _0x1ac9ca - _0x1859c2 * _0x49fc16;
|
||||||
|
let _0x13f1b0 = 0x1af9 + -0x3 * 0xc1b + 0x34 * 0x2e;
|
||||||
|
let _0x2062a9 = -0x6c * -0x21 + -0x13bb * 0x1 + 0x43 * 0x1a;
|
||||||
|
let _0x5c387a = 0x1124 + 0x862 * 0x2 + 0x9b * -0x38;
|
||||||
|
let _0x267dd3 = -0x986 + 0x5 * 0x1f5 + 0x5 * -0x7;
|
||||||
|
for (
|
||||||
|
let _0x3e178a = -0xf01 + 0xd * -0x59 + 0x93 * 0x22;
|
||||||
|
_0x3e178a < -0x16f6 + -0x204b * 0x1 + -0x36 * -0x106;
|
||||||
|
_0x3e178a++
|
||||||
|
) {
|
||||||
|
let _0x828a0c = _0x5d0446;
|
||||||
|
if (_0x3e178a == 0x411 * -0x7 + 0x2 * 0x80f + 0x11 * 0xba) _0x828a0c = _0x8e77ec;
|
||||||
|
if (_0x3e178a == -0x1e6d + -0xdb1 * -0x1 + 0x85f * 0x2) _0x828a0c = _0x14ed58;
|
||||||
|
let _0x57383c =
|
||||||
|
(0x18ab + 0x183f + -0x1 * 0x30e9) /
|
||||||
|
(_0x828a0c < 0x25 * -0xe3 + -0x2424 + 0x44f3 ? -_0x828a0c : _0x828a0c);
|
||||||
|
let _0x4b089b = _0x5d0446 * _0x57383c;
|
||||||
|
let _0x5b3085 = _0x8e77ec * _0x57383c;
|
||||||
|
let _0x1eaaad = _0x14ed58 * _0x57383c;
|
||||||
|
let _0x2a568a = _0x116a45 - (_0x116a45 | (-0xcdb + 0x1447 + -0x13 * 0x64));
|
||||||
|
if (_0x3e178a == 0x1 * -0x1048 + -0x7ea + -0x4d7 * -0x5)
|
||||||
|
_0x2a568a = _0x46282c - (_0x46282c | (-0x1db5 + 0x1 * -0x35 + -0x223 * -0xe));
|
||||||
|
if (_0x3e178a == 0x107e + 0x2c7 + -0x1343 * 0x1)
|
||||||
|
_0x2a568a = _0x1680c5 - (_0x1680c5 | (0x234d + -0x2368 + -0x3 * -0x9));
|
||||||
|
if (_0x828a0c > 0xe00 + 0xc9b * -0x1 + -0x165)
|
||||||
|
_0x2a568a = 0xb * -0x117 + 0x1 * -0x1e0b + 0x2a09 - _0x2a568a;
|
||||||
|
let _0x38c463 = _0x57383c * _0x2a568a;
|
||||||
|
let _0x227002 = _0x116a45 + _0x4b089b * _0x2a568a;
|
||||||
|
let _0x2aec99 = _0x46282c + _0x5b3085 * _0x2a568a;
|
||||||
|
let _0x32116b = _0x1680c5 + _0x1eaaad * _0x2a568a;
|
||||||
|
if (_0x828a0c < 0x180b + 0x19e3 * 0x1 + 0x391 * -0xe) {
|
||||||
|
if (_0x3e178a == 0x1313 * -0x2 + 0x1 * 0x2605 + -0x1 * -0x21) _0x227002--;
|
||||||
|
if (_0x3e178a == 0x3d * 0x15 + 0x114d + 0x164d * -0x1) _0x2aec99--;
|
||||||
|
if (_0x3e178a == -0x1c1 * -0x8 + -0x60c + -0x3fd * 0x2) _0x32116b--;
|
||||||
|
}
|
||||||
|
while (_0x38c463 < _0x267dd3) {
|
||||||
|
let _0x3db6a4 =
|
||||||
|
_0x39dbea[
|
||||||
|
((_0x32116b & (-0x3 * 0x1f3 + 0x1ef2 + -0x18da * 0x1)) <<
|
||||||
|
(-0x8d3 * 0x2 + -0x1f7 * 0x9 + 0x2361)) |
|
||||||
|
((_0x2aec99 & (-0x1a92 + 0x6b8 + 0x1419)) << (-0xe71 * 0x1 + -0x788 + 0x15ff)) |
|
||||||
|
(_0x227002 & (0x1897 + 0x2134 + -0x398c))
|
||||||
|
];
|
||||||
|
if (_0x3db6a4 > -0x18f6 + 0x513 * 0x2 + 0xed0) {
|
||||||
|
let _0x546fb5 =
|
||||||
|
((_0x227002 + _0x32116b) * (-0x861 + -0x16bd + 0x1f2e)) &
|
||||||
|
(0xd1c + -0x94 * -0x8 + -0x11ad);
|
||||||
|
let _0x1667c5 =
|
||||||
|
((_0x2aec99 * (-0x16f5 * -0x1 + -0x47 * -0x2f + 0x2a * -0xdb)) &
|
||||||
|
(-0x2469 + 0x156a * -0x1 + 0x39e2)) +
|
||||||
|
(-0x1 * 0x13f8 + 0x6df + -0x1 * -0xd29);
|
||||||
|
if (_0x3e178a == 0x1 * -0x1a87 + 0x1fdd + -0x555 * 0x1) {
|
||||||
|
(_0x546fb5 =
|
||||||
|
(_0x227002 * (-0x10d7 + 0x1 * 0x15ad + -0x2 * 0x263)) &
|
||||||
|
(-0x25ca * 0x1 + 0x8 * 0x278 + -0x1 * -0x1219)),
|
||||||
|
(_0x1667c5 =
|
||||||
|
(_0x32116b * (0x4d3 + 0x1c09 * -0x1 + 0x3 * 0x7c2)) &
|
||||||
|
(-0xf06 * 0x2 + -0x144f * -0x1 + -0x344 * -0x3));
|
||||||
|
if (_0x5b3085 < -0xa * 0xed + -0xd19 + 0x1 * 0x165b)
|
||||||
|
_0x1667c5 += -0xd48 + 0xf6c + 0xc * -0x2b;
|
||||||
|
}
|
||||||
|
let _0x5206c3 =
|
||||||
|
_0x391e96[
|
||||||
|
_0x546fb5 +
|
||||||
|
_0x1667c5 * (0x1c90 + 0x2 * -0x1297 + 0x8ae) +
|
||||||
|
_0x3db6a4 *
|
||||||
|
(0x208 * -0x13 + -0x2550 + 0x1 * 0x4ce8) *
|
||||||
|
(0xbc5 + -0x7b3 * -0x2 + -0x58 * 0x4f)
|
||||||
|
];
|
||||||
|
_0x5206c3 > 0x65a * -0x4 + 0xf9a + -0x5 * -0x1f6 &&
|
||||||
|
((_0x13f1b0 = _0x5206c3),
|
||||||
|
(_0x5c387a =
|
||||||
|
0x181c +
|
||||||
|
0x1449 +
|
||||||
|
-0x2b66 -
|
||||||
|
(((_0x38c463 / (-0x15 * -0x59 + -0x3 * -0x3c9 + 0x944 * -0x2)) *
|
||||||
|
(0x9cd * 0x1 + -0x15ce + 0x100 * 0xd)) |
|
||||||
|
(-0xc * -0x2a0 + 0x1 * -0x23bf + 0x43f))),
|
||||||
|
(_0x2062a9 =
|
||||||
|
((0x7a0 + 0x3f * 0x51 + 0x8 * -0x352) *
|
||||||
|
(-0xf60 +
|
||||||
|
0x21e9 +
|
||||||
|
-0xa * 0x1c1 -
|
||||||
|
((_0x3e178a + (0x1439 + -0x1049 + -0x1 * 0x3ee)) %
|
||||||
|
(-0xbd7 + -0x1 * -0x7d5 + 0x405)) *
|
||||||
|
(0x214 * -0x7 + 0x17 * 0x7 + 0xe1d * 0x1))) /
|
||||||
|
(-0x2709 + -0x6 * -0x312 + -0x39a * -0x6)),
|
||||||
|
(_0x267dd3 = _0x38c463));
|
||||||
|
}
|
||||||
|
(_0x227002 += _0x4b089b),
|
||||||
|
(_0x2aec99 += _0x5b3085),
|
||||||
|
(_0x32116b += _0x1eaaad),
|
||||||
|
(_0x38c463 += _0x57383c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _0x5cba48 =
|
||||||
|
(((_0x13f1b0 >> (0x8bb + 0x407 * -0x2 + -0x9d * 0x1)) & (-0xc6 + 0xb43 + -0x97e)) *
|
||||||
|
_0x2062a9 *
|
||||||
|
_0x5c387a) /
|
||||||
|
((-0x1df7 * -0x1 + -0x48b * 0x1 + -0x186d) * (-0x5 * -0x585 + -0x1497 + -0x603));
|
||||||
|
let _0x48b547 =
|
||||||
|
(((_0x13f1b0 >> (-0xa42 + -0x760 * 0x2 + 0x5 * 0x502)) &
|
||||||
|
(0xb31 + 0x1 * -0x538 + 0x31 * -0x1a)) *
|
||||||
|
_0x2062a9 *
|
||||||
|
_0x5c387a) /
|
||||||
|
((-0x1cff * -0x1 + -0xecd + -0xd33) * (-0x1 * 0x4d5 + 0x2440 + -0x2c * 0xb1));
|
||||||
|
let _0xdf8389 =
|
||||||
|
((_0x13f1b0 & (-0x247a + -0x4 * -0x9c2 + -0x39 * 0x7)) * _0x2062a9 * _0x5c387a) /
|
||||||
|
((0x1d5 * 0xa + -0x250a + -0x31 * -0x67) * (-0x7 * 0x25f + -0xae7 + 0x1c7f * 0x1));
|
||||||
|
(_0x228047[_0x4626de(0x8e)][
|
||||||
|
(_0x132623 + _0x1a573d * _0x124180) * (0x29 * -0xa9 + -0x94 * -0x2b + -0x239 * -0x1) +
|
||||||
|
(0x55d * 0x2 + 0xeed * 0x1 + -0xc7 * 0x21)
|
||||||
|
] = _0x5cba48),
|
||||||
|
(_0x228047[_0x4626de(0x8e)][
|
||||||
|
(_0x132623 + _0x1a573d * _0x124180) * (0x9 * -0x205 + -0xfb7 * 0x1 + -0x1c * -0x136) +
|
||||||
|
(-0x1 * -0x1e79 + 0x10cd + -0x2f45)
|
||||||
|
] = _0x48b547),
|
||||||
|
(_0x228047[_0x4626de(0x8e)][
|
||||||
|
(_0x132623 + _0x1a573d * _0x124180) * (0x1e2a + -0x21df + -0x1 * -0x3b9) +
|
||||||
|
(0x1e79 + 0x860 * -0x2 + 0x1 * -0xdb7)
|
||||||
|
] = _0xdf8389);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function _0x9cb4(_0x473675, _0x42315f) {
|
||||||
|
let _0x1c3f44 = _0x58e5();
|
||||||
|
return (
|
||||||
|
(_0x9cb4 = function (_0x2f27e0, _0x487e61) {
|
||||||
|
_0x2f27e0 -= -0x6c7 * -0x2 + 0x1 * -0x1b75 + -0x4 * -0x39a;
|
||||||
|
let _0x44ff4c = _0x1c3f44[_0x2f27e0];
|
||||||
|
return _0x44ff4c;
|
||||||
|
}),
|
||||||
|
_0x9cb4(_0x473675, _0x42315f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_0x3ebd0f();
|
||||||
|
|
||||||
|
console.log('404 page loaded');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='relative h-screen w-screen flex items-center justify-center overflow-hidden bg-white'>
|
||||||
|
{/* Canvas Container */}
|
||||||
|
<div className='relative h-full w-full'>
|
||||||
|
<canvas
|
||||||
|
ref={canvasRef}
|
||||||
|
className='absolute left-1/2 top-1/2 h-full w-full object-contain -translate-x-1/2 -translate-y-1/2'
|
||||||
|
style={{
|
||||||
|
maxWidth: '100vw',
|
||||||
|
maxHeight: '100vh',
|
||||||
|
aspectRatio: ASPECT_RATIO,
|
||||||
|
imageRendering: 'crisp-edges',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 404 Text Overlay */}
|
||||||
|
<div className='absolute left-1/2 top-1/2 z-10 -translate-x-1/2 -translate-y-1/2'>
|
||||||
|
<h1 className='text-8xl text-white font-bold'>404 Page not found</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
27
src/pages/404/index.html
Normal file
27
src/pages/404/index.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<title>404</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
<script src="./index.tsx" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
8
src/pages/404/index.tsx
Normal file
8
src/pages/404/index.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'uno.css';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
|
import Page404 from './Page404';
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(<Page404 />);
|
||||||
@@ -31,6 +31,20 @@ chrome.runtime.onInstalled.addListener(details => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// migration/login logic
|
||||||
|
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
|
||||||
|
// console.log(changeInfo);
|
||||||
|
if (changeInfo.url === 'https://utdirect.utexas.edu/apps/registrar/course_schedule/utrp_login/') {
|
||||||
|
function openPopupAction() {
|
||||||
|
chrome.tabs.onActivated.removeListener(openPopupAction);
|
||||||
|
chrome.action.openPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.tabs.onActivated.addListener(openPopupAction);
|
||||||
|
await chrome.tabs.remove(tabId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// initialize the message listener that will listen for messages from the content script
|
// initialize the message listener that will listen for messages from the content script
|
||||||
const messageListener = new MessageListener<BACKGROUND_MESSAGES>({
|
const messageListener = new MessageListener<BACKGROUND_MESSAGES>({
|
||||||
...browserActionHandler,
|
...browserActionHandler,
|
||||||
@@ -45,15 +59,11 @@ messageListener.listen();
|
|||||||
UserScheduleStore.listen('schedules', async schedules => {
|
UserScheduleStore.listen('schedules', async schedules => {
|
||||||
const index = await UserScheduleStore.get('activeIndex');
|
const index = await UserScheduleStore.get('activeIndex');
|
||||||
const numCourses = schedules.newValue[index]?.courses?.length;
|
const numCourses = schedules.newValue[index]?.courses?.length;
|
||||||
if (!numCourses) return;
|
updateBadgeText(numCourses || 0);
|
||||||
|
|
||||||
updateBadgeText(numCourses);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
UserScheduleStore.listen('activeIndex', async ({ newValue }) => {
|
UserScheduleStore.listen('activeIndex', async ({ newValue }) => {
|
||||||
const schedules = await UserScheduleStore.get('schedules');
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
const numCourses = schedules[newValue]?.courses?.length;
|
const numCourses = schedules[newValue]?.courses?.length;
|
||||||
if (!numCourses) return;
|
updateBadgeText(numCourses || 0);
|
||||||
|
|
||||||
updateBadgeText(numCourses);
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { ExtensionStore } from '@shared/storage/ExtensionStore';
|
import { ExtensionStore } from '@shared/storage/ExtensionStore';
|
||||||
|
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||||
|
|
||||||
|
import createSchedule from '../lib/createSchedule';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the extension is updated (or when the extension is reloaded in development mode)
|
* Called when the extension is updated (or when the extension is reloaded in development mode)
|
||||||
@@ -8,4 +11,11 @@ export default async function onUpdate() {
|
|||||||
version: chrome.runtime.getManifest().version,
|
version: chrome.runtime.getManifest().version,
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
|
|
||||||
|
// By invariant, there must always be at least one schedule
|
||||||
|
if (schedules.length === 0) {
|
||||||
|
createSchedule('Schedule 1');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import type { TabWithId } from '@background/util/openNewTab';
|
|||||||
import openNewTab from '@background/util/openNewTab';
|
import openNewTab from '@background/util/openNewTab';
|
||||||
import { tabs } from '@shared/messages';
|
import { tabs } from '@shared/messages';
|
||||||
import type { CalendarBackgroundMessages } from '@shared/messages/CalendarMessages';
|
import type { CalendarBackgroundMessages } from '@shared/messages/CalendarMessages';
|
||||||
|
import { OptionsStore } from '@shared/storage/OptionsStore';
|
||||||
|
import { CRX_PAGES } from '@shared/types/CRXPages';
|
||||||
import type { MessageHandler } from 'chrome-extension-toolkit';
|
import type { MessageHandler } from 'chrome-extension-toolkit';
|
||||||
|
|
||||||
const getAllTabInfos = async () => {
|
const getAllTabInfos = async () => {
|
||||||
@@ -21,16 +23,17 @@ const getAllTabInfos = async () => {
|
|||||||
const calendarBackgroundHandler: MessageHandler<CalendarBackgroundMessages> = {
|
const calendarBackgroundHandler: MessageHandler<CalendarBackgroundMessages> = {
|
||||||
async switchToCalendarTab({ data, sendResponse }) {
|
async switchToCalendarTab({ data, sendResponse }) {
|
||||||
const { uniqueId } = data;
|
const { uniqueId } = data;
|
||||||
const calendarUrl = chrome.runtime.getURL(`calendar.html`);
|
const calendarUrl = chrome.runtime.getURL(CRX_PAGES.CALENDAR);
|
||||||
|
|
||||||
const allTabs = await getAllTabInfos();
|
const allTabs = await getAllTabInfos();
|
||||||
|
|
||||||
const openCalendarTabInfo = allTabs.find(tab => tab.url?.startsWith(calendarUrl));
|
const openCalendarTabInfo = allTabs.find(tab => tab.url?.startsWith(calendarUrl));
|
||||||
|
|
||||||
if (openCalendarTabInfo !== undefined) {
|
if (openCalendarTabInfo !== undefined && !(await OptionsStore.get('alwaysOpenCalendarInNewTab'))) {
|
||||||
const tabid = openCalendarTabInfo.tab.id;
|
const tabid = openCalendarTabInfo.tab.id;
|
||||||
|
|
||||||
chrome.tabs.update(tabid, { active: true });
|
await chrome.tabs.update(tabid, { active: true });
|
||||||
|
await chrome.windows.update(openCalendarTabInfo.tab.windowId, { focused: true, drawAttention: true });
|
||||||
if (uniqueId !== undefined) await tabs.openCoursePopup({ uniqueId }, tabid);
|
if (uniqueId !== undefined) await tabs.openCoursePopup({ uniqueId }, tabid);
|
||||||
|
|
||||||
sendResponse(openCalendarTabInfo.tab);
|
sendResponse(openCalendarTabInfo.tab);
|
||||||
|
|||||||
@@ -2,16 +2,18 @@ import addCourse from '@pages/background/lib/addCourse';
|
|||||||
import clearCourses from '@pages/background/lib/clearCourses';
|
import clearCourses from '@pages/background/lib/clearCourses';
|
||||||
import createSchedule from '@pages/background/lib/createSchedule';
|
import createSchedule from '@pages/background/lib/createSchedule';
|
||||||
import deleteSchedule from '@pages/background/lib/deleteSchedule';
|
import deleteSchedule from '@pages/background/lib/deleteSchedule';
|
||||||
|
import exportSchedule from '@pages/background/lib/exportSchedule';
|
||||||
import removeCourse from '@pages/background/lib/removeCourse';
|
import removeCourse from '@pages/background/lib/removeCourse';
|
||||||
import renameSchedule from '@pages/background/lib/renameSchedule';
|
import renameSchedule from '@pages/background/lib/renameSchedule';
|
||||||
import switchSchedule from '@pages/background/lib/switchSchedule';
|
import switchSchedule from '@pages/background/lib/switchSchedule';
|
||||||
import type { UserScheduleMessages } from '@shared/messages/UserScheduleMessages';
|
import type { UserScheduleMessages } from '@shared/messages/UserScheduleMessages';
|
||||||
import { Course } from '@shared/types/Course';
|
import { Course } from '@shared/types/Course';
|
||||||
|
import { validateLoginStatus } from '@shared/util/checkLoginStatus';
|
||||||
import type { MessageHandler } from 'chrome-extension-toolkit';
|
import type { MessageHandler } from 'chrome-extension-toolkit';
|
||||||
|
|
||||||
const userScheduleHandler: MessageHandler<UserScheduleMessages> = {
|
const userScheduleHandler: MessageHandler<UserScheduleMessages> = {
|
||||||
addCourse({ data, sendResponse }) {
|
addCourse({ data, sendResponse }) {
|
||||||
addCourse(data.scheduleId, new Course(data.course)).then(sendResponse);
|
addCourse(data.scheduleId, new Course(data.course), data.hasColor ?? false).then(sendResponse);
|
||||||
},
|
},
|
||||||
removeCourse({ data, sendResponse }) {
|
removeCourse({ data, sendResponse }) {
|
||||||
removeCourse(data.scheduleId, new Course(data.course)).then(sendResponse);
|
removeCourse(data.scheduleId, new Course(data.course)).then(sendResponse);
|
||||||
@@ -31,6 +33,21 @@ const userScheduleHandler: MessageHandler<UserScheduleMessages> = {
|
|||||||
renameSchedule({ data, sendResponse }) {
|
renameSchedule({ data, sendResponse }) {
|
||||||
renameSchedule(data.scheduleId, data.newName).then(sendResponse);
|
renameSchedule(data.scheduleId, data.newName).then(sendResponse);
|
||||||
},
|
},
|
||||||
|
// proxy so we can add courses
|
||||||
|
addCourseByURL({ data: { url, method, body, response }, sendResponse }) {
|
||||||
|
fetch(url, {
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
.then(res => (response === 'json' ? res.json() : res.text()))
|
||||||
|
.then(sendResponse);
|
||||||
|
},
|
||||||
|
validateLoginStatus({ data, sendResponse }) {
|
||||||
|
validateLoginStatus(data.url).then(sendResponse);
|
||||||
|
},
|
||||||
|
exportSchedule({ data, sendResponse }) {
|
||||||
|
exportSchedule(data.scheduleId).then(sendResponse);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default userScheduleHandler;
|
export default userScheduleHandler;
|
||||||
|
|||||||
@@ -4,21 +4,26 @@ import { getUnusedColor } from '@shared/util/colors';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a course to a user's schedule.
|
* Adds a course to a user's schedule.
|
||||||
|
*
|
||||||
* @param scheduleId - The id of the schedule to add the course to.
|
* @param scheduleId - The id of the schedule to add the course to.
|
||||||
* @param course - The course to add.
|
* @param course - The course to add.
|
||||||
|
* @param hasColor - If the course block already has colors manually set
|
||||||
* @returns A promise that resolves to void.
|
* @returns A promise that resolves to void.
|
||||||
* @throws An error if the schedule is not found.
|
* @throws An error if the schedule is not found.
|
||||||
*/
|
*/
|
||||||
export default async function addCourse(scheduleId: string, course: Course): Promise<void> {
|
export default async function addCourse(scheduleId: string, course: Course, hasColor = false): Promise<void> {
|
||||||
const schedules = await UserScheduleStore.get('schedules');
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
const activeSchedule = schedules.find(s => s.id === scheduleId);
|
const activeSchedule = schedules.find(s => s.id === scheduleId);
|
||||||
if (!activeSchedule) {
|
if (!activeSchedule) {
|
||||||
throw new Error('Schedule not found');
|
throw new Error('Schedule not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
course.colors = getUnusedColor(activeSchedule, course);
|
if (!hasColor) {
|
||||||
|
course.colors = getUnusedColor(activeSchedule, course);
|
||||||
|
}
|
||||||
|
|
||||||
activeSchedule.courses.push(course);
|
activeSchedule.courses.push(course);
|
||||||
activeSchedule.updatedAt = Date.now();
|
activeSchedule.updatedAt = Date.now();
|
||||||
|
|
||||||
await UserScheduleStore.set('schedules', schedules);
|
await UserScheduleStore.set('schedules', schedules);
|
||||||
|
console.log(`Course added: ${course.courseName} (ID: ${course.uniqueId})`);
|
||||||
}
|
}
|
||||||
|
|||||||
65
src/pages/background/lib/addCourseByURL.ts
Normal file
65
src/pages/background/lib/addCourseByURL.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import addCourse from '@pages/background/lib/addCourse';
|
||||||
|
import { background } from '@shared/messages';
|
||||||
|
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||||
|
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
|
||||||
|
import getCourseTableRows from '@views/lib/getCourseTableRows';
|
||||||
|
import { SiteSupport } from '@views/lib/getSiteSupport';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a course to the active schedule by fetching course details from a provided URL.
|
||||||
|
* If no URL is provided, prompts the user to enter one.
|
||||||
|
* Sriram and Elie made this
|
||||||
|
*
|
||||||
|
* @param activeSchedule - The user's active schedule to which the course will be added.
|
||||||
|
* @param link - The URL from which to fetch the course details. If not provided, a prompt will ask for it.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves when the course has been added or the operation is cancelled.
|
||||||
|
*
|
||||||
|
* @throws an error if there is an issue with scraping the course details.
|
||||||
|
*/
|
||||||
|
export async function addCourseByURL(activeSchedule: UserSchedule, link?: string): Promise<void> {
|
||||||
|
// todo: Use a proper modal instead of a prompt
|
||||||
|
// eslint-disable-next-line no-param-reassign, no-alert
|
||||||
|
if (!link) link = prompt('Enter course link') || undefined;
|
||||||
|
|
||||||
|
// Exit if the user cancels the prompt
|
||||||
|
if (!link) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let htmlText: string;
|
||||||
|
try {
|
||||||
|
htmlText = await background.addCourseByURL({
|
||||||
|
url: link,
|
||||||
|
method: 'GET',
|
||||||
|
response: 'text',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
alert(`Failed to fetch url '${link}'`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = new DOMParser().parseFromString(htmlText, 'text/html');
|
||||||
|
|
||||||
|
const scraper = new CourseCatalogScraper(SiteSupport.COURSE_CATALOG_DETAILS, doc, link);
|
||||||
|
const tableRows = getCourseTableRows(doc);
|
||||||
|
const scrapedCourses = scraper.scrape(tableRows, false);
|
||||||
|
|
||||||
|
if (scrapedCourses.length !== 1) return;
|
||||||
|
const description = scraper.getDescription(doc);
|
||||||
|
const row = scrapedCourses[0]!;
|
||||||
|
const course = row.course!;
|
||||||
|
course.description = description;
|
||||||
|
|
||||||
|
if (activeSchedule.courses.every(c => c.uniqueId !== course.uniqueId)) {
|
||||||
|
console.log('Adding course');
|
||||||
|
await addCourse(activeSchedule.id, course);
|
||||||
|
} else {
|
||||||
|
console.log('Course already exists');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error scraping course:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,46 @@
|
|||||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||||
|
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||||
import { generateRandomId } from '@shared/util/random';
|
import { generateRandomId } from '@shared/util/random';
|
||||||
|
import type { Serialized } from 'chrome-extension-toolkit';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new schedule with the given name
|
* Creates a new schedule with the given name
|
||||||
* @param scheduleName the name of the schedule to create
|
*
|
||||||
* @returns undefined if successful, otherwise an error message
|
* @param scheduleName - The name of the schedule to create
|
||||||
|
* @returns Undefined if successful, otherwise an error message
|
||||||
*/
|
*/
|
||||||
export default async function createSchedule(scheduleName: string): Promise<string | undefined> {
|
export default async function createSchedule(scheduleName: string) {
|
||||||
const schedules = await UserScheduleStore.get('schedules');
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
// if (schedules.find(schedule => schedule.name === scheduleName)) {
|
// get the number of schedules that either have the same name or have the same name with a number appended (e.g. "New Schedule (1)")
|
||||||
// return `Schedule ${scheduleName} already exists`;
|
// this way we can prevent duplicate schedule names and increment the number if necessary
|
||||||
// }
|
|
||||||
|
|
||||||
schedules.push({
|
// Regex to match schedule names that follow the pattern "ScheduleName" or "ScheduleName (1)", "ScheduleName (2)", etc.
|
||||||
|
const regex = new RegExp(`^${scheduleName}( \\(\\d+\\))?$`);
|
||||||
|
|
||||||
|
// Find how many schedules match the base name or follow the pattern with a number
|
||||||
|
const count = schedules.filter(s => regex.test(s.name)).length;
|
||||||
|
|
||||||
|
// If any matches are found, append the next number to the schedule name
|
||||||
|
let name = scheduleName;
|
||||||
|
if (count > 0) {
|
||||||
|
name = `${scheduleName} (${count})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSchedule: Serialized<UserSchedule> = {
|
||||||
id: generateRandomId(),
|
id: generateRandomId(),
|
||||||
name: scheduleName,
|
name,
|
||||||
courses: [],
|
courses: [],
|
||||||
hours: 0,
|
hours: 0,
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
});
|
};
|
||||||
|
schedules.push(newSchedule);
|
||||||
|
|
||||||
await UserScheduleStore.set('schedules', schedules);
|
await UserScheduleStore.set('schedules', schedules);
|
||||||
return undefined;
|
|
||||||
|
// If there is only one schedule, set the active index to the new schedule
|
||||||
|
if (schedules.length <= 1) {
|
||||||
|
await UserScheduleStore.set('activeIndex', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newSchedule.id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||||
|
|
||||||
|
import createSchedule from './createSchedule';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a schedule with the specified name.
|
* Deletes a schedule with the specified name.
|
||||||
*
|
*
|
||||||
@@ -16,11 +18,33 @@ export default async function deleteSchedule(scheduleId: string): Promise<string
|
|||||||
if (scheduleIndex === -1) {
|
if (scheduleIndex === -1) {
|
||||||
throw new Error(`Schedule ${scheduleId} does not exist`);
|
throw new Error(`Schedule ${scheduleId} does not exist`);
|
||||||
}
|
}
|
||||||
if (scheduleIndex === activeIndex) {
|
|
||||||
throw new Error('Cannot delete active schedule');
|
|
||||||
}
|
|
||||||
|
|
||||||
schedules.splice(scheduleIndex, 1);
|
schedules.splice(scheduleIndex, 1);
|
||||||
await UserScheduleStore.set('schedules', schedules);
|
await UserScheduleStore.set('schedules', schedules);
|
||||||
|
|
||||||
|
// By invariant, there must always be at least one schedule
|
||||||
|
if (schedules.length === 0) {
|
||||||
|
createSchedule('Schedule 1');
|
||||||
|
}
|
||||||
|
|
||||||
|
let newActiveIndex = activeIndex;
|
||||||
|
if (scheduleIndex < activeIndex) {
|
||||||
|
newActiveIndex = activeIndex - 1;
|
||||||
|
} else if (activeIndex >= schedules.length) {
|
||||||
|
newActiveIndex = schedules.length - 1;
|
||||||
|
}
|
||||||
|
await UserScheduleStore.set('activeIndex', newActiveIndex);
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all schedules.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves when all schedules are deleted
|
||||||
|
*/
|
||||||
|
export async function deleteAllSchedules(): Promise<void> {
|
||||||
|
await UserScheduleStore.set('schedules', []);
|
||||||
|
await UserScheduleStore.set('activeIndex', 0);
|
||||||
|
await createSchedule('Schedule 1');
|
||||||
|
}
|
||||||
|
|||||||
35
src/pages/background/lib/duplicateSchedule.ts
Normal file
35
src/pages/background/lib/duplicateSchedule.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||||
|
import { generateRandomId } from '@shared/util/random';
|
||||||
|
|
||||||
|
import handleDuplicate from './handleDuplicate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new schedule with the given name
|
||||||
|
*
|
||||||
|
* @param scheduleName - The name of the schedule to create
|
||||||
|
* @returns Undefined if successful, otherwise an error message
|
||||||
|
*/
|
||||||
|
export default async function duplicateSchedule(scheduleId: string): Promise<string | undefined> {
|
||||||
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
|
const scheduleIndex = schedules.findIndex(schedule => schedule.id === scheduleId);
|
||||||
|
|
||||||
|
if (scheduleIndex === -1) {
|
||||||
|
throw new Error(`Schedule ${scheduleId} does not exist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const schedule = schedules[scheduleIndex]!;
|
||||||
|
|
||||||
|
const copyOfName = `Copy of ${schedule.name}`;
|
||||||
|
const updatedName = await handleDuplicate(copyOfName);
|
||||||
|
|
||||||
|
schedules.splice(scheduleIndex + 1, 0, {
|
||||||
|
id: generateRandomId(),
|
||||||
|
name: updatedName,
|
||||||
|
courses: JSON.parse(JSON.stringify(schedule.courses)),
|
||||||
|
hours: schedule.hours,
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
} satisfies typeof schedule);
|
||||||
|
|
||||||
|
await UserScheduleStore.set('schedules', schedules);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
24
src/pages/background/lib/exportSchedule.ts
Normal file
24
src/pages/background/lib/exportSchedule.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the provided schedule to a portable JSON
|
||||||
|
*
|
||||||
|
* @param scheduleId - The Id matching the to-be-exported schedule
|
||||||
|
* @returns JSON format of the provided schedule ID, empty if one was not found
|
||||||
|
*/
|
||||||
|
export default async function exportSchedule(scheduleId: string): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
const storageData = await UserScheduleStore.get('schedules');
|
||||||
|
const selectedSchedule = storageData.find(s => s.id === scheduleId);
|
||||||
|
|
||||||
|
if (!selectedSchedule) {
|
||||||
|
console.warn(`Schedule ${scheduleId} does not exist`);
|
||||||
|
return JSON.stringify({});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(selectedSchedule);
|
||||||
|
return JSON.stringify(selectedSchedule, null, 2);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting storage data:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/pages/background/lib/handleDuplicate.ts
Normal file
38
src/pages/background/lib/handleDuplicate.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicates a new schedule with the given name.
|
||||||
|
* Assumes that each schedule has a unique name.
|
||||||
|
*
|
||||||
|
* @param scheduleName - The name of the schedule to handle duplication for
|
||||||
|
* @param schedules - The list of UserSchedules to find existing names
|
||||||
|
* @returns The new name for the schedule, of the form `{baseName}({index})`
|
||||||
|
*/
|
||||||
|
export default async function handleDuplicate(scheduleName: string): Promise<string> {
|
||||||
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
|
|
||||||
|
// No point in checking for duplicates if the name is unique
|
||||||
|
if (schedules.find(schedule => schedule.name === scheduleName) === undefined) {
|
||||||
|
return scheduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regex for matching `{baseName}({index})`, where match[1] = baseName, match[2] = (index)
|
||||||
|
const regex = /^(.+?)(\(\d+\))?$/;
|
||||||
|
|
||||||
|
// Extract base name and existing index
|
||||||
|
const match = scheduleName.match(regex);
|
||||||
|
const baseName = match && match[1] ? match[1].trim() : scheduleName;
|
||||||
|
|
||||||
|
// Extract number from parentheses and increment
|
||||||
|
let index = match && match[2] ? parseInt(match[2].slice(1, -1), 10) + 1 : 1;
|
||||||
|
|
||||||
|
let newName: string;
|
||||||
|
|
||||||
|
// Increment until an unused index is found
|
||||||
|
do {
|
||||||
|
newName = `${baseName} (${index++})`;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
||||||
|
} while (schedules.find(schedule => schedule.name === newName));
|
||||||
|
|
||||||
|
return newName;
|
||||||
|
}
|
||||||
34
src/pages/background/lib/importSchedule.ts
Normal file
34
src/pages/background/lib/importSchedule.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Course } from '@shared/types/Course';
|
||||||
|
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||||
|
import type { Serialized } from 'chrome-extension-toolkit';
|
||||||
|
|
||||||
|
import addCourse from './addCourse';
|
||||||
|
import createSchedule from './createSchedule';
|
||||||
|
import switchSchedule from './switchSchedule';
|
||||||
|
|
||||||
|
function isValidSchedule(data: unknown): data is Serialized<UserSchedule> {
|
||||||
|
if (typeof data !== 'object' || data === null) return false;
|
||||||
|
const schedule = data as Record<string, unknown>;
|
||||||
|
return typeof schedule.id === 'string' && typeof schedule.name === 'string' && Array.isArray(schedule.courses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a user schedule from portable file, creating a new schedule for it
|
||||||
|
|
||||||
|
* @param scheduleData - Data to be parsed back into a course schedule
|
||||||
|
*/
|
||||||
|
export default async function importSchedule(scheduleData: unknown): Promise<void> {
|
||||||
|
if (isValidSchedule(scheduleData)) {
|
||||||
|
const newScheduleId = await createSchedule(scheduleData.name);
|
||||||
|
await switchSchedule(newScheduleId);
|
||||||
|
|
||||||
|
for (const c of scheduleData.courses) {
|
||||||
|
const course = new Course(c);
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await addCourse(newScheduleId, course, true);
|
||||||
|
}
|
||||||
|
console.log('Course schedule successfully parsed!');
|
||||||
|
} else {
|
||||||
|
console.error('No schedule data provided for import');
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/pages/background/lib/migrateUTRPv1Courses.ts
Normal file
78
src/pages/background/lib/migrateUTRPv1Courses.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { validateLoginStatus } from '@shared/util/checkLoginStatus';
|
||||||
|
import { getActiveSchedule } from '@views/hooks/useSchedules';
|
||||||
|
import { courseMigration } from '@views/lib/courseMigration';
|
||||||
|
|
||||||
|
import addCourse from './addCourse';
|
||||||
|
import createSchedule from './createSchedule';
|
||||||
|
import switchSchedule from './switchSchedule';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the saved courses from the extension's chrome sync storage (old store) and returns an array of course links.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves to an array of course links.
|
||||||
|
*/
|
||||||
|
export async function getUTRPv1Courses(): Promise<string[]> {
|
||||||
|
const { savedCourses } = await chrome.storage.sync.get('savedCourses');
|
||||||
|
|
||||||
|
// Check if the savedCourses array is empty
|
||||||
|
if (!savedCourses || savedCourses.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the link property from each course object and return it as an array
|
||||||
|
return savedCourses.map((course: { link: string }) => course.link);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the migration of UTRP v1 courses.
|
||||||
|
*
|
||||||
|
* This function checks if the user is logged into the UT course schedule page.
|
||||||
|
* If the user is not logged in, it logs a message and exits. If the user is
|
||||||
|
* logged in, it retrieves the UTRP v1 courses, creates a new schedule for the
|
||||||
|
* migration, switches to the new schedule, and migrates the courses to the
|
||||||
|
* active schedule.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves when the migration is complete.
|
||||||
|
*/
|
||||||
|
async function migrateUTRPv1Courses() {
|
||||||
|
const loggedInToUT = await validateLoginStatus(
|
||||||
|
'https://utdirect.utexas.edu/apps/registrar/course_schedule/utrp_login/'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!loggedInToUT) {
|
||||||
|
console.warn('Not logged in to UT Registrar.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldCourses = await getUTRPv1Courses();
|
||||||
|
console.log(oldCourses);
|
||||||
|
|
||||||
|
const migratedCourses = await courseMigration(oldCourses);
|
||||||
|
|
||||||
|
if (migratedCourses.length > 0) {
|
||||||
|
console.log(oldCourses, migratedCourses);
|
||||||
|
const migrateSchedule = await createSchedule('Migrated Schedule');
|
||||||
|
await switchSchedule(migrateSchedule);
|
||||||
|
|
||||||
|
const activeSchedule = getActiveSchedule();
|
||||||
|
|
||||||
|
for (const course of migratedCourses) {
|
||||||
|
// Add the course if it doesn't already exist
|
||||||
|
if (activeSchedule.courses.every(c => c.uniqueId !== course.uniqueId)) {
|
||||||
|
// ignore eslint, as we *do* want to spend time on each iteration
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await addCourse(activeSchedule.id, course);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the old courses from storage :>
|
||||||
|
await chrome.storage.sync.remove('savedCourses');
|
||||||
|
console.log('Successfully migrated UTRP v1 courses');
|
||||||
|
} else {
|
||||||
|
console.warn('No courses successfully found to migrate');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default migrateUTRPv1Courses;
|
||||||
@@ -1,21 +1,41 @@
|
|||||||
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
|
||||||
|
|
||||||
|
import handleDuplicate from './handleDuplicate';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames a schedule with the specified name to a new name.
|
* Renames a schedule with the specified name to a new name.
|
||||||
* @param scheduleId - The id of the schedule to be renamed.
|
* @param scheduleId - The id of the schedule to be renamed.
|
||||||
* @param newName - The new name for the schedule.
|
* @param newName - The new name for the schedule.
|
||||||
* @returns A promise that resolves to a string if there is an error, or undefined if the schedule is renamed successfully.
|
* @returns A promise that resolves to the new name if successful, otherwise undefined.
|
||||||
*/
|
*/
|
||||||
export default async function renameSchedule(scheduleId: string, newName: string): Promise<string | undefined> {
|
export default async function renameSchedule(scheduleId: string, newName: string): Promise<string | undefined> {
|
||||||
const schedules = await UserScheduleStore.get('schedules');
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
|
|
||||||
const scheduleIndex = schedules.findIndex(schedule => schedule.id === scheduleId);
|
const scheduleIndex = schedules.findIndex(schedule => schedule.id === scheduleId);
|
||||||
if (scheduleIndex === -1) {
|
if (scheduleIndex === -1) {
|
||||||
return `Schedule ${scheduleId} does not exist`;
|
return undefined;
|
||||||
|
}
|
||||||
|
const schedule = schedules[scheduleIndex];
|
||||||
|
if (schedule === undefined) {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
schedules[scheduleIndex]!.name = newName;
|
// if old name is of the form `{baseName}{index}` and newName === baseName, do nothing.
|
||||||
// schedules[scheduleIndex].updatedAt = Date.now();
|
const oldName = schedule.name;
|
||||||
|
const regex = /^(.+?)(\(\d+\))?$/;
|
||||||
|
const match = oldName?.match(regex);
|
||||||
|
const baseName = match?.[1] ?? '';
|
||||||
|
const baseNameOfNewName = newName.match(regex)?.[1];
|
||||||
|
|
||||||
|
if (baseName === baseNameOfNewName) {
|
||||||
|
return oldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedName = await handleDuplicate(newName);
|
||||||
|
|
||||||
|
schedule.name = updatedName;
|
||||||
|
schedule.updatedAt = Date.now();
|
||||||
|
|
||||||
await UserScheduleStore.set('schedules', schedules);
|
await UserScheduleStore.set('schedules', schedules);
|
||||||
return undefined;
|
return newName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* Represents a tab with an additional `id` property
|
||||||
|
*/
|
||||||
export type TabWithId = Omit<chrome.tabs.Tab, 'id'> & { id: number };
|
export type TabWithId = Omit<chrome.tabs.Tab, 'id'> & { id: number };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import type TabInfoMessages from '@shared/messages/TabInfoMessages';
|
import type TabInfoMessages from '@shared/messages/TabInfoMessages';
|
||||||
import Calendar from '@views/components/calendar/Calendar';
|
import Calendar from '@views/components/calendar/Calendar';
|
||||||
|
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
|
||||||
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||||
|
import { MigrationDialog } from '@views/components/common/MigrationDialog';
|
||||||
|
import { WhatsNewDialog } from '@views/components/common/WhatsNewPopup';
|
||||||
|
import SentryProvider from '@views/contexts/SentryContext';
|
||||||
import { MessageListener } from 'chrome-extension-toolkit';
|
import { MessageListener } from 'chrome-extension-toolkit';
|
||||||
|
import useKC_DABR_WASM from 'kc-dabr-wasm';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,6 +14,7 @@ import React, { useEffect } from 'react';
|
|||||||
* @returns entire page
|
* @returns entire page
|
||||||
*/
|
*/
|
||||||
export default function CalendarMain() {
|
export default function CalendarMain() {
|
||||||
|
useKC_DABR_WASM();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tabInfoListener = new MessageListener<TabInfoMessages>({
|
const tabInfoListener = new MessageListener<TabInfoMessages>({
|
||||||
getTabInfo: ({ sendResponse }) => {
|
getTabInfo: ({ sendResponse }) => {
|
||||||
@@ -25,8 +31,14 @@ export default function CalendarMain() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExtensionRoot className='h-full w-full'>
|
<SentryProvider fullInit>
|
||||||
<Calendar />
|
<ExtensionRoot className='h-full w-full'>
|
||||||
</ExtensionRoot>
|
<DialogProvider>
|
||||||
|
<MigrationDialog />
|
||||||
|
<WhatsNewDialog />
|
||||||
|
<Calendar />
|
||||||
|
</DialogProvider>
|
||||||
|
</ExtensionRoot>
|
||||||
|
</SentryProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,32 @@
|
|||||||
import CourseCatalogMain from '@views/components/CourseCatalogMain';
|
import CourseCatalogMain from '@views/components/CourseCatalogMain';
|
||||||
|
import InjectedButton from '@views/components/injected/AddAllButton';
|
||||||
|
import DaysCheckbox from '@views/components/injected/DaysCheckbox';
|
||||||
import getSiteSupport, { SiteSupport } from '@views/lib/getSiteSupport';
|
import getSiteSupport, { SiteSupport } from '@views/lib/getSiteSupport';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
const support = getSiteSupport(window.location.href);
|
const support = getSiteSupport(window.location.href);
|
||||||
|
|
||||||
if (support === SiteSupport.COURSE_CATALOG_DETAILS || support === SiteSupport.COURSE_CATALOG_LIST) {
|
const renderComponent = (Component: React.ComponentType) => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
container.id = 'extension-root';
|
container.id = 'extension-root';
|
||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
|
|
||||||
createRoot(container).render(
|
createRoot(container).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<CourseCatalogMain support={support} />
|
<Component />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (support === SiteSupport.COURSE_CATALOG_DETAILS || support === SiteSupport.COURSE_CATALOG_LIST) {
|
||||||
|
renderComponent(() => <CourseCatalogMain support={support} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (support === SiteSupport.MY_UT) {
|
||||||
|
renderComponent(InjectedButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (support === SiteSupport.COURSE_CATALOG_SEARCH) {
|
||||||
|
renderComponent(DaysCheckbox);
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/pages/map/Map.tsx
Normal file
19
src/pages/map/Map.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
|
||||||
|
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||||
|
import Map from '@views/components/map/Map';
|
||||||
|
import useKC_DABR_WASM from 'kc-dabr-wasm';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the map page for the UTRP (UT Registration Plus) extension.
|
||||||
|
*/
|
||||||
|
export default function MapPage() {
|
||||||
|
useKC_DABR_WASM();
|
||||||
|
return (
|
||||||
|
<ExtensionRoot>
|
||||||
|
<DialogProvider>
|
||||||
|
<Map />
|
||||||
|
</DialogProvider>
|
||||||
|
</ExtensionRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
16
src/pages/map/index.html
Normal file
16
src/pages/map/index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<title>UTRP Map</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="min-height: 100vh; height: 0; margin: 0">
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
<script src="./index.tsx" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
src/pages/map/index.tsx
Normal file
6
src/pages/map/index.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
|
import Map from './Map';
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(<Map />);
|
||||||
25
src/pages/options/Settings.tsx
Normal file
25
src/pages/options/Settings.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
|
||||||
|
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||||
|
import Settings from '@views/components/settings/Settings';
|
||||||
|
import SentryProvider from '@views/contexts/SentryContext';
|
||||||
|
import useKC_DABR_WASM from 'kc-dabr-wasm';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the settings page for the UTRP (UT Registration Plus) extension.
|
||||||
|
* Allows customization options and displays credits for the development team.
|
||||||
|
*
|
||||||
|
* @returns The JSX element representing the settings page.
|
||||||
|
*/
|
||||||
|
export default function SettingsPage() {
|
||||||
|
useKC_DABR_WASM();
|
||||||
|
return (
|
||||||
|
<SentryProvider fullInit>
|
||||||
|
<ExtensionRoot>
|
||||||
|
<DialogProvider>
|
||||||
|
<Settings />
|
||||||
|
</DialogProvider>
|
||||||
|
</ExtensionRoot>
|
||||||
|
</SentryProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<title>UTRP Options</title>
|
<title>UTRP Options</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body style="min-height: 100vh; height: 0; margin: 0">
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import App from './App';
|
import SettingsPage from './Settings';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(<App />);
|
createRoot(document.getElementById('root')!).render(<SettingsPage />);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 360px;
|
width: 430px;
|
||||||
height: 540px;
|
height: 540px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,20 @@
|
|||||||
import 'uno.css';
|
import 'uno.css';
|
||||||
|
|
||||||
|
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
|
||||||
|
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||||
|
import { MigrationDialog } from '@views/components/common/MigrationDialog';
|
||||||
import PopupMain from '@views/components/PopupMain';
|
import PopupMain from '@views/components/PopupMain';
|
||||||
|
import SentryProvider from '@views/contexts/SentryContext';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(<PopupMain />);
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<SentryProvider fullInit>
|
||||||
|
<ExtensionRoot>
|
||||||
|
<DialogProvider>
|
||||||
|
<MigrationDialog />
|
||||||
|
<PopupMain />
|
||||||
|
</DialogProvider>
|
||||||
|
</ExtensionRoot>
|
||||||
|
</SentryProvider>
|
||||||
|
);
|
||||||
|
|||||||
16
src/pages/report/index.html
Normal file
16
src/pages/report/index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<title>UTRP Report Issue</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
<script src="./index.tsx" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
10
src/pages/report/index.tsx
Normal file
10
src/pages/report/index.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import ReportIssueMain from '@views/components/ReportIssueMain';
|
||||||
|
import SentryProvider from '@views/contexts/SentryContext';
|
||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<SentryProvider fullInit>
|
||||||
|
<ReportIssueMain />
|
||||||
|
</SentryProvider>
|
||||||
|
);
|
||||||
@@ -2,7 +2,7 @@ interface CESMessage {
|
|||||||
/**
|
/**
|
||||||
* Opens the CES page for the specified instructor
|
* Opens the CES page for the specified instructor
|
||||||
*
|
*
|
||||||
* @param data first and last name of the instructor
|
* @param data - First and last name of the instructor
|
||||||
*/
|
*/
|
||||||
openCESPage: (data: { instructorFirstName: string; instructorLastName: string }) => chrome.tabs.Tab;
|
openCESPage: (data: { instructorFirstName: string; instructorLastName: string }) => chrome.tabs.Tab;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,22 @@
|
|||||||
export default interface TabManagementMessages {
|
export default interface TabManagementMessages {
|
||||||
/**
|
/**
|
||||||
* Opens a new tab with the given URL
|
* Opens a new tab with the given URL
|
||||||
* @param data The URL to open
|
*
|
||||||
|
* @param data - The URL to open
|
||||||
*/
|
*/
|
||||||
openNewTab: (data: { url: string }) => chrome.tabs.Tab;
|
openNewTab: (data: { url: string }) => chrome.tabs.Tab;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the ID of the current tab (the tab that sent the message)
|
* Gets the ID of the current tab (the tab that sent the message)
|
||||||
|
*
|
||||||
* @returns The ID of the current tab
|
* @returns The ID of the current tab
|
||||||
*/
|
*/
|
||||||
getTabId: () => number;
|
getTabId: () => number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the tab with the given ID
|
* Removes the tab with the given ID
|
||||||
* @param data The ID of the tab to remove
|
*
|
||||||
|
* @param data - The ID of the tab to remove
|
||||||
* @returns The ID of the tab that was removed
|
* @returns The ID of the tab that was removed
|
||||||
*/
|
*/
|
||||||
removeTab: (data: { tabId: number }) => void;
|
removeTab: (data: { tabId: number }) => void;
|
||||||
|
|||||||
@@ -6,42 +6,77 @@ import type { Course } from '@shared/types/Course';
|
|||||||
export interface UserScheduleMessages {
|
export interface UserScheduleMessages {
|
||||||
/**
|
/**
|
||||||
* Add a course to a schedule
|
* Add a course to a schedule
|
||||||
* @param data the schedule id and course to add
|
*
|
||||||
|
* @param data - The schedule id and course to add
|
||||||
*/
|
*/
|
||||||
addCourse: (data: { scheduleId: string; course: Course }) => void;
|
addCourse: (data: { scheduleId: string; course: Course; hasColor?: boolean }) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a course by URL
|
||||||
|
*
|
||||||
|
* @param data - The URL of the course to add
|
||||||
|
* @returns Response of the requested course URL
|
||||||
|
*/
|
||||||
|
addCourseByURL: (data: { url: string; method: string; body?: string; response: 'json' | 'text' }) => string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a course from a schedule
|
* Remove a course from a schedule
|
||||||
* @param data the schedule id and course to remove
|
*
|
||||||
|
* @param data - The schedule id and course to remove
|
||||||
*/
|
*/
|
||||||
removeCourse: (data: { scheduleId: string; course: Course }) => void;
|
removeCourse: (data: { scheduleId: string; course: Course }) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all courses from a schedule
|
* Clears all courses from a schedule
|
||||||
* @param data the id of the schedule to clear
|
*
|
||||||
|
* @param data - The id of the schedule to clear
|
||||||
*/
|
*/
|
||||||
clearCourses: (data: { scheduleId: string }) => void;
|
clearCourses: (data: { scheduleId: string }) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches the active schedule to the one specified
|
* Switches the active schedule to the one specified
|
||||||
* @param data the id of the schedule to switch to
|
*
|
||||||
|
* @param data - The id of the schedule to switch to
|
||||||
*/
|
*/
|
||||||
switchSchedule: (data: { scheduleId: string }) => void;
|
switchSchedule: (data: { scheduleId: string }) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new schedule with the specified name
|
* Creates a new schedule with the specified name
|
||||||
* @param data the name of the schedule to create
|
*
|
||||||
* @returns undefined if successful, otherwise an error message
|
* @param data - The name of the schedule to create
|
||||||
|
* @returns Undefined if successful, otherwise an error message
|
||||||
*/
|
*/
|
||||||
createSchedule: (data: { scheduleName: string }) => string | undefined;
|
createSchedule: (data: { scheduleName: string }) => string | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a schedule with the specified name
|
* Deletes a schedule with the specified name
|
||||||
* @param data the id of the schedule to delete
|
*
|
||||||
* @returns undefined if successful, otherwise an error message
|
* @param data - The id of the schedule to delete
|
||||||
|
* @returns Undefined if successful, otherwise an error message
|
||||||
*/
|
*/
|
||||||
deleteSchedule: (data: { scheduleId: string }) => string | undefined;
|
deleteSchedule: (data: { scheduleId: string }) => string | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames a schedule with the specified name
|
* Renames a schedule with the specified name
|
||||||
* @param data the id of the schedule to rename and the new name
|
*
|
||||||
* @returns undefined if successful, otherwise an error message
|
* @param data - The id of the schedule to rename and the new name
|
||||||
|
* @returns Undefined if successful, otherwise an error message
|
||||||
*/
|
*/
|
||||||
renameSchedule: (data: { scheduleId: string; newName: string }) => string | undefined;
|
renameSchedule: (data: { scheduleId: string; newName: string }) => string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the login status by making a request to the provided URL.
|
||||||
|
*
|
||||||
|
* @param data - The URL to check the login status against.
|
||||||
|
* @returns true if user was already logged into the provided URL, false otherwise
|
||||||
|
*/
|
||||||
|
validateLoginStatus: (data: { url: string }) => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the current schedule to a JSON file for backing up and sharing
|
||||||
|
*
|
||||||
|
* @param data - Id of schedule that will be exported
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
exportSchedule: (data: { scheduleId: string }) => string | undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/shared/storage/CacheStore.ts
Normal file
15
src/shared/storage/CacheStore.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { CachedData } from '@shared/types/CachedData';
|
||||||
|
import { createLocalStore, debugStore } from 'chrome-extension-toolkit';
|
||||||
|
|
||||||
|
interface ICacheStore {
|
||||||
|
github: Record<string, CachedData<unknown>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A store that is used for storing cached data such as GitHub contributors
|
||||||
|
*/
|
||||||
|
export const CacheStore = createLocalStore<ICacheStore>({
|
||||||
|
github: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
debugStore({ cacheStore: CacheStore });
|
||||||
@@ -4,6 +4,8 @@ import { createLocalStore, debugStore } from 'chrome-extension-toolkit';
|
|||||||
* A store that is used to store data that is only relevant during development
|
* A store that is used to store data that is only relevant during development
|
||||||
*/
|
*/
|
||||||
interface IDevStore {
|
interface IDevStore {
|
||||||
|
/** whether the user is a developer */
|
||||||
|
isDeveloper: boolean;
|
||||||
/** the tabId for the debug tab */
|
/** the tabId for the debug tab */
|
||||||
debugTabId?: number;
|
debugTabId?: number;
|
||||||
/** whether the debug tab is visible */
|
/** whether the debug tab is visible */
|
||||||
@@ -17,6 +19,7 @@ interface IDevStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DevStore = createLocalStore<IDevStore>({
|
export const DevStore = createLocalStore<IDevStore>({
|
||||||
|
isDeveloper: false,
|
||||||
debugTabId: undefined,
|
debugTabId: undefined,
|
||||||
isTabReloading: true,
|
isTabReloading: true,
|
||||||
wasDebugTabVisible: false,
|
wasDebugTabVisible: false,
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ interface IExtensionStore {
|
|||||||
version: string;
|
version: string;
|
||||||
/** When was the last update */
|
/** When was the last update */
|
||||||
lastUpdate: number;
|
lastUpdate: number;
|
||||||
|
/** The last version of the "What's New" popup that was shown to the user */
|
||||||
|
lastWhatsNewPopupVersion: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExtensionStore = createLocalStore<IExtensionStore>({
|
export const ExtensionStore = createLocalStore<IExtensionStore>({
|
||||||
version: chrome.runtime.getManifest().version,
|
version: chrome.runtime.getManifest().version,
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
|
lastWhatsNewPopupVersion: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
debugStore({ ExtensionStore });
|
debugStore({ ExtensionStore });
|
||||||
|
|||||||
@@ -3,20 +3,55 @@ import { createSyncStore, debugStore } from 'chrome-extension-toolkit';
|
|||||||
/**
|
/**
|
||||||
* A store that is used for storing user options
|
* A store that is used for storing user options
|
||||||
*/
|
*/
|
||||||
interface IOptionsStore {
|
export interface IOptionsStore {
|
||||||
/** whether we should automatically highlight conflicts on the course schedule page */
|
/** whether we should enable course status chips (indicator for waitlisted, cancelled, and closed courses) */
|
||||||
shouldHighlightConflicts: boolean;
|
enableCourseStatusChips: boolean;
|
||||||
/** whether we should automatically scroll to load more courses on the course schedule page (without having to click next) */
|
|
||||||
shouldScrollToLoad: boolean;
|
|
||||||
|
|
||||||
// url: URL;
|
/** whether we should automatically highlight conflicts on the course schedule page (adds a red strikethrough to courses that have conflicting times) */
|
||||||
|
enableHighlightConflicts: boolean;
|
||||||
|
|
||||||
|
/** whether we should automatically scroll to load more courses on the course schedule page (without having to click next) */
|
||||||
|
enableScrollToLoad: boolean;
|
||||||
|
|
||||||
|
/** whether we should automatically refresh the data for the waitlist, course status, and other info with the latest data from UT's site */
|
||||||
|
enableDataRefreshing: boolean;
|
||||||
|
|
||||||
|
/** whether we should open the calendar in a new tab; default is to focus an existing calendar tab */
|
||||||
|
alwaysOpenCalendarInNewTab: boolean;
|
||||||
|
|
||||||
|
/** whether the calendar sidebar should be shown when the calendar is opened */
|
||||||
|
showCalendarSidebar: boolean;
|
||||||
|
|
||||||
|
/** whether the promo should be shown */
|
||||||
|
showUTDiningPromo: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OptionsStore = createSyncStore<IOptionsStore>({
|
export const OptionsStore = createSyncStore<IOptionsStore>({
|
||||||
shouldHighlightConflicts: true,
|
enableCourseStatusChips: false,
|
||||||
shouldScrollToLoad: true,
|
enableHighlightConflicts: true,
|
||||||
|
enableScrollToLoad: true,
|
||||||
|
enableDataRefreshing: false,
|
||||||
|
alwaysOpenCalendarInNewTab: false,
|
||||||
|
showCalendarSidebar: true,
|
||||||
|
showUTDiningPromo: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the settings by retrieving the values from the OptionsStore.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves to an object satisfying the IOptionsStore interface.
|
||||||
|
*/
|
||||||
|
export const initSettings = async () =>
|
||||||
|
({
|
||||||
|
enableCourseStatusChips: await OptionsStore.get('enableCourseStatusChips'),
|
||||||
|
enableHighlightConflicts: await OptionsStore.get('enableHighlightConflicts'),
|
||||||
|
enableScrollToLoad: await OptionsStore.get('enableScrollToLoad'),
|
||||||
|
enableDataRefreshing: await OptionsStore.get('enableDataRefreshing'),
|
||||||
|
alwaysOpenCalendarInNewTab: await OptionsStore.get('alwaysOpenCalendarInNewTab'),
|
||||||
|
showCalendarSidebar: await OptionsStore.get('showCalendarSidebar'),
|
||||||
|
showUTDiningPromo: await OptionsStore.get('showUTDiningPromo'),
|
||||||
|
}) satisfies IOptionsStore;
|
||||||
|
|
||||||
// Clothing retailer right
|
// Clothing retailer right
|
||||||
|
|
||||||
debugStore({ OptionsStore });
|
debugStore({ OptionsStore });
|
||||||
|
|||||||
16
src/shared/types/CRXPages.ts
Normal file
16
src/shared/types/CRXPages.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* An object containing the paths to various pages used in the CRX application.
|
||||||
|
*/
|
||||||
|
export const CRX_PAGES = {
|
||||||
|
DEBUG: '/debug.html',
|
||||||
|
CALENDAR: '/calendar.html',
|
||||||
|
OPTIONS: '/options.html',
|
||||||
|
MAP: '/map.html',
|
||||||
|
REPORT: '/report.html',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a type that corresponds to the keys of the `CRX_PAGES` object.
|
||||||
|
* This type is used to ensure that only valid page keys are used within the application.
|
||||||
|
*/
|
||||||
|
export type CRX_Page = keyof typeof CRX_PAGES;
|
||||||
7
src/shared/types/CachedData.ts
Normal file
7
src/shared/types/CachedData.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Represents cached data with its fetch timestamp
|
||||||
|
*/
|
||||||
|
export type CachedData<T> = {
|
||||||
|
data: T;
|
||||||
|
dataFetched: number;
|
||||||
|
};
|
||||||
@@ -25,3 +25,8 @@ export type sRGB = [r: number, g: number, b: number];
|
|||||||
* Represents a Lab color value.
|
* Represents a Lab color value.
|
||||||
*/
|
*/
|
||||||
export type Lab = [l: number, a: number, b: number];
|
export type Lab = [l: number, a: number, b: number];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a HSL color value.
|
||||||
|
*/
|
||||||
|
export type HSL = [h: number, s: number, l: number];
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ export class Course {
|
|||||||
scrapedAt!: number;
|
scrapedAt!: number;
|
||||||
/** The colors of the course when displayed */
|
/** The colors of the course when displayed */
|
||||||
colors: CourseColors;
|
colors: CourseColors;
|
||||||
|
/** The core curriculum requirements the course satisfies */
|
||||||
|
core: string[];
|
||||||
|
|
||||||
constructor(course: Serialized<Course>) {
|
constructor(course: Serialized<Course>) {
|
||||||
Object.assign(this, course);
|
Object.assign(this, course);
|
||||||
@@ -88,12 +90,14 @@ export class Course {
|
|||||||
this.scrapedAt = Date.now();
|
this.scrapedAt = Date.now();
|
||||||
}
|
}
|
||||||
this.colors = course.colors ? structuredClone(course.colors) : getCourseColors('emerald', 500);
|
this.colors = course.colors ? structuredClone(course.colors) : getCourseColors('emerald', 500);
|
||||||
|
this.core = course.core ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list of all the conflicts between this course and another course (i.e. if they have a meeting at the same time)
|
* Gets a list of all the conflicts between this course and another course (i.e. if they have a meeting at the same time)
|
||||||
* @param other another course to compare this course to
|
*
|
||||||
* @returns a list of all the conflicts between this course and the other course as a tuple of the two conflicting meetings
|
* @param other - Another course to compare this course to
|
||||||
|
* @returns A list of all the conflicts between this course and the other course as a tuple of the two conflicting meetings
|
||||||
*/
|
*/
|
||||||
getConflicts(other: Course): [CourseMeeting, CourseMeeting][] {
|
getConflicts(other: Course): [CourseMeeting, CourseMeeting][] {
|
||||||
const conflicts: [CourseMeeting, CourseMeeting][] = [];
|
const conflicts: [CourseMeeting, CourseMeeting][] = [];
|
||||||
|
|||||||
@@ -48,13 +48,13 @@ export class CourseMeeting {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not this meeting conflicts with another meeting
|
* Whether or not this meeting conflicts with another meeting
|
||||||
* MWF 10:00 am - 11:00 am conflicts with a F 10:00 am - 10:30 am
|
*
|
||||||
* @param meeting the meeting to check for conflicts with
|
* @remarks MWF 10:00 am - 11:00 am conflicts with a F 10:00 am - 10:30 am
|
||||||
* @returns true if the given meeting conflicts with this meeting, false otherwise
|
* @param meeting - The meeting to check for conflicts with
|
||||||
|
* @returns True if the given meeting conflicts with this meeting, false otherwise
|
||||||
*/
|
*/
|
||||||
isConflicting(meeting: CourseMeeting): boolean {
|
isConflicting({ days: otherDays, startTime: otherStartTime, endTime: otherEndTime }: CourseMeeting): boolean {
|
||||||
const { days, startTime, endTime } = this;
|
const { days, startTime, endTime } = this;
|
||||||
const { days: otherDays, startTime: otherStartTime, endTime: otherEndTime } = meeting;
|
|
||||||
|
|
||||||
const hasDayConflict = days.some(day => otherDays.includes(day));
|
const hasDayConflict = days.some(day => otherDays.includes(day));
|
||||||
const hasTimeConflict = startTime < otherEndTime && endTime > otherStartTime;
|
const hasTimeConflict = startTime < otherEndTime && endTime > otherStartTime;
|
||||||
@@ -64,17 +64,17 @@ export class CourseMeeting {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the string representation of the days of the week that this meeting is taught
|
* Return the string representation of the days of the week that this meeting is taught
|
||||||
* @param options options for the string representation
|
*
|
||||||
* @returns string representation of the days of the week that this meeting is taught
|
* @param options - Options for the string representation
|
||||||
|
* @returns String representation of the days of the week that this meeting is taught
|
||||||
*/
|
*/
|
||||||
getDaysString(options: DaysStringOptions): string {
|
getDaysString({ format, separator }: DaysStringOptions): string {
|
||||||
let { format, separator } = options;
|
|
||||||
let { days } = this;
|
let { days } = this;
|
||||||
|
|
||||||
if (format === 'short') {
|
if (format === 'short') {
|
||||||
days = Object.keys(DAY_MAP).filter(day => days.includes(DAY_MAP[day as keyof typeof DAY_MAP])) as Day[];
|
days = Object.keys(DAY_MAP).filter(day => days.includes(DAY_MAP[day as keyof typeof DAY_MAP])) as Day[];
|
||||||
}
|
}
|
||||||
if (separator === 'none') {
|
if (!separator) {
|
||||||
return days.join('');
|
return days.join('');
|
||||||
}
|
}
|
||||||
const listFormat = new Intl.ListFormat('en-US', {
|
const listFormat = new Intl.ListFormat('en-US', {
|
||||||
@@ -86,10 +86,11 @@ export class CourseMeeting {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the string representation of the time range for the course
|
* Return the string representation of the time range for the course
|
||||||
* @param options options for the string representation
|
*
|
||||||
* @returns string representation of the time range for the course
|
* @param options - Options for the string representation
|
||||||
|
* @returns String representation of the time range for the course
|
||||||
*/
|
*/
|
||||||
getTimeString(options: TimeStringOptions): string {
|
getTimeString({ separator = '-' }: TimeStringOptions): string {
|
||||||
const { startTime, endTime } = this;
|
const { startTime, endTime } = this;
|
||||||
const startHour = Math.floor(startTime / 60);
|
const startHour = Math.floor(startTime / 60);
|
||||||
const startMinute = startTime % 60;
|
const startMinute = startTime % 60;
|
||||||
@@ -108,7 +109,7 @@ export class CourseMeeting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startTimeString += startMinute === 0 ? ':00' : `:${startMinute}`;
|
startTimeString += startMinute === 0 ? ':00' : `:${startMinute}`;
|
||||||
startTimeString += startHour >= 12 ? ' p.m.' : ' a.m.';
|
startTimeString += startHour >= 12 ? 'pm' : 'am';
|
||||||
|
|
||||||
if (endHour === 0) {
|
if (endHour === 0) {
|
||||||
endTimeString = '12';
|
endTimeString = '12';
|
||||||
@@ -117,15 +118,11 @@ export class CourseMeeting {
|
|||||||
} else {
|
} else {
|
||||||
endTimeString = `${endHour}`;
|
endTimeString = `${endHour}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
endTimeString += endMinute === 0 ? ':00' : `:${endMinute}`;
|
endTimeString += endMinute === 0 ? ':00' : `:${endMinute}`;
|
||||||
endTimeString += endHour >= 12 ? ' p.m.' : ' a.m.';
|
endTimeString += endHour >= 12 ? 'pm' : 'am';
|
||||||
|
|
||||||
if (options.capitalize) {
|
return `${startTimeString} ${separator} ${endTimeString}`;
|
||||||
startTimeString = startTimeString.toUpperCase();
|
|
||||||
endTimeString = endTimeString.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${startTimeString} ${options.separator} ${endTimeString}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,8 +132,6 @@ export class CourseMeeting {
|
|||||||
type TimeStringOptions = {
|
type TimeStringOptions = {
|
||||||
/** the separator between the start and end times */
|
/** the separator between the start and end times */
|
||||||
separator: string;
|
separator: string;
|
||||||
/** capitalizes the AM/PM */
|
|
||||||
capitalize?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,5 +151,5 @@ type DaysStringOptions = {
|
|||||||
*
|
*
|
||||||
* 'narrow' = `Monday Wednesday Friday`
|
* 'narrow' = `Monday Wednesday Friday`
|
||||||
*/
|
*/
|
||||||
separator: Intl.ListFormatStyle | 'none';
|
separator?: Intl.ListFormatStyle;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ export class CourseSchedule {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a string representation of the meeting information for a class, parse it into a CourseMeeting object
|
* Given a string representation of the meeting information for a class, parse it into a CourseMeeting object
|
||||||
* @param dayLine a string representation of the days of the week that the course is taught: MWF, TR, etc.
|
*
|
||||||
* @param timeLine a string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc.
|
* @param dayLine - A string representation of the days of the week that the course is taught: MWF, TR, etc.
|
||||||
* @param locLine a string representation of the location that the course is taught in: JGB 2.302, etc.
|
* @param timeLine - A string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc.
|
||||||
|
* @param locLine - A string representation of the location that the course is taught in: JGB 2.302, etc.
|
||||||
* @returns CourseMeeting object representing the meeting information
|
* @returns CourseMeeting object representing the meeting information
|
||||||
*/
|
*/
|
||||||
static parse(dayLine: string, timeLine: string, locLine: string): CourseMeeting {
|
static parse(dayLine: string, timeLine: string, locLine: string): CourseMeeting {
|
||||||
@@ -72,10 +73,12 @@ export class CourseSchedule {
|
|||||||
days,
|
days,
|
||||||
startTime,
|
startTime,
|
||||||
endTime,
|
endTime,
|
||||||
location: {
|
location: location[0]
|
||||||
building: location[0] ?? '',
|
? {
|
||||||
room: location[1] ?? '',
|
building: location[0],
|
||||||
},
|
room: location[1] ?? '',
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
} satisfies Serialized<CourseMeeting>);
|
} satisfies Serialized<CourseMeeting>);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Failed to parse schedule: ${dayLine} ${timeLine} ${locLine}`);
|
throw new Error(`Failed to parse schedule: ${dayLine} ${timeLine} ${locLine}`);
|
||||||
|
|||||||
@@ -16,57 +16,44 @@ export default class Instructor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the URL to the instructor's directory page on the UT Directory website
|
|
||||||
* @returns a URL string to the instructor's directory page
|
|
||||||
*/
|
|
||||||
getDirectoryUrl(): string {
|
|
||||||
const name = this.toString({
|
|
||||||
format: 'full_name',
|
|
||||||
case: 'capitalize',
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = new URL('https://directory.utexas.edu/index.php');
|
|
||||||
url.searchParams.set('q', name);
|
|
||||||
url.searchParams.set('scope', 'faculty/staff');
|
|
||||||
url.searchParams.set('submit', 'Search');
|
|
||||||
|
|
||||||
return url.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a string representation of the instructor
|
* Get a string representation of the instructor
|
||||||
* @param options the options for how to format the instructor string
|
*
|
||||||
* @returns a string representation of the instructor
|
* @param options - The options for how to format the instructor string
|
||||||
|
* @returns A string representation of the instructor
|
||||||
*/
|
*/
|
||||||
toString(options: InstructorFormatOptions): string {
|
toString(options: InstructorFormatOptions): string {
|
||||||
const { firstName, lastName, fullName } = this;
|
const { firstName, lastName, fullName } = this;
|
||||||
const { format, case: caseType } = options;
|
const { format } = options;
|
||||||
|
|
||||||
const process = (str: string) => {
|
switch (format) {
|
||||||
if (caseType === 'lowercase') {
|
case 'first_last':
|
||||||
return str.toLowerCase();
|
if (firstName && lastName) {
|
||||||
}
|
return `${capitalize(firstName)} ${capitalize(lastName)}`;
|
||||||
if (caseType === 'uppercase') {
|
}
|
||||||
return str.toUpperCase();
|
|
||||||
}
|
|
||||||
return capitalize(str);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (format === 'abbr' && firstName && lastName && firstName[0]) {
|
if (lastName) {
|
||||||
return `${process(firstName[0])}. ${process(lastName)}`;
|
return capitalize(lastName);
|
||||||
}
|
}
|
||||||
if (format === 'full_name' && fullName) {
|
|
||||||
return process(fullName);
|
|
||||||
}
|
|
||||||
if (format === 'first_last' && firstName && lastName) {
|
|
||||||
return `${process(firstName)} ${process(lastName)}`;
|
|
||||||
}
|
|
||||||
if (format === 'last' && lastName) {
|
|
||||||
return process(lastName);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Invalid Instructor String format: ${format}`);
|
if (fullName) {
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
case 'last':
|
||||||
|
if (lastName) {
|
||||||
|
return capitalize(lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullName) {
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
default:
|
||||||
|
throw new Error(`Invalid Instructor String format: ${format}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,9 +62,5 @@ export default class Instructor {
|
|||||||
*/
|
*/
|
||||||
type InstructorFormatOptions = {
|
type InstructorFormatOptions = {
|
||||||
/** How do you want the names of the professors formatted */
|
/** How do you want the names of the professors formatted */
|
||||||
format: 'abbr' | 'first_last' | 'last' | 'full_name';
|
format: 'first_last' | 'last';
|
||||||
/**
|
|
||||||
* What the case of the string should be
|
|
||||||
*/
|
|
||||||
case: 'capitalize' | 'lowercase' | 'uppercase';
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ const MIMEType = {
|
|||||||
ANY: '*/*',
|
ANY: '*/*',
|
||||||
} as const satisfies Record<string, string>;
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a key of the MIMEType object
|
||||||
|
*/
|
||||||
export type MIMETypeKey = keyof typeof MIMEType;
|
export type MIMETypeKey = keyof typeof MIMEType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a value of the MIMEType object
|
||||||
|
*/
|
||||||
|
export type MIMETypeValue = (typeof MIMEType)[MIMETypeKey];
|
||||||
|
|
||||||
export default MIMEType;
|
export default MIMEType;
|
||||||
|
|||||||
1486
src/shared/types/MainCampusBuildings.ts
Normal file
1486
src/shared/types/MainCampusBuildings.ts
Normal file
File diff suppressed because it is too large
Load Diff
26
src/shared/types/Spacing.ts
Normal file
26
src/shared/types/Spacing.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* An object representing various spacing values used throughout the application.
|
||||||
|
* Each key corresponds to a specific spacing size based on the 4px grid system.
|
||||||
|
*/
|
||||||
|
export const spacing = {
|
||||||
|
'spacing-1': '0.125rem',
|
||||||
|
'spacing-2': '0.25rem',
|
||||||
|
'spacing-3': '0.5rem',
|
||||||
|
'spacing-4': '0.75rem',
|
||||||
|
'spacing-5': '1rem',
|
||||||
|
'spacing-6': '1.25rem',
|
||||||
|
'spacing-7': '1.5rem',
|
||||||
|
'spacing-8': '2rem',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type SpacingKey = keyof typeof spacing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a spacing value from rem to pixels
|
||||||
|
* @param key - The spacing key to convert
|
||||||
|
* @returns The spacing value in pixels
|
||||||
|
*/
|
||||||
|
export function getSpacingInPx(key: SpacingKey): number {
|
||||||
|
const remValue = parseFloat(spacing[key]);
|
||||||
|
return remValue * 16; // 1rem = 16px
|
||||||
|
}
|
||||||
@@ -14,12 +14,17 @@ export const colors = {
|
|||||||
blue: '#005F86',
|
blue: '#005F86',
|
||||||
gray: '#9CADB7',
|
gray: '#9CADB7',
|
||||||
offwhite: '#D6D2C4',
|
offwhite: '#D6D2C4',
|
||||||
concrete: '#95A5A6',
|
|
||||||
red: '#B91C1C', // Not sure if this should be here, but it's used for remove course, and add course is ut-green
|
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
red: '#BF0000',
|
red: '#D10000',
|
||||||
black: '#1A2024',
|
black: '#1A2024',
|
||||||
|
offwhite: '#D6D2C4',
|
||||||
|
black1: '#333F4850',
|
||||||
|
black2: '#333F4820',
|
||||||
|
staticwhite: '#FFFFFF',
|
||||||
|
staticblack: '#1A2024',
|
||||||
|
majorgridline: '#D1D5DB',
|
||||||
|
minorgridline: '#F3F4F6',
|
||||||
},
|
},
|
||||||
} as const satisfies Record<string, Record<string, string>>;
|
} as const satisfies Record<string, Record<string, string>>;
|
||||||
|
|
||||||
@@ -38,7 +43,7 @@ export const extendedColors = {
|
|||||||
d: '#DC2626',
|
d: '#DC2626',
|
||||||
dminus: '#B91C1C',
|
dminus: '#B91C1C',
|
||||||
f: '#B91C1C',
|
f: '#B91C1C',
|
||||||
other: '#6B7280',
|
other: '#D6D3D1',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -52,11 +57,15 @@ type NestedKeys<T> = {
|
|||||||
export type ThemeColor = NestedKeys<typeof colors>;
|
export type ThemeColor = NestedKeys<typeof colors>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Tailwind colorway: a colorway is a key in the theme.colors object that has an object as its value.
|
* Represents a Tailwind colorway: a colorway is a key in the theme.colors object that has an object as its value
|
||||||
*/
|
*/
|
||||||
export type TWColorway = {
|
export type TWColorway = {
|
||||||
[K in keyof typeof theme.colors]: (typeof theme.colors)[K] extends Record<string, unknown> ? K : never;
|
[K in keyof typeof theme.colors]: (typeof theme.colors)[K] extends Record<string, unknown> ? K : never;
|
||||||
}[keyof typeof theme.colors];
|
}[keyof typeof theme.colors];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the index type for accessing the theme colors based on the specified TWColorway
|
||||||
|
*/
|
||||||
export type TWIndex = keyof (typeof theme.colors)[TWColorway];
|
export type TWIndex = keyof (typeof theme.colors)[TWColorway];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
19
src/shared/util/appUrls.ts
Normal file
19
src/shared/util/appUrls.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* This file contains URLs for external applications and resources.
|
||||||
|
* Centralizing these URLs makes it easier to track, update, and manage them in a single place.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL to the UT Dining app on the App Store
|
||||||
|
*/
|
||||||
|
export const UT_DINING_APP_STORE_URL = new URL('https://apps.apple.com/us/app/ut-dining/id6743042002').toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL to the UT Dining app on the Google Play Store (currently not available)
|
||||||
|
*/
|
||||||
|
export const UT_DINING_GOOGLE_PLAY_URL = ''; // Placeholder for Google Play URL, Android app not available yet
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL to the promo image
|
||||||
|
*/
|
||||||
|
export const UT_DINING_PROMO_IMAGE_URL = new URL('https://cdn.longhorns.dev/ut-dining-advert1.png').toString();
|
||||||
27
src/shared/util/checkLoginStatus.ts
Normal file
27
src/shared/util/checkLoginStatus.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Checks the login status by making a request to the provided URL.
|
||||||
|
* If the response indicates that the user is not logged in (redirected to a login page or returns a 401/403 status),
|
||||||
|
* it opens a new tab with the login URL and returns `false`.
|
||||||
|
* If the user is logged in, it returns `true`.
|
||||||
|
*
|
||||||
|
* @param url - The URL to check the login status against.
|
||||||
|
* @returns A promise that resolves to `true` if the user is logged in, otherwise `false`.
|
||||||
|
*/
|
||||||
|
export async function validateLoginStatus(url: string) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, { credentials: 'include' });
|
||||||
|
|
||||||
|
// Check if the response is redirecting to a login page or returning a 401/403
|
||||||
|
if (response.redirected || response.status === 401 || response.status === 403) {
|
||||||
|
// User is not logged in
|
||||||
|
chrome.tabs.create({ url });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User is logged in
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking login status:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { Serialized } from 'chrome-extension-toolkit';
|
import type { Serialized } from 'chrome-extension-toolkit';
|
||||||
import { theme } from 'unocss/preset-mini';
|
import { theme } from 'unocss/preset-mini';
|
||||||
|
|
||||||
import type { HexColor, Lab, RGB, sRGB } from '../types/Color';
|
import type { HexColor, HSL, Lab, RGB, sRGB } from '../types/Color';
|
||||||
import { isHexColor } from '../types/Color';
|
import { isHexColor } from '../types/Color';
|
||||||
import type { Course } from '../types/Course';
|
import type { Course } from '../types/Course';
|
||||||
import type { CourseColors, TWColorway, TWIndex } from '../types/ThemeColors';
|
import type { CourseColors, TWColorway, TWIndex } from '../types/ThemeColors';
|
||||||
import { colorwayIndexes } from '../types/ThemeColors';
|
import { colors, colorwayIndexes } from '../types/ThemeColors';
|
||||||
import type { UserSchedule } from '../types/UserSchedule';
|
import type { UserSchedule } from '../types/UserSchedule';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +26,19 @@ export function hexToRGB(hex: HexColor): RGB | undefined {
|
|||||||
return [parseInt(result[1]!, 16), parseInt(result[2]!, 16), parseInt(result[3]!, 16)];
|
return [parseInt(result[1]!, 16), parseInt(result[2]!, 16), parseInt(result[3]!, 16)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given string is a valid hex color.
|
||||||
|
*
|
||||||
|
* A valid hex color is a string that starts with a '#' followed by either
|
||||||
|
* 3 or 6 hexadecimal characters (0-9, A-F, a-f).
|
||||||
|
*
|
||||||
|
* @param hex - The hex color string to validate.
|
||||||
|
* @returns True if the string is a valid hex color, false otherwise.
|
||||||
|
*/
|
||||||
|
export function isValidHexColor(hex: string): boolean {
|
||||||
|
return /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(hex);
|
||||||
|
}
|
||||||
|
|
||||||
export const useableColorways = Object.keys(theme.colors)
|
export const useableColorways = Object.keys(theme.colors)
|
||||||
// check that the color is a colorway (is an object)
|
// check that the color is a colorway (is an object)
|
||||||
.filter(color => typeof theme.colors[color as keyof typeof theme.colors] === 'object')
|
.filter(color => typeof theme.colors[color as keyof typeof theme.colors] === 'object')
|
||||||
@@ -33,7 +46,9 @@ export const useableColorways = Object.keys(theme.colors)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a Tailwind classname for the font color based on the background color
|
* Generate a Tailwind classname for the font color based on the background color
|
||||||
* @param bgColor the hex color of the background
|
*
|
||||||
|
* @param bgColor - The hex color of the background
|
||||||
|
* @returns The Tailwind classname for the font color
|
||||||
*/
|
*/
|
||||||
export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' | 'text-theme-black' {
|
export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' | 'text-theme-black' {
|
||||||
const coefficients = [0.2126729, 0.7151522, 0.072175] as const;
|
const coefficients = [0.2126729, 0.7151522, 0.072175] as const;
|
||||||
@@ -54,9 +69,20 @@ export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' |
|
|||||||
return Ys < 0.365 ? 'text-black' : 'text-theme-black';
|
return Ys < 0.365 ? 'text-black' : 'text-theme-black';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mapping of Tailwind CSS class names to their corresponding hex values
|
||||||
|
export const tailwindColorMap: Record<string, HexColor> = {
|
||||||
|
'text-white': '#FFFFFF',
|
||||||
|
'text-black': '#000000',
|
||||||
|
'text-theme-black': colors.theme.black,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get primary and secondary colors from a Tailwind colorway
|
* Get primary and secondary colors from a Tailwind colorway
|
||||||
* @param colorway the Tailwind colorway ex. "emerald"
|
*
|
||||||
|
* @param colorway - The Tailwind colorway ex. "emerald"
|
||||||
|
* @param index - The index of the primary color in the colorway
|
||||||
|
* @param offset - The offset to get the secondary color
|
||||||
|
* @returns The primary and secondary colors
|
||||||
*/
|
*/
|
||||||
export function getCourseColors(colorway: TWColorway, index?: number, offset: number = 300): CourseColors {
|
export function getCourseColors(colorway: TWColorway, index?: number, offset: number = 300): CourseColors {
|
||||||
if (index === undefined) {
|
if (index === undefined) {
|
||||||
@@ -76,10 +102,15 @@ export function getCourseColors(colorway: TWColorway, index?: number, offset: nu
|
|||||||
* @param color - The hexadecimal color value.
|
* @param color - The hexadecimal color value.
|
||||||
* @returns The Tailwind colorway.
|
* @returns The Tailwind colorway.
|
||||||
*/
|
*/
|
||||||
export function getColorwayFromColor(color: HexColor): TWColorway {
|
export function getColorwayFromColor(color: HexColor): {
|
||||||
|
colorway: TWColorway;
|
||||||
|
index: TWIndex;
|
||||||
|
} {
|
||||||
for (const colorway of useableColorways) {
|
for (const colorway of useableColorways) {
|
||||||
if (Object.values(theme.colors[colorway]).includes(color)) {
|
const colorValues = Object.values(theme.colors[colorway]);
|
||||||
return colorway as TWColorway;
|
const index = colorValues.indexOf(color);
|
||||||
|
if (index !== -1) {
|
||||||
|
return { colorway: colorway as TWColorway, index: (index * 100) as TWIndex };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,10 +146,159 @@ export function getColorwayFromColor(color: HexColor): TWColorway {
|
|||||||
return getColorwayFromColor(closestColor);
|
return getColorwayFromColor(closestColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a hexadecimal color value to HSL (Hue, Saturation, Lightness) format
|
||||||
|
*
|
||||||
|
* @param hex - The hexadecimal color string
|
||||||
|
* @returns An array of [hue (0-360), saturation (0-100), lightness (0-100)]
|
||||||
|
* @throws If the hex color cannot be converted to RGB
|
||||||
|
*/
|
||||||
|
export const hexToHSL = (hex: HexColor): HSL => {
|
||||||
|
const rgb = hexToRGB(hex);
|
||||||
|
|
||||||
|
if (!rgb) {
|
||||||
|
throw new Error('hexToRGB returned undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert RGB to decimals
|
||||||
|
const r = rgb[0] / 255;
|
||||||
|
const g = rgb[1] / 255;
|
||||||
|
const b = rgb[2] / 255;
|
||||||
|
|
||||||
|
// Find min/max/delta
|
||||||
|
const max = Math.max(r, g, b);
|
||||||
|
const min = Math.min(r, g, b);
|
||||||
|
const delta = max - min;
|
||||||
|
|
||||||
|
// Calculate HSL values
|
||||||
|
let h = 0;
|
||||||
|
let s = 0;
|
||||||
|
let l = (max + min) / 2;
|
||||||
|
|
||||||
|
if (delta !== 0) {
|
||||||
|
// Calculate saturation
|
||||||
|
s = delta / (1 - Math.abs(2 * l - 1));
|
||||||
|
|
||||||
|
// Calculate hue
|
||||||
|
if (max === r) {
|
||||||
|
h = ((g - b) / delta) % 6;
|
||||||
|
} else if (max === g) {
|
||||||
|
h = (b - r) / delta + 2;
|
||||||
|
} else {
|
||||||
|
h = (r - g) / delta + 4;
|
||||||
|
}
|
||||||
|
h *= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize values
|
||||||
|
h = Math.round(h < 0 ? h + 360 : h);
|
||||||
|
s = Math.round(s * 100);
|
||||||
|
l = Math.round(l * 100);
|
||||||
|
|
||||||
|
return [h, s, l];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an HSL color value to RGB format.
|
||||||
|
*
|
||||||
|
* @param hsl - The HSL color value
|
||||||
|
* @returns An RGB color value
|
||||||
|
*/
|
||||||
|
function hslToRGB([hue, saturation, lightness]: HSL): RGB {
|
||||||
|
// Convert percentages to decimals
|
||||||
|
const s = saturation / 100;
|
||||||
|
const l = lightness / 100;
|
||||||
|
|
||||||
|
// Calculate intermediate values
|
||||||
|
const c = (1 - Math.abs(2 * l - 1)) * s;
|
||||||
|
const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));
|
||||||
|
const m = l - c / 2;
|
||||||
|
|
||||||
|
let r = 0;
|
||||||
|
let g = 0;
|
||||||
|
let b = 0;
|
||||||
|
|
||||||
|
// Determine RGB values based on hue
|
||||||
|
if (hue >= 0 && hue < 60) {
|
||||||
|
[r, g, b] = [c, x, 0];
|
||||||
|
} else if (hue >= 60 && hue < 120) {
|
||||||
|
[r, g, b] = [x, c, 0];
|
||||||
|
} else if (hue >= 120 && hue < 180) {
|
||||||
|
[r, g, b] = [0, c, x];
|
||||||
|
} else if (hue >= 180 && hue < 240) {
|
||||||
|
[r, g, b] = [0, x, c];
|
||||||
|
} else if (hue >= 240 && hue < 300) {
|
||||||
|
[r, g, b] = [x, 0, c];
|
||||||
|
} else {
|
||||||
|
[r, g, b] = [c, 0, x];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to 0-255 range and round
|
||||||
|
return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a darker shade of the given hex color by reducing the lightness in HSL color space.
|
||||||
|
*
|
||||||
|
* @param color - The hexadecimal color value to darken.
|
||||||
|
* @param offset - The percentage to reduce the lightness by (default is 20).
|
||||||
|
* @returns The darker shade of the given hex color.
|
||||||
|
* @throws If the provided color is not a valid hex color.
|
||||||
|
*/
|
||||||
|
export function getDarkerShade(color: HexColor, offset: number = 20): HexColor {
|
||||||
|
const rgb = hexToRGB(color);
|
||||||
|
if (!rgb) {
|
||||||
|
throw new Error('color: Invalid hex.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to HSL
|
||||||
|
const [h, s, l] = hexToHSL(color);
|
||||||
|
|
||||||
|
// Reduce lightness by offset percentage, ensuring it doesn't go below 0
|
||||||
|
const newL = Math.max(0, l - offset);
|
||||||
|
|
||||||
|
// Convert back to RGB
|
||||||
|
const newRGB = hslToRGB([h, s, newL]);
|
||||||
|
|
||||||
|
// Convert to hex
|
||||||
|
return `#${newRGB.map(c => Math.round(c).toString(16).padStart(2, '0')).join('')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a lighter shade of the given hex color by increasing the lightness in HSL color space.
|
||||||
|
*
|
||||||
|
* @param color - The hexadecimal color value to lighten.
|
||||||
|
* @param offset - The percentage to increase the lightness by (default is 20).
|
||||||
|
* @returns The lighter shade of the given hex color.
|
||||||
|
* @throws If the provided color is not a valid hex color.
|
||||||
|
*/
|
||||||
|
export function getLighterShade(color: HexColor, offset: number = 20): HexColor {
|
||||||
|
const rgb = hexToRGB(color);
|
||||||
|
if (!rgb) {
|
||||||
|
throw new Error('color: Invalid hex.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to HSL
|
||||||
|
const [h, s, l] = hexToHSL(color);
|
||||||
|
|
||||||
|
// Increase lightness by offset percentage, ensuring it doesn't go above 100
|
||||||
|
const newL = Math.min(100, l + offset);
|
||||||
|
|
||||||
|
// Convert back to RGB
|
||||||
|
const newRGB = hslToRGB([h, s, newL]);
|
||||||
|
|
||||||
|
// Convert to hex
|
||||||
|
return `#${newRGB.map(c => Math.round(c).toString(16).padStart(2, '0')).join('')}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get next unused color in a tailwind colorway for a given schedule
|
* Get next unused color in a tailwind colorway for a given schedule
|
||||||
* @param schedule the schedule which the course is in
|
*
|
||||||
* @param course the course to get the color for
|
* @param schedule - The schedule which the course is in
|
||||||
|
* @param course - The course to get the color for
|
||||||
|
* @param index - The index of the primary color in the colorway
|
||||||
|
* @param offset - The offset to get the secondary color
|
||||||
|
* @returns The primary and secondary colors
|
||||||
*/
|
*/
|
||||||
export function getUnusedColor(
|
export function getUnusedColor(
|
||||||
schedule: Serialized<UserSchedule>,
|
schedule: Serialized<UserSchedule>,
|
||||||
@@ -143,17 +323,28 @@ export function getUnusedColor(
|
|||||||
|
|
||||||
const scheduleCourses = schedule.courses.map(c => ({
|
const scheduleCourses = schedule.courses.map(c => ({
|
||||||
...c,
|
...c,
|
||||||
colorway: getColorwayFromColor(c.colors.primaryColor),
|
theme: (() => {
|
||||||
|
try {
|
||||||
|
return getColorwayFromColor(c.colors.primaryColor);
|
||||||
|
} catch (error) {
|
||||||
|
// Default to emerald colorway with index 500
|
||||||
|
return {
|
||||||
|
colorway: 'emerald' as TWColorway,
|
||||||
|
index: 500 as TWIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})(),
|
||||||
}));
|
}));
|
||||||
const usedColorways = new Set(scheduleCourses.map(c => c.colorway));
|
|
||||||
|
const usedColorways = new Set(scheduleCourses.map(c => c.theme.colorway));
|
||||||
const availableColorways = new Set(useableColorways.filter(c => !usedColorways.has(c)));
|
const availableColorways = new Set(useableColorways.filter(c => !usedColorways.has(c)));
|
||||||
|
|
||||||
if (availableColorways.size > 0) {
|
if (availableColorways.size > 0) {
|
||||||
let sameDepartment = scheduleCourses.filter(c => c.department === course.department);
|
let sameDepartment = scheduleCourses.filter(c => c.department === course.department);
|
||||||
|
|
||||||
sameDepartment.sort((a, b) => {
|
sameDepartment.sort((a, b) => {
|
||||||
const aIndex = useableColorways.indexOf(a.colorway);
|
const aIndex = useableColorways.indexOf(a.theme.colorway);
|
||||||
const bIndex = useableColorways.indexOf(b.colorway);
|
const bIndex = useableColorways.indexOf(b.theme.colorway);
|
||||||
|
|
||||||
return aIndex - bIndex;
|
return aIndex - bIndex;
|
||||||
});
|
});
|
||||||
@@ -162,8 +353,8 @@ export function getUnusedColor(
|
|||||||
// check to see if any adjacent colorways are available
|
// check to see if any adjacent colorways are available
|
||||||
const centerCourse = sameDepartment[Math.floor(Math.random() * sameDepartment.length)]!;
|
const centerCourse = sameDepartment[Math.floor(Math.random() * sameDepartment.length)]!;
|
||||||
|
|
||||||
let nextColorway = getNextColorway(centerCourse.colorway);
|
let nextColorway = getNextColorway(centerCourse.theme.colorway);
|
||||||
let prevColorway = getPreviousColorway(centerCourse.colorway);
|
let prevColorway = getPreviousColorway(centerCourse.theme.colorway);
|
||||||
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -207,9 +398,9 @@ export function getUnusedColor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OKLab helper functions (https://github.com/bottosson/bottosson.github.io/blob/master/misc/colorpicker/colorconversion.js)
|
// OKLab helper functions (https://github.com/bottosson/bottosson.github.io/blob/master/misc/colorpicker/colorconversion.js)
|
||||||
function srgbTransferFunction(a: number): number {
|
// function srgbTransferFunction(a: number): number {
|
||||||
return a <= 0.0031308 ? 12.92 * a : 1.055 * a ** 0.4166666666666667 - 0.055;
|
// return a <= 0.0031308 ? 12.92 * a : 1.055 * a ** 0.4166666666666667 - 0.055;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function srgbTransferFunctionInv(a: number): number {
|
function srgbTransferFunctionInv(a: number): number {
|
||||||
return a > 0.04045 ? ((a + 0.055) / 1.055) ** 2.4 : a / 12.92;
|
return a > 0.04045 ? ((a + 0.055) / 1.055) ** 2.4 : a / 12.92;
|
||||||
@@ -221,8 +412,9 @@ function rgbToSrgb(rgb: RGB): sRGB {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an RGB color to the OKLab color space
|
* Convert an RGB color to the OKLab color space
|
||||||
* @param rgb the RGB color
|
*
|
||||||
* @returns the color in the OKLab color space
|
* @param rgb - The RGB color
|
||||||
|
* @returns The color in the OKLab color space
|
||||||
*/
|
*/
|
||||||
function srgbToOKlab([r, g, b]: sRGB): Lab {
|
function srgbToOKlab([r, g, b]: sRGB): Lab {
|
||||||
let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
|
let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import MIMEType from '../types/MIMEType';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads a blob by creating a temporary URL and triggering a download.
|
* Downloads a blob by creating a temporary URL and triggering a download.
|
||||||
* @param blobPart The blob data to be downloaded.
|
*
|
||||||
* @param type The MIME type of the blob.
|
* @param blobPart - The blob data to be downloaded.
|
||||||
* @param fileName The name of the file to be downloaded.
|
* @param type - The MIME type of the blob.
|
||||||
|
* @param fileName - The name of the file to be downloaded.
|
||||||
* @returns A promise that resolves when the download is successful, or rejects with an error if the download fails.
|
* @returns A promise that resolves when the download is successful, or rejects with an error if the download fails.
|
||||||
*/
|
*/
|
||||||
export function downloadBlob(blobPart: BlobPart, type: MIMETypeKey, fileName: string): Promise<void> {
|
export function downloadBlob(blobPart: BlobPart, type: MIMETypeKey, fileName: string): Promise<void> {
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export const enableCourseStatusChips: boolean = false;
|
|
||||||
export const constenableSettingsPage: boolean = false;
|
|
||||||
export const enableCourseRefreshing: boolean = false;
|
|
||||||
@@ -1,27 +1,29 @@
|
|||||||
|
import { ClockUser, LockKey, Prohibit } from '@phosphor-icons/react';
|
||||||
import type { StatusType } from '@shared/types/Course';
|
import type { StatusType } from '@shared/types/Course';
|
||||||
import { Status } from '@shared/types/Course';
|
import { Status } from '@shared/types/Course';
|
||||||
import type { SVGProps } from 'react';
|
import type { SVGProps } from 'react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import ClosedIcon from '~icons/material-symbols/lock';
|
interface StatusIconProps extends SVGProps<SVGSVGElement> {
|
||||||
import WaitlistIcon from '~icons/material-symbols/timelapse';
|
status: StatusType;
|
||||||
import CancelledIcon from '~icons/material-symbols/warning';
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Icon component based on status
|
* Get Icon component based on status
|
||||||
* @param props.status status
|
*
|
||||||
* @returns the icon component
|
* @param props - The props for the StatusIcon component
|
||||||
|
* @returns The rendered icon component
|
||||||
*/
|
*/
|
||||||
export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: StatusType }): JSX.Element | null {
|
export function StatusIcon(props: StatusIconProps): JSX.Element | null {
|
||||||
const { status, ...rest } = props;
|
const { status, ...rest } = props;
|
||||||
|
|
||||||
switch (props.status) {
|
switch (status) {
|
||||||
case Status.WAITLISTED:
|
case Status.WAITLISTED:
|
||||||
return <WaitlistIcon {...rest} />;
|
return <ClockUser weight='fill' {...rest} />;
|
||||||
case Status.CLOSED:
|
case Status.CLOSED:
|
||||||
return <ClosedIcon {...rest} />;
|
return <LockKey weight='fill' {...rest} />;
|
||||||
case Status.CANCELLED:
|
case Status.CANCELLED:
|
||||||
return <CancelledIcon {...rest} />;
|
return <Prohibit weight='fill' {...rest} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/shared/util/openReportWindow.ts
Normal file
28
src/shared/util/openReportWindow.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Open the report window relative centered to the current window.
|
||||||
|
*/
|
||||||
|
export function openReportWindow() {
|
||||||
|
chrome.windows.getCurrent({ populate: false }, currentWindow => {
|
||||||
|
const width = 400;
|
||||||
|
const height = 600;
|
||||||
|
|
||||||
|
// Calculate the new window's position to center it relative to the current window
|
||||||
|
const left =
|
||||||
|
currentWindow.left && currentWindow.width
|
||||||
|
? Math.round(currentWindow.left + (currentWindow.width - width) / 2)
|
||||||
|
: undefined;
|
||||||
|
const top =
|
||||||
|
currentWindow.top && currentWindow.height
|
||||||
|
? Math.round(currentWindow.top + (currentWindow.height - height) / 2)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
chrome.windows.create({
|
||||||
|
url: chrome.runtime.getURL(`report.html`),
|
||||||
|
type: 'popup',
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { customAlphabet } from 'nanoid';
|
|||||||
/**
|
/**
|
||||||
* Generate secure URL-friendly unique ID.
|
* Generate secure URL-friendly unique ID.
|
||||||
*
|
*
|
||||||
* @param size Size of the ID. The default size is 12.
|
* @param size - Size of the ID. The default size is 12.
|
||||||
* @returns A random string.
|
* @returns A random string.
|
||||||
*/
|
*/
|
||||||
export const generateRandomId = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 12);
|
export const generateRandomId = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 12);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Given a string, returns a string with the first letter capitalized.
|
* Given a string, returns a string with the first letter capitalized.
|
||||||
* @input The string to capitalize.
|
*
|
||||||
|
* @param input - The string to capitalize.
|
||||||
*/
|
*/
|
||||||
export function capitalize(input: string): string {
|
export function capitalize(input: string): string {
|
||||||
let capitalized = '';
|
let capitalized = '';
|
||||||
@@ -25,8 +26,9 @@ export function capitalize(input: string): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a string, returns a string with the first letter capitalized.
|
* Given a string, returns a string with the first letter capitalized.
|
||||||
* @param input capitalize the first letter of this string
|
*
|
||||||
* @returns the string with the first letter capitalized
|
* @param input - Capitalize the first letter of this string
|
||||||
|
* @returns The string with the first letter capitalized
|
||||||
*/
|
*/
|
||||||
export function capitalizeFirstLetter(input: string): string {
|
export function capitalizeFirstLetter(input: string): string {
|
||||||
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
|
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
|
||||||
@@ -34,8 +36,9 @@ export function capitalizeFirstLetter(input: string): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Cuts the input string to the specified length and adds an ellipsis if the string is longer than the specified length.
|
* Cuts the input string to the specified length and adds an ellipsis if the string is longer than the specified length.
|
||||||
* @param input The string to ellipsify.
|
*
|
||||||
* @param length The length of the string to return.
|
* @param input - The string to ellipsify.
|
||||||
|
* @param length - The length of the string to return.
|
||||||
* @returns The ellipsified string.
|
* @returns The ellipsified string.
|
||||||
*/
|
*/
|
||||||
export const ellipsify = (input: string, chars: number): string => {
|
export const ellipsify = (input: string, chars: number): string => {
|
||||||
@@ -45,3 +48,22 @@ export const ellipsify = (input: string, chars: number): string => {
|
|||||||
}
|
}
|
||||||
return ellipisifed;
|
return ellipisifed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stringifies a list of items in English format.
|
||||||
|
*
|
||||||
|
* @param items - The list of items to stringify.
|
||||||
|
* @returns A string representation of the list in English format.
|
||||||
|
* @example
|
||||||
|
* englishStringifyList([]) // ''
|
||||||
|
* englishStringifyList(['Alice']) // 'Alice'
|
||||||
|
* englishStringifyList(['Alice', 'Bob']) // 'Alice and Bob'
|
||||||
|
* englishStringifyList(['Alice', 'Bob', 'Charlie']) // 'Alice, Bob, and Charlie'
|
||||||
|
*/
|
||||||
|
export const englishStringifyList = (items: readonly string[]): string => {
|
||||||
|
if (items.length === 0) return '';
|
||||||
|
if (items.length === 1) return items[0]!;
|
||||||
|
if (items.length === 2) return `${items[0]} and ${items[1]}`;
|
||||||
|
|
||||||
|
return `${items.slice(0, -1).join(', ')}, and ${items.at(-1)}`;
|
||||||
|
};
|
||||||
|
|||||||
112
src/shared/util/tests/colors.test.ts
Normal file
112
src/shared/util/tests/colors.test.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { getLighterShade, hexToHSL, isValidHexColor } from '@shared/util/colors';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
describe('hexToHSL', () => {
|
||||||
|
it('should convert pure red to HSL', () => {
|
||||||
|
const result = hexToHSL('#FF0000');
|
||||||
|
expect(result).toEqual([0, 100, 50]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert pure green to HSL', () => {
|
||||||
|
const result = hexToHSL('#00FF00');
|
||||||
|
expect(result).toEqual([120, 100, 50]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert pure blue to HSL', () => {
|
||||||
|
const result = hexToHSL('#0000FF');
|
||||||
|
expect(result).toEqual([240, 100, 50]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert white to HSL', () => {
|
||||||
|
const result = hexToHSL('#FFFFFF');
|
||||||
|
expect(result).toEqual([0, 0, 100]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert black to HSL', () => {
|
||||||
|
const result = hexToHSL('#000000');
|
||||||
|
expect(result).toEqual([0, 0, 0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert UT burnt orange to HSL', () => {
|
||||||
|
const result = hexToHSL('#BF5700');
|
||||||
|
expect(result).toEqual([27, 100, 37]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert gray to HSL', () => {
|
||||||
|
const result = hexToHSL('#808080');
|
||||||
|
expect(result).toEqual([0, 0, 50]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for invalid hex color', () => {
|
||||||
|
expect(() => hexToHSL('#GGGGGG')).toThrow('hexToRGB returned undefined');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidHexColor', () => {
|
||||||
|
it('should validate 6-digit hex colors with hash', () => {
|
||||||
|
expect(isValidHexColor('#000000')).toBe(true);
|
||||||
|
expect(isValidHexColor('#FFFFFF')).toBe(true);
|
||||||
|
expect(isValidHexColor('#BF5700')).toBe(true);
|
||||||
|
expect(isValidHexColor('#D6D2C4')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate 6-digit hex colors without hash', () => {
|
||||||
|
expect(isValidHexColor('000000')).toBe(true);
|
||||||
|
expect(isValidHexColor('FFFFFF')).toBe(true);
|
||||||
|
expect(isValidHexColor('BF5700')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate 3-digit hex colors with hash', () => {
|
||||||
|
expect(isValidHexColor('#000')).toBe(true);
|
||||||
|
expect(isValidHexColor('#FFF')).toBe(true);
|
||||||
|
expect(isValidHexColor('#F0F')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate 3-digit hex colors without hash', () => {
|
||||||
|
expect(isValidHexColor('000')).toBe(true);
|
||||||
|
expect(isValidHexColor('FFF')).toBe(true);
|
||||||
|
expect(isValidHexColor('F0F')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject invalid hex colors', () => {
|
||||||
|
expect(isValidHexColor('#')).toBe(false);
|
||||||
|
expect(isValidHexColor('#GGG')).toBe(false);
|
||||||
|
expect(isValidHexColor('#GGGGGG')).toBe(false);
|
||||||
|
expect(isValidHexColor('GGGGGG')).toBe(false);
|
||||||
|
expect(isValidHexColor('#12345')).toBe(false);
|
||||||
|
expect(isValidHexColor('#1234567')).toBe(false);
|
||||||
|
expect(isValidHexColor('not a color')).toBe(false);
|
||||||
|
expect(isValidHexColor('')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getLighterShade', () => {
|
||||||
|
it('should lighten a color by default offset (20%)', () => {
|
||||||
|
const result = getLighterShade('#BF5700');
|
||||||
|
expect(result).toBe('#ff8624');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should lighten black correctly', () => {
|
||||||
|
const result = getLighterShade('#000000');
|
||||||
|
expect(result).toBe('#333333');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not exceed 100% lightness', () => {
|
||||||
|
const result = getLighterShade('#FFFFFF', 20);
|
||||||
|
expect(result).toBe('#ffffff');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle custom offset values', () => {
|
||||||
|
const result = getLighterShade('#BF5700', 40);
|
||||||
|
expect(result).toBe('#ffbe8a');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should maintain hue while increasing lightness', () => {
|
||||||
|
const result = getLighterShade('#00FF00', 20); // Pure green
|
||||||
|
expect(result.toLowerCase()).toBe('#66ff66');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for invalid hex color', () => {
|
||||||
|
expect(() => getLighterShade('#GGGGGG')).toThrow('color: Invalid hex.');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { capitalize, capitalizeFirstLetter, ellipsify } from '@shared/util/string';
|
import { capitalize, capitalizeFirstLetter, ellipsify, englishStringifyList } from '@shared/util/string';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
// TODO: Fix `string.ts` and `string.test.ts` to make the tests pass
|
// TODO: Fix `string.ts` and `string.test.ts` to make the tests pass
|
||||||
@@ -54,3 +54,49 @@ describe('ellipsify', () => {
|
|||||||
expect(ellipsify('', 5)).toBe('');
|
expect(ellipsify('', 5)).toBe('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('englishStringifyList', () => {
|
||||||
|
it('should handle an empty array', () => {
|
||||||
|
const data = [] satisfies string[];
|
||||||
|
const result = englishStringifyList(data);
|
||||||
|
const expected = '';
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle 1 element', () => {
|
||||||
|
const data = ['Alice'] satisfies string[];
|
||||||
|
const result = englishStringifyList(data);
|
||||||
|
const expected = 'Alice';
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle 2 elements', () => {
|
||||||
|
const data = ['Alice', 'Bob'] satisfies string[];
|
||||||
|
const result = englishStringifyList(data);
|
||||||
|
const expected = 'Alice and Bob';
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle 3 elements', () => {
|
||||||
|
const data = ['Alice', 'Bob', 'Charlie'] satisfies string[];
|
||||||
|
const result = englishStringifyList(data);
|
||||||
|
const expected = 'Alice, Bob, and Charlie';
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle n elements', () => {
|
||||||
|
const testcases = [
|
||||||
|
{ data: [], expected: '' },
|
||||||
|
{ data: ['foo'], expected: 'foo' },
|
||||||
|
{ data: ['foo', 'bar'], expected: 'foo and bar' },
|
||||||
|
{ data: ['foo', 'bar', 'baz'], expected: 'foo, bar, and baz' },
|
||||||
|
{ data: ['a', 'b', 'c', 'd'], expected: 'a, b, c, and d' },
|
||||||
|
{ data: 'abcdefghijk'.split(''), expected: 'a, b, c, d, e, f, g, h, i, j, and k' },
|
||||||
|
] satisfies { data: string[]; expected: string }[];
|
||||||
|
|
||||||
|
for (const { data, expected } of testcases) {
|
||||||
|
const result = englishStringifyList(data);
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import { describe, expect, it } from 'vitest';
|
|||||||
|
|
||||||
describe('sleep', () => {
|
describe('sleep', () => {
|
||||||
it('should resolve after the specified number of milliseconds', async () => {
|
it('should resolve after the specified number of milliseconds', async () => {
|
||||||
const start = Date.now();
|
const start = performance.now();
|
||||||
const milliseconds = 1000;
|
const milliseconds = 1000;
|
||||||
await sleep(milliseconds);
|
await sleep(milliseconds);
|
||||||
const end = Date.now();
|
const end = performance.now();
|
||||||
const elapsed = end - start;
|
const elapsed = end - start;
|
||||||
expect(elapsed).toBeGreaterThanOrEqual(milliseconds);
|
// Flaky test due to JS's lack of precision in setTimeout,
|
||||||
|
// so we allow for a 1ms difference
|
||||||
|
expect(elapsed).toBeGreaterThanOrEqual(milliseconds - 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const DAY = 24 * HOUR;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses the execution for the specified number of milliseconds.
|
* Pauses the execution for the specified number of milliseconds.
|
||||||
|
*
|
||||||
* @param milliseconds - The number of milliseconds to sleep.
|
* @param milliseconds - The number of milliseconds to sleep.
|
||||||
* @returns A promise that resolves after the specified number of milliseconds.
|
* @returns A promise that resolves after the specified number of milliseconds.
|
||||||
*/
|
*/
|
||||||
@@ -14,8 +15,8 @@ export const sleep = (milliseconds: number): Promise<void> => new Promise(resolv
|
|||||||
/**
|
/**
|
||||||
* Checks to see if expired by the time first stored and the time frame that it is stored for
|
* Checks to see if expired by the time first stored and the time frame that it is stored for
|
||||||
*
|
*
|
||||||
* @param time time it was stored
|
* @param time - time it was stored
|
||||||
* @param threshold time frame it can be stored for
|
* @param threshold - time frame it can be stored for
|
||||||
* @return true if expired, false if the time frame is still in range
|
* @returns true if expired, false if the time frame is still in range
|
||||||
*/
|
*/
|
||||||
export const didExpire = (time: number, threshold: number): boolean => time + threshold <= Date.now();
|
export const didExpire = (time: number, threshold: number): boolean => time + threshold <= Date.now();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const BADGE_LIMIT = 10;
|
|||||||
*/
|
*/
|
||||||
export default function updateBadgeText(value: number): void {
|
export default function updateBadgeText(value: number): void {
|
||||||
let badgeText = '';
|
let badgeText = '';
|
||||||
if (value > 0) {
|
if (value >= 0) {
|
||||||
if (value > BADGE_LIMIT) {
|
if (value > BADGE_LIMIT) {
|
||||||
badgeText = `${BADGE_LIMIT}+`;
|
badgeText = `${BADGE_LIMIT}+`;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user