Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Dripnex developers

Plugin and theme API docs for Dripnex. Fumadocs + Next.js static export on Cloudflare Pages.
Plugin and theme **pack SDK** for Dripnex (not a store). Fumadocs + Next.js static export on Cloudflare Pages.

Modeled on [developers.inkdrop.app](https://developers.inkdrop.app/). The user manual lives in [`dripnex/docs-site`](https://github.com/dripnex/docs-site) ([docs.dripnex.app](https://docs.dripnex.app)). Marketing lives in [`dripnex/marketing`](https://github.com/dripnex/marketing).
[docs.dripnex.app](https://docs.dripnex.app) is the user manual ([`dripnex/docs-site`](https://github.com/dripnex/docs-site)). This repo is how to write satellite packs. Marketing lives in [`dripnex/marketing`](https://github.com/dripnex/marketing).

A pack is a satellite git repo (`dripnex/plugin-*` or `dripnex/theme-*`) **plus** a GitHub Release tarball `{id}-{version}.tar.gz`. A git tag alone is not enough. There is no public marketplace, plugin catalog API, or ipm registry. Users install from Settings → Themes → **Available** or Settings → Plugins → **Install** / **Other package**.

The API described here is `@dripnex/plugin-api` in [`dripnex/app`](https://github.com/dripnex/app) (`packages/plugin-api`, default branch `develop`). `PLUGIN_API_VERSION` is `"1"`.

Expand Down
30 changes: 16 additions & 14 deletions content/docs/getting-started/index.mdx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
---
title: Plugin structure
description: How a Dripnex plugin repo is laid out — manifest, main, and package files
description: How a Dripnex satellite pack is laid out — manifest, dist, and package files
---

# Plugin structure

A community plugin is **its own git repository**. It is not a folder inside `dripnex/app`. Version is the git tag. The install artifact is a tarball from `dripnex-plugin pack` attached to a GitHub release.
This site is the **pack SDK**, not a store. A pack is a satellite git repo (`dripnex/plugin-*` or `dripnex/theme-*`) **plus** a GitHub Release tarball `{id}-{version}.tar.gz`. A git tag alone is not enough. It is not a folder inside `dripnex/app`.

## What Dripnex loads

Two worlds share one `PluginHost`:

| Kind | Where it lives | How it activates |
| ---- | -------------- | ---------------- |
| **Built-in** | Shipped in the desktop bundle | Enabled unless `plugin_registry.enabled === false` |
| **Built-in** | Shipped in the desktop bundle | Enabled unless SQLite `plugin_registry.enabled === false` |
| **Discovered** | `userData/plugins/<manifest.id>/` | Scanner → `loadPluginFromSource()` → same host |
| **Hackable files** | Data directory (`init.js`, `styles.css`, `keybindings.json`) | `init.js` loads as plugin `user-init` |

Discovered packs always live in user data. There is no extraResources plugin tree in the app bundle.
Discovered packs always live in user data. Palettes are **not** compiled into the app (`OFFICIAL_THEMES` is `[]`).

### Plugins directory (packaged desktop)

Expand Down Expand Up @@ -56,21 +56,23 @@ Override with `DRIPNEX_DATA_DIR` or `--user-data-dir` if the CLI and the app dis
</Folder>
</Files>

On disk after install, the scanner expects:
Theme packs add `theme.json` next to `manifest.json`. See [Create a theme](/guides/create-a-theme).

On disk after install:

```
userData/plugins/<id>/
manifest.json # id, name, version, main?, configSchema?
<main> # usually dist/index.js
manifest.json # id, name, version, main
dist/index.js # usual main
keymaps/ menus/ styles/ themes/ # optional
theme.json # optional palette
theme.json # theme packs
```

A folder is skipped unless `manifest.main` exists **or** it is theme-only (`theme.json` / `themes/`).
The host evaluates `manifest.main` (typically `dist/index.js`). Theme packs ship that file **and** `theme.json`.

## Manifest

`manifest.json` is the on-disk identity. The **evaluated** module must export the same `id` as `manifest.json`.
`manifest.json` is the on-disk identity. The **evaluated** module must export the same `id`.

```json
{
Expand All @@ -88,7 +90,7 @@ A folder is skipped unless `manifest.main` exists **or** it is theme-only (`them
| `name` | Yes | Display name |
| `version` | Yes | Semver `major.minor.patch` (optional pre-release / build) |
| `description` | No | Short summary |
| `main` | For JS packs | Path to the bundled CommonJS file |
| `main` | Yes for JS packs | Path to the bundled CommonJS file (`dist/index.js`) |
| `apiVersion` | No | Target plugin API major (current: `"1"`) |
| `configSchema` | No | Settings fields rendered in Settings → Plugins |
| `dependencies` | No | `pluginId` → semver range of other packs |
Expand Down Expand Up @@ -130,7 +132,7 @@ Applied **after** `activate()` from `packages/plugin-api` package-file parsers:
| `keymaps/*.json` | Default chords for commands this pack already registered (`plugin:<id>:…` only) |
| `menus/*.json` | Plugins menu + context menus |
| `styles/*.css` | Injected stylesheets |
| `theme.json` / `themes/*.json` | Token palettes |
| `theme.json` / `themes/*.json` | Token palettes (`parsePluginTheme`) |

Keymap chords only bind if the command was registered in `activate()`. They cannot rebind core `app:` / `editor:` commands.

Expand All @@ -146,15 +148,15 @@ Keep markdown portable. These are product rules, not extra host checks:

## Official satellites (verified)

Install with `dripnex-plugin install owner/repo` or **Settings → Plugins → Connect**. Manifest id, GitHub repo, and any registry slug are **not** interchangeable.
How users get a shipped pack: **Settings → Themes → Available** (themes) or **Settings → Plugins → Install** / **Other package**. Manifest id and GitHub repo are **not** interchangeable.

| Repo | Manifest id | What it actually does |
| ---- | ----------- | --------------------- |
| [dripnex/plugin-stamp](https://github.com/dripnex/plugin-stamp) | `stamp` | Insert date / timestamp at the cursor |
| [dripnex/plugin-mermaid](https://github.com/dripnex/plugin-mermaid) | `mermaid` | Insert a ` ```mermaid ` fence. **No renderer.** Diagrams are the built-in `dripnex-mermaid` pack. |
| [dripnex/plugin-math](https://github.com/dripnex/plugin-math) | `math` | Insert `$$…$$`. **No KaTeX.** Math rendering is the built-in `dripnex-math` pack. |
| [dripnex/plugin-vim](https://github.com/dripnex/plugin-vim) | `dripnex-vim-mode` | Real `@replit/codemirror-vim`. Install spec is `dripnex/plugin-vim`, not the manifest id. |
| [dripnex/theme-parchment](https://github.com/dripnex/theme-parchment) | `theme-parchment` | Official warm-paper palette. Many other palettes live in `dripnex/theme-*` repos. |
| [dripnex/theme-parchment](https://github.com/dripnex/theme-parchment) | `theme-parchment` | Warm-paper palette. Other palettes are `dripnex/theme-*` satellites, not core. |

Built-in mermaid, math, and tables **renderers** ship in the app. Do not treat the mermaid/math satellites as those renderers.

Expand Down
2 changes: 1 addition & 1 deletion content/docs/getting-started/init-file.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const unsub = dripnex.store.subscribe(() => {

## Vim maps (after installing Vim)

Vim is **not** built-in. Install `dripnex/plugin-vim` (manifest id `dripnex-vim-mode`) and enable it. The pack publishes `dripnex.vim` via `registerVim` so init can map keys:
Vim is **not** built-in. Users install `dripnex/plugin-vim` from **Settings → Plugins → Install** / **Other package** (manifest id `dripnex-vim-mode`) and enable it. The pack exposes `dripnex.vim` via `registerVim` so init can map keys:

```js
const Vim = dripnex.vim;
Expand Down
6 changes: 3 additions & 3 deletions content/docs/getting-started/style-tweaks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ dripnex.registerCssVariables('my-tweaks', {

| | `styles.css` | Theme pack |
| - | ------------ | ---------- |
| Audience | You | Anyone who installs `owner/repo` |
| Activation | Always, for your user data | Settings → Themes |
| Audience | You | Anyone who installs the satellite pack |
| Activation | Always, for your user data | Settings → Themes → **Available** (one click) |
| Token validation | Raw CSS | `CORE_THEME_TOKENS` + extension scopes |
| Example | A larger editor font | [dripnex/theme-parchment](https://github.com/dripnex/theme-parchment) |

Settings → Themes lists the default `tokens.css` plus installed plugin themes.
The core app does not ship named palettes (`OFFICIAL_THEMES` is `[]`). Settings → Themes lists **installed** satellite packs (and **Available** once a GitHub Release tarball exists). Personal `styles.css` is not a pack.
6 changes: 4 additions & 2 deletions content/docs/guides/create-a-plugin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ context.layout.addComponent('editor-status-bar', {
});
```

Community packs conventionally use `order` 30–99.
Satellite packs conventionally use `order` 30–99.

## 4. Config schema

Expand Down Expand Up @@ -138,6 +138,8 @@ The pack must appear under `…/Dripnex/plugins/<manifest.id>/`. If the CLI prin

CLI `install` **refuses** if `<manifest.id>` is already installed. Settings overwrite is allowed. To refresh from CLI: uninstall, then install again.

Once you ship a GitHub Release tarball, **users** install from **Settings → Plugins → Install** / **Other package** (repo spec `dripnex/plugin-stamp`), not from a store. See [Publishing](/guides/publishing).

## 6. Lifecycle

```
Expand All @@ -157,6 +159,6 @@ scan manifest.json → eval main → activate(context) → apply keymaps/menus/s
## Next

- [Create a theme](/guides/create-a-theme)
- [Publish](/guides/publishing) (`dripnex-plugin pack` → git tag → GitHub release)
- [Publish](/guides/publishing) (`dripnex-plugin pack` → GitHub Release `{id}-{version}.tar.gz`)
- [PluginContext](/reference/plugin-context)
- [Examples](/reference/examples)
100 changes: 62 additions & 38 deletions content/docs/guides/create-a-theme.mdx
Original file line number Diff line number Diff line change
@@ -1,45 +1,62 @@
---
title: Create a theme
description: Ship a Dripnex palette with theme.json, CORE_THEME_TOKENS, and THEME_EXTENSION_SCOPES
description: Satellite theme packs — manifest.json, dist/index.js, and theme.json. Palettes are not in the core app.
---

# Create a theme

A theme pack is a git repo that contributes CSS variables. Official palettes live in `dripnex/theme-*` repositories. [dripnex/theme-parchment](https://github.com/dripnex/theme-parchment) is the reference: pack id `theme-parchment`, palette id `dripnex-parchment`.
Palettes do **not** live in the desktop core. `OFFICIAL_THEMES` is `[]`. Every named palette is a satellite pack: a `dripnex/theme-*` git repo plus a GitHub Release tarball.

You do **not** need JavaScript. The scanner treats a folder as theme-only when `theme.json` or `themes/` is present even if `manifest.main` is missing.
[dripnex/theme-parchment](https://github.com/dripnex/theme-parchment) is the reference (pack id `theme-parchment`, palette id `dripnex-parchment`).

## Scaffold
## Pack shape

```bash
dripnex-plugin init "Harbor Dusk" --type theme
cd harbor-dusk
```
Three files (plus a build that emits `dist/`):

<Files>
<Folder name="theme-parchment" defaultOpen>
<File name="manifest.json" />
<File name="theme.json" />
<Folder name="src">
<File name="index.js" />
</Folder>
<Folder name="dist">
<File name="index.js" />
</Folder>
</Folder>
</Files>

| File | Role |
| ---- | ---- |
| `manifest.json` | Pack identity (`id`, `name`, `version`, `main`: `dist/index.js`) |
| `dist/index.js` | CommonJS module the host evaluates (`activate`) |
| `theme.json` | Token layer parsed by `parsePluginTheme` |

That writes `manifest.json`, `theme.json`, and `styles/index.css` (no `src/`).
`registerTheme()` inside `activate()` is still valid. If there is **no** `theme.json`, Settings harvests the palette from that JavaScript. Prefer shipping `theme.json` so the scanner and Themes → Available agree without executing JS first.

## `manifest.json`

```json
{
"id": "theme-harbor-dusk",
"name": "Harbor Dusk",
"version": "0.1.0",
"description": "A Dripnex theme: Harbor Dusk"
"id": "theme-parchment",
"name": "Parchment",
"version": "0.1.1",
"description": "Warm paper palette. Official Dripnex theme.",
"main": "dist/index.js"
}
```

`id` is kebab-case. It is the install folder name. It does not have to equal the palette id inside `theme.json`.

There is no `themeType` field on `PluginManifest`. Theme-ness is `theme.json` / `themes/*.json` (and optional `registerTheme` in JS).
`id` is kebab-case (install folder). It does not have to equal the palette `id` inside `theme.json`. There is no `themeType` field on `PluginManifest`.

## `theme.json`

Parsed by `parsePluginTheme`. Required: `colorScheme` (`"light"` | `"dark"`) and `tokens` (string map). Optional: `id`, `name`, `description`, `author`, `frosted`.
Parsed by `parsePluginTheme` in `@dripnex/plugin-api`.

**Required:** `id`, `name`, `colorScheme` (`"light"` \| `"dark"`), `tokens` (string map).

If `id` / `name` are omitted, the pack id is used. `frosted: true` marks native window vibrancy; chrome tokens should be translucent.
**Optional:** `description`, `author`, `frosted` (`true` = native window vibrancy; chrome tokens should be translucent).

Tokens pass `validateThemeTokens`:
If `id` / `name` are omitted, the pack id is used. Tokens pass `validateThemeTokens`:

- Names in `CORE_THEME_TOKENS`, or
- Names starting with a `THEME_EXTENSION_SCOPES` prefix (`--syntax-`, `--preview-`, `--ui-`, `--cm-`, `--md-`, `--mde-`)
Expand Down Expand Up @@ -79,37 +96,44 @@ Parchment (light) — subset of the real file:

`--bg-base`, `--bg-surface`, `--bg-elevated`, `--bg-inset`, `--bg-hover`, `--bg-active`, `--accent`, `--accent-primary`, `--accent-hover`, `--accent-muted`, `--accent-subtle`, `--text-primary`, `--text-secondary`, `--text-muted`, `--text-faint`, `--border`, `--border-subtle`, `--border-strong`, `--glass-bg`, `--glass-border`, `--glass-bg-menu`, `--glass-border-menu`, `--danger`, `--danger-muted`, `--warning`, `--warning-muted`, `--success`, `--success-muted`, `--status-active`, `--status-on-hold`, `--status-completed`, `--status-dropped`

Editor / preview extras belong under `--cm-`, `--md-`, `--syntax-`, and so on — for example `--cm-heading`, `--cm-link` in the CLI theme template.
Editor / preview extras belong under `--cm-`, `--md-`, `--syntax-`, and so on.

## Optional CSS

`styles/*.css` is registered as a plugin stylesheet after activate (theme-only packs use an empty `activate()`). Use it for selectors that are not tokens. Prefer tokens so Settings → Themes can switch palettes cleanly.

## Registering from JavaScript
## `dist/index.js`

If the pack has a `main` file, you can also call:
CommonJS, same loader as plugins. Parchment also calls `registerTheme` so the palette exists even if `theme.json` is skipped:

```js
context.registerTheme({
id: 'harbor-dusk',
name: 'Harbor Dusk',
colorScheme: 'dark',
tokens: {
'--bg-base': '#0c1117',
'--accent': '#5eead4',
module.exports = {
id: 'theme-parchment',
name: 'Parchment',
version: '0.1.1',
activate(context) {
const remove = context.registerTheme({
id: 'dripnex-parchment',
name: 'Parchment',
description: 'Warm paper. Reading notes, long sessions.',
author: 'Dripnex',
colorScheme: 'light',
tokens: { /* same map as theme.json */ },
});
return { dispose() { remove(); } };
},
});
};
```

Same whitelist. `context.themes.list()`, `getActive()`, `setActive(id | null)`, and `onDidChange` read the registry.
Keep `module.exports.id` in sync with `manifest.json`.

`context.themes.list()`, `getActive()`, `setActive(id | null)`, and `onDidChange` read installed palettes. There is **no** `context.getTheme()` / `onThemeChanged()`. Appearance (`'dark' | 'light' | 'system'`) is `store.getState().settings.theme`.

## Optional CSS

There is **no** `context.getTheme()` / `onThemeChanged()`. Appearance lives on `store.getState().settings.theme` (`'dark' | 'light' | 'system'`) and `themes.getActive()`.
`styles/*.css` is registered after `activate()`. Prefer tokens so Settings → Themes can switch palettes cleanly.

## Pack and try it

```bash
npm run build # emit dist/index.js
dripnex-plugin pack
dripnex-plugin install .
```

Then Settings → Themes. To share it, follow [Publishing](/guides/publishing): tag `v0.1.0` and attach `theme-harbor-dusk-0.1.0.tar.gz`.
Users install from **Settings → Themes → Available** once the GitHub Release has `{id}-{version}.tar.gz`. Locally, copy the pack into the plugins dir or `dripnex-plugin install .`, then reload. See [Publishing](/guides/publishing).
Loading