Repository navigation
Development
Also a guide: Development on frostdev.io
How Glaze is put together, and how to build, test, package and publish it. See also Contributing, Releasing and Firefox for Android testing.
The content script adds three stylesheets it owns (theme.css, motion.css, extras.css) at page
start, so the popup can switch the theme on and off without a reload. theme.css remaps Blackboard's
own --palette-* custom properties onto Glaze's --gz-* tokens and restyles Ultra's components. Every
setting becomes either a --gz-* variable or a data-gz-* attribute on <html>, which the content
script writes from your saved settings, so the CSS never needs JavaScript to decide anything. A small
DOM sweep tags the long tail — hardcoded light backgrounds, dark text, viewport-fixed bars — with
data-gz-* attributes the stylesheets then restyle.
Each LMS loads only what it needs at first paint: the shared content.js (with the small libraries it uses)
plus that site's own part, content-ultra.js on Blackboard Ultra and content-canvas.js on Canvas. Moodle
has no part; it is themed by content.js and moodle.css. Everything else, from the command palette to the
grade calculators, loads on demand from features/ the first time a page needs it.
./package.shWrites dist/glaze-v<version>.zip, ready for the Chrome Web Store. Requires node and zip.
sh scripts/package-firefox.sh writes it too, plus dist/glaze-v<version>-firefox.zip for Firefox Add-ons.
For a release, sh scripts/release.sh builds everything into release/<version>/; see RELEASING.md.
node --testRuns the tests for the colour, settings and page-text logic. No dependencies to install.
sh scripts/publish-chrome.shPackages the extension, uploads it with the Chrome Web Store API, and submits it for review. Pass a
zip path to upload a specific build; set CWS_NO_PUBLISH=1 to upload without submitting. It signs in
as the cws-publisher service account through gcloud impersonation, so no key file is kept: your
Google account needs the Service Account Token Creator role on it, and the service account must be
added under Account in the Developer Dashboard. The listing text, screenshots and privacy answers
aren't in the API; edit those in the dashboard. The publisher and item IDs default to Glaze's; override them with CWS_PUBLISHER_ID and CWS_EXTENSION_ID (the publisher ID is in the dashboard's address, after /devconsole/).
Safari loads Glaze from this Mac app. The Xcode project is in safari/; to build the app yourself
you need Xcode and the public key from Mac update release setup:
sh scripts/build-safari.shThat packages the extension, copies it into a temporary copy of the project, and builds a universal
app in release/<version>/build/Glaze.app, signed with the configured Developer ID. Override SAFARI_SIGN_IDENTITY
and SAFARI_TEAM_ID to use a different identity. Safari only installs apps from outside the App Store
when they are notarized, so pass a saved notarytool Keychain profile:
NOTARY_PROFILE=your-profile sh scripts/build-safari.shor set ASC_KEY_PATH, ASC_KEY_ID and ASC_ISSUER_ID for an App Store Connect API key. The script
then notarizes the app, staples the ticket, checks Gatekeeper, and writes
release/<version>/github/glaze-<version>-safari-macos.zip and a notarized .dmg, each with its SHA-256 checksum. Without credentials the zip is
marked unnotarized and is for development only.
For the Mac App Store, scripts/appstore-safari.sh archives the same project with App Store signing
and uploads the build to App Store Connect. It needs an App Store Connect API key with the Admin role in
ASC_KEY_PATH, ASC_KEY_ID and ASC_ISSUER_ID; Xcode creates the distribution certificate and profiles
on first use. APPSTORE_EXPORT_ONLY=1 builds the signed package without uploading. Listing text,
screenshots and review submission happen in App Store Connect.
The app bundles only the files the Xcode project lists for its two extension targets (Mac and iOS); a folder
reference brings everything under it. Every build script copies the package into the project's Resources folder
and then runs node scripts/safari-resources.js on it, which stops the build if the package has a file the
project doesn't list. Add a new top-level file or folder to both extension targets in Xcode;
test/safari-resources.test.js checks the project against everything the package ships.
scripts/package-safari.sh regenerates the Xcode project from scratch and replaces its native edits.
Use it only when you mean to regenerate the project. It doesn't know about the iPhone and iPad targets below.
The Developer ID build stages Sparkle 2.10.0 in a temporary Mac project through
scripts/stage-safari-dmg.py; the checked-in App Store project stays free of Sparkle.
Glaze's signing key was made once with the Sparkle distribution's key generator
(bin/generate_keys), which keeps the private key in the login Keychain (account ed25519) of the Mac
that made it. Back it up with generate_keys -x <file> and keep that file somewhere safe: without it,
no update can be signed for apps already installed. scripts/build-safari.sh passes the public key
(0lbeC9MO1QDsjSGjs6iyxSBwXKMpEiKVoB+5wZwQEqY=, or SAFARI_UPDATE_PUBLIC_KEY if set) into
SUPublicEDKey in the staged Mac app's Glaze/Info.plist. Staging on its own without a key leaves
updates disabled; a release build rejects the REPLACE_WITH_SPARKLE_EDDSA_PUBLIC_KEY placeholder.
After building and notarizing a release, generate its signed update entry locally (replace
<version> with the manifest version):
sh scripts/safari-appcast.sh "release/<version>/github/glaze-<version>-safari-macos.dmg" /path/to/Sparkle/binThe helper verifies the notarized DMG and app, checks that the Keychain signing key matches the
app's public key, and writes release/appcast.xml, preserving older entries. It does not publish
anything. Upload the DMG to its GitHub release before publishing that feed at
https://frostdev.io/projects/glaze/appcast.xml (the frostdev.io path is /projects/glaze/appcast.xml).
Use the same Sparkle Keychain account for key generation and appcast signing; if you choose a
custom account, set SAFARI_UPDATE_KEY_ACCOUNT to it when running the helper.
The iOS app is two more targets in the same Xcode project (Glaze iOS and Glaze iOS Extension), sharing
the extension's resources and native handler with the Mac targets. scripts/appstore-ios.sh archives it
with App Store signing into release/<version>/build/Glaze-iOS.xcarchive and release/<version>/app-store/glaze-<version>-ios.ipa. It uses
the App Store profiles already on the Mac; set ASC_KEY_PATH, ASC_KEY_ID and ASC_ISSUER_ID to let Xcode
create them. It never uploads unless APPSTORE_UPLOAD=1 is set. To try it in the Simulator, run the
Glaze iOS scheme in Xcode.
Glaze is MIT licensed (LICENSE). Bundled with it, each under its own license beside it: three.js (MIT, lib/three.LICENSE), lottie-web's light canvas player (MIT, lib/lottie/LICENSE), pdf.js and its JBIG2 and JPEG 2000 decoders (Apache-2.0 and BSD, lib/pdfjs/), and the Tesseract OCR engine and its tessdata_fast models (Apache-2.0, lib/tesseract/), each rebuilt by its scripts/vendor-*.sh.