aknahin/koplugin-skill

Crie, revise, depure e verifique plugins KOReader (*.koplugin, Lua) para e-readers (Kobo, Kindle, PocketBook, Android) e o emulador desktop. Use sempre que o usuário mencionar KOReader, koplugin, plugin koreader, _meta.lua, WidgetContainer, registerToMainMenu, addToMainMenu, ações Dispatcher, UIManager, widgets InfoMessage/InputDialog/Menu, ./kodev, ou quiser adicionar uma funcionalidade/item de menu/ação gestual ao KOReader — mesmo que diga apenas "plugin para e-reader" ou "faça algo para meu Kobo/Kindle". Inclui um gerador de esqueleto e um validador estático para estrutura, sintaxe e luacheck do plugin.

O que é koplugin-skill?

Esta skill é um assistente especializado para desenvolvimento de plugins KOReader, fornecendo um guia completo com convenções, referências e scripts de automação. Ela permite gerar a estrutura básica de um novo plugin, implementar funcionalidades usando padrões testados e verificar a correção do código com um validador estático. A skill é útil para quem deseja adicionar novos menus, ações do Dispatcher ou qualquer funcionalidade ao KOReader, suportando plataformas como Kobo, Kindle, PocketBook e Android, além do emulador desktop. O assistente se baseia nas documentações oficiais e scripts incluídos para garantir que o plugin siga as melhores práticas do ecossistema KOReader.

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

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Desenvolvimento de plugins KOReader

A interface do KOReader é Lua pura (LuaJIT) executando em um pequeno toolkit de widgets. Um plugin é um diretório chamado <nome>.koplugin/ com um main.lua (código) e _meta.lua (informações de exibição). Não há etapa de compilação, então a maneira mais rápida de estar correto é seguir as convenções comprovadas abaixo e verificar com os scripts fornecidos, em vez de adivinhar as APIs.

Documentação oficial: https://koreader.rocks/doc/ (referência da API, gerada a partir do código-fonte) e o código-fonte em https://github.com/koreader/koreader (plugins/ tem cerca de 40 plugins reais; plugins/hello.koplugin é o mínimo). Quando estiver em dúvida sobre uma assinatura, consulte a página de documentação do módulo ou leia o código-fonte — as APIs do KOReader mudam entre versões, e o site de documentação é a autoridade sobre a memória.

Fluxo de trabalho

  1. Gerar esqueleto (novo plugin): python3 <skill>/scripts/new_plugin.py <id> "<Nome Completo>" [--dir <onde>] cria <id>.koplugin/ a partir do modelo com entrada de menu, ação Dispatcher, persistência de configurações e hooks de ciclo de vida configurados. Edite a partir daí em vez de escrever do zero.
  2. Implementar usando os padrões deste arquivo; abra os arquivos de referência apenas quando necessário:
    • references/plugin-anatomy.md — layout de arquivos, regras do carregador, is_doc_only, ordem de inicialização, valores sorting_hint do menu
    • references/ui-widgets.md — InfoMessage, ConfirmBox, InputDialog, MultiInputDialog, Menu, ButtonDialog, SpinWidget, Notification, com trechos funcionais
    • references/events-and-lifecycle.md — manipuladores onXxx, transmissão, agendamento, Dispatcher, contexto de leitor vs gerenciador de arquivos
    • references/settings-and-storage.md — LuaSettings, G_reader_settings, configurações de documento por livro, caminhos do DataStorage
    • references/testing-and-verification.md — emulador (./kodev run), luacheck, especificações busted, instalação no dispositivo e leitura de log
    • references/pitfalls.md — erros que travam o KOReader ou não fazem nada silenciosamente; leia antes de finalizar
  3. Verificar (sempre, antes de dizer que funciona): python3 <skill>/scripts/validate_plugin.py <caminho/para/x.koplugin>. Ele verifica estrutura, _meta.lua, configuração de menu, uso de gettext, sintaxe LuaJIT (luajit -bl) e luacheck quando essas ferramentas existem. Corrija cada ERRO; leia cada AVISO. Depois, diga honestamente o que foi e o que não foi verificado — o validador não pode provar o comportamento em tempo de execução; apenas o emulador ou um dispositivo pode (consulte a referência de teste).

O plugin mínimo viável

-- _meta.lua
local _ = require("gettext")
return {
    fullname = _("Meu Plugin"),
    description = _([[Uma frase mostrada no Gerenciamento de plugins.]]),
}
-- 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 único, corresponde ao prefixo do diretório
    is_doc_only = false,      -- true = carregado apenas enquanto um livro está aberto
}

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

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

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

function MyPlugin:onMyPluginHello()
    UIManager:show(InfoMessage:new{ text = _("Olá do meu plugin") })
    return true  -- evento consumido
end

return MyPlugin

Regras que importam (e por quê)

  • Coloque toda string visível ao usuário em _() — O KOReader é traduzido via gettext; strings não encapsuladas nunca podem ser localizadas. Use T = require("ffi/util").template para interpolação (T(_("Página %1"), n)), não concatenação .., porque a ordem das palavras difere por idioma.
  • Eventos são métodos nomeados on<NomeDoEvento>; retorne true apenas quando você consumiu o evento, caso contrário outros widgets (incluindo o núcleo do KOReader) param de recebê-lo.
  • Nunca bloqueie a thread da interface. Chamadas de rede, varreduras de arquivos grandes e os.execute congelam um dispositivo de tinta eletrônica de thread única. Use NetworkMgr:runWhenOnline, UIManager:scheduleIn, Trapper:wrap (subprocesso + progresso) para trabalhos longos.
  • Persista com LuaSettings, descarregue deliberadamente. Os arquivos de configuração ficam em DataStorage:getSettingsDir(); chame :flush() após alterações (ou em onFlushSettings) — uma falha ou descarga de bateria, caso contrário, os perde.
  • Limpe temporizadores (UIManager:unschedule(fn)) e widgets que você abriu em onCloseWidget/onCloseDocument; callbacks agendados vazados continuam acordando o dispositivo e drenam a bateria.
  • Interface ciente de tinta eletrônica: prefira InfoMessage com timeout, evite animações, mantenha as atualizações mínimas (UIManager:setDirty(widget, "ui") apenas no widget que mudou).
  • Use logger (require("logger"); logger.dbg/info/warn/err) em vez de print; a saída vai para stdout no emulador e crash.log no dispositivo.
  • Proteja dependências opcionais (self.ui.statistics, self.ui.document, Device:hasFrontlight()): o mesmo plugin pode carregar no gerenciador de arquivos ou em dispositivos sem o recurso.

Relatando de volta

Informe o que você criou/alterou, o comando exato do validador e seu resultado, e o que permanece não verificado (por exemplo, "não executado no emulador"). Se o validador ou luacheck não estavam disponíveis, diga isso em vez de insinuar uma aprovação limpa.

Habilidades Relacionadas