@ixo/editor
v6.2.0
Published
A custom BlockNote editor wrapper for IXO team
Readme
@ixo/editor
A collaborative, workflow editor for the IXO ecosystem, built on BlockNote. Beyond rich text, @ixo/editor turns a document into a runnable flow: blocks carry configuration, conditional logic, and executable actions, execution is gated by a flow engine and authorized with UCAN capabilities, and every change is synchronized in real time across collaborators over the Matrix protocol via Yjs CRDTs.
Internal package. Maintained for IXO products (primarily impacts-x-web). It is published publicly but is not intended as a general-purpose editor. The public API changes with product needs.
What it is
The editor operates in two modes, selected by a document's docType:
- Template mode (
docType: 'template') — design time. Authors configure reusable workflow blocks: properties, conditional logic, dependencies, and which actor may execute what. - Flow mode (
docType: 'flow', the default) — run time. Participants execute the configured workflow. Conditions are evaluated live, blocks show/hide/enable accordingly, actions run through the flow engine, and per-block runtime state syncs to every collaborator.
Under the hood it combines:
- BlockNote 0.29 + a custom IXO block schema (checkbox, form, list, proposal, claim, bid, action, notify, domain, …).
- Yjs CRDTs over Matrix (
@ixo/matrix-crdt) for conflict-free, multi-user, persistent state — no separate database required. - A flow engine that runs blocks through an activation → authorization → invocation → action → storage pipeline.
- An action registry of pluggable action types (
qi/<namespace>.<verb>) that the genericactionblock dispatches to. - UCAN delegations and invocations for cryptographic, per-block execution authorization.
- A flow compiler that reads/writes a portable "Base UCAN" flow representation, and a flow agent runtime for automated flow progression.
For the full picture, start with docs/architecture/architecture-overview.md and the plain-language docs/authorization/flows-actions-ucans-explained.md.
Features
- 🧩 Workflow blocks — checkboxes, forms, lists, proposals, claims, bids, API requests, notifications, domain/entity actions, and a generic
actionblock. - 🔀 Template vs flow modes — design reusable workflows, then execute them.
- 🤝 Real-time collaboration — Matrix + Yjs CRDTs; state is collaborative and persistent by default.
- ⚙️ Flow engine — declarative activation conditions, actor authorization, and per-block runtime state.
- 🔐 UCAN authorization — flow owners delegate execution capabilities; invocations provide an auditable trail.
- 🧠 Action registry — add new action types declaratively; the host app supplies the side-effecting handlers.
- 🖥️ Server-friendly core —
@ixo/editor/coreexposes the flow engine, action registry, UCAN, and flow agent with no React/Mantine/BlockNote UI dependencies. - 🎨 Mantine UI — themable light/dark, self-contained CSS bundles.
- 🎯 TypeScript-first — augmented editor types and exported prop schemas.
Installation
pnpm add @ixo/editor
# or: npm install @ixo/editor / yarn add @ixo/editorPeer dependencies
| Package | Version | Needed for |
| --------------------------------------------------- | ---------- | ------------------------------ |
| react, react-dom | ^18.0.0 | Everything |
| @mantine/core, @mantine/hooks, @mantine/dates | ^7.11.2 | The editor UI |
| @ixo/matrix-crdt | * | Collaborative editing |
| @ixo/surveys | ^0.1.0 | Form / survey blocks |
| matrix-js-sdk | >=37.5.0 | Collaborative editing (Matrix) |
v6 is Mantine-only. The Shadcn UI variant that shipped in v2 has been removed. See Migration.
Package entry points
| Import | Contents |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| @ixo/editor | Default entry — the Mantine editor plus the full public API (hooks, components, flow engine, UCAN, flow compiler, flow agent). |
| @ixo/editor/mantine | Explicit Mantine entry (same UI surface). |
| @ixo/editor/core | UI-free flow engine, action registry, UCAN, flow compiler, and flow agent. Safe for Node/server consumers (e.g. oracles). |
CSS bundles
| Stylesheet | Contents |
| ------------------------------- | -------------------------------------------------------------- |
| @ixo/editor/style.css | Complete bundle: Inter fonts + Mantine + IXO styles (default). |
| @ixo/editor/style-mantine.css | Same as style.css. |
| @ixo/editor/style-scoped.css | Scoped variant to reduce global bleed. |
| @ixo/editor/style-core.css | IXO custom styles only (advanced/bring-your-own base). |
Quick start (standalone)
A non-collaborative, single-user editor:
import { MantineProvider } from '@mantine/core';
import { useCreateIxoEditor, IxoEditor } from '@ixo/editor';
import '@ixo/editor/style.css';
function MyEditor() {
const editor = useCreateIxoEditor({
theme: 'light',
initialContent: [
{ type: 'heading', content: 'Welcome to the IXO Editor', props: { level: 1 } },
{ type: 'paragraph', content: 'Type “/” to insert a block.' },
],
});
return (
<MantineProvider>
<IxoEditor editor={editor} onChange={() => console.log('changed')} />
</MantineProvider>
);
}Collaborative editing
Real-time, multi-user editing backed by a Matrix room. useCreateCollaborativeIxoEditor builds the Y.Doc, wires the MatrixProvider, and returns the editor alongside the live CRDT handles.
import { MantineProvider } from '@mantine/core';
import { useCreateCollaborativeIxoEditor, IxoEditor } from '@ixo/editor';
import '@ixo/editor/style.css';
function CollaborativeEditor({ matrixClient }) {
const { editor, connectionStatus, connectedUsers, awarenessInstance } = useCreateCollaborativeIxoEditor({
theme: 'light',
roomId: '!roomId:matrix.org',
matrixClient,
permissions: { write: true },
user: {
id: '@did-ixo-ixo1abc:server', // Matrix user ID
name: 'Jane Doe',
color: '#FF5733',
accessToken: 'matrix-access-token',
address: 'ixo1abc…', // bech32 address
did: 'did:ixo:ixo1abc…', // full DID, required for UCAN signing
},
});
return (
<MantineProvider>
<div>Connection: {connectionStatus}</div>
<IxoEditor editor={editor} connectedUsers={connectedUsers} awarenessInstance={awarenessInstance} />
</MantineProvider>
);
}The hook returns { editor, connectionStatus, title, yDoc, root, flowArray, connectedUsers, awarenessInstance }. permissions.write defaults to false (read-only). See docs/architecture/architecture-overview.md for the Y.Doc structure and docs/authorization/matrix-permissions.md for the access model.
Providing handlers
The editor is domain-agnostic: it never imports blockchain, DAO, or API code. The host app injects that behavior as handlers (and optional blockRequirements) on <IxoEditor>. Blocks and action types call into these to fetch data and perform side effects.
<IxoEditor
editor={editor}
handlers={{
getCurrentUser: () => ({ address: wallet.address, name: wallet.name }),
submitClaim: async (params) => cosmos.submitClaim(params),
sendNotification: async (params) => api.notify(params),
// …implement the handlers your blocks/action types require
}}
blockRequirements={{ proposal: { coreAddress: dao.coreAddress } }}
/>The full BlocknoteHandlers surface (proposals, claims, bids, lists, notifications, domain creation, UCAN signing, user search, …) is exported and documented in docs/architecture/architecture-overview.md. For action types specifically, handlers are adapted into ctx.services via buildServicesFromHandlers — see the action registry section and docs/guides/action-block-pattern.md.
useCreateIxoEditor options
| Option | Type | Default | Description |
| ------------------- | --------------------------------- | ------------------ | -------------------------------------------- |
| theme | 'light' \| 'dark' | 'light' | Editor color theme |
| docType | 'template' \| 'flow' | 'flow' | Design vs execution mode (local, not synced) |
| initialContent | PartialBlock[] | undefined | Initial editor content |
| uploadFile | (file: File) => Promise<string> | Data-URL converter | File upload handler |
| editable | boolean | true | Whether the editor is editable |
| sideMenu | boolean | true | Show side menu (drag handle, plus button) |
| slashMenu | boolean | true | Enable slash command menu |
| formattingToolbar | boolean | true | Show formatting toolbar |
| linkToolbar | boolean | true | Show link toolbar |
| filePanel | boolean | true | Show file panel |
| tableHandles | boolean | true | Show table manipulation handles |
useCreateCollaborativeIxoEditor extends these with roomId, matrixClient, user, permissions, and optional docId / title / sourceTemplateId (IxoCollaborativeEditorOptions).
<IxoEditor> props (selected)
| Prop | Type | Description |
| --------------------------------------------------- | ---------------------------- | --------------------------------------------- |
| editor | IxoEditorType \| undefined | Instance from a create hook |
| handlers | BlocknoteHandlers | Host integration callbacks for blocks/actions |
| blockRequirements | BlockRequirements | Runtime context data for specific blocks |
| translate | Translate | i18n function (t) from the host app |
| mantineTheme | MantineTheme | Theme object to apply to the editor subtree |
| onChange | () => void | Fired on content change |
| onSelectionChange | () => void | Fired on selection change |
| connectedUsers / awarenessInstance | see collaborative hook | Enable per-block presence indicators |
| className, children, coverImageUrl, logoUrl | — | Layout/customization slots |
See IxoEditorProps in the exported types for the complete list.
Core concepts
Flow engine
@ixo/editor/core (src/core/lib/flowEngine/) runs a block through a four-stage pipeline:
- Activation — are upstream dependency conditions satisfied?
- Authorization — is the current actor permitted (whitelist or UCAN delegation chain)?
- Execution —
executeNode(...)orchestrates invocation → action → storage. - Runtime — a Y.Map-backed
FlowRuntimeStateManagertracks per-block state (idle/running/completed/failed) and syncs it via CRDT.
Runtime state is the source of truth for a block's result — never mirror it into local React state. This rule is spelled out in CLAUDE.md and docs/architecture/block-runtime-state.md.
Action registry
The generic action block dispatches to a registered action type keyed by a can string (qi/<namespace>.<verb>). Each action type declares its inputs, output schema, and a run() that calls into ctx.services. The host app implements the underlying handlers; buildServicesFromHandlers wires them in. To add one, follow docs/guides/action-block-pattern.md and, for signed actions, docs/guides/slide-to-sign-and-diff-guide.md.
UCAN authorization
Flow owners (flowOwnerDid) issue delegations granting specific actors permission to execute specific blocks; invocations record proof of execution. Both live in dedicated Y.Doc maps and sync with the document. See docs/authorization/ucan-technical.md and docs/authorization/ucan-flow-authorization.md.
Flow compiler & flow agent
The flow compiler (src/core/lib/flowCompiler/) converts between a portable "Base UCAN" flow description and the live Y.Doc (setupFlowFromBaseUcan, readFlowAsBaseUcan, compileBaseUcanFlow, …), enabling flows to be authored, cloned, and merged. The flow agent runtime (src/core/lib/flowAgent/) drives automated progression of a flow (leases, command queue, policy evaluation, ledger) — see docs/flow-engine/.
Custom blocks
The IXO block schema is registered automatically by the create hooks. Blocks can be inserted from the slash menu (/) or programmatically:
editor.insertBlocks([{ type: 'list', props: { title: 'Members', did: 'did:ixo:entity123', fragmentIdentifier: 'members' } }], editor.getTextCursorPosition().block, 'after');The full block catalog, prop schemas, and conditional-logic system are documented in docs/architecture/architecture-overview.md and docs/architecture/editor-type-map.md.
Server-side usage
Consumers that only need flow logic (e.g. an oracle validating or advancing a flow) can import the UI-free core:
import { executeNode, isAuthorized, createUcanService, compileBaseUcanFlow, tickFlowAgent } from '@ixo/editor/core';This entry point pulls in no React, Mantine, or BlockNote code.
Documentation
All documentation lives in docs/ and is indexed by docs/README.md. Highlights:
docs/architecture/architecture-overview.md— the definitive system reference.docs/authorization/flows-actions-ucans-explained.md— flows, actions & UCANs from zero.docs/guides/— building action blocks, slide-to-sign + diff views, styling.docs/authorization/— UCANs, signing, Matrix permissions.docs/flow-engine/— engine design rationale and roadmap.docs/integrations/— third-party integrations (Calendar, Xero via Composio).CLAUDE.md— project rules and the condensed runtime-state canon.CONTEXT.md— domain glossary (ubiquitous language).
Development
pnpm install # install dependencies
pnpm build # build all bundles (tsup → dist/, + CSS)
pnpm run dev # rebuild on change
pnpm run type-check # tsc --noEmit
pnpm test # vitest
pnpm run example:action-gallery # run the action-type gallery example appProject structure
editor/
├── src/
│ ├── core/ # UI-free core (flow engine, action registry, UCAN,
│ │ │ # flow compiler, flow agent, types, GraphQL client)
│ │ └── index.ts # @ixo/editor/core entry
│ ├── mantine/ # Mantine editor: IxoEditor, hooks, blocks, components, context
│ │ └── index.ts # @ixo/editor/mantine entry
│ ├── styles/ # Source CSS
│ ├── data/ icons/ images/ # Static assets
│ ├── test-utils/ # Test helpers
│ └── index.ts # Default entry (Mantine + full public API)
├── docs/ # Documentation (see docs/README.md)
├── examples/ # Example apps & configs (action-type-gallery, …)
├── plans/ # Implementation plans
├── fonts/ # Inter font files
├── dist/ # Build output (generated)
├── style*.css # Published CSS bundles (generated)
└── package.jsonRequirements
- React 18+ and React DOM 18+
- Mantine 7 (
@mantine/core,@mantine/hooks,@mantine/dates) - A modern browser (ES2020+)
- For collaboration: a Matrix server,
matrix-js-sdk, and@ixo/matrix-crdt
Migration
v2 (multi-UI) → v6 (Mantine-only)
The Shadcn UI variant and its @ixo/editor/shadcn / style-shadcn.css entry points have been removed. Use the default or /mantine entry:
// Before (v2)
import { IxoEditor } from '@ixo/editor/shadcn';
import '@ixo/editor/style-shadcn.css';
// Now (v6)
import { IxoEditor, useCreateIxoEditor } from '@ixo/editor';
import '@ixo/editor/style.css';The editor also evolved from a rich-text wrapper into a full workflow/flow engine — the flow engine, action registry, UCAN authorization, flow compiler, and flow agent are new since v2. See the documentation to adopt them.
Contributing
Internal package. Use Conventional Commits (enforced by commitlint; releases are automated via semantic-release). See CLAUDE.md for project rules and commit format. Record structural repo/doc reorganizations in ORGANIZATION-LOG.md.
License
MIT © IXO Team
