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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Dripnex developers

Plugin and theme **pack SDK** for Dripnex (not a store). Fumadocs + Next.js static export on Cloudflare Pages.
Pack SDK for **Dripnex, the hackable AI note taker** (not a store). Fumadocs + Next.js static export on Cloudflare Pages.

Notes live in local SQLite. `.md` is export. First launch is AuthGate. Personal hacks are `init.js` / `styles.css` / `keybindings.json` from **Settings → Hack**. Satellite packs are `dripnex/plugin-*` / `dripnex/theme-*` plus a GitHub Release tarball.

[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).

Expand Down
4 changes: 2 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const metadata: Metadata = {
template: '%s | Dripnex Developers',
},
description:
'Plugin and theme API for Dripnex — extend the desktop editor with commands, CodeMirror, layout zones, and palettes.',
'Pack SDK for Dripnex, the hackable AI note taker — init.js, plugins, and themes. Not a store.',
applicationName: 'Dripnex Developers',
icons: {
icon: [
Expand Down Expand Up @@ -53,7 +53,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
banner: (
<div className="rounded-lg border border-[var(--color-border-accent)] bg-[var(--color-accent-glow)] px-3 py-2">
<p className="text-xs font-medium text-fd-foreground">
Desktop plugin API.{' '}
Desktop pack SDK for the hackable AI note taker.{' '}
<a
href={DOCS_URL}
target="_blank"
Expand Down
26 changes: 16 additions & 10 deletions content/docs/getting-started/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ description: How a Dripnex satellite pack is laid out — manifest, dist, and pa

# Plugin structure

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`.
This site is the **pack SDK**, not a store. Dripnex is the hackable AI note taker: messy input → a document a person will send. Notes live in local SQLite. `.md` is export, not identity.

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`.

Personal hacks (`init.js` / `styles.css` / `keybindings.json`) are **not** packs. Open those from **Settings → Hack**.

## What Dripnex loads

Expand All @@ -15,7 +19,7 @@ Two worlds share one `PluginHost`:
| ---- | -------------- | ---------------- |
| **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` |
| **Hackable files** | Data directory (`init.js`, `styles.css`, `keybindings.json`) | Open from **Settings → Hack**. `init.js` loads as plugin `user-init` |

Discovered packs always live in user data. Palettes are **not** compiled into the app (`OFFICIAL_THEMES` is `[]`).

Expand All @@ -29,6 +33,8 @@ Discovered packs always live in user data. Palettes are **not** compiled into th

Override with `DRIPNEX_DATA_DIR` or `--user-data-dir` if the CLI and the app disagree about the folder.

That directory is for **packs**. The note store is SQLite in the same data dir. Do not treat notes as files on disk.

## Repo layout

`dripnex-plugin init "My Plugin"` writes:
Expand Down Expand Up @@ -58,7 +64,7 @@ Override with `DRIPNEX_DATA_DIR` or `--user-data-dir` if the CLI and the app dis

Theme packs add `theme.json` next to `manifest.json`. See [Create a theme](/guides/create-a-theme).

On disk after install:
After install:

```
userData/plugins/<id>/
Expand Down Expand Up @@ -138,13 +144,13 @@ Keymap chords only bind if the command was registered in `activate()`. They cann

## Plugin contract

Keep markdown portable. These are product rules, not extra host checks:
Notes live in SQLite. Export is Markdown. Packs should stay optional chrome and commands:

1. **Removable without breaking `.md` files** — uninstall and notes stay valid markdown.
2. **No new syntax** other editors cannot read.
3. **No automatic content mutation** — never rewrite the user's markdown without an explicit command.
4. **Not required to interpret text** — notes must be readable without the pack.
5. **No inter-note dependencies** that break if a note is moved or deleted.
1. **Removable** — uninstall and notes stay readable. Do not make the store depend on your pack.
2. **No invented syntax** other tools cannot read when the user exports `.md`.
3. **No automatic content mutation** — never rewrite a note without an explicit command.
4. **Not required to interpret text** — a note must still make sense without the pack.
5. **No inter-note graph the pack owns** that breaks if a note is moved or deleted.

## Official satellites (verified)

Expand All @@ -155,7 +161,7 @@ How users get a shipped pack: **Settings → Themes → Available** (themes) or
| [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/plugin-vim](https://github.com/dripnex/plugin-vim) | `dripnex-vim-mode` | Real `@replit/codemirror-vim`. Satellite, not built-in. Install spec is `dripnex/plugin-vim`, not the manifest id. |
| [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
63 changes: 47 additions & 16 deletions content/docs/getting-started/init-file.mdx
Original file line number Diff line number Diff line change
@@ -1,32 +1,59 @@
---
title: The init file
description: Customize Dripnex on startup with init.js — the host PluginContext, not Inkdrop’s Atom APIs
description: Settings → Hack opens init.js. Lead with registerAiCommand “Make this sendable.”
---

# The init file

`init.js` is a **user file** in the data directory, not a plugin repo. Open it from **Settings → Plugins**. Dripnex writes a template on first open.
`init.js` is a **user file** in the data directory, not a plugin repo. Open it from **Settings → Hack** (Open init.js). Dripnex writes a template on first launch. Existing files are not overwritten.

It is the same idea as Inkdrop's init file: run JavaScript at startup. The object you get is **Dripnex's** host API (`createInitApi` in `@dripnex/plugin-api`), not Inkdrop's Atom-style `inkdrop` global.
That is the user-facing hack surface: **Settings → Hack** opens `init.js`, `styles.css`, and the keymap (`keybindings.json`). Save to apply. Reload plugins after changing `init.js`.

## Two formats

`loadInitScript()` accepts:
The object you get is Dripnex’s host API (`createInitApi` in `@dripnex/plugin-api`), injected as the `dripnex` argument. There is no Atom-style global.

1. **Free-form (default).** The file is wrapped as plugin `user-init` and runs at activate with a `dripnex` argument.
2. **CommonJS `PluginManifest`.** If the file assigns `module.exports` / `exports.*` and looks like a manifest (`id`, `activate`, `name`, or `version`), it is validated and loaded as a normal pack.
## Make this sendable

Free-form is what the default template uses (`dripnex.menu.add`, `dripnex.commands.add`). Do not assume a browser `window.dripnex` with a `.menu` — the host injects the init API as the `dripnex` function argument.
The default template registers an AI command. Messy notes become a document a person would actually send — not a model dump.

```js
dripnex.commands.add('paste-as-link', 'Paste as Link', () => {
const { from, to } = dripnex.editor.getSelection();
const text = dripnex.editor.getContent().slice(from, to);
dripnex.editor.replaceRange(from, to, '[' + text + '](url)');
dripnex.registerAiCommand({
id: 'make-this-sendable',
name: 'Make this sendable',
description: 'Turn this note into a document a person would actually send.',
systemPrompt:
'Turn messy notes into a document a person would actually send. ' +
'Clear, specific, human. Not a model dump. Preserve facts. Cut filler. ' +
'Match the implied genre (email, message, brief, post).',
userPromptTemplate:
'Turn the following into a document a person would actually send.\n\n' +
'Title: {{title}}\n\n' +
'Selection (if any):\n{{selection}}\n\n' +
'Full note:\n{{note}}',
outputTarget: 'replace',
category: 'writing',
});
```

Reload plugins (**Settings → Plugins → Reload**) after saving.
Placeholders in `userPromptTemplate`: `{{selection}}`, `{{note}}`, `{{title}}`. `outputTarget`: `'replace'` | `'insert'` | `'panel'` (default `'panel'`). The command appears in the AI panel and the command palette.

A palette command that is not AI still works. It is a comment in the template — it does not lead:

```js
// dripnex.commands.add('insert-date', 'Insert Date', () => {
// dripnex.editor.insertAtCursor(new Date().toISOString().slice(0, 10));
// });
```

Reload after saving: **Settings → Plugins → Reload**, or the Reload Plugins command.

## Two formats

`loadInitScript()` accepts:

1. **Free-form (default).** The file is wrapped as plugin `user-init` and runs at activate with a `dripnex` argument.
2. **CommonJS `PluginManifest`.** If the file assigns `module.exports` / `exports.*` and looks like a manifest (`id`, `activate`, `name`, or `version`), it is validated and loaded as a normal pack.

Free-form is what the default template uses. Do not assume a browser `window.dripnex` with a `.menu` — the host injects the init API as the `dripnex` function argument.

## The `dripnex` object (`InitApi`)

Expand All @@ -43,7 +70,7 @@ Reload plugins (**Settings → Plugins → Reload**) after saving.
| `vim` | `@replit/codemirror-vim` `Vim` object **when** [plugin-vim](https://github.com/dripnex/plugin-vim) is loaded; otherwise unavailable |
| `getActiveEditor()` | `{ editor, cm }` where `cm` is `editor.getView()` (live CodeMirror 6 view or `null`) |

There is no Inkdrop `inkdrop.packages`, `CompositeDisposable`, or Atom command registry. Map keys with `dripnex.commands.add` / `registerCommand`, or with `Vim.map` after the Vim pack has called `registerVim`.
Map keys with `dripnex.commands.add` / `registerCommand`, or with `Vim.map` after the Vim pack has called `registerVim`.

## Read-only store

Expand All @@ -60,9 +87,11 @@ const unsub = dripnex.store.subscribe(() => {
- Mutate through `dripnex.commands.dispatch`, `editor`, or `data`.
- `settings` is appearance only (theme, accent, zoom). It never includes API keys.

Notes themselves live in local SQLite. The store is a projection, not a folder of files.

## Vim maps (after installing Vim)

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:
Vim is **not** built-in. It is the satellite [dripnex/plugin-vim](https://github.com/dripnex/plugin-vim). Users install it 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 All @@ -77,6 +106,8 @@ if (Vim) {

## Also in the data directory

Open all three from **Settings → Hack**.

| File | Role |
| ---- | ---- |
| `init.js` | This page |
Expand Down
10 changes: 6 additions & 4 deletions content/docs/getting-started/style-tweaks.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
title: Style tweaks
description: Personal CSS and theme tokens without shipping a full theme pack
description: Personal CSS from Settings → Hack — not a theme pack, not notes-as-files
---

# Style tweaks

`styles.css` in the data directory is injected into every renderer window. Save to apply. Open it from **Settings → Plugins** (written on first open, same as `init.js`).
`styles.css` in the data directory is injected into every renderer window. Save to apply. Open it from **Settings → Hack** (Open styles.css). Written on first launch if missing, same as `init.js` and `keybindings.json`. Existing files are not overwritten.

Use this for personal chrome. To **share** a palette, ship a [theme pack](/guides/create-a-theme).

Expand Down Expand Up @@ -34,9 +34,11 @@ Unknown names are rejected for **registered themes**. Your personal `styles.css`
}
```

Enable **Development Mode** in Settings → General, then right-click → Inspect Element.

## From a plugin or init.js

Packs can register variables without a `theme.json`:
Packs (and `init.js`) can register variables without a `theme.json`:

```js
dripnex.registerCssVariables('my-tweaks', {
Expand All @@ -52,7 +54,7 @@ dripnex.registerCssVariables('my-tweaks', {
| | `styles.css` | Theme pack |
| - | ------------ | ---------- |
| Audience | You | Anyone who installs the satellite pack |
| Activation | Always, for your user data | Settings → Themes → **Available** (one click) |
| How you open it | Settings → Hack | 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) |

Expand Down
16 changes: 11 additions & 5 deletions content/docs/guides/create-a-plugin.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
---
title: Create a plugin
description: Scaffold, activate, bundle to CommonJS, and load a Dripnex plugin from disk
description: Scaffold, activate, bundle to CommonJS, and load a Dripnex plugin
---

# Create a plugin

This walkthrough builds a pack you can load locally. The real first-party example is [dripnex/plugin-stamp](https://github.com/dripnex/plugin-stamp): CommonJS, `menu.add`, `editor.insertAtCursor`.
Personal AI commands belong in `init.js` (**Settings → Hack**). This walkthrough is a **satellite pack** you can load locally. The real first-party example is [dripnex/plugin-stamp](https://github.com/dripnex/plugin-stamp): CommonJS, `menu.add`, `editor.insertAtCursor`.

If you only want “Make this sendable,” stop here and edit [init.js](/getting-started/init-file). You do not need a repo for that.

## 1. Scaffold

Expand Down Expand Up @@ -57,7 +59,7 @@ Keep `id` / `name` / `version` in sync with `manifest.json`. Return `{ dispose()

TypeScript is fine if your bundler emits CJS `module.exports`. Named ESM `export const plugin` is **not** what `loadPluginFromSource` reads.

## 3. Commands and layout (optional)
## 3. Commands, AI commands, and layout (optional)

Commands land in the palette as `plugin:<id>:<commandId>`:

Expand All @@ -77,6 +79,8 @@ const off = context.registerCommand(
);
```

AI commands use `context.registerAiCommand` — same options as [init.js](/getting-started/init-file#make-this-sendable). Use that when the pack’s job is “turn this note into something a person will send,” not when you are inserting a date.

Mount UI with `context.layout.addComponent`. Zones are listed in [Layout zones](/reference/layout-zones). Status bar example:

```js
Expand Down Expand Up @@ -150,8 +154,10 @@ scan manifest.json → eval main → activate(context) → apply keymaps/menus/s

## Five rules

1. Removable without breaking `.md` files.
2. No invented markdown other editors cannot read.
Notes live in SQLite. `.md` is export.

1. Removable — uninstall and notes stay readable.
2. No invented markdown other tools cannot read on export.
3. No automatic content mutation.
4. Notes stay readable without the pack.
5. No brittle inter-note graph that the pack owns.
Expand Down
4 changes: 3 additions & 1 deletion content/docs/guides/create-a-theme.mdx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
---
title: Create a theme
description: Satellite theme packs — manifest.json, dist/index.js, and theme.json. Palettes are not in the core app.
description: Satellite theme packs — Settings → Hack is personal CSS; packs are palettes you share.
---

# Create a theme

Personal chrome is `styles.css` from **Settings → Hack**. A **theme pack** is how you share a palette.

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.

[dripnex/theme-parchment](https://github.com/dripnex/theme-parchment) is the reference (pack id `theme-parchment`, palette id `dripnex-parchment`).
Expand Down
6 changes: 5 additions & 1 deletion content/docs/guides/publishing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ description: A pack is a satellite git repo plus a GitHub Release tarball. Not a

# Publishing

This site is the pack SDK. Publishing does **not** mean upload, browse, or “publish to Dripnex.” There is no public marketplace and no ipm-style registry.
This site is the pack SDK for the hackable AI note taker. Publishing does **not** mean upload, browse, or “publish to Dripnex.” There is no public marketplace and no ipm-style registry.

`init.js` / `styles.css` / `keybindings.json` are personal. They are not packs. Do not ship them as a Release.

A pack is:

Expand Down Expand Up @@ -66,3 +68,5 @@ CLI `install` errors if that `manifest.id` is already present. Settings can over
- Proof toys (word count, typewriter, reading time) are not satellite packs.
- Do not open a plugin PR against `dripnex/app` unless it is becoming a **built-in**.
- Do not invent a second version number besides the git tag.
- Do not claim notes are files on disk. The store is SQLite; `.md` is export.
- Vim is `dripnex/plugin-vim`, not built-in.
Loading