=============== LIBRARY RULES =============== From library maintainers: - Use Bun for every package operation: `bun add`, `bun run`, `bunx`. Never npm, yarn, pnpm or npx, and never write `npm install` in an example. - Install a docs.plus Tiptap extension with `bun add @docs.plus/extension-`. The five are extension-hyperlink, extension-hypermultimedia, extension-indent, extension-inline-code and extension-placeholder. - Read each extension's README and package.json for its version and Tiptap peer requirements. Resolve catalog references from the root package.json. - With extension-hyperlink set `StarterKit.configure({ link: false })`. StarterKit v3 bundles its own link mark claiming the same commands and the same a[href] parse rule. The clash is silent. - With extension-inline-code set `StarterKit.configure({ code: false })`. Both marks render and both bind Mod-e. The clash is silent. - With extension-placeholder, remove the Tiptap built-in placeholder from the extensions array. Both register the name `placeholder`. - extension-hypermultimedia requires its own stylesheet, or media nodes render unstyled. extension-hyperlink ships an optional one. - extension-indent, extension-inline-code and extension-placeholder ship no CSS. extension-placeholder renders nothing until the host adds the CSS rule from its README. - A docs.plus document is a tree of headings. One heading id serves the table of contents, the Heading Chatroom and the URL. Never write a heading id into stored content. - Durable persistence is eventual. The collaboration server debounces a save for 10 seconds idle, or 60 seconds while typing continues. - A BullMQ worker writes the version row and publishes `document:saved`. The editor status chip is a local 300 ms timer, not a durable acknowledgement. - Private is owner-only. A signed-in stranger is denied and an anonymous visitor must sign in. An open document has no owner, so it cannot be made private. - docs.plus has no AI features, no tracked changes and no page layout. Do not generate examples implying any of them exist. ### Install Audio Node with bun and configure HyperMultimediaKit Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/src/nodes/audio/README.md Install the extension package and enable the Audio node in HyperMultimediaKit. The configuration excerpt shows the basic setup for the Audio node. ```sh bun add @docs.plus/extension-hypermultimedia ``` ```js import { HyperMultimediaKit } from '@docs.plus/extension-hypermultimedia' HyperMultimediaKit.configure({ Audio: true }) ``` -------------------------------- ### Quickstart: Editor setup with Hyperlink extension Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/README.md Sets up a Tiptap editor with the Hyperlink extension and configures the three popovers. Requires a
mount point. Disables StarterKit's built-in link mark. ```ts import { Editor } from '@tiptap/core' import StarterKit from '@tiptap/starter-kit' import { Hyperlink, createHyperlinkPopover, editHyperlinkPopover, previewHyperlinkPopover } from '@docs.plus/extension-hyperlink' import '@docs.plus/extension-hyperlink/styles.css' const editor = new Editor({ element: document.querySelector('#editor'), content: '

Try this link.

