Skip to content
Merged

sync #20

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
# [1.3.0](https://github.com/skid-dev/UltraBox/compare/v1.2.1...v1.3.0) (2026-05-05)


### Bug Fixes

* Remove inject for pulling RSS feed ([faa111f](https://github.com/skid-dev/UltraBox/commit/faa111fad47880e55772d536b82674ebada0c48a))


### Features

* Add option to change width of launcher ([2fa2492](https://github.com/skid-dev/UltraBox/commit/2fa249247432beff53e1877a5f37fcbe99e13b4c))
* Keyboard navigation for news search autocomplete ([b1e7efa](https://github.com/skid-dev/UltraBox/commit/b1e7efadcbfcbf31ea509042b601e18843df8676))
* Reduce content width module ([1736083](https://github.com/skid-dev/UltraBox/commit/1736083b3d055bfef144f3d1d2cb20f67146b4b4))

## [1.2.1](https://github.com/skid-dev/UltraBox/compare/v1.2.0...v1.2.1) (2026-05-04)


### Bug Fixes

* Fix memory issues with large diffs ([e296a8e](https://github.com/skid-dev/UltraBox/commit/e296a8e14f1d96b6d397b32cb6d027bbb8897f4b))

# [1.2.0](https://github.com/skid-dev/UltraBox/compare/v1.1.1...v1.2.0) (2026-04-30)


### Bug Fixes

* Fix improper styling of timeline dots in Schooltape Compatibility. ([16467a8](https://github.com/skid-dev/UltraBox/commit/16467a8fb83ee592db08be03f9e10b092919b1f4))
* Fix launcher scrolling to bottom of page ([e5f52d8](https://github.com/skid-dev/UltraBox/commit/e5f52d87fd23d0fa466f2d21c21854a3f9cd1fbb))
* Schooltape styles for autocomplete ([#14](https://github.com/skid-dev/UltraBox/issues/14)) ([20b6886](https://github.com/skid-dev/UltraBox/commit/20b6886e79d247d25def27c835e98fcf9955145b))


### Features

* Schooltape detection ([ef94564](https://github.com/skid-dev/UltraBox/commit/ef945645b7dbf567fa85aadce2cbe7daa8222b65))
* some minor improvements ([b988dfd](https://github.com/skid-dev/UltraBox/commit/b988dfd1eac6bbf121d0834641bee5c8052ad7f2))

## [1.1.1](https://github.com/skid-dev/UltraBox/compare/v1.1.0...v1.1.1) (2026-04-02)


Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,48 @@ A Chrome extension for the SchoolBox LMS with integrations for the Box of Books

## Features

### **Schooltape users: Please enable __Schooltape compatibility__ in the extension settings to ensure proper styling of elements created by modules in this extension!**
> This is a temporary workaround while automatic Schooltape detection is still work in progress.

- Launcher: A search bar for quick access to news articles and classes, as well as textbooks from Box of Books.
- Search through book headings in Box of Books.
- Search through book headings in Box of Books.
- Dark theme: Dark theme for the SchoolBox website and its pages (still a work in progress)
- News Chips: Replaces boring SchoolBox tabs in the news section with much more elegant chips and a search bar.
- Announcement / Post / News Item Edit history.
- Automatic detection for the news headlines RSS feed.


### In progress

- Frequently viewed pages: quick access for pages and resources you commonly access, separated by your class periods.

## Manual installation (via pre-built package)

### For chrome and chromium based browsers
1. Download the latest release (or a specific version you wish to install)
1. Unzip the file
1. Open your browsers extensions page (For chrome it's chrome://extensions)
1. If not already enabled, click the toggle in the top to enable developer mode.
1. A new ribbon should appear underneath the top navigation bar. Click the "Load Unpacked" button.
1. Select the folder that was extracted earlier.

1. Download the latest release (or a specific version you wish to install)
1. Unzip the file
1. Open your browsers extensions page (For chrome it's chrome://extensions)
1. If not already enabled, click the toggle in the top to enable developer mode.
1. A new ribbon should appear underneath the top navigation bar. Click the "Load Unpacked" button.
1. Select the folder that was extracted earlier.

## Development

Install dependencies using Bun:

```bash
# install dependencies
bun install

# build
bun run build
```

For development mode with file watching:

```bash
bun run dev
```

Load the `dist/` directory as an unpacked extension in Chrome to test.

1. Open Google Chrome and go to `chrome://extensions/`.
2. Enable the Developer Mode toggle in the top right.
3. Click on "load unpacked" in the toolbar that appears.
Expand Down
6 changes: 6 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "schoolbox-chrome-extension",
"version": "1.1.1",
"version": "1.3.0",
"description": "A Chrome extension for SchoolBox built with TypeScript, React, and Bun.",
"main": "src/background/background.ts",
"scripts": {
Expand All @@ -25,10 +25,12 @@
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.27.1",
"@types/chrome": "^0.1.38",
"@types/diff": "^8.0.0",
"@types/html-to-text": "^9.0.4",
"babel-loader": "^10.0.0",
"copy-webpack-plugin": "^13.0.0",
"css-loader": "^7.1.2",
"diff": "^9.0.0",
"fast-xml-parser": "^5.2.3",
"fuse.js": "^7.1.0",
"html-to-text": "^9.0.5",
Expand Down
22 changes: 11 additions & 11 deletions src/background/events/injects/display_history.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import { Module } from "../../../types/module";
import { Module } from "../../../types/module"

export default <Module> {
export default <Module>{
setting: s => {
return !!s.recents_list_module
},
condition: async (base, settings, helper_fns) => {
return (await helper_fns.url_begins_with("/news"))
return await helper_fns.url_begins_with("/news")
},
action: async (base, settings, helper_fns) => {
await chrome.scripting.executeScript({
target: {tabId: base.tab_id},
files: ["display_history.js"]
target: { tabId: base.tab_id },
files: ["display_history.js"],
})

await chrome.scripting.insertCSS({
target: {tabId: base.tab_id},
files: ["news_history.css"]
target: { tabId: base.tab_id },
files: ["news_history.css"],
})

if (settings.schooltape_compatibility) {
await chrome.scripting.insertCSS({
target: {tabId: base.tab_id},
files: ["schooltape/post_history_styles.css"]
target: { tabId: base.tab_id },
files: ["schooltape/post_history_styles.css"],
})
}
}
}
},
}
10 changes: 4 additions & 6 deletions src/background/events/injects/news_tracker.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Module } from "../../../types/module"

import { poll_feed } from "../../pull_feed"

export default <Module>{
setting: s => {
return !!s.record_post_history
},
action: async (base) => {
await chrome.scripting.executeScript({
target: { tabId: base.tab_id },
files: ["history_puller.js"],
})
action: async () => {
await poll_feed()
},
}
16 changes: 16 additions & 0 deletions src/background/events/injects/reduce_width.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Module } from "../../../types/module"

export default <Module>{
setting: s => {
return s.reduce_content_width
},
condition: (_base, _settings, helper_fns) => {
return helper_fns.is_schoolbox_page
},
action: async base => {
await chrome.scripting.insertCSS({
target: { tabId: base.tab_id },
files: ["reduce_width.css"],
})
},
}
11 changes: 9 additions & 2 deletions src/background/events/on_update.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { check_in_schoolbox_domain, get_stored_settings, is_page, url_begins_with } from "../functions/utls/is_page"
import {
check_in_schoolbox_domain,
get_stored_settings,
is_page,
url_begins_with,
} from "../functions/utls/is_page"
import track_page_if_in_domain from "../functions/utls/track_page"

import dark_theme_css from "./injects/dark_theme_css"
Expand All @@ -9,6 +14,7 @@ import launcher_shortcut from "./injects/launcher_shortcut"
import news_tracker_fetch from "./injects/news_tracker"
import news_tracker_display from "./injects/display_history"
import detect_schooltape from "./injects/st_detect_inject"
import reduce_width from "./injects/reduce_width"

const INJECTS = [
dark_theme_css,
Expand All @@ -18,7 +24,8 @@ const INJECTS = [
launcher_shortcut,
news_tracker_fetch,
news_tracker_display,
detect_schooltape
detect_schooltape,
reduce_width,
]

export default async function on_update(tab_id: number, _: any, tab: chrome.tabs.Tab) {
Expand Down
89 changes: 14 additions & 75 deletions src/background/functions/calculate_rev_metrics.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,24 @@
import { RevisionData } from "../../types/rev_history"
import { diffChars } from "diff"

export async function calculate_new_metrics(old_data: RevisionData, new_data: RevisionData) {
const old_title = old_data.title || ""
const new_title = new_data.title || ""
let added_chars = 0
let removed_chars = 0
let unchanged_chars = 0

let new_lines = 0
let modified_lines = 0
let deleted_lines = 0
const changes = diffChars(old_data.content.toLowerCase(), new_data.content.toLowerCase())

if (old_title != new_title) {
modified_lines++
}

// split into individual chars
const old_text = [...old_data.content.toLowerCase()]
const new_text = [...new_data.content.toLowerCase()]

const m = old_text.length
const n = new_text.length

// create a DP table of size (m+1) x (n+1)
// dp[i][j] represents the edit distance between old_text[0..i-1] and new_text[0..j-1]
const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0))

// base cases
for (let i = 0; i <= m; i++) {
dp[i][0] = i // deleting all lines from old_text
}
for (let j = 0; j <= n; j++) {
dp[0][j] = j // adding all lines from new_text
}

// build the DP table
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (old_text[i - 1] === new_text[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] // no change
} else {
dp[i][j] = Math.min(
dp[i - 1][j] + 1, // delete old line
dp[i][j - 1] + 1, // add new line
dp[i - 1][j - 1] + 1 // modify line
)
}
}
}

// backtrack to count the number of new, modified, and deleted lines
let i = m
let j = n
while (i > 0 && j > 0) {
if (old_text[i - 1] === new_text[j - 1]) {
// if lines are the same, move diagonally
i--
j--
} else if (dp[i][j] === dp[i - 1][j] + 1) {
// line deleted from old_text
deleted_lines++
i--
} else if (dp[i][j] === dp[i][j - 1] + 1) {
// line added in new_text
new_lines++
j--
for (const part of changes) {
if (part.added) {
added_chars += part.count || 0
} else if (part.removed) {
removed_chars += part.count || 0
} else {
modified_lines++
i--
j--
unchanged_chars += part.count || 0
}
}

// if there are remaining lines in old_text, they are deleted
while (i > 0) {
deleted_lines++
i--
}

// if there are remaining lines in new_text, they are added
while (j > 0) {
new_lines++
j--
}

return { new_lines, modified_lines, deleted_lines }
// A "modification" at the character level isn't a standard metric,
// it's usually just represented as a removal followed by an addition.
return { added_chars, removed_chars, unchanged_chars }
}
8 changes: 4 additions & 4 deletions src/background/pull_feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async function append_revision(
}

const now = Date.now()
const { new_lines, modified_lines, deleted_lines } = await calculate_new_metrics(
const { added_chars, removed_chars, unchanged_chars } = await calculate_new_metrics(
prev_data_obj,
rev_object
)
Expand All @@ -74,9 +74,9 @@ async function append_revision(
guid,
prev_data_obj,
now,
new_lines,
modified_lines,
deleted_lines
added_chars,
removed_chars,
unchanged_chars
)

return data.rev_id
Expand Down
Loading