Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
8cfcebe
ai comment
felixfeng33 Aug 26, 2025
b226b25
fix
felixfeng33 Aug 26, 2025
0f2bc3e
fix
felixfeng33 Aug 26, 2025
5b81c3c
fix
felixfeng33 Aug 27, 2025
2bd7ba7
chore: add changesets and update changelog for AI review feature
claude[bot] Aug 27, 2025
645952c
fix
felixfeng33 Aug 27, 2025
11c33c1
fix ci
felixfeng33 Aug 27, 2025
168573e
withBlock
felixfeng33 Aug 28, 2025
4ac2e4c
refactor
felixfeng33 Aug 30, 2025
9d59b50
fix
felixfeng33 Aug 30, 2025
8a77455
fix
felixfeng33 Aug 31, 2025
ba45ae9
fix
felixfeng33 Aug 31, 2025
f74818f
fix
felixfeng33 Aug 31, 2025
b0f22fd
fix mention deserialize
felixfeng33 Aug 31, 2025
a3f9d0d
lint
felixfeng33 Aug 31, 2025
56d45e9
fix
felixfeng33 Aug 31, 2025
c5ca9ca
fix
felixfeng33 Aug 31, 2025
80db6c3
fix
felixfeng33 Aug 31, 2025
2b9b3c4
fix
felixfeng33 Aug 31, 2025
1f68d91
ai-sdk5
felixfeng33 Sep 1, 2025
3d18e91
fix streaming
felixfeng33 Sep 1, 2025
4801335
fix
felixfeng33 Sep 2, 2025
aaa23c2
api
felixfeng33 Sep 2, 2025
47d4562
comment
felixfeng33 Sep 3, 2025
bf67044
api
felixfeng33 Sep 3, 2025
5ae4c00
test
felixfeng33 Sep 3, 2025
63d6b6b
lint
felixfeng33 Sep 3, 2025
5e0831b
docs
felixfeng33 Sep 3, 2025
ac9da51
fix
felixfeng33 Sep 4, 2025
802d41e
fix
felixfeng33 Sep 4, 2025
c835263
choice => toolName
felixfeng33 Sep 4, 2025
ab171ba
fix
felixfeng33 Sep 4, 2025
d759ce2
fix
felixfeng33 Sep 4, 2025
fa1f820
fix
felixfeng33 Sep 4, 2025
3f7fec8
revert
felixfeng33 Sep 4, 2025
dcf90fc
chore: add changesets and update changelog for AI comment feature
claude[bot] Sep 4, 2025
1bc8131
fix
felixfeng33 Sep 4, 2025
2e78f7d
fix
felixfeng33 Sep 4, 2025
59fba9d
fake ai comment
felixfeng33 Sep 5, 2025
1d0bb4d
docs
felixfeng33 Sep 5, 2025
f459eb3
docs: update API documentation based on changesets
claude[bot] Sep 5, 2025
4b5106a
fix
felixfeng33 Sep 8, 2025
1ed7783
docs
felixfeng33 Sep 8, 2025
ce5d462
fix
felixfeng33 Sep 8, 2025
91614a5
fix
felixfeng33 Sep 8, 2025
1e5a142
fix
felixfeng33 Sep 8, 2025
ff579b3
Merge branch 'main' into feat/ai-comments
felixfeng33 Sep 8, 2025
ecde7a8
fix
zbeyens Sep 8, 2025
049d46f
fix
zbeyens Sep 8, 2025
5282c58
move prompt template to server side
felixfeng33 Sep 9, 2025
facb52b
fix
felixfeng33 Sep 9, 2025
442f265
lint
felixfeng33 Sep 9, 2025
808df79
fix
felixfeng33 Sep 9, 2025
bf2a8f4
fix
felixfeng33 Sep 9, 2025
432ac73
ci
felixfeng33 Sep 9, 2025
3f7f8eb
docs
felixfeng33 Sep 9, 2025
5674186
Merge branch 'main' into feat/ai-comments
felixfeng33 Sep 9, 2025
c678bc6
fix
felixfeng33 Sep 9, 2025
c9b704b
fix
felixfeng33 Sep 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .changeset/platejs-ai-review-minor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
"@platejs/ai": minor
---

