wayBetter Store

(FOSS) Um pull request é uma submissão

Publique o seu.

Um tema é um arquivo JSON. Um plugin é um arquivo JavaScript. Crie, teste no app e depois adicione à Loja: uma pasta em um pull request, ou o arquivo em uma issue se você não usa git. Os dois caminhos pedem uma conta gratuita em git.crt.fyi, e mais nada.

1

Crie

Comece de um arquivo que funciona. O gerador escreve um para você; a seção Criar do próprio app (wayBetter → Criar) tem exemplos e os valores do tema ao vivo.

Um tema

Cores, fontes, cantos, densidade, layout, fundo, efeitos. Três cores bastam; o resto é derivado e o texto continua legível.

Ou monte no app: Aparência → Criador de temas → Exportar. O THEMES.md lista tudo o que um tema pode definir.

Um plugin

Uma classe com start() e stop(), e um cabeçalho que diz quem fez e o que ela usa.

Adicione --ts para um projeto em TypeScript. O PLUGINS.md traz a API, as permissões e o recarregamento ao vivo.

hide-trends.plugin.js
/**
 * @name Hide Trends
 * @description Hides the trends box in the right column, so the timeline is the only thing moving.
 * @version 1.0.0
 * @author you
 * @permissions network:none
 */
module.exports = class HideTrends {
  start() { wayBetter.api.injectCSS('hide-trends', '[data-testid="sidebarColumn"] section { display: none }'); }
  stop()  { wayBetter.api.clearCSS('hide-trends'); }
};

A Loja lê o nome, o autor, a versão, a descrição e as permissões de um plugin nesse cabeçalho, e o nome, o autor e a descrição de um tema no JSON dele. A tela de instalação mostra os mesmos valores, então os dois nunca divergem.

2

Teste

A Loja é opcional. Um arquivo que funciona já pode ser compartilhado.

  • Solte o arquivo na janela do wayBetter. Você vê a mesma tela de instalação de um item da Loja: nome, autor, versão, SHA-256, permissões, o código.
  • Ligue o modo desenvolvedor (Configurações → Desenvolvedor) enquanto trabalha: um plugin, um arquivo de tema ou o custom.css salvo recarrega em todas as abas de uma vez.
  • Compartilhe sem a Loja. Envie o arquivo, ou um link waybetter://install para uma URL https em waybetter.fyi, cdn1.crt.fyi ou um arquivo bruto em git.crt.fyi. Ele nunca é instalado sem a tela de instalação.
3

Adicione uma pasta

Todo item da Loja é uma pasta dentro de store/<app>/ no repositório. O nome da pasta é o id do item e o endereço dele aqui: /store/plugins/<id>/.

store/x/ — os itens do wayBetter for X
store/x/plugins/hide-trends/
  hide-trends.plugin.js   the plugin, exactly as it installs
  item.json               { "category": "focus", "tags": ["trends", "sidebar"] }
  preview.svg             optional, 640×400 (.svg or .png); drawn for you without one

store/x/themes/midnight-mint/
  theme.json              the theme, as Appearance → Export writes it
  item.json               { "version": "1.0.0" }
  preview.svg             optional; drawn from the theme without one
category
Plugins: reading, writing, filters, focus ou tools. Temas: dark ou light, tirado do mode do tema quando você não informa.
tags
Até 12 palavras que as pessoas buscariam.
version
Só para temas (a de um plugin fica no cabeçalho). Aumente quando mudar o arquivo.
Descrição
Uma ou duas frases que dizem o que ele faz, com 50 caracteres ou mais. Os primeiros 160 viram a descrição da página nos buscadores.

Essa é a submissão inteira. A página, o cartão, a entrada de busca, a prévia do link, os dados estruturados e a entrada do sitemap são gerados a partir do catálogo assinado quando o site é construído. O store/README.md traz todas as regras.

4

Rode as verificações

Não há CI hospedada. As verificações são comandos que você roda antes de enviar e que um mantenedor roda de novo antes do merge.

