Recently Written · git

subread-extension

Show your own .srt over a YouTube video, in time with it. Chrome and Firefox.

git clone https://github.com/equwal/subread-extension

Log | Files | Refs


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);
+  }
+});