Community程式設計與開發github.com

aknahin/koplugin-skill

在KOReader(Kobo、Kindle、PocketBook、Android)及桌面模擬器中建置、檢視、除錯及驗證KOReader外掛(*.koplugin, Lua)。當使用者提及KOReader、koplugin、koreader外掛、_meta.lua、WidgetContainer、registerToMainMenu、addToMainMenu、Dispatcher動作、UIManager、InfoMessage/InputDialog/Menu小工具、./kodev,或想為KOReader新增功能/選單項目/手勢動作時使用——即使他們只說「e-reader外掛」或「為我的Kobo/Kindle做點東西」。包含一個外掛產生器與靜態驗證器,可檢查外掛結構、語法及luacheck。

koplugin-skill 是什麼?

這個技能(aknahin/koplugin-skill)專注於KOReader外掛開發,提供完整的工作流程:從產生新外掛專案、實作功能到靜態驗證。KOReader是一個基於LuaJIT的電子閱讀器軟體,其外掛由一個帶有`main.lua`和`_meta.lua`的目錄組成。技能內建`new_plugin.py`產生器可快速建立外掛骨架,並使用`validate_plugin.py`腳本進行結構、語法和luacheck檢查。支援的平台包括Kobo、Kindle、PocketBook、Android以及桌面模擬器。參考文件中還包含了UI小工具、事件與生命週期、設定儲存、測試與驗證、以及常見陷阱等主題,幫助開發者避免常見錯誤。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/aknahin/koplugin-skill/tree/HEAD/skills/koplugin-skill

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

KOReader 外掛開發

KOReader 的前端是純 Lua(LuaJIT),運行在一個小型 widget 工具集上。外掛是一個名為 <名稱>.koplugin/ 的目錄,內含 main.lua(程式碼)與 _meta.lua(顯示資訊)。沒有建置步驟,因此最快正確的方式是遵循以下經過驗證的慣例,並使用附帶的腳本進行驗證,而不是猜測 API。

官方文件:https://koreader.rocks/doc/(API 參考,從原始碼產生)以及原始碼位於 https://github.com/koreader/koreader(`plugins/` 中有約 40 個真實外掛;plugins/hello.koplugin 是最簡範例)。若對某個函式簽章不確定,請查閱該模組的文件頁面或直接閱讀原始碼——KOReader API 在不同版本間會有變動,而文件網站是權威來源。

工作流程

  1. 產生骨架(新外掛):python3 <skill>/scripts/new_plugin.py <id> "<Full Name>" [--dir <where>] 會從範本建立 <id>.koplugin/,包含選單項目、Dispatcher 動作、設定持久化與生命週期鉤子。從此處開始編輯,而非從頭撰寫。
  2. 實作:使用本文件中的模式;只在必要時才開啟參考檔案:
    • references/plugin-anatomy.md — 檔案佈局、載入器規則、is_doc_only、初始化順序、選單 sorting_hint 值
    • references/ui-widgets.md — InfoMessage、ConfirmBox、InputDialog、MultiInputDialog、Menu、ButtonDialog、SpinWidget、Notification,附有可運作的程式碼片段
    • references/events-and-lifecycle.md — onXxx 處理器、廣播、排程、Dispatcher、閱讀器與檔案管理員上下文
    • references/settings-and-storage.md — LuaSettings、G_reader_settings、每本書的文件設定、DataStorage 路徑
    • references/testing-and-verification.md — 模擬器(./kodev run)、luacheck、busted 規格、裝置端安裝與日誌讀取
    • references/pitfalls.md — 導致 KOReader 崩潰或無聲失效的錯誤;完成前請瀏覽一遍
  3. 驗證(務必在聲明可運作前執行):python3 <skill>/scripts/validate_plugin.py <path/to/x.koplugin>。它會檢查結構、_meta.lua、選單接線、gettext 使用情況、LuaJIT 語法(luajit -bl)以及 luacheck(若這些工具存在)。修正每個 ERROR;閱讀每個 WARN。然後誠實報告哪些已驗證、哪些未驗證——驗證器無法證明執行階段行為;只有模擬器或實際裝置可以(請參閱測試參考)。

最小可行外掛

-- _meta.lua
local _ = require("gettext")
return {
    fullname = _("My Plugin"),
    description = _([[在 Plugin management 中顯示的一行說明。]]),
}
-- main.lua
local Dispatcher = require("dispatcher")
local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local _ = require("gettext")

local MyPlugin = WidgetContainer:extend{
    name = "myplugin",        -- 唯一 ID,與目錄前綴相符
    is_doc_only = false,      -- true = 僅在書籍開啟時載入
}

function MyPlugin:onDispatcherRegisterActions()
    Dispatcher:registerAction("myplugin_hello", {
        category = "none", event = "MyPluginHello",
        title = _("My Plugin: hello"), general = true,
    })
end

function MyPlugin:init()
    self:onDispatcherRegisterActions()
    self.ui.menu:registerToMainMenu(self)
end

function MyPlugin:addToMainMenu(menu_items)
    menu_items.myplugin = {
        text = _("My Plugin"),
        sorting_hint = "more_tools",
        callback = function() self:onMyPluginHello() end,
    }
end

function MyPlugin:onMyPluginHello()
    UIManager:show(InfoMessage:new{ text = _("Hello from my plugin") })
    return true  -- 事件已消耗
end

return MyPlugin

重要的規則(及其原因)

  • 將每個使用者可見的字串包裹在 _() 中 — KOReader 透過 gettext 進行翻譯;未包裹的字串永遠無法本地化。使用 T = require("ffi/util").template 進行插值(T(_("Page %1"), n)),而非 .. 串聯,因為不同語言的詞序不同。
  • 事件是名為 on<EventName> 的方法;僅在消耗事件時返回 true,否則其他 widget(包括 KOReader 核心)將無法收到它。
  • 絕不阻塞 UI 執行緒。 網路呼叫、大型檔案掃描與 os.execute 會凍結單執行緒的電子墨水裝置。對於長時間工作,請使用 NetworkMgr:runWhenOnline、UIManager:scheduleIn、Trapper:wrap(子程序 + 進度)。
  • 使用 LuaSettings 進行持久化,並刻意呼叫 :flush()。 設定檔位於 DataStorage:getSettingsDir() 中;在變更後(或在 onFlushSettings 中)呼叫 :flush() — 崩潰或電量耗盡否則會遺失它們。
  • 清理計時器(UIManager:unschedule(fn))以及你在 onCloseWidget/onCloseDocument 中開啟的 widget;洩漏的排程回呼會持續喚醒裝置並耗電。
  • 電子墨水感知 UI:偏好使用帶有 timeout 的 InfoMessage,避免動畫,保持最小程度的重新整理(僅在變更的 widget 上呼叫 UIManager:setDirty(widget, "ui"))。
  • 使用 logger(require("logger");logger.dbg/info/warn/err)而非 print;輸出會傳送到模擬器中的 stdout 以及裝置上的 crash.log。
  • 保護可選依賴(self.ui.statistics、self.ui.document、Device:hasFrontlight()):同一個外掛可能在檔案管理員或缺少該功能的裝置上載入。

回報結果

說明你建立/更改了什麼、確切的驗證指令及其結果,以及哪些部分尚未驗證(例如「未在模擬器中執行」)。如果驗證器或 luacheck 不可用,請如實說明,而非暗示通過檢查。

相關技能