no seu clone
git submodule update --init
bun install --frozen-lockfile
bun run check:store      # your folder, against the Store's rules
bun run test             # everything else, including the site's SEO test
  • Ids e nomesid da pasta (a-z 0-9 -, até 40), name.plugin.js, sem duplicatas em um app
  • Tamanhoarquivos de itens de até 2 MiB, prévias de até 300 KB em 16:10
  • O cabeçalho@name, @version, uma descrição e uma linha @permissions que informe network ou network:none
  • Permissões honestasnetwork:none é recusado quando o código chama fetch, XMLHttpRequest, WebSocket, EventSource ou sendBeacon; chamadas à API restrita precisam ser declaradas
  • Sem código remotosem eval, new Function ou import(); sem scripts ou arquivos externos em uma prévia; sem @import ou URLs externas no CSS de um tema
  • O catálogoos itens formam um catálogo que os apps aceitam (as regras do próprio framework), e o catálogo publicado continua batendo com a assinatura e os hashes
  • Busca e compartilhamentotítulo da página com até 60 caracteres, descrição de 50 a 160, ambos únicos
5

Envie

Dois caminhos. Os dois precisam de uma conta gratuita no git.crt.fyi: qualquer pessoa pode se cadastrar, e um mantenedor aprova cada conta nova à mão.

Pull request

  1. Faça um fork do repositório em git.crt.fyi.
  2. Adicione sua pasta em um branch e rode as verificações.
  3. Abra o pull request. O modelo pergunta o que ele faz e o que usa.

Sem git? Abra uma issue

  1. Escolha o modelo Tema ou Plugin.
  2. Anexe o arquivo (e uma prévia, se tiver).
  3. Um mantenedor adiciona a pasta para você.

Depois um mantenedor assina

A chave de assinatura da Loja fica offline de propósito, então fazer merge não é publicar. Depois do merge, um mantenedor lê o código, roda waybetter store build e waybetter store sign na máquina que guarda a chave e envia. O site é reconstruído, seu item ganha a própria página, e todo app o enxerga na próxima vez que ler o catálogo assinado.

Tudo o que está no catálogo assinado é garantido por essa chave, e é por isso que uma pessoa lê cada plugin antes de assiná-lo.

{ }

O formato do catálogo

Um arquivo para todos os apps wayBetter. Você nunca o edita à mão: ele é gerado a partir das pastas.

catalog.json (formato 2)Aberto
{ "format": 2, "name": "wayBetter Store", "generatedAt": "2026-09-30T…Z", "items": [
  { "apps": ["x"], "kind": "plugin", "id": "thread-reader", "name": "Thread Reader",
    "author": "wayBetter", "version": "1.2.0", "description": "…",
    "category": "reading", "tags": ["threads"],
    "url": "https://waybetter.fyi/store/plugins/bt-thread-reader.plugin.js",
    "file": "bt-thread-reader.plugin.js", "sha256": "<64 hex>", "size": 10961,
    "preview": "https://waybetter.fyi/store/previews/plugin-thread-reader.svg",
    "permissions": ["ui", "clipboard", "commands", "network:none"] }
] }
  • Assinado. O catalog.json.sig é uma assinatura Ed25519 sobre os bytes exatos do catalog.json. Os apps a verificam com a chave pública compilada neles e depois conferem o tamanho e o SHA-256 de cada download.
  • Rígido. Campos desconhecidos, mais de 500 itens ou um arquivo acima de 1 MiB fazem o catálogo inteiro ser recusado. O store-catalog.schema.json é o JSON Schema; o STORE.md, no framework, tem todas as regras.
  • Compartilhado. Cada item informa seus apps. Um app verifica o arquivo inteiro e fica só com os próprios itens.
Aa

Idiomas

Não são itens da Loja: os 51 idiomas vêm embutidos no app e são carregados sob demanda.

  • Corrija ou conclua uma tradução em apps/desktop/locales/<tag>.json, por pull request ou por uma issue com o arquivo alterado em anexo.
  • Verifique com bun run i18n:check: chaves, marcadores, formas de plural, sem marcação. Ele precisa imprimir 0 error(s).
  • Ela chega com a próxima atualização do app, não pela Loja. O TRANSLATING.md explica a sintaxe das mensagens e como ver suas palavras no app.

Veja o que já existe.

Todo item da Loja mostra o próprio código. Os plugins incluídos são os melhores exemplos de como usar bem a API.