Added **AIReviewPlugin** that enables AI-powered document review functionality. This feature allows AI to analyze document content and suggest comments on specific text sections.

### New APIs:

- **AIReviewPlugin** - Main plugin for AI review functionality
- `generateComment()` - Trigger AI review on selected text or entire document
- Configurable `promptTemplate` and `systemTemplate` options
- **useEditorCompletion** - Hook for AI completion setup with streaming responses
- **useAIEditorReview** - Hook for processing AI-generated markdown content
- **applyAIReview** - Transform function to apply AI suggestions to the editor
- Uses advanced text matching with LCS algorithm
- Provides `onComment` callback for comment system integration
- **getAIReviewCommentKey** - Utility function for generating AI comment keys

### Example:

```typescript
const editor = createPlateEditor({
plugins: [
AIReviewPlugin.configure({
promptTemplate: (params) => 'Review this document...',
systemTemplate: (params) => 'You are a helpful editor...'
})
]
});

// Trigger AI review
editor.api.aiReview.generateComment();
```
5 changes: 5 additions & 0 deletions .changeset/platejs-markdown-patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@platejs/markdown": patch
---

Simplified the comment rule in markdown default rules to support AI comment integration. Removed custom attribute parsing logic while maintaining core comment mark functionality.
5 changes: 5 additions & 0 deletions .changeset/platejs-utils-patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@platejs/utils": patch
---

