diff --git a/.claude/docs/table/block-selection-core-migration-design-2026-03-12.md b/.claude/docs/table/block-selection-core-migration-design-2026-03-12.md new file mode 100644 index 0000000000..0432433d1e --- /dev/null +++ b/.claude/docs/table/block-selection-core-migration-design-2026-03-12.md @@ -0,0 +1,184 @@ +# Block Selection Core Migration Design Draft + +## Background + +- The current block selection implementation lives in [BlockSelectionPlugin.tsx](/Users/felixfeng/Desktop/udecode/plate/packages/selection/src/react/BlockSelectionPlugin.tsx). +- The implementation is already on the right track: state is lightweight, the core data is `selectedIds: Set`, and it provides per-id selectors. +- In contrast, table selection takes a heavier path: + - [useSelectedCells.ts](/Users/felixfeng/Desktop/udecode/plate/packages/table/src/react/components/TableElement/useSelectedCells.ts) recomputes the table/cell grid and writes `selectedCells` / `selectedTables` + - [useIsCellSelected.ts](/Users/felixfeng/Desktop/udecode/plate/packages/table/src/react/components/TableCellElement/useIsCellSelected.ts) makes every cell subscribe to the entire selection state +- The long-term direction: selection becomes a core editor capability, and block selection / table selection both build on top of it. +- Phase 1 does not address table selection. It only completes the block selection migration, but the design must leave room for table extension. + +## Problem Definition + +Although the current block selection works, it is still "a complete selection state and behavior set owned by a plugin", which causes several structural issues: + +1. Selection is a feature-level capability, not a core-level capability +2. Other selection modes cannot reuse the same underlying model +3. Table selection will likely grow another parallel state set instead of reusing unified selection infrastructure + +We need to take the first step: move block selection state ownership down to core, without immediately changing the base semantics of `editor.selection`. + +## Goals + +- Make block selection a core-backed capability +- Keep existing block selection interactions and external behavior unchanged +- Do not modify `editor.selection` in Phase 1 +- Introduce a core model that can accommodate future non-text selection +- Keep Phase 1 within a scope that can be landed and verified independently + +## Non-Goals + +- Do not redo table selection in this phase +- Do not change Slate's `editor.selection` to `Range | Range[]` +- Do not require all transforms to understand multi-range discrete ranges in Phase 1 +- Do not detail table merge / border implementation in this document + +## Phase 1 Scope + +Phase 1 does one thing: migrate block selection from "plugin-owned state" to "core-owned state". + +Expected outcome after this phase: + +- Block selection state is registered and managed by core +- Block selection UI can remain in the selection package +- Existing block selection commands continue to work, but delegate to core internally +- External callers no longer treat block plugin options as the ultimate source of truth + +## Proposed Core State Model + +The Phase 1 model should be minimal — do not prematurely include the full table shape. + +```ts +type EditorSelectionState = { + primary: Range | null; + block: { + anchorId: string | null; + selectedIds: Set; + isSelecting: boolean; + isSelectionAreaVisible: boolean; + }; +}; +``` + +Notes: + +- `primary` continues to correspond to the current text selection +- `block` is a new core selection channel, not a plugin-private store +- `selectedIds` continues to use `Set` because it is already the correct data shape: cheap per-id lookups, low-cost membership checks +- Phase 1 does not add a table descriptor, but the state boundary must not be hardcoded to "only block as an extra selection type" + +## API Direction + +Core should expose a thin selection API layer, and block selection adapts on top of it. + +```ts +editor.api.selection.getPrimary() +editor.api.selection.setPrimary(range) + +editor.api.selection.block.get() +editor.api.selection.block.clear() +editor.api.selection.block.set(ids) +editor.api.selection.block.add(ids) +editor.api.selection.block.delete(ids) +editor.api.selection.block.has(id) +editor.api.selection.block.isSelecting() +``` + +Existing block-facing helpers can be retained, but their semantics should change: + +- `editor.getApi(BlockSelectionPlugin).blockSelection.add(...)` +- `editor.getApi(BlockSelectionPlugin).blockSelection.clear()` +- `editor.getApi(BlockSelectionPlugin).blockSelection.getNodes(...)` + +These helpers should become compatibility wrappers rather than continuing to hold their own real state. + +## Rendering Layer Direction + +Phase 1 does not need to rewrite block selection visual interactions — only migrate state ownership. + +- Block selection area UI can remain in the selection package +- [useBlockSelected.ts](/Users/felixfeng/Desktop/udecode/plate/packages/selection/src/react/hooks/useBlockSelected.ts) switches to reading a core-backed selector +- `BlockSelectionPlugin` shrinks to an adapter: event wiring, render integration, and compatibility layer API + +This approach carries significantly lower risk than "rewriting the entire interaction model at once". + +## Migration Steps + +### Step 1: Introduce core block selection state + +- Add block selection state structure in core +- Expose minimal selectors and mutators +- Keep `editor.selection` behavior unchanged + +### Step 2: Redirect block selection API + +- Redirect reads/writes of `selectedIds`, `anchorId`, `isSelecting`, etc. behind the core API +- Continue exposing the existing block selection command surface externally + +### Step 3: Redirect hooks and render + +- Hooks like [useBlockSelected.ts](/Users/felixfeng/Desktop/udecode/plate/packages/selection/src/react/hooks/useBlockSelected.ts) switch to consuming the core-backed selector +- UI behavior remains unchanged + +### Step 4: Reduce plugin state ownership + +- `BlockSelectionPlugin` retains: + - Event wiring + - Adapter APIs + - Rendering integration +- Core becomes the sole state owner + +## Compatibility Strategy + +To keep the blast radius under control, Phase 1 should adhere to these rules: + +- `editor.selection` continues to be `Range | null` +- Not all editing commands are required to understand block selection immediately +- Block-specific operations continue to explicitly read block selection state +- Avoid introducing large-scale type modifications in Phase 1 + +This allows the migration to be incremental rather than affecting the entire Slate / Plate command surface at once. + +## Why This Step First + +The value of this phase: + +- Reclaim selection ownership into core +- Remove a feature-level state owner +- Provide a unified foundation for other future selection modes + +It is also low-risk because block selection's current data model is already relatively healthy — the main issue is "where the state lives", not "what the state looks like". + +## Table Direction as Design Constraint Only + +Table is not in Phase 1 scope, but Phase 1 design must avoid blocking future table work. + +Phase 1 should explicitly avoid: + +- Hardcoding core selection to serve only block ids +- Treating "flat id set" as the only non-text selection shape +- Letting future table selection still depend on materialized node arrays + +Future table selection will likely need: + +- A table-scoped descriptor instead of `selectedCells: TElement[]` +- Keyed selectors instead of each cell subscribing to the entire selection +- Expressive power for non-contiguous / grid-shaped selection semantics + +No need to detail the table design here — just ensure Phase 1 state boundaries and API do not prevent Phase 2 from extending in this direction. + +## Open Questions + +- Should core selection expose a channeled model (`block` / `table` / `primary`) or a more generic descriptor registry? +- After migration, which `BlockSelectionPlugin` APIs are still worth keeping as public interfaces? +- Should block selection render logic stay in `packages/selection` long-term, or continue moving toward core? + +## Phase 1 Acceptance Criteria + +- Block selection state is owned by core +- Existing block selection interaction behavior remains consistent +- `useBlockSelected` and related selectors switch to reading core-backed state +- Existing block selection commands continue to work, delegating to core via compatibility wrappers +- Phase 1 does not require any changes to table selection behavior diff --git a/.claude/docs/table/dev-table-perf-performance-2026-03-10.md b/.claude/docs/table/dev-table-perf-performance-2026-03-10.md new file mode 100644 index 0000000000..5f28210ce5 --- /dev/null +++ b/.claude/docs/table/dev-table-perf-performance-2026-03-10.md @@ -0,0 +1,50 @@ +# dev/table-perf 性能快照 + +## 环境 + +- 测试时间:2026-03-10 23:36:30 CST +- 页面地址:`http://localhost:3002/dev/table-perf` +- 应用:`apps/www`,Next.js 16.1.6(Turbopack dev) +- 浏览器:`agent-browser` + Chromium 138.0.7204.15 +- 机器:macOS 15.7.3 +- 页面错误:`agent-browser errors` 未发现报错 + +## 采样方法 + +- 阅读 `apps/www/src/app/dev/table-perf/page.tsx`,确认页面内置了两套测试: + - benchmark:`5` 次 warmup + `20` 次 measured remount + - input latency:`10` 次 warmup + `50` 次 measured inserts +- 使用 `agent-browser` 打开 `/dev/table-perf` +- 读取页面 Metrics 面板和 console 输出 +- 本次记录两组数据: + - 默认 `10 x 10`(100 cells) + - 压力 `60 x 60`(3600 cells) + +## 结果 + +| 配置 | Cells | Initial render | Benchmark mean | Benchmark median | Benchmark p95 | Input mean | Input median | Input p95 | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| 10 x 10 | 100 | 209.30 ms | 99.32 ms | 83.80 ms | 147.50 ms | 28.07 ms | 28.40 ms | 30.50 ms | +| 60 x 60 | 3600 | 2663.30 ms | 2499.24 ms | 2453.30 ms | 2848.70 ms | 390.48 ms | 379.40 ms | 443.70 ms | + +## 对比 + +- `60 x 60` 相比 `10 x 10` +- Initial render:`12.72x` +- Benchmark mean:`25.16x` +- Input mean:`13.91x` + +## 结论 + +- `10 x 10` 基线可接受。input latency 均值约 `28 ms`,交互感觉应当是顺的。 +- `60 x 60` 仍可跑完,但已经明显进入高延迟区间: + - 初始挂载约 `2.66 s` + - benchmark 均值约 `2.50 s` + - 单次输入延迟均值约 `390 ms` +- 以当前页面表现看,大表场景下主要问题不是偶发尖峰,而是整体延迟已经稳定抬高到肉眼可感知的程度。 + +## 解读注意点 + +- 切换 preset 后点击 `Generate Table`,页面会刷新当前表格和基础 metrics。 +- `Benchmark Results` 会被清空后重新计算。 +- `Input Latency` 结果不会在 `Generate Table` 时自动清空;如果切到新 preset,必须重新跑一次 input latency 才能读到当前配置的数据。 diff --git a/.claude/docs/table/dev-table-perf-performance-2026-03-12.md b/.claude/docs/table/dev-table-perf-performance-2026-03-12.md new file mode 100644 index 0000000000..80545a8ada --- /dev/null +++ b/.claude/docs/table/dev-table-perf-performance-2026-03-12.md @@ -0,0 +1,59 @@ +# dev/table-perf Performance Snapshot + +## Environment + +- Test time: 2026-03-12 21:46:54 CST +- Page URL: `http://localhost:3000/dev/table-perf` +- App: `apps/www` +- Commit: `98ae3116f` +- Browser: Playwright + Chromium `138.0.7204.15` +- Page accessibility check: confirmed `/dev/table-perf` loads via `agent-browser` + +## Sampling Method + +- Read `apps/www/src/app/dev/table-perf/page.tsx`, confirmed the page has two built-in tests: + - Benchmark: `5` warmup + `20` measured remount iterations + - Input latency: `10` warmup + `50` measured inserts +- Used `Large (1600)` preset, corresponding to `40 x 40` +- Click sequence: + - `Generate Table` + - `Run Benchmark (20 iter)` + - `Test Input Latency (50 samples)` +- Metrics read from the page Metrics panel + +## Results + +### 40 x 40 (1600 cells) + +| Category | Metric | Value | +| --- | --- | ---: | +| Metrics | Initial render | 1002.00 ms | +| Metrics | Re-render count | 3 | +| Metrics | Last render | 0.40 ms | +| Metrics | Avg render | 351.27 ms | +| Metrics | Render median | 51.40 ms | +| Metrics | Render p95 | 1002.00 ms | +| Benchmark Results | Mean | 841.44 ms | +| Benchmark Results | Median | 827.10 ms | +| Benchmark Results | P95 | 959.30 ms | +| Benchmark Results | P99 | 959.30 ms | +| Benchmark Results | Min | 804.00 ms | +| Benchmark Results | Max | 959.30 ms | +| Benchmark Results | Std Dev | 31.64 ms | +| Input Latency | Mean | 40.74 ms | +| Input Latency | Median | 38.70 ms | +| Input Latency | P95 | 52.70 ms | +| Input Latency | Min | 28.00 ms | +| Input Latency | Max | 61.60 ms | + +## Conclusion + +- The `40 x 40` remount benchmark is still in the high-cost range, with a mean of ~`841 ms`. +- The `40 x 40` input latency mean is ~`41 ms`, median ~`39 ms`, well below the `100+ ms` threshold where noticeable lag occurs. +- This snapshot is better suited for tracking large-table input performance; if the focus shifts to resize/hover interactions, a separate drag/hover profiling session is recommended. + +## Interpretation Notes + +- `Initial render`, `Re-render count`, `Last render`, and `Avg render / Median / P95` come from the left-side Metrics panel. +- `Benchmark Results` are the remount benchmark statistics. +- `Input Latency` results do not auto-clear when switching presets or clicking `Generate Table`; re-run after changing configuration. diff --git a/apps/www/public/r/components-changelog-docs.json b/apps/www/public/r/components-changelog-docs.json index fa9956110c..1d81262ad0 100644 --- a/apps/www/public/r/components-changelog-docs.json +++ b/apps/www/public/r/components-changelog-docs.json @@ -7,7 +7,7 @@ "files": [ { "path": "../../docs/components/changelog.mdx", - "content": "---\ntitle: Changelog\ndescription: Latest component updates and announcements.\ntoc: true\n---\n\nSince Plate UI is not a component library, a changelog is maintained here.\n\nUse the [CLI](https://platejs.org/docs/components/cli) to install the latest version of the components.\n\n## January 2026 #28\n\n### January 20 #28.3\n- **AIChat**: Fixed assistant input width not expanding when panel is expanded\n\n### January 19 #28.2\n- **`inline-combobox`**: Added keyboard navigation cycling at list boundaries\n - ArrowUp at first item cycles to last item\n - ArrowDown at last item cycles to first item\n - Prevents null activeId using capture-phase event handling\n\n### January 17 #28.1\n- **DOCX Import/Export**: New `@platejs/docx-io` package for Word document handling\n - `import-toolbar-button`: Import DOCX files with `importDocx`\n - `export-toolbar-button`: Export to DOCX with `exportToDocx`\n - `docx-export-kit`: DOCX-optimized components using `DocxExportPlugin.configure({ override: { components } })`\n- **DOCX Static Components**: Added to existing static files with `*Docx` naming pattern:\n - `callout-node-static`: `CalloutElementDocx`\n - `code-block-node-static`: `CodeBlockElementDocx`, `CodeLineElementDocx`, `CodeSyntaxLeafDocx`\n - `column-node-static`: `ColumnElementDocx`, `ColumnGroupElementDocx`\n - `equation-node-static`: `EquationElementDocx`, `InlineEquationElementDocx`\n - `toc-node-static`: `TocElementDocx`\n\n\n## December 2025 #27\n\n### December 23 #27.3\n- **`code-drawing-node`**: Added new code drawing component with inline editing support\n - Support for multiple diagram types: PlantUML, Graphviz, Flowchart, and Mermaid\n - Inline code editing with real-time preview (no popup dialog)\n - Multiple view modes: Both (code + preview), Code only, Image only\n - Responsive layout: horizontal on desktop, vertical on mobile\n - Floating toolbar with language selector, view mode selector, and download button\n - Debounced rendering (500ms) for optimal performance\n\n### December 23 #27.2\n- **AI Prompt System Refactoring**: Complete restructuring of AI command prompts for better maintainability\n - Split monolithic `prompts.ts` into focused modules: `getEditPrompt`, `getGeneratePrompt`, `getCommentPrompt`, `getEditTablePrompt`, `getChooseToolPrompt`\n - Added new `common.ts` for shared prompt utilities\n - Enhanced table cell editing capabilities with specialized prompts\n- **AI Table Cell Handling**: Improved support for editing single-cell tables\n - `use-chat`: Enhanced with parser options for better AI content processing\n - `cursor-overlay`: Fixed overlay positioning and behavior\n - Added tests for complex table markdown generation\n- **API Routes**: Updated AI command and copilot routes with improved prompt selection logic\n\n### December 16 #27.1\n- `table-node`: Fixed table row drag and drop - rows could be dragged but not dropped, and dropline wasn't showing. Added missing `nodeRef` to element ref composition.\n\n### November 30 #26.10\n- `inline-combobox`: Added Yjs collaboration support - combobox popover now only shows for the user who triggered it, preventing the popover from opening for all users in collaborative editing\n\n### October 21 #26.9\n- `suggestion-kit`: Remove `BlockSuggestion`use `SuggestionLineBreak` instead to fixes styles.\n- `use-chat`: Fix AI comment hiding when finished.\n\n### October 17 #26.8\n- **Static Components**: Updated all static component imports to use new `platejs/static` path\n - `*-node-static` components: Updated imports from `@platejs/core/react` to `platejs/static`\n - `editor-static`: Updated `PlateStatic` import path\n - `export-toolbar-button`: Updated static utilities import path\n - `import-toolbar-button`: Updated static utilities import path\n - `slate-to-html/page`: Updated static rendering imports\n - `comment-kit`, `suggestion-kit`: Updated static types imports\n\n### October 6 #26.7\n- `api/ai/command/route.ts`: Fix poor AI generation quality when blockSelecting.\n\n### October 5 #26.6\n- `ai-kit`: Removed unused `api` parameter from `useHooks` destructuring\n- `block-selection-kit`: Added keyboard shortcut handler to open AI menu with `mod+j` when blocks are selected\n\n### October 4 #26.5\n- `api/ai/command/route.ts`: fix #4669\n\n## September 2025 #26\n\n### September 28 #26.5\n- `transforms`: Fixed slash command duplicate block insertion - prevents creating duplicate blocks when selecting the same block type in empty blocks\n\n### September 20 #26.4\n- **AI Suggestions**: Major improvements to AI suggestion system with better content handling and UI enhancements\n - `ai-menu`: \n - Added new `AILoadingBar` component with animated spinner and stop functionality\n - Enhanced comment acceptance UI with Accept/Reject buttons\n - Improved context-aware menu states based on selection and operation mode\n - Better keyboard navigation with ESC to stop operations\n - `ai-toolbar-button`: Streamlined implementation\n - `api/ai/command/route.ts`: \n - Added multi-tool support (generate, edit, comment) with automatic intent classification\n - Switched to Google Gemini model (`gemini-2.5-flash`)\n - Enhanced prompt templates with placeholder support\n - Added MDX tag preservation\n - `markdown-joiner-transform`: Added smart buffering for smoother streaming of markdown content\n - `use-chat`: Simplified implementation with better error handling\n - `ai-kit`: Added markdown plugin to AI configuration\n - `markdown-kit`: Added AI plugin integration\n - `link-node`: Added AI-specific click handler functionality\n - `block-context-menu`: Removed redundant AI options (now handled by ai-menu)\n - `block-discussion`: Minor styling improvements\n - `fixed-toolbar-buttons`: Simplified AI button implementation\n\n### September 7 #26.3\n- `block-context-menu`: Fixed menu position sticking when triggered multiple times in different locations\n\n### September 5 #26.2\n- `block-draggable`: Fixed block selection to work with right-click events\n\n### September 4 #26.1\n- **AI Comments**: Added AI-powered comment functionality for document review and feedback\n - `use-chat`: Enhanced chat hook with AI comment support and improved streaming capabilities\n - `ai-menu`: Updated AI menu with comment generation options and improved UI\n - `ai-toolbar-button`: Added support for AI comment actions\n - `block-context-menu`: Integrated AI comment options into block context menu\n - `fixed-toolbar-buttons`: Added AI comment button to toolbar\n - `ai-kit`: remove all prompt templates, use directly in `api/ai/command/route.ts`.\n - `api/ai/command/route.ts`: Added comment functionality.\n\n## August 2025 #25\n\n\n### August 17 #25.2\n- `block-discussion`: Removed `useFocusedLast` check for showing discussion popover.\n\n### August 1 #25.1\n- **Floating toolbar improvements**: Multiple components now use the new `useFocusedLast` hook to only show their floating toolbars when their editor is the last focused editor, preventing toolbar conflicts in multi-editor scenarios:\n - `ai-menu`\n - `block-discussion`\n - `column-node`\n - `media-toolbar`\n - `table-node`\n- `block-draggable`: \n - Select list children on handle click\n - Focus selected blocks on handle click\n\n## July 2025 #24\n\n### July 29 #24.11\n- `block-draggable`: Fixed table drag and drop preview display with horizontal scroll compensation. Drag preview elements now correctly display content even when the original element has horizontal scroll\n- `block-draggable`: Added `isAboutToDrag` state to improve preview handling - tracks when drag is about to start (mousedown but not yet dragging) for better preview cleanup\n\n### July 27 #24.10\n- `ai-kit`: support custom node type\n- `indent-kit`: add `KEYS.img` to `IndentPlugin`\n- `list-kit`: add `KEYS.img` to `ListPlugin`\n- `markdown-joiner-transform.ts`: add `markdownJoinerTransform` to transform chunks like [**,bold,**] to [**bold**] make the md deserializer happy.\n- `api/ai/command/route.ts`: use `markdownJoinerTransform` to transform chunks.\n\n### July 26 #24.9\n- `list-classic-kit`: Added `ListPlugin` to restore List functionalities (indent with Tab / Shift+Tab, new item when pressing enter, ...).\n\n### July 25 #24.8\n- `block-draggable`: Added support for automatically selecting list item children when dragging. When dragging a list item, all nested items with bigger indent are now included in the drag operation\n\n### July 23 #24.7\n- `block-draggable`: Updated to use new `addOnContextMenu` API from BlockSelectionPlugin for cleaner context menu handling\n\n### July 18 #24.6\n- `block-context-menu`: Fixed context menu not opening when right-clicking on block margin areas\n- `block-draggable`: Added wrapper div with context menu handler to ensure block selection on margin clicks\n\n### July 14 #24.5\n- `block-draggable`: Added support for dragging multiple blocks using editor's native selection (previously only block-selection was supported)\n\n### July 3 #24.4\n- `slate-to-html`: Added `EditorViewDemo` component for static editor rendering using `createStaticEditor`\n### July 4 #24.3\n\n- `list-classic-node`: Fix styling that affects `TaskListElement` by force overriding list-style-type (set to none).\n\n### July 3 #24.2\n\n- **Task list support in list-classic**: Added task list functionality with checkboxes to the list-classic plugin\n - `list-classic-kit`: Added `TaskListPlugin` with `TaskListElement` component\n - `list-classic-node`: Added `TaskListElement` and `TaskListItemElement` components with checkbox support\n - `transforms-classic`: New file for classic list transforms\n - `insert-toolbar-classic-button`: New component for inserting classic list types (bulleted, numbered, task)\n - `turn-into-toolbar-classic-button`: New component for converting blocks to classic list types\n - `floating-toolbar-classic-buttons`: New component for floating toolbar with classic list support\n - `floating-toolbar-classic-kit`: New kit that includes classic list toolbar buttons\n\n### July 2 #24.1\n- `editor`: Added `EditorView` component using the new `PlateView` from `@platejs/core/react` for static editor rendering with copy functionality\n\n## June 2025 #23\n\n### June 29 #23.9\n- `link-node`: Remove `useLink`\n- `link-node-static`: missing `getLinkAttributes`\n- `media-image-node`: `attributes.alt` type casting\n\n### June 26 #23.7\n- `inline-combobox`: Fixed combobox not closing when clicking outside the editor\n\n### June 24 #23.6\n- `transform.ts`: add `toggleCodeBlock` to `setBlockMap`. Fix the structural error of the code_block created by `turn-into-toolbar-button.tsx`.\n\n### June 20 #23.5\n- [Drag and drop improvements](https://github.com/udecode/plate/pull/4385)\n- `block-draggable`: Fixed drag and drop functionality with multiple selected blocks and resolved drop positioning issues on margins.\n- `block-selection-kit`: It is now possible to select the entire table (table), but the rows (tr) will only be selected if your selection box is within the table.\n- `table-node`: Add block selection styles to the table.\n\n### June 18 #23.4\n\n- `table-node`: Fix bug affecting cursor position and improve performance\n\n### June 16 #23.3\n\n- `block-draggable`: use `getPluginByType` instead of `getContainerTypes`\n\n### June 13 #23.2\n\n- `editor`: Fix placeholder positioning `**:data-slate-placeholder:!top-1/2 **:data-slate-placeholder:-translate-y-1/2`.\n- `block-placeholder-kit`: Change placeholder color to `text-muted-foreground/80` to match `editor` one.\n\n### June 9 #23.1\n\n**Plate 49**\n\nMerging files, using a more consistent naming convention, and removing unused `export` statements.\n\nComponents:\n\n- Now that basic nodes have a default HTML element, you can remove `withProps(..., { as: '...' })` plugin components.\n- To improve decoupling, plugins are not imported anymore only for their keys. Import `KEYS` from `@udecode/plate` instead, as a unified source of keys.\n - `ParagraphPlugin.key` -> `KEYS.p`\n - `INDENT_LIST_KEYS.listStyleType` -> `KEYS.listType`\n - `ListStyleType.Decimal` -> `KEYS.ol`\n - `ListStyleType.Disc` -> `KEYS.ul`\n - `list` (classic) -> `KEYS.listClassic`\n - `ol` (classic) -> `KEYS.olClassic`\n - `ul` (classic) -> `KEYS.ulClassic`\n - `li` (classic) -> `KEYS.liClassic`\n - `action_item` (classic) -> `KEYS.listTodoClassic`\n- Rename all `*-element`, `*-leaf` files to `*-node` (and static versions)\n- Removed `ai-anchor-element`, merged into `ai-node`\n- Removed `ai-loading-bar`, merged into `ai-menu`\n- Removed `ai-menu-items`, merged into `ai-menu`\n- Renamed `align-dropdown-menu` to `align-toolbar-button`, `AlignDropdownMenu` to `AlignToolbarButton`\n- Renamed `api-ai` to `ai-api`\n- Renamed `api-uploadthing` to `media-uploadthing-api`\n- `BlockSelection`: fix block selection for tables\n- Removed `code-block-combobox`, merged into `code-block-node`\n- Removed `code-line-element`, merged into `code-block-node` (and static version)\n- Removed `code-syntax-leaf`, merged into `code-block-node` (and static version)\n- Rename `color-toolbar-button` to `font-color-toolbar-button`, `ColorDropdownMenu` to `FontColorToolbarButton`\n- Removed all `color-*` files, merged into `font-color-toolbar-button`\n - Rename `color-dropdown-menu` to `font-color-toolbar-button`\n- Removed `column-group-element`, merged into `column-node` (and static version)\n- Removed `comment-create-form`, merged into `comment`\n- Renamed `draggable` to `block-draggable`, `DraggableAboveNodes` to `BlockDraggable`\n- Renamed `emoji-input-element` to `emoji-node`\n- Removed all `emoji-*` files (except `emoji-input-node`), merged into `emoji-toolbar-button`\n - Rename `EmojiToolbarDropdown` to `EmojiPopover`, `EmojiDropdownMenu` to `EmojiToolbarButton`\n - `EmojiPicker` `icons` prop is now optional and defaulted to `emojiCategoryIcons` and `emojiSearchIcons`\n- Renamed `image-preview` to `media-preview-dialog`, `ImagePreview` to `MediaPreviewDialog`\n- Renamed `image-element` to `media-image-node`\n - Renamed `MediaFileElement` to `FileElement` (and static version)\n - Renamed `MediaAudioElement` to `AudioElement` (and static version)\n - Renamed `MediaVideoElement` to `VideoElement` (and static version)\n- Renamed `indent-list-toolbar-button` to `list-toolbar-button`\n - Renamed `BulletedIndentListToolbarButton` to `BulletedListToolbarButton`\n - Renamed `NumberedIndentListToolbarButton` to `NumberedListToolbarButton`\n- Renamed `indent-todo-marker` to `block-list`\n- Removed `indent-fire-marker`\n- Removed `indent-todo-toolbar-button`, merged into `list-toolbar-button`\n- Renamed `IndentTodoToolbarButton` into `TodoListToolbarButton`\n- Removed `inline-equation-element` and `equation-popover`, merged into `equation-node` (and static version)\n- Removed `inline-equation-toolbar-button`, merged into `equation-toolbar-button`\n- Renamed `insert-dropdown-menu` to `insert-toolbar-button`, `InsertDropdownMenu` to `InsertToolbarButton`\n- Renamed `line-height-dropdown-menu` to `line-height-toolbar-button`, `LineHeightDropdownMenu` to `LineHeightToolbarButton`\n- Rename `link-floating-toolbar` to `link-toolbar`\n- Removed `list-indent-toolbar-button`, merged into `list-classic-toolbar-button`\n- Renamed `ListIndentToolbarButton` to `IndentToolbarButton`\n- Renamed `list-node` to `list-classic-node`\n- Renamed `media-popover` to `media-toolbar`, `MediaPopover` to `MediaToolbar`\n- Renamed `mode-dropdown-menu` to `mode-toolbar-button`, `ModeDropdownMenu` to `ModeToolbarButton`\n- Renamed `more-dropdown-menu` to `more-toolbar-button`, `MoreDropdownMenu` to `MoreToolbarButton`\n- Removed `outdent-toolbar-button`, merged into `indent-toolbar-button`\n- `table-icons`: rename `Border*` to `Border*Icon`\n- Renamed `slash-input-element` to `slash-input-node`\n- Renamed `SuggestionBelowNodes` to `SuggestionLineBreak`\n- Removed `table-cell-element`, merged into `table-node` (and static version)\n- Removed `table-row-element`, merged into `table-node` (and static version)\n- Renamed `table-dropdown-menu` to `table-toolbar-button`, `TableDropdownMenu` to `TableToolbarButton`\n- Removed `todo-list-node`, merged into `list-classic-node`\n- Renamed `turn-into-dropdown-menu` to `turn-into-toolbar-button`, `TurnIntoDropdownMenu` to `TurnIntoToolbarButton`\n- `export-toolbar-button`, `indent-list-plugins`: remove fire from `listStyleTypes`\n- `useCommentEditor`: `usePlateEditor` instead of `useCreateEditor`\n- Removed `placeholder`, `withPlaceholder`. Migration: use `block-placeholder-kit`, `BlockPlaceholderPlugin` instead.\n- `line-height-toolbar-button`: remove `useLineHeightDropdownMenu` hook.\n- `font-color-toolbar-button`: remove `useColorInput` hook.\n\nPlugins:\n\n- Renamed all `*-plugin`, `*-plugins` files to `-kit`, and `*Plugin`, `*Plugins` to `*Kit`. A **plugin kit** is a collection of configured plugins.\n - Renamed `editor-plugins` to `editor-kit`\n - Renamed `equation-plugins` to `math-kit`, `equationPlugins` to `MathKit`\n - Renamed `indent-list-plugins` to `list-kit`, `indentListPlugins` to `ListKit`\n - Added `BlockList` component to `block-list`, used in `list-kit`\n - Removed `use-create-editor`, use `usePlateEditor` instead\n- `ai-kit`: add `show` shortcut. Remove `useHotkeys('mod+j')` from `ai-menu`\n- `comment-kit`: add `setDraft` transform, shortcut\n- `basic-marks-kit`, `basic-blocks-kit`: add shortcuts\n\n- `transforms`, `block-draggable`: remove `STRUCTURE_TYPES`, those are now inferred from `plugin.node.isContainer`. Use instead `editor.meta.containerTypes`.\n- Remove `structuralTypes` from `useSelectionFragmentProp` usages.\n\n## May 2025 #22\n\n### May 26 #22.7\n\n- [Fix combobox closing issue](https://github.com/udecode/plate/pull/4322)\n- `inline-combobox`: fix `insertPoint` not being updated when the combobox is closed.\n\n### May 15 #22.6\n\n- [Fix inline math keyboard behavior and style](https://github.com/udecode/plate/pull/4305)\n- `equation-popover`: Focus back to the editor when the popover is closed.\n- `inline-equation-element`: When selecting it should be highlighted.\n\n### May 11-12 #22.5\n\n- [Templates migration to Plate 48](https://github.com/udecode/plate/pull/4298/files)\n- Migration to shadcn v4: \n - Plate had a forked version of shadcn/ui primitives that could conflict with your existing components. Our components now **fully depend** on the original shadcn/ui primitives, easing the integration of Plate into your existing shadcn/ui set of components.\n - All components updated to [Tailwind v4](https://ui.shadcn.com/docs/tailwind-v4). \n - See the updated [installation guide](/docs/components/installation).\n- Migration to React 19. If you're using React 18, you may need to use `React.forwardRef` in a few places.\n- Migration to [shadcn CLI](https://ui.shadcn.com/docs/cli):\n - Remove `registries` from `components.json`\n - Use `npx shadcn` instead of `npx shadcx`\n- [MCP support](/docs/mcp)\n- Remove `withRef` from all components\n- Import `cn` from `@/lib/utils` instead of `@udecode/cn` to stay consistent with shadcn/ui\n- Remove unused `className`, `style` props from all element and leaf components\n- `draggable`:\n - Fix dnd in Firefox\n- `media-placeholder-element`: refactor to use `use-upload-file` hook instead of `uploadthing`\n - Migration: `npx shadcn@latest add @plate/api-uploadthing`\n\n### May 6 #22.3\n\n- `ai-chat-editor`: support none-standard markdown nodes.\n- `slash-input-element`: add callout support.\n- `block-selection-plugins.tsx`: fix block selection not working.\n\n### May 4 #22.2\n\n- `ai/command`: forked smoothStream from `ai` package now uses 30ms delay by default (only code blocks and tables use 100ms delay).\n\nv48 migration:\n- `PlateElement`, `PlateLeaf` and `PlateText` HTML attributes are moved from top-level props to `attributes` prop.\n- Remove `nodeProps` prop from `PlateElement`, `PlateLeaf`, `PlateText`. Use `attributes` prop instead.\n- Migrated components: \n - `block-discussion`\n - `comment-leaf`\n - `date-element`\n - `draggable`\n - `excalidraw-element`\n - `hr-element` + `-static`\n - `image-element` + `-static`\n - `link-element`\n - `media-audio-element`\n - `media-file-element`\n - `media-placeholder-element`\n - `media-video-element`\n - `mention-element`\n - `placeholder`\n - `suggestion-leaf`\n - `table-cell-element` + `-static`\n - `table-element`\n - `tag-element`\n\n### May 2 #22.1\n- `use-chat`: add `_abortFakeStream`.\n- `ai-menu`: Fix menu items are displayed incorrectly.\n- `ai-loading-bar`: Move esc keydown handler to `ai-menu`.\n- `ai/command`: add chunking delay to 100ms (Temporary workaround with performance issue).\n\n\n## April 2025 #21\n\n### April 30 #21.3\n\n- `autoformat-plugin`: allow starting a new indent list with numbers other than 1\n\n### April 29 #21.2\n\n- `ai-leaf`: add `aiIndicatorVariants` to display loading state.\n- `cursor-overlay`: hide when ai is streaming.\n- `api/ai/command`: fix chunking issue.\n\nAdd `discussion-plugin`:\n- add `discussionPlugin` to `editor-plugins`, remove `configure` from `suggestionPlugin`\n- refactor `block-suggestion`, `comment` to use `discussionPlugin`\n- fix `comment-create-form` to create discussion when none exists\n- style changes in `suggestion-leaf`\n- fix `link-floating-toolbar` to support internal links, and placement top when suggestion or comment is active\n\n### April 19 #21.1\n\n- `ai-anchor-element`: add `ai-anchor-element` component that is inserted before streaming, removed after streaming, and used for positioning the ai-menu\n- `ai-loading-bar`: add `ai-loading-bar` component that is used to display the loading progress of the insert mode streaming\n- `ai-menu`: migrate to latest `ai` package\n- `ai-menu-items`: add `generateMarkdownSample`\n- `ai-plugins`: Remove the single-paragraph limit from prompts\n- `editor`: introduce `PlateContainer` component\n\n### April 2 #21.1\n\n- `export-toolbar-button`: fix pdf export issue with `html2canvas-pro`\n- `import-toolbar-button`: fix sometimes unable to select the file\n\n## March 2025 #20\n\n### March 12 2025 #20.4\n\n- `ai-toolbar-button`: add missing `@udecode/plate-ai` dependency.\n- `comment-toolbar-button`: add missing `comments-plugin` registry dependency.\n- `font-size-toolbar-button`: add missing `popover` registry dependency.\n- `tooltip`: add missing `button` registry dependency.\n\n### March 10 #20.3\n- `block-context-menu`: Prevent opening context menu in read-only mode\n\n### March 2 #20.2\n\n- `block-suggestion`: fix styles\n- `suggestion-leaf-static`: add static versions\n\n### March 1 #20.1\n\nPlate 46 - new code block\n\n- Migrated from Prism.js to lowlight for syntax highlighting\n - `code-block-element-static`, `code-block-element`, `code-block-combobox`: Updated to use lowlight classes. Default to github theme.\n - `code-syntax-leaf-static`, `code-syntax-leaf`: Updated to use lowlight token classes\n - Removed `prismjs` dependency and related styles\n - Use `lowlight` plugin option instead of `prism` option\n - `code-block-combobox`: add `Auto` language option, change language values to match lowlight\n- `autoformat-plugin`: prevent autoformat on code blocks\n\n```tsx\nimport { all, createLowlight } from 'lowlight';\n\nconst lowlight = createLowlight(all);\n\nCodeBlockPlugin.configure({\n options: {\n lowlight,\n },\n});\n```\n\n### Feburary 21 #19.3\n\n- `image-preview`: prevent block menu on image preivew mask\n- `media-popover`: hide media popover when image preivew is open\n\n### February 18 #19.2\n\nPlate 45 - new comments & suggestions UI\n\n- NEW `block-discussion` as the main container, used in `plate-element`\n- NEW `comment` for individual comment display\n- NEW `comment-create-form` with minimal Plate editor for input\n- Removed legacy components:\n - `comments-popover`\n - `comment-avatar`\n - `comment-reply-items`\n - `comment-value`\n - `comment-resolve-button`\n- NEW `block-suggestion`\n- NEW `suggestion-leaf`\n- NEW `suggestion-line-break`\n- Remove `plate-element`, import `PlateElement` from `@udecode/plate/react` instead. Add in `block-selection-plugins`:\n```tsx\nrender: {\n belowRootNodes: (props) => {\n if (!props.className?.includes('slate-selectable')) return null;\n\n return ;\n },\n},\n```\n\n### February 3 #19.1\n\n- React 19\n- TailwindCSS 4\n- Plate 45\n- Jotai 2\n- Zustand 6\n- `comment-more-dropdown`: remove `useCommentEditButtonState`, `useCommentDeleteButtonState`\n- `image-element`, `media-embed-element`, `media-video-element`, `mode-dropdown-menu`\n - use `const width = useResizableValue('width')`\n- `image-preview`: remove `useScaleInputState`, `useImagePreviewState`\n- `floating-toolbar`: \n - replace `useEventEditorSelectors` with `useEventEditorValue`\n- `media-popover`: \n - replace `floatingMediaActions` with `FloatingMediaStore.set`, \n - replace `useFloatingMediaSelectors` with `useFloatingMediaValue`\n\n## January 2025 #18\n\n### January 23 #18.8\n\n- `table-element`: fix styles, show table border controls when collapsed\n- `table-row-element`: refactor\n- `table-cell-element`: selection bg-brand\n\n### January 21 #18.7\n\n- `table-element`, `table-row-element`: support row dnd and selection\n- `plate-element`: add `blockSelectionClassName` prop\n- `editor`: z-50 for selection area\n- `draggable`: \n - Replace `editor.api.blockSelection.replaceSelectedIds` with `editor.api.blockSelection.clear`\n - Use `TooltipButton` for drag handle\n - Block select on drag handle click\n - Hide drag handle in table cells\n- `column-element`, `table-cell-element`: add `isSelectionAreaVisible` check\n- `block-selection`: hide if dragging\n- Replace `editor.api.blockSelection.addSelectedRow` with `editor.api.blockSelection.set`:\n - `ai-menu`\n - `equation-popover`\n- `align-dropdown-menu`: deprecate \n\n\n### January 18 #18.6\n\n- `inline-equation-element` and `equation-popover`: Fix: When selecting an inline equation, the popover should not open, as it causes the selection to be lost.\n\n### January 17 #18.5\n\n- `emoji-picker-search-bar`: add `autoFocus`\n\n### January 16 #18.4\n\n- `import-toolbar-button` and `export-toolbar-button`: add `markdown` support\n\n### January 14 #18.3\n- `fixed-toolbar-buttons`: add `import-toolbar-button`\n- `indent-fire-marker.tsx` Add `data-plate-prevent-deserialization` to prevent deserialization of the fire marker. Change the `span` tag to `li`.\n- `indent-todo-marker.tsx` change the `span` tag to `li`.\n- `image-element-static.tsx` and `hr-element-static.tsx`: Fix `nodeProps` not being passed to `SlateElement`.\n- `ai-chat-editor`:\n```tsx\nconst aiEditor = usePlateEditor({ plugins });\nuseAIChatEditor(aiEditor, content);\n```\n\n### January 12 #18.2\n\n- `ai-plugins`: remove `createAIEditor`, it's now created in `ai-chat-editor`\n- `ai-chat-editor`: just use `useAIChatEditor` (v42.1)\n- `ai-menu`: avoid collisions, remove `aiEditorRef`\n- `command`: add `focus-visible:outline-none`\n- `editor-static`: update `aiChat` padding\n- `transforms`: fix `insertBlock` used by slash commands: it should insert a new block if the newly inserted block is of the same type as the command.\n- `block-selection-plugins`: update `BlockSelectionPlugin`\n\n```tsx\nBlockSelectionPlugin.configure(({ editor }) => ({\n options: {\n enableContextMenu: true,\n isSelectable: (element, path) => {\n return (\n !['code_line', 'column', 'td'].includes(element.type) &&\n !editor.api.block({ above: true, at: path, match: { type: 'tr' } })\n );\n },\n },\n}))\n```\n\n \n### January 8 #18.1\n\n- v42 migration\n- `table-element`, `table-element-static`\n - Move icons to `table-icons`\n - Remove `colgroup`, col width is now set in `table-cell-element`\n- `table-row-element`: remove `hideBorder` prop\n- `table-cell-element`, `table-cell-element-static`: \n - column hover/resizing state is now using Tailwind instead of JS\n - **Major performance improvement**: all table cells were re-rendering on a single cell change. This is now fixed.\n - React.memo\n- `table-dropdown-menu`:\n - dynamic table insert\n - merge/split cells\n - insert row/col before\n- `tooltip`: add `TooltipButton`\n- `indent-list-toolbar-button`: Remove `IndentListToolbarButton` use `NumberedIndentListToolbarButton` and `BulletedIndentListToolbarButton` instead.\n- `table-dropdown-menu`: new insert table interface.\n- `column-group-element`: fix `ColumnFloatingToolbar` onColumnChange\n\n## December 2024 #17\n\n### December 28 #17.8\n\n- `export-toolbar-button`: add `katex` support\n- `plate-element`: remove `relative` className\n- All components using the `PlateElement` have had redundant `relative` class names removed.\n### December 27 #17.7\n\n- `fixed-toolbar-buttons`: add `font-size-toolbar-button`\n- `floating-toolbar`: add `inline-equation-toolbar-button`\n- `turn-into-dropdown-menu`: Fix: after turn into other block, the editor should regain focus.\n- `insert-dropdown-menu`: add `inline equation` and `equation` & fix the focus issue\n- `slash-input-element`: add `equation` and `inline equation`\n\n### December 23 #17.5\n\n- `table-element`: fix selection\n- before: `isSelectingCell && '[&_*::selection]:bg-none'`\n- after: `isSelectingCell && '[&_*::selection]:!bg-transparent'`\n\n\n### December 21 #17.4\n\nUpdate `tailwind.config.cjs` for better font support in the HTML export:\n\n```ts\nfontFamily: {\n heading: [\n 'var(--font-heading)',\n 'ui-sans-serif',\n '-apple-system',\n 'BlinkMacSystemFont',\n 'Segoe UI Variable Display',\n 'Segoe UI',\n 'Helvetica',\n 'Apple Color Emoji',\n 'Arial',\n 'sans-serif',\n 'Segoe UI Emoji',\n 'Segoe UI Symbol',\n 'Noto Color Emoji',\n ],\n mono: ['var(--font-mono)', ...fontFamily.mono],\n sans: [\n 'var(--font-sans)',\n 'ui-sans-serif',\n '-apple-system',\n 'BlinkMacSystemFont',\n 'Segoe UI Variable Display',\n 'Segoe UI',\n 'Helvetica',\n 'Apple Color Emoji',\n 'Arial',\n 'sans-serif',\n 'Segoe UI Emoji',\n 'Segoe UI Symbol',\n 'Noto Color Emoji',\n ],\n```\n\n\n### December 20 #17.3\n\n- `insertColumnGroup`, `toggleColumnGroup`: use `columns` option instead of `layout` \n- Remove `with-draggables`. Add [`DraggableAboveNodes`](https://github.com/udecode/plate/pull/3878/files#diff-493c12ebed9c3ef9fd8c3a723909b18ad439a448c0132d2d93e5341ee0888ad2) to `draggable`. Add to `DndPlugin` config:\n```tsx\nDndPlugin.configure({ render: { aboveNodes: DraggableAboveNodes } }),\n```\n- `column-element`, `image-element`, `media-video-element`: Remove `useDraggableState`. Use `const { isDragging, previewRef, handleRef } = useDraggable`\n- `column-group-element`: Remove `useColumnState`. Use instead:\n```tsx\nconst columnGroupElement = useElement(ColumnPlugin.key);\n\nconst onColumnChange = (widths: string[]) => {\n setColumns(editor, {\n at: findNodePath(editor, columnGroupElement),\n widths,\n });\n};\n```\n- `export-toolbar-button`: add `exportToHtml`\n\n### December 19 #17.2\n\nPlate 41\n\n- New RSC components for element and leaf components, filename ending with `-static.tsx`. Those are now added along with the default client components.\n- `editor`: add `select-text` to `editorVariants`\n- `date-element`: remove popover when read-only\n- `indent-todo-marker`: use `SlateRenderElementProps` type instead of `PlateRenderElementProps`\n- `hr-element`, `media-audio-element`, `media-embed-element`, `mention-element`: improve cursor styling\n- `media-file-element`: use `` instead of `div` + `onClick`\n- all element and leaf components: `className` prop is now placed before inline prop.\n\n### December 16 #17.1\n\n- `column-element`:\n - Add drag and drop support for columns\n - Add drag handle with tooltip\n - Fix column spacing and padding\n\n- `column-group-element`:\n - Remove gap between columns\n - Remove margin top\n\n- `draggable`:\n - Remove `DraggableProvider` HOC\n - Remove `DropLine` children prop\n\n## November 2024 #16\n\n### November 26 #16.9\n\nhttps://github.com/udecode/plate/pull/3809/files\n- Add `select-editor`, `tag-element`, `label`, `form`\n- Replace `cmdk` dependency with `@udecode/cmdk`. It's a controllable version of `cmdk`.\n- `command`: add variants\n- `editor`: add `select` variant\n- `popover`: add `animate` variant\n\nhttps://github.com/udecode/plate/pull/3807/files\n- `toc-element`: remove `