commit 4ed7bb20d16ada2ef368bb2b8cd26d2244cd9b18 equwal <13551856+equwal@users.noreply.github.com> 2026-09-26 12:55:37 -0700 Add a More apps section with links to the other apps The options page shows the list in a "More apps" section below the sync settings. The popup footer has one "More apps" link that opens this section in a new tab. The README has a More projects list. The manifest does not change. A new test checks the addresses, the link attributes and the permissions.
README.md | 24 ++++++++++++++++++++++++ options.html | 21 +++++++++++++++++++++ popup.html | 2 +- style.css | 1 + test/more-apps.test.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md index 4ad4d18..be63764 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,30 @@ The two zips have the same files. Only the manifest.json of the Firefox zip has no `background.service_worker`: Firefox ignores it and warns about it, and Chrome needs it. +## 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). + ## License AGPL-3.0. If sbm is useful to you, you can support it on diff --git a/options.html b/options.html index 64ce1cb..8f10b4f 100644 --- a/options.html +++ b/options.html @@ -39,6 +39,27 @@ other browsers.</p> <p id="message" role="status"></p> +<section id="more-apps"> + <h2>More apps</h2> + <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> +</section> + <footer> <a href="https://github.com/equwal/sbm-extension" target="_blank" rel="noopener">Source code</a> · <a href="https://sbmsync.com/privacy" target="_blank" rel="noopener">Privacy</a> · diff --git a/popup.html b/popup.html index 1c570a7..3975674 100644 --- a/popup.html +++ b/popup.html @@ -21,7 +21,7 @@ <footer> <button id="add-page" type="button">Add this page</button> <span id="status" class="note"></span> - <nav><a href="#" id="options">Options</a> · <a href="https://ko-fi.com/truex" target="_blank" rel="noopener">Support sbm on Ko-fi</a></nav> + <nav><a href="#" id="options">Options</a> · <a href="options.html#more-apps" target="_blank" rel="noopener">More apps</a> · <a href="https://ko-fi.com/truex" target="_blank" rel="noopener">Support sbm on Ko-fi</a></nav> </footer> <script src="lib/tsv.js"></script> <script src="lib/fuzzy.js"></script> diff --git a/style.css b/style.css index 50adfe8..7b2ecb7 100644 --- a/style.css +++ b/style.css @@ -20,4 +20,5 @@ label { display: block; } .options h1 { color: var(--accent); } .options section { margin: 1.5em 0; } .options #billing button { margin: 0 6px 6px 0; } +.options #more-apps li { margin-bottom: 6px; color: var(--muted); } .options footer { margin-top: 3em; font-size: 13px; color: var(--muted); } diff --git a/test/more-apps.test.js b/test/more-apps.test.js new file mode 100644 index 0000000..4168883 --- /dev/null +++ b/test/more-apps.test.js @@ -0,0 +1,50 @@ +"use strict"; +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 options page: the name, the +// address and the attributes of each. +function moreApps() { + const section = read("options.html").match(/<section id="more-apps">([\s\S]*?)<\/section>/); + assert.ok(section, "options.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, /sbm-extension/, app.name); +}); + +test("the popup has one More apps link, to the section of the options page", () => { + const links = read("popup.html").match(/<a [^>]*>More apps<\/a>/g) || []; + assert.equal(links.length, 1); + assert.match(links[0], /href="options\.html#more-apps"/); + assert.match(links[0], /target="_blank"/); + assert.match(links[0], /rel="noopener"/); +}); + +test("the add-on asks for no new permission and puts nothing into web pages", () => { + const manifest = JSON.parse(read("manifest.json")); + assert.deepEqual(manifest.permissions, ["storage", "activeTab", "alarms"]); + assert.deepEqual(manifest.optional_host_permissions, ["https://*/*", "http://*/*"]); + assert.equal(manifest.content_scripts, undefined); +});