Added `ELEMENT_AI_REVIEW` constant to plate-keys for AI review functionality.
2 changes: 1 addition & 1 deletion apps/www/public/r/ai-kit.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"files": [
{
"path": "src/registry/components/editor/plugins/ai-kit.tsx",
"content": "'use client';\n\nimport type { AIChatPluginConfig } from '@platejs/ai/react';\nimport type { UseChatOptions } from 'ai/react';\n\nimport { streamInsertChunk, withAIBatch } from '@platejs/ai';\nimport { AIChatPlugin, AIPlugin, useChatChunk } from '@platejs/ai/react';\nimport { getPluginType, KEYS, PathApi } from 'platejs';\nimport { usePluginOption } from 'platejs/react';\n\nimport { AILoadingBar, AIMenu } from '@/registry/ui/ai-menu';\nimport { AIAnchorElement, AILeaf } from '@/registry/ui/ai-node';\n\nimport { CursorOverlayKit } from './cursor-overlay-kit';\nimport { MarkdownKit } from './markdown-kit';\n\nexport const aiChatPlugin = AIChatPlugin.extend({\n options: {\n chatOptions: {\n api: '/api/ai/command',\n body: {},\n } as UseChatOptions,\n promptTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.userBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.userSelecting\n : PROMPT_TEMPLATES.userDefault;\n },\n systemTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.systemBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.systemSelecting\n : PROMPT_TEMPLATES.systemDefault;\n },\n },\n render: {\n afterContainer: AILoadingBar,\n afterEditable: AIMenu,\n node: AIAnchorElement,\n },\n shortcuts: { show: { keys: 'mod+j' } },\n useHooks: ({ editor, getOption }) => {\n const mode = usePluginOption(\n { key: KEYS.aiChat } as AIChatPluginConfig,\n 'mode'\n );\n\n useChatChunk({\n onChunk: ({ chunk, isFirst, nodes }) => {\n if (isFirst && mode == 'insert') {\n editor.tf.withoutSaving(() => {\n editor.tf.insertNodes(\n {\n children: [{ text: '' }],\n type: getPluginType(editor, KEYS.aiChat),\n },\n {\n at: PathApi.next(editor.selection!.focus.path.slice(0, 1)),\n }\n );\n });\n editor.setOption(AIChatPlugin, 'streaming', true);\n }\n\n if (mode === 'insert' && nodes.length > 0) {\n withAIBatch(\n editor,\n () => {\n if (!getOption('streaming')) return;\n editor.tf.withScrolling(() => {\n streamInsertChunk(editor, chunk, {\n textProps: {\n [getPluginType(editor, KEYS.ai)]: true,\n },\n });\n });\n },\n { split: isFirst }\n );\n }\n },\n onFinish: () => {\n editor.setOption(AIChatPlugin, 'streaming', false);\n editor.setOption(AIChatPlugin, '_blockChunks', '');\n editor.setOption(AIChatPlugin, '_blockPath', null);\n },\n });\n },\n});\n\nexport const AIKit = [\n ...CursorOverlayKit,\n ...MarkdownKit,\n AIPlugin.withComponent(AILeaf),\n aiChatPlugin,\n];\n\nconst systemCommon = `\\\nYou are an advanced AI-powered note-taking assistant, designed to enhance productivity and creativity in note management.\nRespond directly to user prompts with clear, concise, and relevant content. Maintain a neutral, helpful tone.\n\nRules:\n- <Document> is the entire note the user is working on.\n- <Reminder> is a reminder of how you should reply to INSTRUCTIONS. It does not apply to questions.\n- Anything else is the user prompt.\n- Your response should be tailored to the user's prompt, providing precise assistance to optimize note management.\n- For INSTRUCTIONS: Follow the <Reminder> exactly. Provide ONLY the content to be inserted or replaced. No explanations or comments.\n- For QUESTIONS: Provide a helpful and concise answer. You may include brief explanations if necessary.\n- CRITICAL: DO NOT remove or modify the following custom MDX tags: <u>, <callout>, <kbd>, <toc>, <sub>, <sup>, <mark>, <del>, <date>, <span>, <column>, <column_group>, <file>, <audio>, <video> in <Selection> unless the user explicitly requests this change.\n- CRITICAL: Distinguish between INSTRUCTIONS and QUESTIONS. Instructions typically ask you to modify or add content. Questions ask for information or clarification.\n- CRITICAL: when asked to write in markdown, do not start with \\`\\`\\`markdown.\n- CRITICAL: When writing the column, such line breaks and indentation must be preserved.\n<column_group>\n <column>\n 1\n </column>\n <column>\n 2\n </column>\n <column>\n 3\n </column>\n</column_group>\n`;\n\nconst systemDefault = `\\\n${systemCommon}\n- <Block> is the current block of text the user is working on.\n- Ensure your output can seamlessly fit into the existing <Block> structure.\n\n<Block>\n{block}\n</Block>\n`;\n\nconst systemSelecting = `\\\n${systemCommon}\n- <Block> is the block of text containing the user's selection, providing context.\n- Ensure your output can seamlessly fit into the existing <Block> structure.\n- <Selection> is the specific text the user has selected in the block and wants to modify or ask about.\n- Consider the context provided by <Block>, but only modify <Selection>. Your response should be a direct replacement for <Selection>.\n<Block>\n{block}\n</Block>\n<Selection>\n{selection}\n</Selection>\n`;\n\nconst systemBlockSelecting = `\\\n${systemCommon}\n- <Selection> represents the full blocks of text the user has selected and wants to modify or ask about.\n- Your response should be a direct replacement for the entire <Selection>.\n- Maintain the overall structure and formatting of the selected blocks, unless explicitly instructed otherwise.\n- CRITICAL: Provide only the content to replace <Selection>. Do not add additional blocks or change the block structure unless specifically requested.\n<Selection>\n{block}\n</Selection>\n`;\n\nconst userDefault = `<Reminder>\nCRITICAL: NEVER write <Block>.\n</Reminder>\n{prompt}`;\nconst userSelecting = `<Reminder>\nIf this is a question, provide a helpful and concise answer about <Selection>.\nIf this is an instruction, provide ONLY the text to replace <Selection>. No explanations.\nEnsure it fits seamlessly within <Block>. If <Block> is empty, write ONE random sentence.\nNEVER write <Block> or <Selection>.\n</Reminder>\n{prompt} about <Selection>`;\n\nconst userBlockSelecting = `<Reminder>\nIf this is a question, provide a helpful and concise answer about <Selection>.\nIf this is an instruction, provide ONLY the content to replace the entire <Selection>. No explanations.\nMaintain the overall structure unless instructed otherwise.\nNEVER write <Block> or <Selection>.\n</Reminder>\n{prompt} about <Selection>`;\n\nexport const PROMPT_TEMPLATES = {\n systemBlockSelecting,\n systemDefault,\n systemSelecting,\n userBlockSelecting,\n userDefault,\n userSelecting,\n};\n",
"content": "'use client';\n\nimport type { UseStreamObjectReturn } from '@/registry/hooks/useStreamObject';\nimport type { AIChatPluginConfig } from '@platejs/ai/react';\nimport type { UseChatOptions } from 'ai/react';\n\nimport { streamInsertChunk, withAIBatch } from '@platejs/ai';\nimport { AIChatPlugin, AIPlugin, useChatChunk } from '@platejs/ai/react';\nimport { getPluginType, KEYS, PathApi } from 'platejs';\nimport { createPlatePlugin, usePluginOption } from 'platejs/react';\n\nimport { AILoadingBar, AIMenu } from '@/registry/ui/ai-menu';\nimport { AIAnchorElement, AILeaf } from '@/registry/ui/ai-node';\nimport { AIReviewPreview } from '@/registry/ui/ai-review-preview';\n\nimport { CursorOverlayKit } from './cursor-overlay-kit';\nimport { MarkdownKit } from './markdown-kit';\n\nexport const aiReviewPlugin = createPlatePlugin({\n key: 'aiReview',\n options: {\n streamObject: null as UseStreamObjectReturn | null,\n },\n render: { afterContainer: AIReviewPreview },\n});\n\nexport const aiChatPlugin = AIChatPlugin.extend({\n options: {\n chatOptions: {\n api: '/api/ai/command',\n body: {},\n } as UseChatOptions,\n promptTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.userBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.userSelecting\n : PROMPT_TEMPLATES.userDefault;\n },\n systemTemplate: ({ isBlockSelecting, isSelecting }) => {\n return isBlockSelecting\n ? PROMPT_TEMPLATES.systemBlockSelecting\n : isSelecting\n ? PROMPT_TEMPLATES.systemSelecting\n : PROMPT_TEMPLATES.systemDefault;\n },\n },\n render: {\n afterContainer: AILoadingBar,\n afterEditable: AIMenu,\n node: AIAnchorElement,\n },\n shortcuts: { show: { keys: 'mod+j' } },\n useHooks: ({ editor, getOption }) => {\n const mode = usePluginOption(\n { key: KEYS.aiChat } as AIChatPluginConfig,\n 'mode'\n );\n\n useChatChunk({\n onChunk: ({ chunk, isFirst, nodes }) => {\n if (isFirst && mode == 'insert') {\n editor.tf.withoutSaving(() => {\n editor.tf.insertNodes(\n {\n children: [{ text: '' }],\n type: getPluginType(editor, KEYS.aiChat),\n },\n {\n at: PathApi.next(editor.selection!.focus.path.slice(0, 1)),\n }\n );\n });\n editor.setOption(AIChatPlugin, 'streaming', true);\n }\n\n if (mode === 'insert' && nodes.length > 0) {\n withAIBatch(\n editor,\n () => {\n if (!getOption('streaming')) return;\n editor.tf.withScrolling(() => {\n streamInsertChunk(editor, chunk, {\n textProps: {\n [getPluginType(editor, KEYS.ai)]: true,\n },\n });\n });\n },\n { split: isFirst }\n );\n }\n },\n onFinish: () => {\n editor.setOption(AIChatPlugin, 'streaming', false);\n editor.setOption(AIChatPlugin, '_blockChunks', '');\n editor.setOption(AIChatPlugin, '_blockPath', null);\n },\n });\n },\n});\n\nexport const AIKit = [\n ...CursorOverlayKit,\n ...MarkdownKit,\n AIPlugin.withComponent(AILeaf),\n aiChatPlugin,\n aiReviewPlugin,\n];\n\nconst systemCommon = `\\\nYou are an advanced AI-powered note-taking assistant, designed to enhance productivity and creativity in note management.\nRespond directly to user prompts with clear, concise, and relevant content. Maintain a neutral, helpful tone.\n\nRules:\n- <Document> is the entire note the user is working on.\n- <Reminder> is a reminder of how you should reply to INSTRUCTIONS. It does not apply to questions.\n- Anything else is the user prompt.\n- Your response should be tailored to the user's prompt, providing precise assistance to optimize note management.\n- For INSTRUCTIONS: Follow the <Reminder> exactly. Provide ONLY the content to be inserted or replaced. No explanations or comments.\n- For QUESTIONS: Provide a helpful and concise answer. You may include brief explanations if necessary.\n- CRITICAL: DO NOT remove or modify the following custom MDX tags: <u>, <callout>, <kbd>, <toc>, <sub>, <sup>, <mark>, <del>, <date>, <span>, <column>, <column_group>, <file>, <audio>, <video> in <Selection> unless the user explicitly requests this change.\n- CRITICAL: Distinguish between INSTRUCTIONS and QUESTIONS. Instructions typically ask you to modify or add content. Questions ask for information or clarification.\n- CRITICAL: when asked to write in markdown, do not start with \\`\\`\\`markdown.\n- CRITICAL: When writing the column, such line breaks and indentation must be preserved.\n<column_group>\n <column>\n 1\n </column>\n <column>\n 2\n </column>\n <column>\n 3\n </column>\n</column_group>\n`;\n\nconst systemDefault = `\\\n${systemCommon}\n- <Block> is the current block of text the user is working on.\n- Ensure your output can seamlessly fit into the existing <Block> structure.\n\n<Block>\n{block}\n</Block>\n`;\n\nconst systemSelecting = `\\\n${systemCommon}\n- <Block> is the block of text containing the user's selection, providing context.\n- Ensure your output can seamlessly fit into the existing <Block> structure.\n- <Selection> is the specific text the user has selected in the block and wants to modify or ask about.\n- Consider the context provided by <Block>, but only modify <Selection>. Your response should be a direct replacement for <Selection>.\n<Block>\n{block}\n</Block>\n<Selection>\n{selection}\n</Selection>\n`;\n\nconst systemBlockSelecting = `\\\n${systemCommon}\n- <Selection> represents the full blocks of text the user has selected and wants to modify or ask about.\n- Your response should be a direct replacement for the entire <Selection>.\n- Maintain the overall structure and formatting of the selected blocks, unless explicitly instructed otherwise.\n- CRITICAL: Provide only the content to replace <Selection>. Do not add additional blocks or change the block structure unless specifically requested.\n<Selection>\n{block}\n</Selection>\n`;\n\nconst userDefault = `<Reminder>\nCRITICAL: NEVER write <Block>.\n</Reminder>\n{prompt}`;\nconst userSelecting = `<Reminder>\nIf this is a question, provide a helpful and concise answer about <Selection>.\nIf this is an instruction, provide ONLY the text to replace <Selection>. No explanations.\nEnsure it fits seamlessly within <Block>. If <Block> is empty, write ONE random sentence.\nNEVER write <Block> or <Selection>.\n</Reminder>\n{prompt} about <Selection>`;\n\nconst userBlockSelecting = `<Reminder>\nIf this is a question, provide a helpful and concise answer about <Selection>.\nIf this is an instruction, provide ONLY the content to replace the entire <Selection>. No explanations.\nMaintain the overall structure unless instructed otherwise.\nNEVER write <Block> or <Selection>.\n</Reminder>\n{prompt} about <Selection>`;\n\nexport const PROMPT_TEMPLATES = {\n systemBlockSelecting,\n systemDefault,\n systemSelecting,\n userBlockSelecting,\n userDefault,\n userSelecting,\n};\n",
"type": "registry:component"
}
]
Expand Down
2 changes: 1 addition & 1 deletion apps/www/public/r/components-changelog-docs.json

Large diffs are not rendered by default.

Loading