commit 79f9302e8929e4d7156b580504de2ec1d68d3175 equwal <13551856+equwal@users.noreply.github.com> 2026-09-26 12:52:25 -0700 Add a More apps list to the popup and a More projects list to the README The add-on has no options page. The list is in a "More apps" section at the bottom of the popup. The section opens only on a click. Each link opens in a new tab. The manifest does not change. A new test checks the addresses, the link attributes and the permissions.
README.md | 27 ++++++++++++++++++++++++++- popup.html | 23 ++++++++++++++++++++++- tests/more-apps.test.cjs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md index 4fa8362..10e6291 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,9 @@ YouTube, and YouTube's terms do not allow it; this one only draws text. | `manifest.json` | Manifest V3, the same for both browsers (`browser_specific_settings` is for Firefox) | | `src/srt.js` | Reads .srt and .vtt; finds the cue for a moment. No browser APIs, so Node tests it | | `src/content.js`, `src/content.css` | The caption box, the **SR** button and the panel, inside `#movie_player` | -| `popup.html` | How to use it | +| `popup.html` | How to use it, and the links to the other apps | | `tests/srt.test.cjs` | `node --test tests/srt.test.cjs` | +| `tests/more-apps.test.cjs` | `node --test tests/more-apps.test.cjs` | | `tests/mock/server.py` | A stand-in for a watch page, to try the content script by hand | | `tools/pack.py` | Makes `dist/subread-chrome.zip` and `dist/subread-firefox.zip` | | `tools/graphics.py` | Makes the Chrome promo tiles in `store/graphics/` | @@ -48,6 +49,30 @@ YouTube, and YouTube's terms do not allow it; this one only draws text. Chrome: `chrome://extensions` > Developer mode > Load unpacked > this folder. Firefox: `about:debugging` > This Firefox > Load Temporary Add-on > `manifest.json`. +## More projects + +- [SubRead](https://subread.space/): read along with an audiobook, in the browser. + Also [for Android](https://github.com/equwal/subread-android/releases/latest), + [for YouTube](https://github.com/equwal/subread-extension/releases/latest) + and [for KOReader](https://github.com/equwal/subread.koplugin). +- [SubRead Overlay](https://github.com/equwal/subread-overlay/releases/latest): subtitle lines over any Android media player. +- [SubRead Dictionary](https://github.com/equwal/subread-dictionary/releases/latest): a pop-up dictionary for Android that reads Yomitan dictionaries. +- [SubRead Anki](https://github.com/equwal/subread-anki): one tap makes an Anki card from any Android app. +- [Subrep](https://github.com/equwal/subrep-android/releases/latest): live captions of the sound of your phone. +- [Book Simulator](https://booksimulator.com/): a reading room for Aozora Bunko and Project Gutenberg books. +- [honjimaku.com](https://honjimaku.com/): subtitles for Japanese audiobooks. +- [sbm Sync](https://sbmsync.com/): your bookmarks, the same on every device, + with [sbm](https://github.com/equwal/sbm) for dmenu, + [sbm for Android](https://github.com/equwal/sbm-android/releases/latest) + and the [sbm add-on](https://github.com/equwal/sbm-extension/releases/latest) for Firefox and Chrome. +- [Rebind](https://github.com/equwal/rebind/releases): remap the hardware buttons of e-ink readers and Android, + with [Ink Recents](https://github.com/equwal/ink-recents/releases/latest), + [Ink Dim](https://github.com/equwal/ink-dim/releases/latest) + and [Ink Update](https://github.com/equwal/ink-update/releases/latest). +- [dickt.store](https://dickt.store/): language-learning tools, flashcards and web toys. +- [hentaibun.online](https://hentaibun.online/): learn kanbun and kobun. +- [Recently Written](https://recentlywritten.com/): the blog, and a list of [all projects](https://recentlywritten.com/projects.html). + ## Licence AGPL-3.0: see `LICENSE`. diff --git a/popup.html b/popup.html index e2c41cb..4f37fc9 100644 --- a/popup.html +++ b/popup.html @@ -4,9 +4,10 @@ <style> body { width: 320px; margin: 0; padding: 14px 16px; font: 14px/1.45 system-ui, Arial, sans-serif; color: #111; background: #fff; } h1 { font-size: 16px; margin: 0 0 8px; } - ol { padding-left: 20px; margin: 8px 0; } + ol, ul { padding-left: 20px; margin: 8px 0; } li { margin: 5px 0; } p { margin: 8px 0; } + summary { cursor: pointer; } .muted { color: #555; font-size: 12.5px; } @media (prefers-color-scheme: dark) { body { background: #1b1b1f; color: #eee; } .muted { color: #aaa; } a { color: #9cc3ff; } } </style> @@ -22,3 +23,23 @@ <p class="muted">The extension does not read, record or download the video. Your subtitles stay in this browser. Open source: <a href="https://github.com/equwal/subread-extension" target="_blank" rel="noopener">GitHub</a>. Free, no ads: <a href="https://ko-fi.com/truex" target="_blank" rel="noopener">support it on Ko-fi</a>.</p> +<details id="more-apps" class="muted"> + <summary>More apps</summary> + <ul> + <li><a href="https://subread.space/" target="_blank" rel="noopener">SubRead</a><br>Read along with an audiobook.</li> + <li><a href="https://booksimulator.com/" target="_blank" rel="noopener">Book Simulator</a><br>A reading room for Aozora Bunko and Project Gutenberg books.</li> + <li><a href="https://honjimaku.com/" target="_blank" rel="noopener">honjimaku.com</a><br>Subtitles for Japanese audiobooks.</li> + <li><a href="https://sbmsync.com/" target="_blank" rel="noopener">sbm Sync</a><br>Your bookmarks, the same on every device.</li> + <li><a href="https://github.com/equwal/subread-android/releases/latest" target="_blank" rel="noopener">SubRead for Android</a><br>Times an audiobook against its ebook on the device.</li> + <li><a href="https://github.com/equwal/subread-overlay/releases/latest" target="_blank" rel="noopener">SubRead Overlay</a><br>Subtitle lines over any Android media player.</li> + <li><a href="https://github.com/equwal/subread-dictionary/releases/latest" target="_blank" rel="noopener">SubRead Dictionary</a><br>Pop-up dictionary that reads Yomitan dictionaries.</li> + <li><a href="https://github.com/equwal/subread-anki" target="_blank" rel="noopener">SubRead Anki</a><br>One tap makes an Anki card from any app.</li> + <li><a href="https://github.com/equwal/subrep-android/releases/latest" target="_blank" rel="noopener">Subrep</a><br>Live captions of the sound of your phone.</li> + <li><a href="https://github.com/equwal/sbm-android/releases/latest" target="_blank" rel="noopener">sbm for Android</a><br>Fuzzy search for your bookmarks.</li> + <li><a href="https://github.com/equwal/rebind/releases" target="_blank" rel="noopener">Rebind</a><br>Remap the hardware buttons of e-ink readers and Android.</li> + <li><a href="https://github.com/equwal/ink-recents/releases/latest" target="_blank" rel="noopener">Ink Recents</a><br>A recent-apps switcher for e-ink.</li> + <li><a href="https://github.com/equwal/ink-dim/releases/latest" target="_blank" rel="noopener">Ink Dim</a><br>Frontlight below the lowest system level.</li> + <li><a href="https://github.com/equwal/ink-update/releases/latest" target="_blank" rel="noopener">Ink Update</a><br>Tells you when Rebind and its extensions update.</li> + <li><a href="https://recentlywritten.com/projects.html" target="_blank" rel="noopener">All projects</a><br>Everything, with source code.</li> + </ul> +</details> diff --git a/tests/more-apps.test.cjs b/tests/more-apps.test.cjs new file mode 100644 index 0000000..a20f0b5 --- /dev/null +++ b/tests/more-apps.test.cjs @@ -0,0 +1,45 @@ +/* node --test tests/more-apps.test.cjs */ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const read = (file) => fs.readFileSync(path.join(__dirname, '..', file), 'utf8'); + +// The links in the "More apps" section of the popup: the name, the address and the attributes of each. +function moreApps() { + const section = read('popup.html').match(/<details id="more-apps"[^>]*>([\s\S]*?)<\/details>/); + assert.ok(section, 'popup.html has no More apps section'); + return [...section[1].matchAll(/<a ([^>]*)>([^<]*)<\/a>/g)].map(([, attrs, name]) => ({ + name, + attrs, + href: (attrs.match(/href="([^"]*)"/) || [])[1], + })); +} + +test('More apps links to the other apps with https, each in a new tab', () => { + const apps = moreApps(); + assert.ok(apps.length > 1); + for (const app of apps) { + assert.match(app.href, /^https:\/\/[^?#]+$/, app.name); // no tracking parameters + assert.match(app.attrs, /target="_blank"/, app.name); + assert.match(app.attrs, /rel="noopener"/, app.name); + } + assert.equal(new Set(apps.map((app) => app.href)).size, apps.length); + assert.equal(apps.at(-1).href, 'https://recentlywritten.com/projects.html'); +}); + +test('More apps does not list the add-on itself', () => { + for (const app of moreApps()) assert.doesNotMatch(app.href, /subread-extension/, app.name); +}); + +test('the add-on asks for no new permission and puts no link into YouTube pages', () => { + const manifest = JSON.parse(read('manifest.json')); + assert.deepEqual(manifest.permissions, ['storage']); + for (const key of ['host_permissions', 'optional_permissions', 'optional_host_permissions']) { + assert.equal(manifest[key], undefined, key); + } + for (const script of manifest.content_scripts) { + for (const file of script.js) assert.doesNotMatch(read(file), /More apps|recentlywritten\.com/, file); + } +});