npm package: @rtif-sdk/web
Description: RTIF web platform implementation (contenteditable)
Author: coryrobinson42@gmail.com
Latest version: 2.14.0

# @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

```bash
npm install @rtif-sdk/web
```

## Quick Start

```typescript
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:

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

```typescript
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`/`dark` class and `prefers-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.

```typescript
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_marks` operations 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:

```typescript
engine.getBlocksInSelection();                           // All blocks in selection
engine.isBlockTypeInSelection('heading', { level: 1 });  // Check all selected blocks
engine.splitMarkOpsAcrossBlocks(offset, count, marks);   // Per-block mark ops
```

Configure toolbar `isActive` behavior via `blockTypeActiveStrategy`:

```typescript
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:

```typescript
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:

```ts
import '@rtif-sdk/web/styles/all.css';
```

For dark mode, add the optional dark theme after:

```ts
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:

```bash
npx rtif-setup
```

This installs framework guidance, plugin scaffolding commands, code review checklists, and a troubleshooting agent into your project. Supports selective targets:

```bash
npx rtif-setup claude        # Claude Code only
npx rtif-setup cursor copilot # Cursor + Copilot
npx rtif-setup --gitignore   # Also add generated files to .gitignore
```

## License

MIT