', extensions: [ // Disable StarterKit's bundled link mark — see Caveats. StarterKit.configure({ link: false }), Hyperlink.configure({ popovers: { previewHyperlink: previewHyperlinkPopover, editHyperlink: editHyperlinkPopover, createHyperlink: createHyperlinkPopover } }) ] }) ``` -------------------------------- ### Clone and start local dev with make dev-local Source: https://github.com/docs-plus/docs.plus/blob/main/README.md Clones the repository and runs the one-command local development bootstrap. The first run downloads Docker images and takes several minutes; later runs start in seconds. If the first run stops, see the Development Setup section in CONTRIBUTING.md for the environment health check. ```bash git clone https://github.com/docs-plus/docs.plus.git cd docs.plus make dev-local ``` -------------------------------- ### Start production stack with make up-prod Source: https://github.com/docs-plus/docs.plus/blob/main/docs/self-hosting/install.md Starts the production stack. Run this after configuration to bring up all services. ```bash make up-prod ``` -------------------------------- ### Start full Docker development environment with make up-dev Source: https://github.com/docs-plus/docs.plus/blob/main/README.md Starts the full Docker development environment. The first command copies the environment template, the second builds and starts all containers, and the third starts the Supabase backend process because `make up-dev` does not start Supabase; the containers read `SUPABASE_URL: http://host.docker.internal:54321` from the host, so the third command supplies it. This also opens Supabase Studio at http://127.0.0.1:54323. ```bash cp .env.example .env.development make up-dev bun --filter @docs.plus/supabase_back start ``` -------------------------------- ### Install with bun Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/README.md Install the extension using bun. Requires @tiptap/core and @tiptap/pm version ^3.31.3. ```sh bun add @docs.plus/extension-hyperlink ``` -------------------------------- ### Install with bun add Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-placeholder/README.md Install the extension with Bun. Requires @tiptap/core and @tiptap/pm version ^3.31.3 (Tiptap 3.x). Installs with no runtime dependencies. ```sh bun add @docs.plus/extension-placeholder ``` -------------------------------- ### Install and enable Video node Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/src/nodes/video/README.md Install the extension with bun and enable the Video node by passing `Video: true` to `HyperMultimediaKit.configure`. The import path is `@docs.plus/extension-hypermultimedia`. ```sh bun add @docs.plus/extension-hypermultimedia ``` ```js import { HyperMultimediaKit } from '@docs.plus/extension-hypermultimedia' HyperMultimediaKit.configure({ Video: true }) ``` -------------------------------- ### Install with bun Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-inline-code/README.md Install the extension using bun. Requires @tiptap/core and @tiptap/pm version ^3.31.3, and an engine with RegExp lookbehind support. ```sh bun add @docs.plus/extension-inline-code ``` -------------------------------- ### Install with bun Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/README.md Install the extension using the bun package manager. Requires @tiptap/core and @tiptap/pm version 3.31.3 or later. ```sh bun add @docs.plus/extension-hypermultimedia ``` -------------------------------- ### Install Loom extension with bun Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/src/nodes/loom/README.md Install the @docs.plus/extension-hypermultimedia package using bun. ```sh bun add @docs.plus/extension-hypermultimedia ``` -------------------------------- ### Development commands for the extension package Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/CONTRIBUTING.md Run these commands from the repo root. `bun install` installs dependencies, `bun run build` produces the dist/ output (ESM + CJS + d.ts + styles.css), `bun run dev` starts tsup in watch mode, and `bun run typecheck` runs the TypeScript type checker. ```sh bun install # from the repo root ``` ```sh bun run build # tsup → dist/ (ESM + CJS + d.ts + styles.css) ``` ```sh bun run dev # tsup --watch ``` ```sh bun run typecheck ``` -------------------------------- ### Install @docs.plus/extension-indent with Bun Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-indent/README.md Install the extension with Bun. Requires @tiptap/core and @tiptap/pm version ^3.31.3 (Tiptap 3.x). No runtime dependencies are installed. ```sh bun add @docs.plus/extension-indent ``` -------------------------------- ### Quickstart: Configure HyperMultimediaKit Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/README.md Sets up a Tiptap editor with the HyperMultimediaKit extension. The configuration enables inline images with base64 support, enables inline Vimeo, and disables SoundCloud. Requires the styles.css import for proper rendering. ```ts import { Editor } from '@tiptap/core' import StarterKit from '@tiptap/starter-kit' import { HyperMultimediaKit } from '@docs.plus/extension-hypermultimedia' // Required. Without it the toolbar, gripper, caption and loading shell render unstyled. // The gripper is the drag-handle overlay the kit draws on the media node. import '@docs.plus/extension-hypermultimedia/styles.css' const editor = new Editor({ element: document.querySelector('#editor')!, content: '', extensions: [ StarterKit, HyperMultimediaKit.configure({ // `inline: true` moves the node into the inline group, so it flows in a paragraph. // `allowBase64: true` admits `data:image/*` sources on paste and on parse. Image: { inline: true, allowBase64: true }, Vimeo: { inline: true }, // `false` drops the node. Do not set `resizeGripper: false` instead: // that removes the whole media toolbar for the node, not only the drag handles. SoundCloud: false }) ] }) ``` -------------------------------- ### Install and build with bun commands Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/CONTRIBUTING.md Install dependencies and build the package. bun run build produces ESM, CJS, and d.ts outputs via tsup. bun run dev runs tsup in watch mode. A fresh clone requires a root-level build script because dist/ is gitignored and bun install does not build it. ```sh bun install # from the repo root bun run build # tsup → dist/ (ESM + CJS + d.ts) bun run dev # tsup --watch bun run typecheck ``` -------------------------------- ### Run development processes with Bun Source: https://github.com/docs-plus/docs.plus/blob/main/apps/hocuspocus.server/Readme.md Use Bun only. The first command starts all three processes from the monorepo root. The individual commands start each process via workspace filters, loading configuration from ../../.env.local. ```bash # All three processes (from monorepo root) make dev-backend # Or individually, via workspace filters bun run --filter @docs.plus/hocuspocus dev:rest # REST API bun run --filter @docs.plus/hocuspocus dev:ws # WebSocket bun run --filter @docs.plus/hocuspocus dev:worker # Worker ``` -------------------------------- ### Install and configure HyperMultimediaKit with Image node Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/src/nodes/image/README.md Install the package with bun, then configure the HyperMultimediaKit with the Image node. The default inline setting is false, rendering images as blocks. ```sh bun add @docs.plus/extension-hypermultimedia ``` ```js import { HyperMultimediaKit } from '@docs.plus/extension-hypermultimedia' HyperMultimediaKit.configure({ Image: { inline: false } }) ``` -------------------------------- ### Install extension package with bun Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/README.md Install any of the five docs.plus extension packages using bun. Use the matching package name from the table above, such as @docs.plus/extension-hypermultimedia or @docs.plus/extension-indent. Requires Tiptap 3.x (@tiptap/core and @tiptap/pm ^3.31.3). ```sh bun add @docs.plus/extension-hyperlink ``` -------------------------------- ### Run the database migration Source: https://github.com/docs-plus/docs.plus/blob/main/docs/self-hosting/install.md Runs the database migration as a one-shot service. Run this as its own step before starting the stack, because the backend services set `RUN_MIGRATIONS: '0'` and the migrate service has no ordering relationship with them, so a plain start can bring applications up against an unmigrated database. ```bash docker compose -f docker-compose.prod.yml --env-file .env.production run --rm migrate ``` -------------------------------- ### Migration from @tiptap/extension-code to @docs.plus/extension-inline-code Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-inline-code/CHANGELOG.md Shows the old and new editor setup and command usage when migrating from Tiptap's built-in Code mark. The new setup uses StarterKit.configure({ code: false }) and InlineCode, and replaces toggleCode() with toggleInlineCode(). ```typescript // @tiptap/extension-code new Editor({ extensions: [StarterKit] }) editor.commands.toggleCode() // @docs.plus/extension-inline-code new Editor({ extensions: [StarterKit.configure({ code: false }), InlineCode] }) editor.commands.toggleInlineCode() ``` -------------------------------- ### Chain setX with maxwidth Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/README.md Chain the setX command with focus and run. The x node has no width/height attributes, so size it with maxwidth. This example sets a dark theme and a maxwidth of 550. ```ts editor .chain() .focus() .setX({ src: 'https://x.com/user/status/123', maxwidth: 550, theme: 'dark' }) .run() ``` -------------------------------- ### Override --hm-* CSS custom properties for theming Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/README.md Override any of the documented --hm-* custom properties to retheme the package. The example sets the active toolbar background and resize border colors. ```CSS :root { --hm-toolbar-active: #ecfdf5; --hm-resize-border: #059669; } ``` -------------------------------- ### Style custom link class Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hyperlink/README.md Example CSS to style the custom link class 'my-link' applied via the Hyperlink configuration. This is needed because the mark renders a plain with no class by default. ```css a.my-link { color: #2563eb; text-decoration: underline; } ``` -------------------------------- ### Quickstart: Set up editor with InlineCode Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-inline-code/README.md Create a Tiptap editor with the InlineCode extension. Disable StarterKit's code mark to avoid conflicts. No element is passed, so mount the editor in your app before typing. ```ts import { Editor } from '@tiptap/core' import StarterKit from '@tiptap/starter-kit' import { InlineCode } from '@docs.plus/extension-inline-code' const editor = new Editor({ extensions: [ // StarterKit's `code` mark claims the same `` tag and the same // `Mod-e` key. Turn it off, or the document carries two code marks. StarterKit.configure({ code: false }), InlineCode ] }) ``` -------------------------------- ### setVimeo command example Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-hypermultimedia/src/nodes/vimeo/README.md Example of using the setVimeo command to embed a Vimeo video. Returns false for an invalid URL. Options include src, start, autoplay, byline, and layout options. ```javascript editor.commands.setVimeo({ src: 'https://vimeo.com/123456789', start: 30, autoplay: false, byline: true }) ``` -------------------------------- ### GET /api/documents/:documentId/changes response Source: https://github.com/docs-plus/docs.plus/blob/main/apps/hocuspocus.server/API.md Example response for GET /api/documents/:documentId/changes. Shows the window echoed back, the baseline/head anchors, the changed flag, and the summary rollup with contributors. The baseline is older than the requested since, which is the anchor rule, not a bug. ```json { "success": true, "data": { "documentId": "kR4pZ2mQ7tY1nB8xW3v", "since": "2026-08-01T00:00:00.000Z", "until": "2026-08-31T23:59:59.000Z", "baseline": { "version": 11, "createdAt": "2026-07-29T16:40:02.884Z" }, "head": { "version": 18, "createdAt": "2026-08-30T09:14:51.220Z" }, "changed": true, "summary": { "sectionsAdded": 1, "sectionsRemoved": 0, "sectionsModified": 2, "wordsAdded": 74, "wordsRemoved": 12, "versions": 7, "triggers": ["websocket", "checkpoint"], "contributors": [ { "id": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed", "avatar_url": "https://example.test/ada.png", "avatar_updated_at": "2026-07-01T09:12:00.000Z", "full_name": "Ada Lovelace", "display_name": "Ada", "status": "online" } ] } } } ``` -------------------------------- ### GET /api/documents/:documentId/versions response Source: https://github.com/docs-plus/docs.plus/blob/main/apps/hocuspocus.server/API.md Example response for GET /api/documents/:documentId/versions. Shows the metadata shape: version rows with trigger, triggeredBy, contributors, and createdAt. The total field is read in the same transaction as the page, so it always matches the rows beside it. ```json { "success": true, "data": { "documentId": "kR4pZ2mQ7tY1nB8xW3v", "versions": [ { "version": 14, "name": "Before the board review", "trigger": "checkpoint", "triggeredBy": null, "contributors": [], "createdAt": "2026-07-25T14:02:11.301Z" }, { "version": 13, "name": null, "trigger": "websocket", "triggeredBy": { "id": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed", "avatar_url": "https://example.test/ada.png", "avatar_updated_at": "2026-07-01T09:12:00.000Z", "full_name": "Ada Lovelace", "display_name": "Ada", "status": "online" }, "contributors": [], "createdAt": "2026-07-25T13:58:40.117Z" } ], "total": 14, "limit": 50, "offset": 0 } } ``` -------------------------------- ### outdent() example with two-space default Source: https://github.com/docs-plus/docs.plus/blob/main/extensions/extension-indent/README.md Use `outdent()` to remove `indentChars` from lines whose text starts with it. With the two-space default, the first line loses its indent and the second line, already flush, stays unchanged. `outdent()` returns `false` only when no covered line carries an indent. ```ts // Before, with the two-space default: // ' AA' // 'BB' editor.chain().focus().outdent().run() // After: 'AA' and 'BB'. The second line was already flush, so it did not move. ``` -------------------------------- ### Make and Bun command reference Source: https://github.com/docs-plus/docs.plus/blob/main/README.md Lists the main Make and Bun commands for running, building, and managing the project. Includes local and Docker-based development, production, and management targets. Note that `make clean` deletes volumes and causes data loss. ```bash # Running (local apps on host) make dev-local # Full local stack (bootstraps everything) make dev-backend # Backend only make infra-up # Start Postgres + Redis only make infra-down # Stop Postgres + Redis bun --filter @docs.plus/supabase_back stop # Stop Supabase # Running (all services in Docker) make up-dev # Development make up-prod # Production # Building make build # Production images make build-dev # Development images # Other Bun entrypoints bun run dev # Webapp only bun run dev:admin # Admin dashboard bun run doctor # Environment health check # Management make down # Stop services (auto-detects env) make logs # All logs (auto-detects env) make ps # Container status make clean # Cleanup + delete volumes (DATA LOSS) ```