commit ded68097d271952a8813f842cdfec90c5e3c5bb8 equwal <13551856+equwal@users.noreply.github.com> 2026-09-26 12:58:07 -0700 Add a More apps menu entry with the addresses of the other apps The SubRead menu has a new last entry, "More apps". For each app, a TextViewer shows the name, one line about the app and the address. KOReader cannot open a web browser on each device, so the addresses are text. The list is in subread/more_apps.lua, and a new spec tests it. The README has a More projects list.
README.md | 24 ++++++++++++++++++++ main.lua | 18 +++++++++++++++ spec/more_apps_spec.lua | 33 ++++++++++++++++++++++++++++ spec/run.lua | 2 +- subread/more_apps.lua | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md index a97a19a..b8ef084 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,30 @@ Without a Lua interpreter with busted: lua spec/run.lua ``` +## 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-or-later. See `LICENSE`. KOReader is AGPL-3.0, and this plugin is diff --git a/main.lua b/main.lua index 53bce65..ee17b41 100644 --- a/main.lua +++ b/main.lua @@ -34,6 +34,7 @@ local T = require("ffi/util").template local Clock = require("subread.clock") local Cues = require("subread.cues") +local MoreApps = require("subread.more_apps") local PlayerState = require("subread.player_state") local Srt = require("subread.srt") local Text = require("subread.text") @@ -970,6 +971,17 @@ function SubRead:showNotification(text) UIManager:show(Notification:new{ text = text, timeout = 2 }) end +--- Shows the other apps of the author. The list is long, so a TextViewer +--- shows it. KOReader cannot open a web browser on each device, so the +--- addresses are text. +function SubRead:showMoreApps() + local TextViewer = require("ui/widget/textviewer") + UIManager:show(TextViewer:new{ + title = _("More apps"), + text = MoreApps.text(), + }) +end + function SubRead:statusText() local now = self:_now() local clock_text = datetime.secondsToClock(self.clock:getPosition(now), false) @@ -1260,6 +1272,12 @@ function SubRead:addToMainMenu(menu_items) self:showOffset() if touchmenu_instance then touchmenu_instance:updateItems() end end, + separator = true, + }) + table.insert(items, { + text = _("More apps"), + keep_menu_open = true, + callback = function() self:showMoreApps() end, }) end diff --git a/spec/more_apps_spec.lua b/spec/more_apps_spec.lua new file mode 100644 index 0000000..bd0e29d --- /dev/null +++ b/spec/more_apps_spec.lua @@ -0,0 +1,33 @@ +local MoreApps = require("subread.more_apps") + +describe("MoreApps", function() + it("gives each app a name, a line and an https address without parameters", function() + assert.is_true(#MoreApps.list > 1) + local seen = {} + for _, app in ipairs(MoreApps.list) do + assert.is_true(type(app.name) == "string" and app.name ~= "") + assert.is_true(type(app.line) == "string" and app.line ~= "") + -- No query and no fragment, so no tracking parameters. + assert.is_not_nil(app.url:match("^https://[^?#%s]+$")) + assert.is_nil(seen[app.url]) + seen[app.url] = true + end + end) + + it("does not list the plugin itself", function() + for _, app in ipairs(MoreApps.list) do + assert.is_nil(app.url:find("subread.koplugin", 1, true)) + end + end) + + it("ends with the list of all projects", function() + assert.equals("https://recentlywritten.com/projects.html", MoreApps.list[#MoreApps.list].url) + end) + + it("shows the name, the line and the address of each app as text", function() + local text = MoreApps.text() + for _, app in ipairs(MoreApps.list) do + assert.is_not_nil(text:find(app.name .. "\n" .. app.line .. "\n" .. app.url, 1, true)) + end + end) +end) diff --git a/spec/run.lua b/spec/run.lua index 78d7db8..31c071e 100644 --- a/spec/run.lua +++ b/spec/run.lua @@ -38,7 +38,7 @@ function _G.it(name, body) end _G.setup, _G.teardown, _G.before_each, _G.after_each = function(f) f() end, function() end, nil, nil -for _, name in ipairs({ "text", "srt", "cues", "clock", "player_state", "languages" }) do +for _, name in ipairs({ "text", "srt", "cues", "clock", "player_state", "languages", "more_apps" }) do dofile(here .. name .. "_spec.lua") end print(string.format("%d passed, %d failed", passed, failed)) diff --git a/subread/more_apps.lua b/subread/more_apps.lua new file mode 100644 index 0000000..d621432 --- /dev/null +++ b/subread/more_apps.lua @@ -0,0 +1,58 @@ +--[[-- +The other apps and sites of the author, for the "More apps" entry of the +menu. + +Pure Lua. No KOReader dependency, so it can be unit tested on a PC. + +KOReader cannot open a web browser on each device, so the menu shows each +address as text. +--]]-- + +local MoreApps = {} + +-- The order of the link catalog: the sites, then the Android apps, then the +-- list of all projects. The plugin itself is not in the list. +MoreApps.list = { + { name = "SubRead", url = "https://subread.space/", + line = "Read along with an audiobook." }, + { name = "Book Simulator", url = "https://booksimulator.com/", + line = "A reading room for Aozora Bunko and Project Gutenberg books." }, + { name = "honjimaku.com", url = "https://honjimaku.com/", + line = "Subtitles for Japanese audiobooks." }, + { name = "sbm Sync", url = "https://sbmsync.com/", + line = "Your bookmarks, the same on every device." }, + { name = "SubRead for Android", url = "https://github.com/equwal/subread-android/releases/latest", + line = "Times an audiobook against its ebook on the device." }, + { name = "SubRead Overlay", url = "https://github.com/equwal/subread-overlay/releases/latest", + line = "Subtitle lines over any Android media player." }, + { name = "SubRead Dictionary", url = "https://github.com/equwal/subread-dictionary/releases/latest", + line = "Pop-up dictionary that reads Yomitan dictionaries." }, + { name = "SubRead Anki", url = "https://github.com/equwal/subread-anki", + line = "One tap makes an Anki card from any app." }, + { name = "Subrep", url = "https://github.com/equwal/subrep-android/releases/latest", + line = "Live captions of the sound of your phone." }, + { name = "sbm for Android", url = "https://github.com/equwal/sbm-android/releases/latest", + line = "Fuzzy search for your bookmarks." }, + { name = "Rebind", url = "https://github.com/equwal/rebind/releases", + line = "Remap the hardware buttons of e-ink readers and Android." }, + { name = "Ink Recents", url = "https://github.com/equwal/ink-recents/releases/latest", + line = "A recent-apps switcher for e-ink." }, + { name = "Ink Dim", url = "https://github.com/equwal/ink-dim/releases/latest", + line = "Frontlight below the lowest system level." }, + { name = "Ink Update", url = "https://github.com/equwal/ink-update/releases/latest", + line = "Tells you when Rebind and its extensions update." }, + { name = "All projects", url = "https://recentlywritten.com/projects.html", + line = "Everything, with source code." }, +} + +--- Returns the list as plain text: the name, the line and the address of +--- each app, with an empty line between two apps. +function MoreApps.text() + local blocks = {} + for _, app in ipairs(MoreApps.list) do + blocks[#blocks + 1] = app.name .. "\n" .. app.line .. "\n" .. app.url + end + return table.concat(blocks, "\n\n") +end + +return MoreApps