wayBetter for X
This commit is contained in:
commit
df5dff619e
720 changed files with 147465 additions and 0 deletions
93
store/README.md
Normal file
93
store/README.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# The Store's source
|
||||
|
||||
Every item in the wayBetter Store is one folder here. A pull request that adds a folder adds an
|
||||
item; nothing else has to be written by hand. The catalog, the item's page on
|
||||
waybetter.fyi/store/, its card, search entry, link preview, structured data and sitemap entry are
|
||||
all generated from these folders.
|
||||
|
||||
```
|
||||
store/
|
||||
featured.json the storefront's "Featured" shelf: ["x/theme/nord", …]
|
||||
x/ one folder per app id ("x" = wayBetter for X)
|
||||
plugins/<id>/
|
||||
<name>.plugin.js the plugin, exactly as it installs
|
||||
item.json { "category": "tools", "tags": ["notes", "profiles"] }
|
||||
preview.svg | preview.png optional
|
||||
areas/<id>/
|
||||
item.json an area plugin's page: { "category", "tags", "license", "credits", media }
|
||||
themes/<id>/
|
||||
theme.json the theme, as Appearance → Export writes it
|
||||
item.json { "version": "1.0.0" }
|
||||
preview.svg | preview.png optional
|
||||
strings/<id>/
|
||||
<name>.strings.json a string pack (waybetter/docs/STRING-PACKS.md): name, authors, version,
|
||||
description and language are the pack's own; every string must fit
|
||||
the app's inventory (node scripts/check-store.mjs checks it)
|
||||
item.json { "category": "language", "tags", "license", "credits" }
|
||||
```
|
||||
|
||||
The folder name is the item's **id**: `a-z`, `0-9` and `-`, at most 40 characters, unique among
|
||||
the app's plugins (or themes). It is also the item's address: `/store/plugins/<id>/`.
|
||||
|
||||
## What goes where
|
||||
|
||||
Everything the app shows on its install screen comes from the item's own file, so the Store and
|
||||
the install screen can never disagree. `item.json` holds only what the file cannot say.
|
||||
|
||||
| | Plugin | Theme |
|
||||
| --- | --- | --- |
|
||||
| Name, author, description | the header: `@name`, `@author`, `@description` | `name`, `author`, `description` in `theme.json` |
|
||||
| Version | the header: `@version` | `"version"` in `item.json` |
|
||||
| Permissions | the header: `@permissions` | none (a theme is data) |
|
||||
| Category | `"category"` in `item.json`: `reading`, `writing`, `filters`, `focus` or `tools` | `dark` or `light`, from the theme's `mode` (or `"category"`) |
|
||||
| Tags | `"tags"` in `item.json`: up to 12 search words | the same (default: the category) |
|
||||
| Preview | `preview.svg` or `preview.png`, else a card drawn from the name and category | the same, else a miniature drawn from the theme |
|
||||
|
||||
Rules the checks enforce:
|
||||
|
||||
- **Description**: at least 50 characters that say what it does. The app keeps the first 300 of a
|
||||
plugin's; search engines show the first 160 or fewer, cut at a sentence.
|
||||
- **Plugins** start with a `/** … */` header that has `@name`, `@version`, `@description`,
|
||||
`@author` and a `@permissions` line naming `network` or `network:none`. `network:none` is refused
|
||||
when the code calls `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource` or `sendBeacon`. The
|
||||
gated API (`api.storage`, `api.ui`, `api.toast`, `api.copyText`, `api.account`, `api.users`,
|
||||
`api.commands`) must be declared. No `eval`, `new Function`, `import()` or added `<script>`
|
||||
elements: the code that is read and signed is the code that runs. At most 2 MiB, UTF-8 text.
|
||||
- **Themes** are a JSON object with a `name` that the app's sanitizer accepts, at most 2 MiB. A
|
||||
theme's optional `css` may not `@import` or load files from another server.
|
||||
- **Previews** are 16:10 (640×400 is the size the Store shows), at most 300 KB. An SVG may not
|
||||
contain scripts, event handlers, `foreignObject` or links to other servers.
|
||||
|
||||
## Area plugins
|
||||
|
||||
Compose+, Reader+, Profile+, Chat+, Media+, Search+, Notifications+ and Navigation+ are built into
|
||||
wayBetter for X (docs/AREA-PLUGINS.md), so they are not catalog items and have no Install button.
|
||||
Each has a page on the site from `store/x/areas/<id>/item.json` (category, tags, `license`,
|
||||
`credits`, and the same optional cover, screenshots and video as an item). Its name, description
|
||||
and feature list come from the app's feature table and words. The bundled plugin files those
|
||||
features replaced stay in the app for the one-time move of an existing install and have no Store
|
||||
folder.
|
||||
|
||||
`license` (1 to 60 characters, e.g. an SPDX id) and `credits` (up to 600) are optional on every
|
||||
item and go into the catalog as they are.
|
||||
|
||||
## First-party items
|
||||
|
||||
The plugins the app ships and its built-in themes are in the Store too, but their files stay where
|
||||
the app builds them, so the two copies cannot drift:
|
||||
|
||||
- `"bundled": "<file>.plugin.js"` takes the plugin from `apps/desktop/src-tauri/bundled-plugins/`;
|
||||
`"file"` is the name it is installed under (`bt-<file>`), so a bundled copy counts as installed.
|
||||
- `"builtin": true` exports the built-in theme whose id is the folder name, the way Appearance →
|
||||
Export does.
|
||||
|
||||
## Commands
|
||||
|
||||
```sh
|
||||
bun run check:store # check the folders and the published catalog (no key needed)
|
||||
node scripts/build-store.mjs # maintainers: rebuild apps/web/public/store/ from the folders
|
||||
node scripts/sign-store.mjs # maintainers: sign it, on the machine that holds the Store key
|
||||
```
|
||||
|
||||
How a contribution becomes a Store item, step by step:
|
||||
[docs/STORE.md](../docs/STORE.md#add-your-theme-or-plugin-with-a-pull-request).
|
||||
Loading…
Add table
Add a link
Reference in a new issue