From d3825715f322f3967fda2e8d1275336d7158b6c8 Mon Sep 17 00:00:00 2001 From: Bryan Fawcett Date: Tue, 3 Nov 2026 02:18:51 +0000 Subject: [PATCH 1/2] Release v0.13.0 @bundu/ui 0.9.0, for the latest Nyuchi branding and styling. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq --- CHANGELOG.md | 5 +++++ package-lock.json | 23 +++++++++++++++-------- package.json | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86237d6..9b0c5ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,11 @@ numbers below were the site's earlier, hand-kept numbering and are retired; the ### Changed +- **@bundu/ui 0.9.0** (was 0.5.0), for the latest Nyuchi branding and + styling. `globals.css` now brings in Bundu's density rules, which the + site already imports. Nothing the site uses was removed. The one new + dependency is `uqr`, the QR-code library behind Bundu's new + address cards. - **Firefox is coming, and the pages say so.** The extension now builds for Firefox; until its addons.mozilla.org listing is live (`toddleExtension.firefoxStore` in `src/data/site.ts`, null for now) the diff --git a/package-lock.json b/package-lock.json index 43a46ed..4f96047 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "3.0.0", "license": "UNLICENSED", "dependencies": { - "@bundu/ui": "^0.5.0", + "@bundu/ui": "^0.9.0", "astro": "^7.3.5" }, "devDependencies": { @@ -649,15 +649,16 @@ } }, "node_modules/@bundu/ui": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@bundu/ui/-/ui-0.5.0.tgz", - "integrity": "sha512-391EJhoMD0K+nuwJPQGOPDvEpmUJsIjRrxWX9+W0fFqr5oPliQVov/ZpZjOMZO3lxAa7bu5zaIxVO8t9h8gaBA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@bundu/ui/-/ui-0.9.0.tgz", + "integrity": "sha512-NPhnyPmxLyktPiDa6j/NCp1MmtTi9HnaYJB/zPkgXza82WRaPTVjGIuVKoStuqn1WppJohssE2DFRaWAYq584A==", "license": "MIT", "dependencies": { "@bundu/server": "^0.1.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "tailwind-merge": "^3.0.2" + "class-variance-authority": ">=0.7.1 <0.8.0", + "clsx": ">=2.1.1 <3", + "tailwind-merge": ">=3.6.0 <4", + "uqr": ">=0.1.3 <0.2.0" } }, "node_modules/@capsizecss/unpack": { @@ -7563,6 +7564,12 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/uqr": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.3.tgz", + "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==", + "license": "MIT" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8830,7 +8837,7 @@ "version": "8.22.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 2b9d426..6a0993b 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "ci:check": "astro check && vp fmt --check" }, "dependencies": { - "@bundu/ui": "^0.5.0", + "@bundu/ui": "^0.9.0", "astro": "^7.3.5" }, "devDependencies": { From 5f2a75604bd0be44343608d832fcd485fd703018 Mon Sep 17 00:00:00 2001 From: Bryan Fawcett Date: Sat, 7 Nov 2026 03:59:35 +0000 Subject: [PATCH 2/2] Release v0.13.0: staging with Firefox live Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CoDNYz27iJ7o8PPztCzTpq --- CHANGELOG.md | 1 + src/data/legal.ts | 2 +- src/data/site.ts | 12 +++--- src/pages/legal/privacy.astro | 39 +++++++++++++++++++ src/pages/legal/terms.astro | 2 +- .../legal/vulnerability-disclosure.astro | 2 +- vercel.json | 2 +- 7 files changed, 50 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b0c5ad..9efb95d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ numbers below were the site's earlier, hand-kept numbering and are retired; the ### Added +- **Firefox.** The extension is on Firefox Add-ons (version 0.16.3). Every page now says it runs in Chrome, Chromium browsers and Firefox, the extension page offers "Add to Firefox" beside "Add to Chrome", and the privacy policy has an "In Firefox" section (claim links are pasted; Firefox's storage and Sync; school policy through policies.json; what the listing declares to Mozilla). The terms and the vulnerability disclosure scope cover Firefox too. - **The Chrome Web Store numbers on the home page.** The proof section says what the store listing shows, dated: rated 5.0 out of 5 (1 rating), 27 users, as of 29 October 2026, from `toddleExtension.storeStats` in diff --git a/src/data/legal.ts b/src/data/legal.ts index 2c0327f..a099da8 100644 --- a/src/data/legal.ts +++ b/src/data/legal.ts @@ -10,7 +10,7 @@ export const legal = { /** Last substantive change, for every legal page. Update when a claim changes, not on typos. */ - updated: "2026-10-29", + updated: "2026-11-07", entity: "Nyuchi Africa (Private) Limited", shortEntity: "Nyuchi", country: "Zimbabwe", diff --git a/src/data/site.ts b/src/data/site.ts index 2f242e2..a96920d 100644 --- a/src/data/site.ts +++ b/src/data/site.ts @@ -168,10 +168,9 @@ export const MINERALS = [ * fall back to an enquiry, with no other edit.) * * `firefoxStore` is Firefox's: the addons.mozilla.org listing. The extension - * builds for Firefox from 0.17.0 (the extension's docs/firefox.md); until the - * listing is live it stays null and the pages say Firefox is coming. Set it - * and every page says Chrome and Firefox, and offers "Add to Firefox" beside - * "Add to Chrome", with no other edit. + * builds for Firefox (the extension's docs/firefox.md), listed since 0.16.3. + * Set, every page says Chrome and Firefox and offers "Add to Firefox" beside + * "Add to Chrome"; null, and the pages say Firefox is coming. */ /** A link that may not exist yet. Named so that every Prettier version formats the cast below the same way. */ @@ -184,8 +183,9 @@ export const toddleExtension = { /** The Chrome Web Store listing (live since version 0.7.1). */ webStore: "https://chromewebstore.google.com/detail/ofliokikjmkkdkinbdnadbjjdmkjdjfi" as Url, - /** The addons.mozilla.org listing: null until it is live. */ - firefoxStore: null as Url, + /** The addons.mozilla.org listing (live since version 0.16.3). */ + firefoxStore: + "https://addons.mozilla.org/firefox/addon/toddle-enhancement-extension/" as Url, /** What the Chrome Web Store listing showed, read by the owner on the date given. Update all three together; never round up. */ storeStats: { diff --git a/src/pages/legal/privacy.astro b/src/pages/legal/privacy.astro index f59d943..7e0921a 100644 --- a/src/pages/legal/privacy.astro +++ b/src/pages/legal/privacy.astro @@ -257,6 +257,45 @@ const description = sees or pastes it; an organisation key typed into the extension is refused.

