CommunityCodierung & Entwicklunggithub.com

aknahin/koplugin-skill

Entwicklung, Überprüfung, Fehlerbehebung und Verifizierung von KOReader-Plugins (*.koplugin, Lua) für E-Reader (Kobo, Kindle, PocketBook, Android) und den Desktop-Emulator.

Was ist koplugin-skill?

Dieses Skill ermöglicht die vollständige Entwicklung von KOReader-Plugins auf Basis von Lua und dem koreader-Framework. Es bietet ein strukturiertes Workflow-Management mit Scaffolding (automatische Plugin-Generierung), Implementierungshilfen durch detaillierte Referenzdokumente (UI-Widgets, Events, Einstellungen, Tests) und einer statischen Validierung mittels eines speziell entwickelten Validierungsskripts (inklusive Syntax- und Luacheck-Prüfung). Ziel ist es, Fehler frühzeitig zu erkennen und die Kompatibilität mit den Konventionen der KOReader-Plattform sicherzustellen. Unterstützt werden alle gängigen E-Reader (Kobo, Kindle, PocketBook, Android) sowie der Desktop-Emulator, sodass Plugins sowohl für reale Geräte als auch für Entwicklungstests optimiert werden können. Der Skill stellt sicher, dass Entwickler die bewährten Praktiken wie gettext-Lokalisierung, ereignisgesteuerte Programmierung und ressourcenschonende UI-Interaktionen auf e‑Ink-Geräten korrekt umsetzen. Zudem werden typische Fallstricke dokumentiert, die bei der Plugin-Entwicklung auftreten können (z. B. UI-Thread-Blockierung, fehlende Bereinigung von Timern).

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/aknahin/koplugin-skill/tree/HEAD/skills/koplugin-skill

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

KOReader Plugin-Entwicklung

Die Oberfläche von KOReader basiert auf einfachem Lua (LuaJIT) mit einem kleinen Widget-Toolkit. Ein Plugin ist ein Verzeichnis namens <name>.koplugin/ mit einem main.lua (Code) und einer _meta.lua (Anzeigeinfo). Es gibt keinen Build-Schritt, daher ist der schnellste Weg, korrekt zu sein, die nachfolgenden bewährten Konventionen zu befolgen und mit den mitgelieferten Skripten zu überprüfen, anstatt APIs zu erraten.

Offizielle Dokumentation: https://koreader.rocks/doc/ (API-Referenz, aus dem Quellcode generiert) und der Quellcode unter https://github.com/koreader/koreader (plugins/ enthält ~40 echte Plugins; plugins/hello.koplugin ist das minimale Beispiel). Bei Unsicherheit über eine Signatur prüfen Sie die Dokumentationsseite des Moduls oder lesen Sie den Quellcode – KOReader-APIs ändern sich zwischen Releases, und die Dokumentationsseite hat Vorrang vor dem Gedächtnis.

Arbeitsablauf

  1. Scaffold (neues Plugin): python3 <skill>/scripts/new_plugin.py <id> "<Vollständiger Name>" [--dir <wo>] erstellt <id>.koplugin/ aus der Vorlage mit Menüeintrag, Dispatcher-Aktion, Einstellungspersistenz und Lebenszyklus-Hooks. Bearbeiten Sie von dort aus, anstatt von Grund auf zu schreiben.
  2. Implementieren Sie mit den Mustern in dieser Datei; öffnen Sie die Referenzdateien nur, wenn Sie sie benötigen:
    • references/plugin-anatomy.md – Dateiaufbau, Laderregeln, is_doc_only, Initialisierungsreihenfolge, Menü-sorting_hint-Werte
    • references/ui-widgets.md – InfoMessage, ConfirmBox, InputDialog, MultiInputDialog, Menu, ButtonDialog, SpinWidget, Notification mit funktionierenden Codeausschnitten
    • references/events-and-lifecycle.md – onXxx-Handler, Broadcasting, Scheduling, Dispatcher, Reader- vs. Dateimanager-Kontext
    • references/settings-and-storage.md – LuaSettings, G_reader_settings, dokumentbezogene Einstellungen, DataStorage-Pfade
    • references/testing-and-verification.md – Emulator (./kodev run), luacheck, busted-Spezifikationen, Installation auf dem Gerät und Log-Lesen
    • references/pitfalls.md – Fehler, die KOReader zum Absturz bringen oder stillschweigend nichts bewirken; überfliegen Sie diese vor dem Abschluss
  3. Verifizieren (immer, bevor Sie sagen, dass es funktioniert): python3 <skill>/scripts/validate_plugin.py <pfad/zu/x.koplugin>. Es prüft Struktur, _meta.lua, Menüverdrahtung, gettext-Verwendung, LuaJIT-Syntax (luajit -bl) und luacheck, falls diese Tools vorhanden sind. Beheben Sie jeden FEHLER; lesen Sie jede WARNUNG. Sagen Sie dann ehrlich, was verifiziert wurde und was nicht – der Validator kann das Laufzeitverhalten nicht beweisen; das kann nur der Emulator oder ein Gerät (siehe Testreferenz).

