1. Create the plugin
Prepare plugin.json, map the site, and build chapter downloads.
Nyxovira Plugin API
KapiTomo documentation
A plugin connects Nyxovira to a reading site, shows the chapter list quickly, and prepares only the chapters selected by the user.
Prepare plugin.json, map the site, and build chapter downloads.
Import the local folder into Nyxovira and repeat the test while developing.
If only you will use the plugin, no publication, catalog, or website is required.
Publish the finished plugin in the official catalog when you want other users to find it.
Choose this only to maintain your own distribution of ready plugins from you and other creators.
The creator prepares plugin.json and browser/download_target.js.
During development, the creator imports the local plugin folder into Nyxovira and tests it on the supported site.
The plugin may remain private. Publication is optional and happens only after testing.
On supported pages, the browser script shows chapters immediately and prepares the selected content after confirmation.
Place the manifest and browser script inside one folder named after the plugin id.
| File | Purpose |
|---|---|
plugin.json | Defines the plugin id, version, host, browser entry, icon, and parser. |
browser/download_target.js | Runs inside the open page and builds the work download plan. |
The manifest describes the site and tells Nyxovira which browser script to run.
{
"schema_version": 1,
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"tags": ["english", "manga"],
"match": { "hosts": ["example.com"] },
"browser": {
"home_url": "https://example.com/",
"icon_url": "https://example.com/icon.png",
"icon_mode": "pinned",
"short_label": "Source",
"download_target_script_file": "browser/download_target.js"
},
"parser": {
"adapter": "html_series",
"base_url": "https://example.com"
}
}
Map the site's names to the fields Nyxovira expects: title, cover, chapters, text, and page images.
var plugin = {
siteBaseUrl: "https://example.com",
siteVariable: "EXAMPLE_WORK_INDEX",
siteRoutes: {
detailsHash: "series",
readerHash: "reader"
},
appRoutes: {
publicSeriesPath: "manga",
publicChapterPath: "chapter"
},
fields: {
workId: "slug",
workTitle: "name",
workSummary: "synopsis",
workCover: "cover_url",
workChapters: "episodes",
chapterParagraphs: "text_blocks",
chapterPages: "page_urls",
imageSource: "url"
}
};
When the user taps download, set window.__nyxoviraChapterPlan immediately and return the canonical work URL.
window.__nyxoviraChapterPlan = JSON.stringify({
title: "Work title",
summary: "Short summary",
canonicalUrl: "https://example.com/manga/work/",
coverUrl: "https://example.com/cover.png",
chapters: [
{
id: "chapter-forest-hunt",
number: "1",
title: "The Forest Hunt",
contentType: "novel",
paragraphs: ["First paragraph.", "Second paragraph."]
}
]
});
return "https://example.com/manga/work/";
For large comics or APIs, keep the first plan lightweight. Load page URLs only after the user confirms selected chapters.
window.__nyxoviraPrepareDownloadPlan = function (context) {
var selectedIds = Array.isArray(context.selectedChapterIds)
? context.selectedChapterIds
: [];
var plan = context.chapterPlan;
plan.chapters.forEach(function (chapter) {
if (selectedIds.length > 0 && selectedIds.indexOf(chapter.id) < 0) {
return;
}
var payload = getJson(chapter.chapterDataPath);
chapter.pages = payload.pages.map(function (page) {
return page.imageUrl;
});
delete chapter.chapterDataPath;
});
window.__nyxoviraChapterPlan = JSON.stringify(plan);
return plan;
};
Nyxovira passes { selectedChapterIds, chapterPlan }. The function may return the final plan object or a JSON string. If it returns nothing, the app keeps the original plan.
Use paragraphs for novels and pages for image chapters. The app also reads images for compatibility.
{
"id": "chapter-forest-hunt",
"title": "The Forest Hunt",
"contentType": "novel",
"paragraphs": [
"First paragraph.",
"Second paragraph."
]
}
{
"id": "chapter-arrival",
"title": "Arrival at the Ruins",
"contentType": "images",
"pages": [
"https://example.com/page-001.png",
"https://example.com/page-002.png"
]
}
Manual import is the normal development loop and also supports plugins intended only for personal use.
Keep plugin.json and the browser folder together inside the plugin folder.
In Nyxovira, open Sites, tap Import plugins, and select the plugin folder. You may also select a parent folder containing several plugin folders.
Open the supported site and verify work recognition, the chapter list, and the download.
After changing the files, import the folder again and repeat the test.
If the plugin is only for you, you are done. You do not need GitHub, a public catalog, or a plugin website.
Use the Plugin Hub so the community can find the plugin under Online plugins.
This is the last and most involved option, intended for distributing a catalog of your plugins and plugins from other creators.
Use this option only when the plugin should appear in the official catalog. The public GitHub repository is the installation source; developers do not manually write a catalog entry.
plugin.json must contain a public HTTPS icon and a tags list with one language first, one to three content types, and adult last when needed.
Place it last when the source exposes adult-restricted material.
Keep plugin.json and browser/download_target.js in the repository.
Open the Plugin Hub and paste the GitHub repository URL.
Confirm the generated GitHub request and accept the current catalog rules.
Automation checks the files, icon, tags, hosts, and repository ownership.
A technically valid request is published in the catalog.
Only one visible plugin may cover a host. Responsibility, review, correction, and removal details are kept in the Terms and Plugin Catalog Rules.
Use this option only after the plugins are ready and tested. It is intended for anyone maintaining an independent distribution that may contain their own plugins and plugins from other creators.
A minimal external store can use this structure:
Host the folder at a public HTTPS address. The appearance, search, and plugin cards belong to the store itself; Nyxovira only needs to discover the catalog and receive the installation request. Add one object to the plugins list for every distributed plugin and identify its author and source site correctly.
Declare the catalog in the store's main page. A meta tag with the same name and a content URL is also supported.
<link rel="nyxovira-plugin-catalog" href="catalog.json">
Without a declaration, Nyxovira looks for catalog.json, catalog-store.json, and plugins.json in the page's directory. Users may also connect the JSON URL directly.
{
"schema_version": 1,
"name": "My plugin store",
"hub_url": "https://plugins.example.com/",
"plugins": [{
"id": "my-plugin",
"name": "My Plugin",
"author": "Author",
"version": "1.0.0",
"manifest_url": "plugins/my-plugin/plugin.json",
"icon_url": "https://example.com/icon.png",
"site_url": "https://example.com/",
"tags": ["english", "manga"],
"status": "active"
}]
}
hub_url identifies the storefront when a JSON URL is connected directly. store_url and homepage are accepted aliases. Relative URLs are resolved against the catalog URL. Repository-based entries may use repository_url, repository_ref, and plugin_path.
This complete example creates the button, explains when the page is open outside the app, and displays the result returned by Nyxovira.
<button id="install-my-plugin" type="button">Install My Plugin</button>
<p id="install-status" aria-live="polite"></p>
<script>
const catalogUrl = new URL("catalog.json", location.href).href;
const status = document.querySelector("#install-status");
document.querySelector("#install-my-plugin").addEventListener("click", () => {
const bridge = globalThis.NyxoviraAndroidBridge
|| globalThis.ArchiveInkAndroidBridge;
if (!bridge || typeof bridge.installCommunityPlugin !== "function") {
status.textContent = "Open this site from Nyxovira to install.";
return;
}
try {
const result = JSON.parse(
bridge.installCommunityPlugin(
catalogUrl,
JSON.stringify({ id: "my-plugin" })
) || "{}"
);
status.textContent = result.message
|| (result.success ? "Plugin installed." : "Could not install the plugin.");
} catch (error) {
status.textContent = "Could not complete the installation.";
}
});
</script>
When the store is open in a regular browser, the example tells the person to open it from Nyxovira. Inside the app, the button installs the matching plugin from the connected catalog.
Host the page, catalog, and plugin files at public HTTPS addresses.
For every plugin, accurately identify the author, source site, and manifest path.
Test the Install button by opening the store from its card under External sites in Nyxovira.
Publish every file over HTTPS.
In Nyxovira, open Sites → External sites and connect the store page or directory.
Open the connected site from its card and tap Install.
Confirm the message returned by the installation.
Valid manifest fields; a working browser script; paragraphs for novels or pages for comics; no malware, credential collection, or access-control bypass.
A public repository owned by the requester; a public icon; accepted tags; no existing visible plugin for the same host; submission through the Plugin Hub.
Page, catalog, and plugins published; correct author and source-site information; Install button tested by opening the store from Nyxovira.
Um plugin conecta o Nyxovira a um site de leitura, mostra a lista de capítulos rapidamente e prepara apenas os capítulos escolhidos pelo usuário.
Prepare o plugin.json, mapeie o site e monte os downloads de capítulos.
Importe a pasta local no Nyxovira e repita o teste enquanto desenvolve.
Se apenas você usará o plugin, não precisa publicar, criar catálogo nem montar site.
Publique o plugin pronto no catálogo oficial quando quiser que outros usuários o encontrem.
Escolha isso somente para manter uma distribuição própria de plugins prontos, seus e de outros criadores.
O criador prepara plugin.json e browser/download_target.js.
Durante o desenvolvimento, o criador importa a pasta local do plugin no Nyxovira e testa no site compatível.
O plugin pode continuar particular. A publicação é opcional e acontece somente depois dos testes.
Nas páginas compatíveis, o script mostra os capítulos imediatamente e prepara o conteúdo escolhido depois da confirmação.
Coloque o manifesto e o script do navegador em uma pasta com o mesmo nome do id do plugin.
| Arquivo | Função |
|---|---|
plugin.json | Define id, versão, host, entrada do navegador, ícone e parser. |
browser/download_target.js | Executa dentro da página aberta e monta o plano de download da obra. |
O manifesto descreve o site e informa ao Nyxovira qual script deve rodar no navegador interno.
{
"schema_version": 1,
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"tags": ["portuguese", "manga"],
"match": { "hosts": ["example.com"] },
"browser": {
"home_url": "https://example.com/",
"icon_url": "https://example.com/icon.png",
"icon_mode": "pinned",
"short_label": "Source",
"download_target_script_file": "browser/download_target.js"
},
"parser": {
"adapter": "html_series",
"base_url": "https://example.com"
}
}
Relacione os nomes usados pelo site com os campos que o Nyxovira espera: título, capa, capítulos, texto e imagens.
var plugin = {
siteBaseUrl: "https://example.com",
siteVariable: "EXAMPLE_WORK_INDEX",
siteRoutes: {
detailsHash: "series",
readerHash: "reader"
},
appRoutes: {
publicSeriesPath: "manga",
publicChapterPath: "chapter"
},
fields: {
workId: "slug",
workTitle: "name",
workSummary: "synopsis",
workCover: "cover_url",
workChapters: "episodes",
chapterParagraphs: "text_blocks",
chapterPages: "page_urls",
imageSource: "url"
}
};
Quando o usuário toca em baixar, defina window.__nyxoviraChapterPlan imediatamente e retorne a URL canônica da obra.
window.__nyxoviraChapterPlan = JSON.stringify({
title: "Work title",
summary: "Short summary",
canonicalUrl: "https://example.com/manga/work/",
coverUrl: "https://example.com/cover.png",
chapters: [
{
id: "chapter-forest-hunt",
number: "1",
title: "The Forest Hunt",
contentType: "novel",
paragraphs: ["First paragraph.", "Second paragraph."]
}
]
});
return "https://example.com/manga/work/";
Para quadrinhos grandes ou APIs, mantenha o primeiro plano leve. Carregue as URLs das páginas somente depois que o usuário confirmar os capítulos escolhidos.
window.__nyxoviraPrepareDownloadPlan = function (context) {
var selectedIds = Array.isArray(context.selectedChapterIds)
? context.selectedChapterIds
: [];
var plan = context.chapterPlan;
plan.chapters.forEach(function (chapter) {
if (selectedIds.length > 0 && selectedIds.indexOf(chapter.id) < 0) {
return;
}
var payload = getJson(chapter.chapterDataPath);
chapter.pages = payload.pages.map(function (page) {
return page.imageUrl;
});
delete chapter.chapterDataPath;
});
window.__nyxoviraChapterPlan = JSON.stringify(plan);
return plan;
};
O Nyxovira passa { selectedChapterIds, chapterPlan }. A função pode retornar o plano final como objeto ou JSON. Se não retornar nada, o app mantém o plano original.
Use paragraphs para novels e pages para capítulos com imagens. O app também lê images por compatibilidade.
{
"id": "chapter-forest-hunt",
"title": "The Forest Hunt",
"contentType": "novel",
"paragraphs": [
"First paragraph.",
"Second paragraph."
]
}
{
"id": "chapter-arrival",
"title": "Arrival at the Ruins",
"contentType": "images",
"pages": [
"https://example.com/page-001.png",
"https://example.com/page-002.png"
]
}
A importação manual é o caminho normal durante o desenvolvimento e também permite usar um plugin somente para você.
Mantenha plugin.json e a pasta browser juntos dentro da pasta do plugin.
No Nyxovira, abra Sites, toque em Importar plugins e selecione a pasta do plugin. Você também pode selecionar uma pasta que contenha várias pastas de plugins.
Abra o site compatível e confira o reconhecimento da obra, a lista de capítulos e o download.
Depois de alterar os arquivos, importe a pasta novamente e repita o teste.
Se o plugin é somente para você, o processo termina aqui. Não é necessário usar GitHub, catálogo público ou site de plugins.
Use o Plugin Hub para a comunidade encontrar o plugin em Plugins online.
Esta é a última opção e a mais trabalhosa, destinada a distribuir um catálogo com plugins seus e de outros criadores.
Use esta opção somente quando o plugin deve aparecer no catálogo oficial. O repositório GitHub público é a fonte da instalação; o desenvolvedor não escreve manualmente uma entrada no catálogo.
plugin.json precisa ter um ícone HTTPS público e uma lista tags com um idioma primeiro, de um a três tipos de conteúdo e adult por último quando necessário.
Use por último quando a fonte expõe material restrito a adultos.
Mantenha plugin.json e browser/download_target.js no repositório.
Abra o Plugin Hub e cole a URL do repositório GitHub.
Confirme a solicitação gerada no GitHub e aceite as regras atuais do catálogo.
A automação verifica arquivos, ícone, tags, hosts e propriedade do repositório.
Uma solicitação tecnicamente válida é publicada no catálogo.
Um host só pode ter um plugin visível. Responsabilidade, revisão, correção e remoção estão nos Termos e Regras do Catálogo de Plugins.
Use esta opção somente depois que os plugins estiverem prontos e testados. Ela é destinada a quem mantém uma distribuição independente com plugins próprios e, se quiser, plugins de outros criadores.
Uma loja externa mínima pode usar esta estrutura:
Hospede a pasta em um endereço HTTPS público. A aparência, a busca e os cards pertencem à própria loja; o Nyxovira precisa apenas descobrir o catálogo e receber a solicitação de instalação. Adicione um objeto à lista plugins para cada plugin distribuído e informe corretamente seu autor e site de origem.
Declare o catálogo na página principal da loja. Também é aceita uma tag meta com o mesmo nome e a URL em content.
<link rel="nyxovira-plugin-catalog" href="catalog.json">
Sem uma declaração, o Nyxovira procura catalog.json, catalog-store.json e plugins.json na mesma pasta da página. O usuário também pode conectar diretamente a URL do JSON.
{
"schema_version": 1,
"name": "Minha loja de plugins",
"hub_url": "https://plugins.example.com/",
"plugins": [{
"id": "my-plugin",
"name": "My Plugin",
"author": "Author",
"version": "1.0.0",
"manifest_url": "plugins/my-plugin/plugin.json",
"icon_url": "https://example.com/icon.png",
"site_url": "https://example.com/",
"tags": ["portuguese", "manga"],
"status": "active"
}]
}
hub_url identifica a página da loja quando uma URL JSON é conectada diretamente. store_url e homepage são aliases aceitos. URLs relativas são resolvidas a partir do catálogo. Entradas baseadas em repositório podem usar repository_url, repository_ref e plugin_path.
Este exemplo completo cria o botão, informa quando a página está fora do aplicativo e mostra o resultado retornado pelo Nyxovira.
<button id="install-my-plugin" type="button">Instalar My Plugin</button>
<p id="install-status" aria-live="polite"></p>
<script>
const catalogUrl = new URL("catalog.json", location.href).href;
const status = document.querySelector("#install-status");
document.querySelector("#install-my-plugin").addEventListener("click", () => {
const bridge = globalThis.NyxoviraAndroidBridge
|| globalThis.ArchiveInkAndroidBridge;
if (!bridge || typeof bridge.installCommunityPlugin !== "function") {
status.textContent = "Abra este site pelo Nyxovira para instalar.";
return;
}
try {
const result = JSON.parse(
bridge.installCommunityPlugin(
catalogUrl,
JSON.stringify({ id: "my-plugin" })
) || "{}"
);
status.textContent = result.message
|| (result.success ? "Plugin instalado." : "Não foi possível instalar.");
} catch (error) {
status.textContent = "Não foi possível concluir a instalação.";
}
});
</script>
Quando a loja é aberta em um navegador comum, o exemplo orienta a pessoa a abri-la pelo Nyxovira. Dentro do aplicativo, o botão instala o plugin correspondente no catálogo conectado.
Hospede a página, o catálogo e os arquivos dos plugins em endereços HTTPS públicos.
Para cada plugin, informe corretamente o autor, o site de origem e o caminho do manifesto.
Teste o botão Instalar abrindo a loja pelo cartão criado em Sites externos no Nyxovira.
Publique todos os arquivos em HTTPS.
No Nyxovira, abra Sites → Sites externos e conecte a página ou pasta da loja.
Abra o site conectado pelo cartão e toque em Instalar.
Confirme a mensagem retornada pela instalação.
Campos válidos no manifesto; script do navegador funcionando; paragraphs para novels ou pages para quadrinhos; sem malware, coleta de credenciais ou quebra de controle de acesso.
Repositório público do solicitante; ícone público; tags aceitas; nenhum plugin visível no mesmo host; envio pelo Plugin Hub.
Página, catálogo e plugins publicados; informações corretas de autor e site de origem; botão Instalar testado abrindo a loja pelo Nyxovira.