Initial Commit
This commit is contained in:
83
openspec/changes/enterprise-react-rich-text/proposal.md
Normal file
83
openspec/changes/enterprise-react-rich-text/proposal.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# OpenSpec Change: enterprise-react-rich-text
|
||||
|
||||
- **Change ID**: enterprise-react-rich-text
|
||||
- **Package**: `@suaveplan/react-rich-text`
|
||||
- **Category**: react
|
||||
- **Target path**: `react/media/react-rich-text`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-04-24
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/react-rich-text.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/react-rich-text` on genesis from scratch at enterprise quality. Tiptap-based rich-text WYSIWYG: `RichTextEditor` (composite), `EditorEngine`, `Toolbar`, `EditorContent`, `SlashCommandMenu`, `MentionList`, `BubbleMenu`, hooks (`useRichText`, `useMentions`, `useSlashCommands`), markdown round-trip, slash-command registry, and typed state. Genesis adds the missing `BubbleMenu` story, extends test coverage to 100%, and adds Playwright E2E.
|
||||
|
||||
---
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
Package does not exist on genesis — fresh build.
|
||||
|
||||
---
|
||||
|
||||
## Available workspace deps on genesis
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| `@suaveplan/error` (`workspace:^`) | `RichTextError` |
|
||||
| `@suaveplan/react-translate` (`workspace:^`) | i18n for toolbar/placeholder |
|
||||
| `zod` (`catalog:forms`) | Config schemas |
|
||||
| `@opentelemetry/api` (`catalog:observability`) | `rich_text.submit` span |
|
||||
|
||||
External: `@tiptap/core catalog:editor`, `@tiptap/react catalog:editor`, `@tiptap/starter-kit catalog:editor`, `@tiptap/extension-mention catalog:editor`, `@tiptap/extension-placeholder catalog:editor`, `@tiptap/extension-link catalog:editor`, `@tiptap/pm catalog:editor`, `@tiptap/suggestion catalog:editor`, `tslib catalog:build`. React 18 peer.
|
||||
|
||||
---
|
||||
|
||||
## Preserved public API surface
|
||||
|
||||
```ts
|
||||
RichTextEditor, EditorContent, Toolbar, BubbleMenu, SlashCommandMenu, MentionList
|
||||
useRichText, useMentions, useSlashCommands
|
||||
htmlToMarkdown, markdownToHtml
|
||||
filterSlashCommands, groupCommandsByCategory, executeSlashCommand
|
||||
|
||||
// Types
|
||||
FormatState, FormatMark, BlockType, HeadingLevel, MarkdownShortcutRule,
|
||||
MentionItem, SlashCommand, SlashCommandCategory
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## New additions for genesis
|
||||
|
||||
1. **`RichTextError`** — codes: `RICH_TEXT_INIT_FAILED`, `RICH_TEXT_PARSE_FAILED`.
|
||||
2. **`BubbleMenu` story** — default, formatting, playground.
|
||||
3. **100% test coverage** — all branches in `editor-engine` and all hooks.
|
||||
4. **Playwright E2E** — type text, apply bold, verify output; type `/` → slash menu; type `@` → mention list.
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
react/media/react-rich-text/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
editor-engine/ (ts + types + md + test + index)
|
||||
editor-content/ (tsx + types + md + test + stories + index)
|
||||
toolbar/ (tsx + types + md + test + stories + index)
|
||||
bubble-menu/ (tsx + types + md + test + stories + index)
|
||||
slash-command-menu/ (tsx + types + md + test + stories + index)
|
||||
mention-list/ (tsx + types + md + test + stories + index)
|
||||
rich-text-editor/ (tsx + types + md + test + stories + index)
|
||||
hooks/
|
||||
use-rich-text/ use-mentions/ use-slash-commands/
|
||||
types/
|
||||
e2e/
|
||||
rich-text.spec.ts
|
||||
```
|
||||
107
openspec/changes/enterprise-react-rich-text/tasks.md
Normal file
107
openspec/changes/enterprise-react-rich-text/tasks.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Tasks: enterprise-react-rich-text
|
||||
|
||||
**Change ID**: enterprise-react-rich-text
|
||||
**Package**: `@suaveplan/react-rich-text`
|
||||
**Target path**: `react/media/react-rich-text`
|
||||
**Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/react-translate`
|
||||
|
||||
---
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [ ] Create directory tree. Write `package.json` at `0.1.0`, `tsconfig.json`, `vitest.config.ts`, scripts.
|
||||
- [ ] Dependencies: `@suaveplan/error workspace:^`, `@suaveplan/react-translate workspace:^`, `@tiptap/core catalog:editor`, `@tiptap/react catalog:editor`, `@tiptap/starter-kit catalog:editor`, `@tiptap/extension-mention catalog:editor`, `@tiptap/extension-placeholder catalog:editor`, `@tiptap/extension-link catalog:editor`, `@tiptap/pm catalog:editor`, `@tiptap/suggestion catalog:editor`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Error codes and types
|
||||
|
||||
- [ ] Define `RichTextError`; codes: `RICH_TEXT_INIT_FAILED`, `RICH_TEXT_PARSE_FAILED`.
|
||||
- [ ] Define all types: `FormatState`, `FormatMark`, `BlockType`, `HeadingLevel`, `MarkdownShortcutRule`, `MentionItem`, `SlashCommand`, `SlashCommandCategory`.
|
||||
|
||||
---
|
||||
|
||||
## 3. EditorEngine
|
||||
|
||||
- [ ] `buildExtensions(options)` — assembles Tiptap extensions (StarterKit + Mention + Placeholder + Link + custom slash-command + markdown shortcuts). Throws `RichTextError(RICH_TEXT_INIT_FAILED)` on invalid config.
|
||||
- [ ] `htmlToMarkdown(html: string): string`, `markdownToHtml(md: string): string` — round-trip via regex transforms.
|
||||
- [ ] `filterSlashCommands(query, commands)`, `groupCommandsByCategory(commands)`, `executeSlashCommand(cmd, editor)`.
|
||||
- [ ] Write `editor-engine.test.ts` (≤500 lines): extensions build, htmlToMarkdown/markdownToHtml round-trip, filterSlashCommands, groupByCategory, executeSlashCommand. `{ timeout: 10000 }`.
|
||||
- [ ] Write `editor-engine.md`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Hooks
|
||||
|
||||
### useRichText
|
||||
|
||||
- [ ] Creates Tiptap `Editor` instance; returns `{ editor, html, markdown, isEmpty }`. OTel `rich_text.submit` span on submit.
|
||||
- [ ] Write test + md.
|
||||
|
||||
### useMentions
|
||||
|
||||
- [ ] Provides suggestion callbacks for `@tiptap/extension-mention`; accepts `onSearch(query): Promise<MentionItem[]>`.
|
||||
- [ ] Write test + md.
|
||||
|
||||
### useSlashCommands
|
||||
|
||||
- [ ] Registers slash-command suggestion plugin; returns `{ commands, filter, execute }`.
|
||||
- [ ] Write test + md.
|
||||
|
||||
---
|
||||
|
||||
## 5. Components
|
||||
|
||||
### EditorContent
|
||||
|
||||
- [ ] `<EditorContent editor={editor} />` — renders Tiptap editor area.
|
||||
- [ ] Write test, stories (default, with-content, read-only, playground), md.
|
||||
|
||||
### Toolbar
|
||||
|
||||
- [ ] Bold/italic/underline/strike/heading/list/quote/code/link buttons wired to `editor.chain()`. `useTranslation` for tooltips.
|
||||
- [ ] Write test, stories (default, formatting-applied, playground), md.
|
||||
|
||||
### BubbleMenu
|
||||
|
||||
- [ ] Selection-anchored menu using Tiptap `BubbleMenu`. Bold/italic/link buttons.
|
||||
- [ ] Write test, stories (**default**, formatting, playground — previously missing), md.
|
||||
|
||||
### SlashCommandMenu
|
||||
|
||||
- [ ] Tiptap suggestion dropdown; renders filtered `SlashCommand` list.
|
||||
- [ ] Write test, stories, md.
|
||||
|
||||
### MentionList
|
||||
|
||||
- [ ] Mention suggestion dropdown with avatar + name.
|
||||
- [ ] Write test, stories, md.
|
||||
|
||||
### RichTextEditor
|
||||
|
||||
- [ ] Composes all components. Accepts `initialContent`, `placeholder`, `onUpdate`, `onSubmit`, `mentionSearch`.
|
||||
- [ ] Write test, stories (default, with-mentions, with-slash-commands, markdown, playground), md.
|
||||
|
||||
---
|
||||
|
||||
## 6. E2E
|
||||
|
||||
- [ ] `e2e/rich-text.spec.ts` — Playwright:
|
||||
- Type text, click Bold in toolbar → `<strong>` in output.
|
||||
- Type `/` → slash menu appears; select "Heading 1" → H1 applied.
|
||||
- Type `@` → mention list appears; click item → mention inserted.
|
||||
|
||||
---
|
||||
|
||||
## 7. Verify and publish
|
||||
|
||||
- [ ] `bunx turbo lint typecheck test --filter=@suaveplan/react-rich-text` — green.
|
||||
- [ ] `bunx turbo test:coverage --filter=@suaveplan/react-rich-text` — 100%.
|
||||
- [ ] `bun scripts/publish.ts --package @suaveplan/react-rich-text`.
|
||||
- [ ] Archive change folder.
|
||||
Reference in New Issue
Block a user