+

+ In Firefox +

+

+ The extension is also on + Firefox Add-ons. It is the same extension, built from the same code, and everything on this page + applies to it in the same way, with these differences: +

+
    +
  • + Claim links. The licence claim page cannot + hand a link to the extension in Firefox, so you paste the link from your licence + email into the extension's licence page. The request it makes is the same one, + carrying the link's code and nothing else. +
  • +
  • + Storage and sync. Where this page says + Chrome's storage, in Firefox it is Firefox's storage for the extension. If you use + Firefox Sync, Firefox may carry your licence key to your other computers through + your own Firefox account; the extension never sends it anywhere. +
  • +
  • + Schools. A school's IT sets the licence and + the modules for every teacher through Firefox enterprise policy + (policies.json) instead of Chrome policy. The extension reads it on the + computer, and nothing about it is sent anywhere. +
  • +
  • + What Mozilla is told. The listing declares + that the extension collects nothing by itself, and that feedback and Toddle + compatibility reports, which you choose to send, contain technical information and + never student data. +
  • +
+

Security

diff --git a/src/pages/legal/terms.astro b/src/pages/legal/terms.astro index d49840e..1f4b80d 100644 --- a/src/pages/legal/terms.astro +++ b/src/pages/legal/terms.astro @@ -126,7 +126,7 @@ const description =

How licence keys work

