2026-05-11 16:51:54 +02:00
|
|
|
# Naiv-QR
|
2026-05-11 16:04:22 +02:00
|
|
|
|
|
|
|
|
A small Firefox extension that turns the current tab's URL into a scannable QR
|
|
|
|
|
code, so you can hand a page off to a phone (or another machine) without
|
|
|
|
|
typing.
|
|
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
## What you see
|
|
|
|
|
|
|
|
|
|
Click the toolbar icon while you're on any normal web page. A small popup
|
|
|
|
|
opens showing:
|
|
|
|
|
|
|
|
|
|
- a black-and-white QR code (~280 px square) encoding the current URL,
|
|
|
|
|
- the URL itself in muted text below the QR,
|
|
|
|
|
- a **Copy URL** button that puts the URL on your clipboard.
|
|
|
|
|
|
|
|
|
|
The popup honours `prefers-color-scheme`, so dark-mode users get a dark
|
|
|
|
|
chrome around a white QR (the QR itself must stay light-on-dark for cameras
|
|
|
|
|
to read it).
|
|
|
|
|
|
|
|
|
|
On browser-internal pages (`about:`, `chrome:`, `view-source:`,
|
|
|
|
|
`moz-extension:`, `data:`, `javascript:`, `resource:`) the popup shows a
|
|
|
|
|
*"This page can't be shared"* message instead of attempting to encode the
|
|
|
|
|
URL. URLs longer than the largest QR version (~2.9 KB) get a *"too long to
|
|
|
|
|
encode"* message.
|
|
|
|
|
|
|
|
|
|
## Supported Firefox versions
|
|
|
|
|
|
|
|
|
|
`manifest.json` sets `strict_min_version: 142.0`. That floor exists because
|
|
|
|
|
the manifest declares
|
|
|
|
|
`browser_specific_settings.gecko.data_collection_permissions`, which was
|
|
|
|
|
introduced in Firefox 140 (desktop) / 142 (Android).
|
2026-05-11 16:04:22 +02:00
|
|
|
|
|
|
|
|
## Develop
|
|
|
|
|
|
|
|
|
|
This project uses [bun](https://bun.sh) as both the script runner and the
|
2026-05-11 16:12:34 +02:00
|
|
|
bundler. `popup/popup.js` imports the vendored QR library; `bun build`
|
|
|
|
|
inlines it into a single minified ESM file under `dist/`. Firefox loads the
|
|
|
|
|
extension from `dist/`, never from the source tree directly.
|
|
|
|
|
|
|
|
|
|
There are no runtime dependencies and no `node_modules` to install — `bunx`
|
|
|
|
|
pulls `web-ext` on demand. The first run is slow (download + cache); later
|
|
|
|
|
runs are fast.
|
2026-05-11 16:04:22 +02:00
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
bun run build # bundle popup.js + assets into dist/
|
2026-05-11 16:12:34 +02:00
|
|
|
bun run lint # build + web-ext lint (must be 0 errors / 0 warnings)
|
2026-05-11 16:04:22 +02:00
|
|
|
bun run package # build + produce a signable .zip in web-ext-artifacts/
|
|
|
|
|
bun run start # build + launch Firefox with the extension loaded
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
### Loading the extension manually
|
|
|
|
|
|
|
|
|
|
After `bun run build`:
|
2026-05-11 16:04:22 +02:00
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
1. Open `about:debugging#/runtime/this-firefox` in Firefox 142+.
|
|
|
|
|
2. Click **Load Temporary Add-on…** and pick **`dist/manifest.json`**.
|
|
|
|
|
3. The QR icon appears in the toolbar. Click it on any normal page.
|
2026-05-11 16:04:22 +02:00
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
The temporary install is wiped when Firefox restarts; reload the same way
|
|
|
|
|
during dev. For a more persistent dev loop use `bun run start`, which
|
|
|
|
|
launches Firefox with the extension already loaded and auto-reloads on file
|
|
|
|
|
changes.
|
|
|
|
|
|
|
|
|
|
### Build flow
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
popup/popup.js ──┐
|
|
|
|
|
│ bun build (esm, minified, browser target)
|
|
|
|
|
vendor/qrcode.js ─┘ │
|
|
|
|
|
▼
|
|
|
|
|
dist/popup/popup.js (~23 KB)
|
|
|
|
|
|
|
|
|
|
manifest.json, popup/{html,css}, icons/, vendor/LICENSE.qrcode-generator
|
|
|
|
|
└──── copied verbatim into dist/ ───────► dist/
|
|
|
|
|
|
|
|
|
|
web-ext operates on dist/ only, so the packaged zip contains exactly what
|
|
|
|
|
the build emitted — never AGENTS.md, .dogcats/, scripts/, or other repo-only
|
|
|
|
|
files.
|
|
|
|
|
```
|
2026-05-11 16:04:22 +02:00
|
|
|
|
|
|
|
|
## Privacy
|
|
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
The extension makes **no network requests**. The QR code is generated
|
|
|
|
|
locally in the popup using the bundled `vendor/qrcode.js` library. The
|
2026-05-11 16:04:22 +02:00
|
|
|
`browser_specific_settings.gecko.data_collection_permissions` field in the
|
2026-05-11 16:12:34 +02:00
|
|
|
manifest declares `"none"` accordingly, which surfaces during AMO review and
|
|
|
|
|
in the install prompt.
|
|
|
|
|
|
|
|
|
|
Permissions used (both are the minimum the feature needs):
|
2026-05-11 16:04:22 +02:00
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
| Permission | Why |
|
|
|
|
|
| ---------------- | ------------------------------------------------ |
|
|
|
|
|
| `activeTab` | Read the active tab's URL when the popup opens. |
|
|
|
|
|
| `clipboardWrite` | Implement the **Copy URL** button. |
|
2026-05-11 16:04:22 +02:00
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
## Publishing to AMO
|
2026-05-11 16:04:22 +02:00
|
|
|
|
2026-05-11 16:51:54 +02:00
|
|
|
The plan is to distribute Naiv-QR through
|
|
|
|
|
[addons.mozilla.org](https://addons.mozilla.org) as a **listed** extension.
|
|
|
|
|
With listed distribution Firefox handles auto-updates from AMO directly —
|
|
|
|
|
the manifest must therefore **not** declare `gecko.update_url`.
|
|
|
|
|
|
|
|
|
|
`bun run package` produces `web-ext-artifacts/naiv_qr-<ver>.zip` from a
|
|
|
|
|
clean checkout. The zip contains only the files in `dist/`:
|
2026-05-11 16:12:34 +02:00
|
|
|
|
|
|
|
|
```
|
|
|
|
|
dist/
|
|
|
|
|
manifest.json
|
|
|
|
|
LICENSE.qrcode-generator
|
|
|
|
|
icons/icon.svg
|
|
|
|
|
popup/popup.html
|
|
|
|
|
popup/popup.css
|
|
|
|
|
popup/popup.js (bundled, minified)
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-11 16:51:54 +02:00
|
|
|
For first-time AMO submission, use the web UI at
|
|
|
|
|
https://addons.mozilla.org/developers/addon/submit/ — upload the zip, then
|
|
|
|
|
fill in summary, description, screenshots, categories, and license (MIT).
|
|
|
|
|
Subsequent versions can be uploaded via the API with `web-ext sign
|
|
|
|
|
--channel listed` once you have AMO credentials.
|
|
|
|
|
|
|
|
|
|
For the AMO source-code review step (required because of the `bun build`
|
|
|
|
|
step), point the reviewer at this repository and the pinned upstream commit
|
|
|
|
|
of `vendor/qrcode.js` documented below.
|
2026-05-11 16:04:22 +02:00
|
|
|
|
|
|
|
|
## Third-party code
|
|
|
|
|
|
|
|
|
|
- `vendor/qrcode.js` — [kazuhikoarase/qrcode-generator](https://github.com/kazuhikoarase/qrcode-generator),
|
|
|
|
|
pinned to commit `83b7e8fe3fddd3b0368dbafd6ce56995bd25e3c8`. MIT licensed;
|
|
|
|
|
full license text in `vendor/LICENSE.qrcode-generator` (also copied into
|
|
|
|
|
`dist/` so it ships with every build).
|
|
|
|
|
|
2026-05-11 16:12:34 +02:00
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
This project is MIT-licensed; see [LICENSE](LICENSE).
|
|
|
|
|
|
2026-05-11 16:04:22 +02:00
|
|
|
## Project layout
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
manifest.json MV3 manifest
|
|
|
|
|
popup/ Toolbar popup source (HTML/CSS/JS)
|
|
|
|
|
vendor/qrcode.js Bundled QR encoder (MIT, vendored, imported by popup.js)
|
|
|
|
|
icons/icon.svg Toolbar icon (single SVG, used at every size)
|
|
|
|
|
scripts/build.mjs Bun build + asset copy → dist/
|
|
|
|
|
web-ext-config.cjs Tells web-ext to operate on dist/
|
|
|
|
|
dist/ Build output (gitignored). What ships.
|
|
|
|
|
```
|