wayBetter Store

(FOSS) Un pull request es un envío

Publica el tuyo.

Un tema es un archivo JSON. Un plugin es un archivo JavaScript. Créalo, pruébalo en la app y añádelo a la Tienda: una carpeta en un pull request, o el archivo en una incidencia si no usas git. Ambas opciones necesitan una cuenta gratuita en git.crt.fyi, y nada más.

1

Créalo

Parte de un archivo que funcione. El generador escribe uno; la sección Crear de la propia app (wayBetter → Crear) tiene ejemplos y los valores del tema en vivo.

Un tema

Colores, fuentes, esquinas, densidad, diseño, fondo, efectos. Con tres colores basta; el resto se deriva y el texto se mantiene legible.

O créalo en la app: Apariencia → Creador de temas → Exportar. THEMES.md enumera todo lo que un tema puede definir.

Un plugin

Una clase con start() y stop(), y una cabecera que dice quién lo hizo y qué usa.

Añade --ts para un proyecto en TypeScript. PLUGINS.md tiene la API, los permisos y la recarga en 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'); }
};

La Tienda lee el nombre, el autor, la versión, la descripción y los permisos de un plugin de esta cabecera, y el nombre, el autor y la descripción de un tema de su JSON. La pantalla de instalación muestra los mismos valores, así que nunca pueden discrepar.

2

Pruébalo

La Tienda es opcional. Un archivo que funciona ya se puede compartir.

  • Suelta el archivo en la ventana de wayBetter. Verás la misma pantalla de instalación que un elemento de la Tienda: nombre, autor, versión, SHA-256, permisos, el código.
  • Activa el modo desarrollador (Configuración → Desarrollador) mientras trabajas: un plugin, un archivo de tema o custom.css guardados se recargan en todas las pestañas a la vez.
  • Compártelo sin la Tienda. Envía el archivo, o un enlace waybetter://install a una URL https en waybetter.fyi, cdn1.crt.fyi o un archivo raw en git.crt.fyi. Nunca se instala sin la pantalla de instalación.
3

Añade una carpeta

Cada elemento de la Tienda es una carpeta bajo store/<app>/ en el repositorio. El nombre de la carpeta es el id del elemento y su dirección aquí: /store/plugins/<id>/.

store/x/ — los elementos de 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 o tools. Temas: dark o light, tomado del mode del tema si lo omites.
tags
Hasta 12 palabras que la gente buscaría.
version
Solo para temas (la de un plugin está en su cabecera). Súbela cuando cambies el archivo.
Descripción
Una o dos frases que digan qué hace, de 50 caracteres o más. Los primeros 160 serán la descripción de su página para los buscadores.

Ese es todo el envío. La página, la tarjeta, la entrada de búsqueda, la vista previa del enlace, los datos estructurados y la entrada del mapa del sitio se generan a partir del catálogo firmado cuando se compila el sitio. store/README.md tiene todas las reglas.

4

Ejecuta las comprobaciones

No hay CI alojada. Las comprobaciones son comandos que ejecutas antes de enviar y que un mantenedor vuelve a ejecutar antes de fusionar.

en tu clon
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 y nombresid de carpeta (a-z 0-9 -, hasta 40), name.plugin.js, sin duplicados en una app
  • Tamañoarchivos de elementos de hasta 2 MiB, vistas previas de hasta 300 KB en 16:10
  • La cabecera@name, @version, una descripción y una línea @permissions que indique network o network:none
  • Permisos honestosnetwork:none se rechaza cuando el código llama a fetch, XMLHttpRequest, WebSocket, EventSource o sendBeacon; las llamadas a la API restringida deben declararse
  • Sin código remotosin eval, new Function ni import(); sin scripts ni archivos externos en una vista previa; sin @import ni URL externas en el CSS de un tema
  • El catálogolos elementos forman un catálogo que las apps aceptan (las reglas del propio framework), y el catálogo publicado sigue coincidiendo con su firma y sus hashes
  • Búsqueda y compartirtítulo de página de hasta 60 caracteres, descripción de 50 a 160, ambos únicos
5

Envíalo

Dos formas de entrar. Las dos necesitan una cuenta gratuita en git.crt.fyi: cualquiera puede registrarse, y un mantenedor aprueba a mano cada cuenta nueva.

Pull request

  1. Haz un fork del repositorio en git.crt.fyi.
  2. Añade tu carpeta en una rama y ejecuta las comprobaciones.
  3. Abre el pull request. La plantilla pregunta qué hace y qué usa.

¿Sin git? Abre una incidencia

  1. Elige la plantilla Tema o Plugin.
  2. Adjunta el archivo (y una vista previa, si la tienes).
  3. Un mantenedor añade la carpeta por ti.

Después firma un mantenedor

La clave de firma de la Tienda está sin conexión a propósito, así que fusionar no es publicar. Tras la fusión, un mantenedor lee el código, ejecuta waybetter store build y waybetter store sign en la máquina que guarda la clave y sube los cambios. El sitio se recompila, tu elemento obtiene su página y cada app lo ve la próxima vez que lee el catálogo firmado.

Todo lo que hay en el catálogo firmado está avalado por esa clave; por eso una persona lee cada plugin antes de firmarlo.

{ }

El formato del catálogo

Un archivo para todas las apps de wayBetter. Nunca lo editas a mano: se genera a partir de las carpetas.

catalog.json (formato 2)Abierto
{ "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"] }
] }
  • Firmado. catalog.json.sig es una firma Ed25519 sobre los bytes exactos de catalog.json. Las apps la verifican con la clave pública compilada en ellas y después comprueban el tamaño y el SHA-256 de cada descarga.
  • Estricto. Campos desconocidos, más de 500 elementos o un archivo de más de 1 MiB hacen que se rechace el catálogo entero. store-catalog.schema.json es el JSON Schema; STORE.md, en el framework, tiene todas las reglas.
  • Compartido. Cada elemento indica sus apps. Una app verifica el archivo entero y se queda solo con sus propios elementos.
Aa

Idiomas

No son elementos de la Tienda: los 51 idiomas vienen integrados en la app y se cargan cuando hacen falta.

  • Corrige o termina una traducción en apps/desktop/locales/<tag>.json, con un pull request o con una incidencia que adjunte el archivo modificado.
  • Compruébala con bun run i18n:check: claves, marcadores, formas de plural, sin marcado. Debe imprimir 0 error(s).
  • Llega con la próxima actualización de la app, no a través de la Tienda. TRANSLATING.md explica la sintaxis de los mensajes y cómo ver tus palabras en la app.

Mira lo que ya hay.

Cada elemento de la Tienda muestra su código. Los plugins incluidos son los mejores ejemplos de cómo usar bien la API.