@rtif-sdk/web
v2.14.0
Published
RTIF web platform implementation (contenteditable)
Maintainers
Readme
@rtif-sdk/web
Web platform implementation for RTIF (Rich Text Input Format). A vanilla TypeScript rich text editor built on contenteditable, with plugin presets, keyboard shortcuts, clipboard handling, IME support, and accessibility.
Install
npm install @rtif-sdk/webQuick Start
import { createWebEditor, PRESET_STANDARD } from '@rtif-sdk/web';
const editor = createWebEditor({
root: document.getElementById('editor')!,
plugins: PRESET_STANDARD,
placeholder: 'Start typing...',
accessibleLabel: 'Document editor',
});
// Subscribe to changes
editor.onChange((state) => {
console.log('Document:', JSON.stringify(state.doc));
});Presets
Four plugin presets are included, each a superset of the previous:
| Preset | Plugins | Includes |
|--------|---------|----------|
| PRESET_PLAINTEXT | 0 | No formatting |
| PRESET_BASIC | 4 | Bold, italic, underline, link |
| PRESET_STANDARD | 11 | + strikethrough, code, heading, list, blockquote, code block, HR |
| PRESET_FULL | 20 | + text color, background color, font size, font family, alignment, indent, callout, image, embed |
Features
- 8 RTIF operation types with full undo/redo
- IME/composition input (CJK, accent keys, dictation)
- Clipboard: copy/cut/paste with RTIF JSON, HTML, and plain text formats
- Keyboard shortcuts (Cmd/Ctrl+B, I, U, K, Z, etc.)
- Plugin system for custom block types, marks, commands, and input rules
- Content pipeline for paste/drop handling (images, URLs, files)
- Streaming content replacement (AI-assisted editing, incremental updates)
- @mention trigger infrastructure
- Accessibility: ARIA roles, keyboard-only operation, screen reader support
EditorFeatures
UI features are tree-shakeable opt-ins, passed via the features array:
import { createWebEditor, blockGutter, contextMenu, linkPopover, defaultFeatures } from '@rtif-sdk/web';
// Individual features
const editor = createWebEditor({
root,
engine,
features: [blockGutter(), contextMenu(), linkPopover()],
});
// Or use the convenience helper (blockGutter + contextMenu + linkPopover)
features: [defaultFeatures()]blockDrag()
Drag-to-reorder blocks via a drag handle, with keyboard support and a theme-aware preview.
Use blockDrag as an alternative to blockGutter — do not use both at the same time.
import { createWebEditor, blockDrag } from '@rtif-sdk/web';
const editor = createWebEditor({
root,
engine,
features: [blockDrag()], // drag handle + keyboard shortcuts + preview
});
// With custom config
features: [blockDrag({ preview: { maxWidth: 400 }, keyboard: true })]- Keyboard shortcuts: Alt+ArrowUp / Alt+ArrowDown reorder the focused block
- Drag preview: theme-aware (respects
rtif-dark/darkclass andprefers-color-scheme) - Preview config: customize via
DragPreviewConfig(e.g.,maxWidth,opacity)
Streaming Content (LLM Integration)
Stream AI-generated content into the editor. Built on engine transactions for single-undo behavior. Adds .rtif-streaming CSS class to editor root during active sessions. Escape cancels.
import { createStreamingPlugin, createWebStreamInsert, createWebStreamRewrite } from '@rtif-sdk/web';
const { plugin, attach } = createStreamingPlugin();
engine.use(plugin);
const handle = attach(editor);
// Insert at cursor (no selection required)
const insert = createWebStreamInsert({
handle, editor, offset: cursor, deserialize,
});
// Or rewrite a selection (boundary-aware corrections)
const rewrite = createWebStreamRewrite({
handle, editor, startOffset: start, endOffset: end, deserialize,
});
// Both share the same StreamSession interface
for await (const token of llm.stream(prompt)) {
insert.pushToken(token); // or rewrite.pushToken(token)
}
insert.commit();For format-aware streaming (e.g., markdown from an LLM), use createStreamingAdapter(). See @rtif-sdk/engine README for details.
Multi-Block Selection
All mark commands and block type toggle commands work on multi-block selections. When multiple blocks are selected:
- Mark commands (bold, italic, color, fontSize, fontFamily, etc.) split into per-block
set_span_marksoperations automatically. - Block type toggles (heading, blockquote, code block, callout, list) apply to all selected blocks as a single undo group.
The engine provides three methods for multi-block operations:
engine.getBlocksInSelection(); // All blocks in selection
engine.isBlockTypeInSelection('heading', { level: 1 }); // Check all selected blocks
engine.splitMarkOpsAcrossBlocks(offset, count, marks); // Per-block mark opsConfigure toolbar isActive behavior via blockTypeActiveStrategy:
const engine = createEngine(doc, {
blockTypeActiveStrategy: 'first', // 'all' (default) | 'first'
});'all'— toolbar shows active only when every selected block matches (default)'first'— toolbar reflects the first block in the selection (Google Docs/Notion behavior)
Plugin Kit
Create custom plugins with minimal boilerplate:
import { defineBooleanMark, defineBlockType } from '@rtif-sdk/web';
const highlight = defineBooleanMark({
markType: 'highlight',
shortcut: { key: 'h', mod: true, shift: true },
renderer: { apply(el) { el.style.backgroundColor = 'yellow'; } },
});Styles
All RTIF CSS lives in this package. Import the aggregated stylesheet:
import '@rtif-sdk/web/styles/all.css';For dark mode, add the optional dark theme after:
import '@rtif-sdk/web/styles/all.css';
import '@rtif-sdk/web/styles/dark.css';Dark mode activates automatically via prefers-color-scheme: dark, the .rtif-dark class, or the .dark class (Tailwind/shadcn convention). All rules use :where() zero-specificity so your CSS always wins.
For selective imports, the full CSS reference, and custom theming, see docs/styling.md in this package.
AI Agent Setup
This package ships AI coding assistant rules for Claude Code, Cursor, and GitHub Copilot — including React integration guidance. Since @rtif-sdk/react depends on this package, npx rtif-setup works whether you installed @rtif-sdk/web or @rtif-sdk/react. After installing, run:
npx rtif-setupThis installs framework guidance, plugin scaffolding commands, code review checklists, and a troubleshooting agent into your project. Supports selective targets:
npx rtif-setup claude # Claude Code only
npx rtif-setup cursor copilot # Cursor + Copilot
npx rtif-setup --gitignore # Also add generated files to .gitignoreLicense
MIT