Das minimal lebensfähige Plugin

-- _meta.lua
local _ = require("gettext")
return {
    fullname = _("Mein Plugin"),
    description = _([[Ein Satz, der in der Plugin-Verwaltung angezeigt wird.]]),
}
-- 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",        -- eindeutige ID, entspricht dem Verzeichnis-Präfix
    is_doc_only = false,      -- true = nur geladen, während ein Buch geöffnet ist
}

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

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

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

function MyPlugin:onMyPluginHello()
    UIManager:show(InfoMessage:new{ text = _("Hallo von meinem Plugin") })
    return true  -- Ereignis verarbeitet
end

return MyPlugin

Wichtige Regeln (und warum)

  • Jeden benutzersichtbaren Text in _() kapseln – KOReader wird über gettext übersetzt; nicht gekapselte Zeichenfolgen können nie lokalisiert werden. Verwenden Sie T = require("ffi/util").template für Interpolation (T(_("Seite %1"), n)), nicht ..-Verkettung, da die Wortreihenfolge sprachabhängig ist.
  • Ereignisse sind Methoden namens on<EreignisName>; geben Sie true nur zurück, wenn Sie das Ereignis verarbeitet haben, andernfalls empfangen andere Widgets (einschließlich des KOReader-Kerns) es nicht mehr.
  • Blockieren Sie niemals den UI-Thread. Netzwerkaufrufe, große Dateiscans und os.execute frieren ein single-threaded e‑Ink-Gerät ein. Verwenden Sie NetworkMgr:runWhenOnline, UIManager:scheduleIn, Trapper:wrap (Unterprozess + Fortschritt) für langwierige Arbeiten.
  • Persistieren Sie mit LuaSettings, leeren Sie bewusst. Einstellungsdateien befinden sich in DataStorage:getSettingsDir(); rufen Sie :flush() nach Änderungen auf (oder in onFlushSettings) – ein Absturz oder eine entladene Batterie würde sie sonst verlieren.
  • Bereinigen Sie Timer (UIManager:unschedule(fn)) und Widgets, die Sie in onCloseWidget/onCloseDocument geöffnet haben; undichte geplante Callbacks wecken das Gerät ständig auf und entladen den Akku.
  • E‑Ink-bewusste UI: Bevorzugen Sie InfoMessage mit timeout, vermeiden Sie Animationen, halten Sie Aktualisierungen minimal (UIManager:setDirty(widget, "ui") nur auf dem geänderten Widget).
  • Verwenden Sie logger (require("logger"); logger.dbg/info/warn/err) anstelle von print; die Ausgabe geht im Emulator auf stdout und auf dem Gerät in crash.log.
  • Schützen Sie optionale Abhängigkeiten (self.ui.statistics, self.ui.document, Device:hasFrontlight()): dasselbe Plugin kann im Dateimanager oder auf Geräten ohne das entsprechende Feature geladen werden.

Rückmeldung

Geben Sie an, was Sie erstellt/geändert haben, den genauen Validator-Befehl und sein Ergebnis sowie was unverifiziert bleibt (z. B. „nicht im Emulator ausgeführt“). Wenn der Validator oder luacheck nicht verfügbar war, sagen Sie dies, anstatt eine saubere Prüfung zu suggerieren.

Verwandte Skills