An individual licence's email carries a one-time link, not the key: open it in Chrome, click - Add it to Chrome, and the extension fetches the key once from our licence + Add it to Chrome (in Firefox, paste the link into the extension's licence page), and the extension fetches the key once from our licence server, in exchange for the link's code alone. The link works once, for 72 hours. A licence key is a signed statement that the extension checks on your own device, so checking it contacts nothing and works offline. A key carries the buyer's email diff --git a/src/pages/legal/vulnerability-disclosure.astro b/src/pages/legal/vulnerability-disclosure.astro index 3263f03..2b13a4e 100644 --- a/src/pages/legal/vulnerability-disclosure.astro +++ b/src/pages/legal/vulnerability-disclosure.astro @@ -43,7 +43,7 @@ const description = >

  • This website, learning.nyuchi.com.
  • - The {toddleExtension.name}, version {security.supportedFrom} and later from the Chrome Web Store + The {toddleExtension.name}, version {security.supportedFrom} and later from the Chrome Web Store, and from Firefox Add-ons (earlier versions are not supported; extension ID {toddleExtension.extensionId}).
  • diff --git a/vercel.json b/vercel.json index c9d08cc..730c980 100644 --- a/vercel.json +++ b/vercel.json @@ -27,7 +27,7 @@ }, { "key": "Content-Security-Policy", - "value": "default-src 'self'; script-src 'self' https://app.intercom.io https://widget.intercom.io https://js.intercomcdn.com https://www.googletagmanager.com 'sha256-Brc4B09cZ9aNF4dBE7dQSlIzUMTvBIzHAPqI3zLF8+I=' 'sha256-FFGFlD4m+iq+1VF7UwfmwBt3tn7qHB2INa+7rAycUC8=' 'sha256-H7lTFTHP+LM756VatSdCPdQqI5KgMvle+T0W7x3MQnM=' 'sha256-VIgVtD7fAVrU4eaS8Crl3kwF00ggST1rFqthmXLJELQ='; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://js.intercomcdn.com https://fonts.intercomcdn.com; img-src 'self' data: https://js.intercomcdn.com https://static.intercomassets.com https://downloads.intercomcdn.com https://uploads.intercomusercontent.com https://gifs.intercomcdn.com https://video-messages.intercomcdn.com https://messenger-apps.intercomcdn.com https://www.google-analytics.com https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://formspree.io https://via.intercom.io https://api.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://*.intercom-messenger.com wss://*.intercom-messenger.com https://nexus-websocket-a.intercom.io wss://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io wss://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://www.google-analytics.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com; form-action 'self' https://formspree.io; frame-ancestors 'none'; base-uri 'self'; object-src 'none'; media-src 'self' https://js.intercomcdn.com; frame-src 'self' https://intercom-sheets.com https://www.intercom-reporting.com" + "value": "default-src 'self'; script-src 'self' https://app.intercom.io https://widget.intercom.io https://js.intercomcdn.com https://www.googletagmanager.com 'sha256-Brc4B09cZ9aNF4dBE7dQSlIzUMTvBIzHAPqI3zLF8+I=' 'sha256-FFGFlD4m+iq+1VF7UwfmwBt3tn7qHB2INa+7rAycUC8=' 'sha256-H7lTFTHP+LM756VatSdCPdQqI5KgMvle+T0W7x3MQnM=' 'sha256-k/SaY/US23qudvvmBdO1E+8Kcnq0gtOWaQxF58YESag='; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://js.intercomcdn.com https://fonts.intercomcdn.com; img-src 'self' data: https://js.intercomcdn.com https://static.intercomassets.com https://downloads.intercomcdn.com https://uploads.intercomusercontent.com https://gifs.intercomcdn.com https://video-messages.intercomcdn.com https://messenger-apps.intercomcdn.com https://www.google-analytics.com https://*.google-analytics.com https://*.googletagmanager.com; connect-src 'self' https://formspree.io https://via.intercom.io https://api.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://*.intercom-messenger.com wss://*.intercom-messenger.com https://nexus-websocket-a.intercom.io wss://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io wss://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://www.google-analytics.com https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com; form-action 'self' https://formspree.io; frame-ancestors 'none'; base-uri 'self'; object-src 'none'; media-src 'self' https://js.intercomcdn.com; frame-src 'self' https://intercom-sheets.com https://www.intercom-reporting.com" } ] }